This commit is contained in:
Edwin Jakobs
2019-01-26 21:24:58 +01:00
parent a3385abee0
commit 149a7fd183
42 changed files with 2758 additions and 2291 deletions

View File

@@ -1,42 +1,42 @@
# ORX (OPENRNDR EXTRA)
[![](https://jitpack.io/v/openrndr/orx.svg)](https://jitpack.io/#openrndr/orx)
A growing library of assorted data structures, algorithms and utilities.
- [`orx-camera`](orx-camera/README.md), 3d camera and controls
- [`orx-compositor`](orx-compositor/README.md), a simple toolkit to make composite (layered) images
- [`orx-filter-extension`](orx-filter-extension/README.md), Program extension method that provides Filter based `extend()`
- [`orx-integral-image`](orx-integral-image/README.md), a CPU-based implementation for integral images (summed area tables)
- `orx-jumpflood`, a filter/shader based implementation of the jump flood algorithm for finding fast approximate (directional) distance fields
- `orx-kdtree`, a kd-tree implementation for fast nearest point searches
- [`orx-mesh-generators`](orx-mesh-generators/README.md), triangular mesh generators
- [`orx-noise`](orx-noise/README.md), library for random number generation and noise
- [`orx-no-clear`](orx-no-clear/README.md), a simple extension that provides drawing without clearing the background
- [`orx-obj-loader`](orx-obj-loader/README.md), simple Wavefront .obj mesh loader
## Usage
ORX 0.0.19 is built against OPENRNDR 0.3.32, make sure you use this version in your project. Because OPENRNDR's API is pre 1.0 it tends to change from time to time.
The easiest way to add ORX to your project is through the use of Jitpack. [Jitpack](http://jitpack.io) is a service that pulls Gradle based libraries from Github, builds them and serves the jar files.
To setup Jitpack support in your project all you have to do is add the Jitpack repository to your `repositories {}`. It is advised to have the jitpack repository as the last entry.
```
repositories {
maven { url 'https://jitpack.io' }
}
```
You can then add any of the ORX artefacts to your `dependencies {}`:
```
dependencies {
compile 'com.github.openrndr.orx:<orx-artifact>:v0.0.19'
}
```
For example if you want to use the `orx-no-clear` artifact one would use:
```
dependencies {
compile 'com.github.openrndr.orx:orx-no-clear:v0.0.19'
}
```
# ORX (OPENRNDR EXTRA)
[![](https://jitpack.io/v/openrndr/orx.svg)](https://jitpack.io/#openrndr/orx)
A growing library of assorted data structures, algorithms and utilities.
- [`orx-camera`](orx-camera/README.md), 3d camera and controls
- [`orx-compositor`](orx-compositor/README.md), a simple toolkit to make composite (layered) images
- [`orx-filter-extension`](orx-filter-extension/README.md), Program extension method that provides Filter based `extend()`
- [`orx-integral-image`](orx-integral-image/README.md), a CPU-based implementation for integral images (summed area tables)
- `orx-jumpflood`, a filter/shader based implementation of the jump flood algorithm for finding fast approximate (directional) distance fields
- `orx-kdtree`, a kd-tree implementation for fast nearest point searches
- [`orx-mesh-generators`](orx-mesh-generators/README.md), triangular mesh generators
- [`orx-noise`](orx-noise/README.md), library for random number generation and noise
- [`orx-no-clear`](orx-no-clear/README.md), a simple extension that provides drawing without clearing the background
- [`orx-obj-loader`](orx-obj-loader/README.md), simple Wavefront .obj mesh loader
## Usage
ORX 0.0.19 is built against OPENRNDR 0.3.32, make sure you use this version in your project. Because OPENRNDR's API is pre 1.0 it tends to change from time to time.
The easiest way to add ORX to your project is through the use of Jitpack. [Jitpack](http://jitpack.io) is a service that pulls Gradle based libraries from Github, builds them and serves the jar files.
To setup Jitpack support in your project all you have to do is add the Jitpack repository to your `repositories {}`. It is advised to have the jitpack repository as the last entry.
```
repositories {
maven { url 'https://jitpack.io' }
}
```
You can then add any of the ORX artefacts to your `dependencies {}`:
```
dependencies {
compile 'com.github.openrndr.orx:<orx-artifact>:v0.0.19'
}
```
For example if you want to use the `orx-no-clear` artifact one would use:
```
dependencies {
compile 'com.github.openrndr.orx:orx-no-clear:v0.0.19'
}
```

24
ShaderError.txt Normal file
View File

@@ -0,0 +1,24 @@
#version 330 core
uniform sampler2D tex0;
in vec2 v_texCoord0;
out vec4 o_color;
void main() {
vec2 step = 1.0 / textureSize(tex0, 0);
float ref = step(0.5 , texture(tex0, v_texCoord0).r);
vec4 outc = vec4(-1.0, -1.0, 0.0, 1.0);
float contour = 0.0;
for (y = -1; y <= 1; ++y) {
for (x = -1; x <= 1; ++x) {
float smp = step(0.5, texture(tex0, v_texCoord0 + vec2(x,y) * step).r);
if (smp != ref) {
contour = 1.0;
}
}
}
o_color = vec4(contour, contour, contour, 1.0);
}

View File

@@ -1,67 +1,67 @@
plugins {
id 'org.jetbrains.kotlin.jvm' version '1.3.10'
}
allprojects {
group 'org.openrndr.extra'
version '0.0.19'
}
repositories {
mavenLocal()
mavenCentral()
}
ext {
openrndrVersion = "0.3.32-rc1"
}
subprojects {
apply plugin: 'kotlin'
apply plugin: 'maven'
apply plugin: 'maven-publish'
repositories {
mavenLocal()
mavenCentral()
maven {
url = "https://dl.bintray.com/openrndr/openrndr"
}
}
dependencies {
compile "org.openrndr:openrndr-core:$openrndrVersion"
compile "org.openrndr:openrndr-filter:$openrndrVersion"
compile "org.openrndr:openrndr-shape:$openrndrVersion"
compile group: 'org.jetbrains.kotlinx', name: 'kotlinx-coroutines-core', version: '1.0.1'
}
publishing {
publications {
mavenJava(MavenPublication) {
from components.java
artifact sourceJar
}
}
}
task sourceJar(type: Jar) {
classifier = 'sources'
from sourceSets.main.kotlin
}
}
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
}
compileKotlin {
kotlinOptions.jvmTarget = "1.8"
}
compileTestKotlin {
kotlinOptions.jvmTarget = "1.8"
plugins {
id 'org.jetbrains.kotlin.jvm' version '1.3.10'
}
allprojects {
group 'org.openrndr.extra'
version '0.0.20'
}
repositories {
mavenLocal()
mavenCentral()
}
ext {
openrndrVersion = "0.3.32-rc1"
}
subprojects {
apply plugin: 'kotlin'
apply plugin: 'maven'
apply plugin: 'maven-publish'
repositories {
mavenLocal()
mavenCentral()
maven {
url = "https://dl.bintray.com/openrndr/openrndr"
}
}
dependencies {
compile "org.openrndr:openrndr-core:$openrndrVersion"
compile "org.openrndr:openrndr-filter:$openrndrVersion"
compile "org.openrndr:openrndr-shape:$openrndrVersion"
compile group: 'org.jetbrains.kotlinx', name: 'kotlinx-coroutines-core', version: '1.0.1'
}
publishing {
publications {
mavenJava(MavenPublication) {
from components.java
artifact sourceJar
}
}
}
task sourceJar(type: Jar) {
classifier = 'sources'
from sourceSets.main.kotlin
}
}
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
}
compileKotlin {
kotlinOptions.jvmTarget = "1.8"
}
compileTestKotlin {
kotlinOptions.jvmTarget = "1.8"
}

View File

@@ -1,3 +1,3 @@
# orx-camera
# orx-camera
3D camera and controls for OPENRNDR. This supersedes the to be deprecated functionality in OPENRNDR.

View File

@@ -1,74 +1,74 @@
package org.openrndr.extras.camera
import org.openrndr.Extension
import org.openrndr.Program
import org.openrndr.color.ColorRGBa
import org.openrndr.draw.*
import org.openrndr.math.Matrix44
import org.openrndr.math.Vector3
@Suppress("unused")
class Debug3D(eye: Vector3 = Vector3(0.0, 0.0, 10.0), lookAt: Vector3 = Vector3.ZERO, private val fov: Double = 90.0) : Extension {
override var enabled: Boolean = true
var showGrid = false
val orbitalCamera = OrbitalCamera(eye, lookAt)
private val orbitalControls = OrbitalControls(orbitalCamera)
private var lastSeconds: Double = -1.0
private val grid = vertexBuffer(
vertexFormat {
position(3)
}
, 4 * 21).apply {
put {
for (x in -10..10) {
write(Vector3(x.toDouble(), 0.0, -10.0))
write(Vector3(x.toDouble(), 0.0, 10.0))
write(Vector3(-10.0, 0.0, x.toDouble()))
write(Vector3(10.0, 0.0, x.toDouble()))
}
}
}
override fun beforeDraw(drawer: Drawer, program: Program) {
if (lastSeconds == -1.0) lastSeconds = program.seconds
val delta = program.seconds - lastSeconds
lastSeconds = program.seconds
orbitalCamera.update(delta)
drawer.background(ColorRGBa.BLACK)
drawer.perspective(fov, program.window.size.x / program.window.size.y, 0.1, 1000.0)
drawer.view = orbitalCamera.viewMatrix()
if (showGrid) {
drawer.isolated {
drawer.fill = ColorRGBa.WHITE
drawer.stroke = ColorRGBa.WHITE
drawer.vertexBuffer(grid, DrawPrimitive.LINES)
// Axis cross
drawer.fill = ColorRGBa.RED
drawer.lineSegment(Vector3.ZERO, Vector3.UNIT_X)
drawer.fill = ColorRGBa.GREEN
drawer.lineSegment(Vector3.ZERO, Vector3.UNIT_Y)
drawer.fill = ColorRGBa.BLUE
drawer.lineSegment(Vector3.ZERO, Vector3.UNIT_Z)
}
}
}
override fun afterDraw(drawer: Drawer, program: Program) {
drawer.isolated {
drawer.view = Matrix44.IDENTITY
drawer.ortho()
}
}
override fun setup(program: Program) {
orbitalControls.setup(program)
}
package org.openrndr.extras.camera
import org.openrndr.Extension
import org.openrndr.Program
import org.openrndr.color.ColorRGBa
import org.openrndr.draw.*
import org.openrndr.math.Matrix44
import org.openrndr.math.Vector3
@Suppress("unused")
class Debug3D(eye: Vector3 = Vector3(0.0, 0.0, 10.0), lookAt: Vector3 = Vector3.ZERO, fov:Double = 90.0) : Extension {
override var enabled: Boolean = true
var showGrid = false
val orbitalCamera = OrbitalCamera(eye, lookAt, 90.0)
private val orbitalControls = OrbitalControls(orbitalCamera)
private var lastSeconds: Double = -1.0
private val grid = vertexBuffer(
vertexFormat {
position(3)
}
, 4 * 21).apply {
put {
for (x in -10..10) {
write(Vector3(x.toDouble(), 0.0, -10.0))
write(Vector3(x.toDouble(), 0.0, 10.0))
write(Vector3(-10.0, 0.0, x.toDouble()))
write(Vector3(10.0, 0.0, x.toDouble()))
}
}
}
override fun beforeDraw(drawer: Drawer, program: Program) {
if (lastSeconds == -1.0) lastSeconds = program.seconds
val delta = program.seconds - lastSeconds
lastSeconds = program.seconds
orbitalCamera.update(delta)
drawer.background(ColorRGBa.BLACK)
drawer.perspective(orbitalCamera.fov, program.window.size.x / program.window.size.y, 0.1, 1000.0)
drawer.view = orbitalCamera.viewMatrix()
if (showGrid) {
drawer.isolated {
drawer.fill = ColorRGBa.WHITE
drawer.stroke = ColorRGBa.WHITE
drawer.vertexBuffer(grid, DrawPrimitive.LINES)
// Axis cross
drawer.fill = ColorRGBa.RED
drawer.lineSegment(Vector3.ZERO, Vector3.UNIT_X)
drawer.fill = ColorRGBa.GREEN
drawer.lineSegment(Vector3.ZERO, Vector3.UNIT_Y)
drawer.fill = ColorRGBa.BLUE
drawer.lineSegment(Vector3.ZERO, Vector3.UNIT_Z)
}
}
}
override fun afterDraw(drawer: Drawer, program: Program) {
drawer.isolated {
drawer.view = Matrix44.IDENTITY
drawer.ortho()
}
}
override fun setup(program: Program) {
orbitalControls.setup(program)
}
}

View File

@@ -1,120 +1,139 @@
package org.openrndr.extras.camera
import org.openrndr.*
import org.openrndr.color.ColorRGBa
import org.openrndr.draw.DrawPrimitive
import org.openrndr.draw.Drawer
import org.openrndr.draw.isolated
import org.openrndr.draw.vertexBuffer
import org.openrndr.draw.vertexFormat
import org.openrndr.math.Matrix44
import org.openrndr.math.Spherical
import org.openrndr.math.Vector2
import org.openrndr.math.Vector3
import org.openrndr.math.transforms.lookAt as lookAt_
class OrbitalCamera(eye: Vector3, lookAt: Vector3) {
// current position in spherical coordinates
var spherical = Spherical.fromVector(eye)
private set
var lookAt = lookAt
private set
private var sphericalEnd = Spherical.fromVector(eye)
private var lookAtEnd = lookAt.copy()
private var dirty: Boolean = true
var dampingFactor = 0.05
var zoomSpeed = 1.0
fun rotate(rotX: Double, rotY: Double) {
sphericalEnd += Spherical(0.0, rotX, rotY)
sphericalEnd = sphericalEnd.makeSafe()
dirty = true
}
fun rotateTo(rotX: Double, rotY: Double) {
sphericalEnd = sphericalEnd.copy(theta = rotX, phi = rotY)
sphericalEnd = sphericalEnd.makeSafe()
dirty = true
}
fun rotateTo(eye: Vector3) {
sphericalEnd = Spherical.fromVector(eye)
sphericalEnd = sphericalEnd.makeSafe()
dirty = true
}
fun dollyIn() {
val zoomScale = Math.pow(0.95, zoomSpeed)
dolly(sphericalEnd.radius * zoomScale - sphericalEnd.radius)
}
fun dollyOut() {
val zoomScale = Math.pow(0.95, zoomSpeed)
dolly(sphericalEnd.radius / zoomScale - sphericalEnd.radius)
}
private fun dolly(distance: Double) {
sphericalEnd += Spherical(distance, 0.0, 0.0)
dirty = true
}
fun pan(x: Double, y: Double, z: Double) {
val view = viewMatrix()
val xColumn = Vector3(view.c0r0, view.c1r0, view.c2r0) * x
val yColumn = Vector3(view.c0r1, view.c1r1, view.c2r1) * y
val zColumn = Vector3(view.c0r2, view.c1r2, view.c2r2) * z
lookAtEnd += xColumn + yColumn + zColumn
dirty = true
}
fun panTo(target : Vector3) {
lookAtEnd = target
dirty = true
}
fun dollyTo(distance: Double) {
sphericalEnd = sphericalEnd.copy(radius = distance )
dirty = true
}
fun update(timeDelta: Double) {
if (!dirty) return
dirty = false
val dampingFactor = dampingFactor * timeDelta / 0.0060
val sphericalDelta = sphericalEnd - spherical
val lookAtDelta = lookAtEnd - lookAt
if (
Math.abs(sphericalEnd.radius) > EPSILON ||
Math.abs(sphericalEnd.theta) > EPSILON ||
Math.abs(sphericalEnd.phi) > EPSILON ||
Math.abs(lookAtDelta.x) > EPSILON ||
Math.abs(lookAtDelta.y) > EPSILON ||
Math.abs(lookAtDelta.z) > EPSILON
) {
spherical += (sphericalDelta * dampingFactor)
lookAt += (lookAtDelta * dampingFactor)
dirty = true
} else {
spherical = sphericalEnd.copy()
lookAt = lookAtEnd.copy()
}
spherical = spherical.makeSafe()
}
fun viewMatrix(): Matrix44 {
return lookAt_(Vector3.fromSpherical(spherical) + lookAt, lookAt, Vector3.UNIT_Y)
}
companion object {
private const val EPSILON = 0.000001
}
}
package org.openrndr.extras.camera
import org.openrndr.*
import org.openrndr.color.ColorRGBa
import org.openrndr.draw.DrawPrimitive
import org.openrndr.draw.Drawer
import org.openrndr.draw.isolated
import org.openrndr.draw.vertexBuffer
import org.openrndr.draw.vertexFormat
import org.openrndr.math.Matrix44
import org.openrndr.math.Spherical
import org.openrndr.math.Vector2
import org.openrndr.math.Vector3
import org.openrndr.math.transforms.lookAt as lookAt_
class OrbitalCamera(eye: Vector3, lookAt: Vector3, var fov:Double) {
// current position in spherical coordinates
var spherical = Spherical.fromVector(eye)
private set
var lookAt = lookAt
private set
private var sphericalEnd = Spherical.fromVector(eye)
private var lookAtEnd = lookAt.copy()
private var dirty: Boolean = true
var fovEnd = fov
var dampingFactor = 0.05
var zoomSpeed = 1.0
fun setView(lookAt: Vector3, spherical: Spherical, fov:Double) {
this.lookAt = lookAt
this.lookAtEnd = lookAt
this.spherical = spherical
this.sphericalEnd = spherical
this.fov = fov
this.fovEnd= fov
}
fun rotate(rotX: Double, rotY: Double) {
sphericalEnd += Spherical(0.0, rotX, rotY)
sphericalEnd = sphericalEnd.makeSafe()
dirty = true
}
fun rotateTo(rotX: Double, rotY: Double) {
sphericalEnd = sphericalEnd.copy(theta = rotX, phi = rotY)
sphericalEnd = sphericalEnd.makeSafe()
dirty = true
}
fun rotateTo(eye: Vector3) {
sphericalEnd = Spherical.fromVector(eye)
sphericalEnd = sphericalEnd.makeSafe()
dirty = true
}
fun dollyIn() {
val zoomScale = Math.pow(0.95, zoomSpeed)
dolly(sphericalEnd.radius * zoomScale - sphericalEnd.radius)
}
fun dollyOut() {
val zoomScale = Math.pow(0.95, zoomSpeed)
dolly(sphericalEnd.radius / zoomScale - sphericalEnd.radius)
}
fun dolly(distance: Double) {
sphericalEnd += Spherical(distance, 0.0, 0.0)
dirty = true
}
fun pan(x: Double, y: Double, z: Double) {
val view = viewMatrix()
val xColumn = Vector3(view.c0r0, view.c1r0, view.c2r0) * x
val yColumn = Vector3(view.c0r1, view.c1r1, view.c2r1) * y
val zColumn = Vector3(view.c0r2, view.c1r2, view.c2r2) * z
lookAtEnd += xColumn + yColumn + zColumn
dirty = true
}
fun panTo(target : Vector3) {
lookAtEnd = target
dirty = true
}
fun dollyTo(distance: Double) {
sphericalEnd = sphericalEnd.copy(radius = distance )
dirty = true
}
fun zoom(degrees: Double) {
fovEnd += degrees
}
fun update(timeDelta: Double) {
if (!dirty) return
dirty = false
val dampingFactor = dampingFactor * timeDelta / 0.0060
val sphericalDelta = sphericalEnd - spherical
val lookAtDelta = lookAtEnd - lookAt
val fovDelta = fovEnd - fov
if (
Math.abs(sphericalEnd.radius) > EPSILON ||
Math.abs(sphericalEnd.theta) > EPSILON ||
Math.abs(sphericalEnd.phi) > EPSILON ||
Math.abs(lookAtDelta.x) > EPSILON ||
Math.abs(lookAtDelta.y) > EPSILON ||
Math.abs(lookAtDelta.z) > EPSILON ||
Math.abs(fovDelta) > EPSILON
) {
fov += (fovDelta * dampingFactor)
spherical += (sphericalDelta * dampingFactor)
spherical = spherical.makeSafe()
lookAt += (lookAtDelta * dampingFactor)
dirty = true
} else {
spherical = sphericalEnd.copy()
lookAt = lookAtEnd.copy()
}
spherical = spherical.makeSafe()
}
fun viewMatrix(): Matrix44 {
return lookAt_(Vector3.fromSpherical(spherical) + lookAt, lookAt, Vector3.UNIT_Y)
}
companion object {
private const val EPSILON = 0.000001
}
}

View File

@@ -1,100 +1,128 @@
package org.openrndr.extras.camera
import org.openrndr.*
import org.openrndr.math.Vector2
import org.openrndr.math.Vector3
class OrbitalControls(val orbitalCamera: OrbitalCamera) {
enum class STATE {
NONE,
ROTATE,
PAN,
}
private var state = STATE.NONE
var fov = 90.0
private lateinit var program: Program
private lateinit var lastMousePosition: Vector2
private fun mouseScrolled(event: MouseEvent) {
if (Math.abs(event.rotation.x) > 0.1) return
when {
event.rotation.y > 0 -> orbitalCamera.dollyIn()
event.rotation.y < 0 -> orbitalCamera.dollyOut()
}
}
private fun mouseMoved(event: MouseEvent) {
if (state == STATE.NONE) return
val delta = lastMousePosition - event.position
lastMousePosition = event.position
if (state == STATE.PAN) {
val offset = Vector3.fromSpherical(orbitalCamera.spherical) - orbitalCamera.lookAt
// half of the fov is center to top of screen
val targetDistance = offset.length * Math.tan((fov / 2) * Math.PI / 180)
val panX = (2 * delta.x * targetDistance / program.window.size.x)
val panY = (2 * delta.y * targetDistance / program.window.size.y)
orbitalCamera.pan(panX, -panY, 0.0)
} else {
val rotX = 2 * Math.PI * delta.x / program.window.size.x
val rotY = 2 * Math.PI * delta.y / program.window.size.y
orbitalCamera.rotate(rotX, rotY)
}
}
private fun mouseButtonDown(event: MouseEvent) {
val previousState = state
when (event.button) {
MouseButton.LEFT -> {
state = STATE.ROTATE
}
MouseButton.RIGHT -> {
state = STATE.PAN
}
MouseButton.CENTER -> {
}
MouseButton.NONE -> {
}
}
if (previousState == STATE.NONE) {
lastMousePosition = event.position
}
}
fun keyPressed(keyEvent: KeyEvent) {
if (keyEvent.key == KEY_ARROW_RIGHT) {
orbitalCamera.pan(1.0, 0.0, 0.0)
}
if (keyEvent.key == KEY_ARROW_LEFT) {
orbitalCamera.pan(-1.0, 0.0, 0.0)
}
if (keyEvent.key == KEY_ARROW_UP) {
orbitalCamera.pan(0.0, 1.0, 0.0)
}
if (keyEvent.key == KEY_ARROW_DOWN) {
orbitalCamera.pan(0.0, -1.0, 0.0)
}
}
fun setup(program: Program) {
this.program = program
program.mouse.moved.listen { mouseMoved(it) }
program.mouse.buttonDown.listen { mouseButtonDown(it) }
program.mouse.buttonUp.listen { state = STATE.NONE }
program.mouse.scrolled.listen { mouseScrolled(it) }
program.keyboard.keyDown.listen { keyPressed(it) }
program.keyboard.keyRepeat.listen{ keyPressed(it) }
}
}
package org.openrndr.extras.camera
import org.openrndr.*
import org.openrndr.math.Vector2
import org.openrndr.math.Vector3
class OrbitalControls(val orbitalCamera: OrbitalCamera) {
enum class STATE {
NONE,
ROTATE,
PAN,
}
private var state = STATE.NONE
var fov = 90.0
private lateinit var program: Program
private lateinit var lastMousePosition: Vector2
private fun mouseScrolled(event: MouseEvent) {
if (Math.abs(event.rotation.x) > 0.1) return
when {
event.rotation.y > 0 -> orbitalCamera.dollyIn()
event.rotation.y < 0 -> orbitalCamera.dollyOut()
}
}
private fun mouseMoved(event: MouseEvent) {
if (state == STATE.NONE) return
val delta = lastMousePosition - event.position
lastMousePosition = event.position
if (state == STATE.PAN) {
val offset = Vector3.fromSpherical(orbitalCamera.spherical) - orbitalCamera.lookAt
// half of the fov is center to top of screen
val targetDistance = offset.length * Math.tan((fov / 2) * Math.PI / 180)
val panX = (2 * delta.x * targetDistance / program.window.size.x)
val panY = (2 * delta.y * targetDistance / program.window.size.y)
orbitalCamera.pan(panX, -panY, 0.0)
} else {
val rotX = 2 * Math.PI * delta.x / program.window.size.x
val rotY = 2 * Math.PI * delta.y / program.window.size.y
orbitalCamera.rotate(rotX, rotY)
}
}
private fun mouseButtonDown(event: MouseEvent) {
val previousState = state
when (event.button) {
MouseButton.LEFT -> {
state = STATE.ROTATE
}
MouseButton.RIGHT -> {
state = STATE.PAN
}
MouseButton.CENTER -> {
}
MouseButton.NONE -> {
}
}
if (previousState == STATE.NONE) {
lastMousePosition = event.position
}
}
fun keyPressed(keyEvent: KeyEvent) {
if (keyEvent.key == KEY_ARROW_RIGHT) {
orbitalCamera.pan(1.0, 0.0, 0.0)
}
if (keyEvent.key == KEY_ARROW_LEFT) {
orbitalCamera.pan(-1.0, 0.0, 0.0)
}
if (keyEvent.key == KEY_ARROW_UP) {
orbitalCamera.pan(0.0, 1.0, 0.0)
}
if (keyEvent.key == KEY_ARROW_DOWN) {
orbitalCamera.pan(0.0, -1.0, 0.0)
}
if (keyEvent.name == "q") {
orbitalCamera.pan(0.0, -1.0, 0.0)
}
if (keyEvent.name == "e") {
orbitalCamera.pan(0.0, 1.0, 0.0)
}
if (keyEvent.name == "w") {
orbitalCamera.pan(0.0, 0.0, -1.0)
}
if (keyEvent.name == "s") {
orbitalCamera.pan(0.0, 0.0, 1.0)
}
if (keyEvent.name == "a") {
orbitalCamera.pan(-1.0, 0.0, 0.0)
}
if (keyEvent.name == "d") {
orbitalCamera.pan(1.0, 0.0, 0.0)
}
if (keyEvent.key == KEY_PAGE_UP) {
orbitalCamera.zoom(1.0)
}
if (keyEvent.key == KEY_PAGE_DOWN) {
orbitalCamera.zoom(-1.0)
}
}
fun setup(program: Program) {
this.program = program
program.mouse.moved.listen { mouseMoved(it) }
program.mouse.buttonDown.listen { mouseButtonDown(it) }
program.mouse.buttonUp.listen { state = STATE.NONE }
program.mouse.scrolled.listen { mouseScrolled(it) }
program.keyboard.keyDown.listen { keyPressed(it) }
program.keyboard.keyRepeat.listen{ keyPressed(it) }
}
}

View File

@@ -1,138 +1,138 @@
package org.openrndr.extra.compositor
import org.openrndr.color.ColorRGBa
import org.openrndr.draw.*
import org.openrndr.math.Matrix44
private val postBufferCache = mutableListOf<ColorBuffer>()
fun RenderTarget.deepDestroy() {
val cbcopy = colorBuffers.map { it}
val dbcopy = depthBuffer
detachDepthBuffer()
detachColorBuffers()
cbcopy.forEach {
it.destroy()
}
dbcopy?.destroy()
destroy()
}
/**
* A single layer representation
*/
class Layer internal constructor() {
var drawFunc: () -> Unit = {}
val children: MutableList<Layer> = mutableListOf()
var blendFilter: Pair<Filter, Filter.() -> Unit>? = null
val postFilters: MutableList<Pair<Filter, Filter.() -> Unit>> = mutableListOf()
private var layerTarget:RenderTarget? = null
/**
* draw the layer
*/
fun draw(drawer: Drawer) {
val rt = RenderTarget.active
val llt = layerTarget
if (llt == null || (llt.width != rt.width || llt.height != rt.height)) {
layerTarget?.deepDestroy()
layerTarget = renderTarget(rt.width, rt.height) {
colorBuffer()
depthBuffer()
}
}
layerTarget?.let { target ->
drawer.isolatedWithTarget(target) {
drawer.background(ColorRGBa.TRANSPARENT)
drawFunc()
children.forEach {
it.draw(drawer)
}
}
if (postFilters.size > 0) {
val sizeMismatch = if (postBufferCache.isNotEmpty()) {
postBufferCache[0].width != rt.width || postBufferCache[0].height != rt.height
} else {
false
}
if (sizeMismatch) {
postBufferCache.forEach { it.destroy() }
postBufferCache.clear()
}
if (postBufferCache.isEmpty()) {
postBufferCache += colorBuffer(rt.width, rt.height)
postBufferCache += colorBuffer(rt.width, rt.height)
}
}
val layerPost = postFilters.let { filters ->
val targets = postBufferCache
val result = filters.foldIndexed(target.colorBuffer(0)) { i, source, filter ->
val target = targets[i % targets.size]
filter.first.apply(filter.second)
filter.first.apply(source, target)
target
}
result
}
val lblend = blendFilter
if (lblend == null) {
drawer.isolatedWithTarget(rt) {
//drawer.ortho(rt)
drawer.ortho()
drawer.view = Matrix44.IDENTITY
drawer.model = Matrix44.IDENTITY
drawer.image(layerPost, layerPost.bounds, drawer.bounds)
}
} else {
lblend.first.apply(lblend.second)
lblend.first.apply(arrayOf(rt.colorBuffer(0), layerPost), rt.colorBuffer(0))
}
}
}
}
/**
* create a layer within the composition
*/
fun Layer.layer(function: Layer.() -> Unit) {
children.add(Layer().apply { function() })
}
/**
* set the draw contents of the layer
*/
fun Layer.draw(function: () -> Unit) {
drawFunc = function
}
/**
* add a post-processing filter to the layer
*/
fun <F : Filter> Layer.post(filter: F, configure: F.() -> Unit = {}) {
postFilters.add(Pair(filter as Filter, configure as Filter.() -> Unit))
}
/**
* add a blend filter to the layer
*/
fun <F : Filter> Layer.blend(filter: F, configure: F.() -> Unit = {}) {
blendFilter = Pair(filter as Filter, configure as Filter.() -> Unit)
}
/**
* create a layered composition
*/
fun compose(function: Layer.() -> Unit): Layer {
val root = Layer()
root.function()
return root
package org.openrndr.extra.compositor
import org.openrndr.color.ColorRGBa
import org.openrndr.draw.*
import org.openrndr.math.Matrix44
private val postBufferCache = mutableListOf<ColorBuffer>()
fun RenderTarget.deepDestroy() {
val cbcopy = colorBuffers.map { it}
val dbcopy = depthBuffer
detachDepthBuffer()
detachColorBuffers()
cbcopy.forEach {
it.destroy()
}
dbcopy?.destroy()
destroy()
}
/**
* A single layer representation
*/
class Layer internal constructor() {
var drawFunc: () -> Unit = {}
val children: MutableList<Layer> = mutableListOf()
var blendFilter: Pair<Filter, Filter.() -> Unit>? = null
val postFilters: MutableList<Pair<Filter, Filter.() -> Unit>> = mutableListOf()
private var layerTarget:RenderTarget? = null
/**
* draw the layer
*/
fun draw(drawer: Drawer) {
val rt = RenderTarget.active
val llt = layerTarget
if (llt == null || (llt.width != rt.width || llt.height != rt.height)) {
layerTarget?.deepDestroy()
layerTarget = renderTarget(rt.width, rt.height) {
colorBuffer()
depthBuffer()
}
}
layerTarget?.let { target ->
drawer.isolatedWithTarget(target) {
drawer.background(ColorRGBa.TRANSPARENT)
drawFunc()
children.forEach {
it.draw(drawer)
}
}
if (postFilters.size > 0) {
val sizeMismatch = if (postBufferCache.isNotEmpty()) {
postBufferCache[0].width != rt.width || postBufferCache[0].height != rt.height
} else {
false
}
if (sizeMismatch) {
postBufferCache.forEach { it.destroy() }
postBufferCache.clear()
}
if (postBufferCache.isEmpty()) {
postBufferCache += colorBuffer(rt.width, rt.height)
postBufferCache += colorBuffer(rt.width, rt.height)
}
}
val layerPost = postFilters.let { filters ->
val targets = postBufferCache
val result = filters.foldIndexed(target.colorBuffer(0)) { i, source, filter ->
val target = targets[i % targets.size]
filter.first.apply(filter.second)
filter.first.apply(source, target)
target
}
result
}
val lblend = blendFilter
if (lblend == null) {
drawer.isolatedWithTarget(rt) {
//drawer.ortho(rt)
drawer.ortho()
drawer.view = Matrix44.IDENTITY
drawer.model = Matrix44.IDENTITY
drawer.image(layerPost, layerPost.bounds, drawer.bounds)
}
} else {
lblend.first.apply(lblend.second)
lblend.first.apply(arrayOf(rt.colorBuffer(0), layerPost), rt.colorBuffer(0))
}
}
}
}
/**
* create a layer within the composition
*/
fun Layer.layer(function: Layer.() -> Unit) {
children.add(Layer().apply { function() })
}
/**
* set the draw contents of the layer
*/
fun Layer.draw(function: () -> Unit) {
drawFunc = function
}
/**
* add a post-processing filter to the layer
*/
fun <F : Filter> Layer.post(filter: F, configure: F.() -> Unit = {}) {
postFilters.add(Pair(filter as Filter, configure as Filter.() -> Unit))
}
/**
* add a blend filter to the layer
*/
fun <F : Filter> Layer.blend(filter: F, configure: F.() -> Unit = {}) {
blendFilter = Pair(filter as Filter, configure as Filter.() -> Unit)
}
/**
* create a layered composition
*/
fun compose(function: Layer.() -> Unit): Layer {
val root = Layer()
root.function()
return root
}

31
orx-examples/build.gradle Normal file
View File

@@ -0,0 +1,31 @@
plugins {
id 'org.jetbrains.kotlin.jvm' version '1.2.71'
}
group 'org.openrndr.extra'
version '0.0.1'
repositories {
mavenCentral()
maven {
url "https://dl.bintray.com/openrndr/openrndr"
}
}
ext.openrndrVersion = "0.3.26"
ext.orxVersion = "0.0.5"
dependencies {
compile "org.openrndr.extra:orx:0.0.1"
compile "org.openrndr.extra:orx-jumpflood:$orxVersion"
compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
compile "org.openrndr:openrndr-core:$openrndrVersion"
runtime "org.openrndr:openrndr-gl3:$openrndrVersion"
runtime "org.openrndr:openrndr-gl3-natives-windows:$openrndrVersion"
}
compileKotlin {
kotlinOptions.jvmTarget = "1.8"
}
compileTestKotlin {
kotlinOptions.jvmTarget = "1.8"
}

Binary file not shown.

View File

@@ -0,0 +1,6 @@
#Fri Oct 05 14:22:23 CEST 2018
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.8-all.zip

172
orx-examples/gradlew vendored Normal file
View File

@@ -0,0 +1,172 @@
#!/usr/bin/env sh
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"

84
orx-examples/gradlew.bat vendored Normal file
View File

@@ -0,0 +1,84 @@
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

View File

@@ -0,0 +1,2 @@
rootProject.name = 'orx-examples'

View File

@@ -0,0 +1,56 @@
import org.openrndr.Program
import org.openrndr.application
import org.openrndr.color.ColorRGBa
import org.openrndr.configuration
import org.openrndr.draw.ColorType
import org.openrndr.draw.colorBuffer
import org.openrndr.extra.jumpfill.EncodePoints
import org.openrndr.extra.jumpfill.JumpFlood
class JumpFill001 : Program() {
var drawFunc = {}
override fun setup() {
val encodePoints = EncodePoints()
val jumpFill = JumpFlood()
val input = colorBuffer(512, 1024)
val coordinates =
listOf(colorBuffer(input.width, input.height, type = ColorType.FLOAT32),
colorBuffer(input.width, input.height, type = ColorType.FLOAT32))
for (i in 0 until 100) {
input.shadow[(Math.random() * input.width).toInt(), (Math.random() * input.height).toInt()] =
ColorRGBa.WHITE
}
input.shadow.upload()
drawFunc = {
encodePoints.apply(input, coordinates[0])
drawer.image(coordinates[0])
jumpFill.maxSteps = 10
for (i in 0 until 10) {
jumpFill.step = i
jumpFill.apply(coordinates[i % 2], coordinates[(i + 1) % 2])
}
drawer.image(coordinates[0])
}
}
override fun draw() {
drawFunc()
}
}
fun main(args: Array<String>) {
application(JumpFill001(), configuration {
width = 1024
height = 1024
})
}

View File

@@ -0,0 +1,45 @@
import org.openrndr.Program
import org.openrndr.application
import org.openrndr.color.ColorRGBa
import org.openrndr.configuration
import org.openrndr.draw.colorBuffer
import org.openrndr.extra.jumpfill.contourPoints
import org.openrndr.extra.jumpfill.jumpFlood
import org.openrndr.extra.jumpfill.threshold
class JumpFill002 : Program() {
var drawFunc = {}
override fun setup() {
val input = colorBuffer(512, 1024)
for (i in 0 until 3) {
input.shadow[(Math.random() * input.width).toInt(), (Math.random() * input.height).toInt()] =
ColorRGBa.WHITE
}
input.shadow.upload()
val result = jumpFlood(drawer, input)
// threshold.apply(result, result)
// contourPoints.apply(result, result)
drawFunc = {
drawer.image(result)
}
}
override fun draw() {
drawFunc()
}
}
fun main(args: Array<String>) {
application(JumpFill002(), configuration {
width = 1024
height = 1024
})
}

View File

@@ -1,77 +1,77 @@
package org.openrndr.extra.integralimage
import org.openrndr.draw.*
import org.openrndr.filter.blend.passthrough
import org.openrndr.math.Vector2
import org.openrndr.resourceUrl
class FastIntegralImageFilter : Filter(filterShaderFromUrl(resourceUrl(
"/shaders/gl3/integral-image.frag"
))) {
var passIndex: Int by parameters
var passDirection: Vector2 by parameters
var sampleCount: Int by parameters
var sampleCountBase: Int by parameters
}
class FastIntegralImage : Filter(filterShaderFromUrl(resourceUrl(
"/shaders/gl3/integral-image.frag"
))) {
var intermediate: ColorBuffer? = null
val filter = FastIntegralImageFilter()
private fun sampleCounts(size:Int, sampleCountBase:Int) : List<Int> {
var remainder = size
val sampleCounts = mutableListOf<Int>()
while (remainder > 0) {
sampleCounts += if (remainder >= sampleCountBase) {
sampleCountBase
} else {
remainder
}
remainder /= sampleCountBase
}
return sampleCounts
}
override fun apply(source: Array<ColorBuffer>, target: Array<ColorBuffer>) {
val sampleCountBase = 16
val xSampleCounts = sampleCounts(source[0].width, sampleCountBase)
val ySampleCounts = sampleCounts(source[0].height, sampleCountBase)
val li = intermediate
if (li == null || (li.width != source[0].width || li.height != source[0].height)) {
intermediate?.destroy()
intermediate = colorBuffer(source[0].width, source[0].height, 1.0, ColorFormat.RGBa, ColorType.FLOAT32)
}
val targets = arrayOf(target, arrayOf(intermediate!!))
var targetIndex = 0
filter.sampleCountBase = sampleCountBase
filter.passDirection = Vector2.UNIT_X
for (pass in 0 until xSampleCounts.size) {
filter.sampleCount = xSampleCounts[pass]
filter.passIndex = pass
filter.apply( if (pass == 0) source else targets[targetIndex%2], targets[(targetIndex+1)%2])
targetIndex++
}
filter.passDirection = Vector2.UNIT_Y
for (pass in 0 until ySampleCounts.size) {
filter.sampleCount = ySampleCounts[pass]
filter.passIndex = pass
filter.apply( targets[targetIndex%2], targets[(targetIndex+1)%2])
targetIndex++
}
if (targetIndex%2 == 1) {
passthrough.apply(targets[1], targets[0])
}
}
package org.openrndr.extra.integralimage
import org.openrndr.draw.*
import org.openrndr.filter.blend.passthrough
import org.openrndr.math.Vector2
import org.openrndr.resourceUrl
class FastIntegralImageFilter : Filter(filterShaderFromUrl(resourceUrl(
"/shaders/gl3/integral-image.frag"
))) {
var passIndex: Int by parameters
var passDirection: Vector2 by parameters
var sampleCount: Int by parameters
var sampleCountBase: Int by parameters
}
class FastIntegralImage : Filter(filterShaderFromUrl(resourceUrl(
"/shaders/gl3/integral-image.frag"
))) {
var intermediate: ColorBuffer? = null
val filter = FastIntegralImageFilter()
private fun sampleCounts(size:Int, sampleCountBase:Int) : List<Int> {
var remainder = size
val sampleCounts = mutableListOf<Int>()
while (remainder > 0) {
sampleCounts += if (remainder >= sampleCountBase) {
sampleCountBase
} else {
remainder
}
remainder /= sampleCountBase
}
return sampleCounts
}
override fun apply(source: Array<ColorBuffer>, target: Array<ColorBuffer>) {
val sampleCountBase = 16
val xSampleCounts = sampleCounts(source[0].width, sampleCountBase)
val ySampleCounts = sampleCounts(source[0].height, sampleCountBase)
val li = intermediate
if (li == null || (li.width != source[0].width || li.height != source[0].height)) {
intermediate?.destroy()
intermediate = colorBuffer(source[0].width, source[0].height, 1.0, ColorFormat.RGBa, ColorType.FLOAT32)
}
val targets = arrayOf(target, arrayOf(intermediate!!))
var targetIndex = 0
filter.sampleCountBase = sampleCountBase
filter.passDirection = Vector2.UNIT_X
for (pass in 0 until xSampleCounts.size) {
filter.sampleCount = xSampleCounts[pass]
filter.passIndex = pass
filter.apply( if (pass == 0) source else targets[targetIndex%2], targets[(targetIndex+1)%2])
targetIndex++
}
filter.passDirection = Vector2.UNIT_Y
for (pass in 0 until ySampleCounts.size) {
filter.sampleCount = ySampleCounts[pass]
filter.passIndex = pass
filter.apply( targets[targetIndex%2], targets[(targetIndex+1)%2])
targetIndex++
}
if (targetIndex%2 == 1) {
passthrough.apply(targets[1], targets[0])
}
}
}

View File

@@ -1,26 +1,26 @@
#version 330 core
uniform sampler2D tex0;
in vec2 v_texCoord0;
out vec4 o_color;
uniform int passIndex;
uniform int sampleCount;
uniform int sampleCountBase;
uniform vec2 passDirection;
void main() {
vec2 passOffset = vec2(pow(sampleCountBase, passIndex)) * (1.0/textureSize(tex0, 0)) * passDirection;
vec2 uv0 = v_texCoord0;
vec4 result = vec4(0.0);
for (int i = 0; i < sampleCount; ++i) {
vec2 readUV = v_texCoord0 - vec2(i *passOffset);
float factor = step(0.0, readUV.x) * step(0.0, readUV.y);
result += factor * texture(tex0, readUV);
}
o_color = result;
#version 330 core
uniform sampler2D tex0;
in vec2 v_texCoord0;
out vec4 o_color;
uniform int passIndex;
uniform int sampleCount;
uniform int sampleCountBase;
uniform vec2 passDirection;
void main() {
vec2 passOffset = vec2(pow(sampleCountBase, passIndex)) * (1.0/textureSize(tex0, 0)) * passDirection;
vec2 uv0 = v_texCoord0;
vec4 result = vec4(0.0);
for (int i = 0; i < sampleCount; ++i) {
vec2 readUV = v_texCoord0 - vec2(i *passOffset);
float factor = step(0.0, readUV.x) * step(0.0, readUV.y);
result += factor * texture(tex0, readUV);
}
o_color = result;
}

View File

@@ -1,128 +1,128 @@
package org.openrndr.extra.jumpfill
import org.openrndr.color.ColorRGBa
import org.openrndr.draw.*
import org.openrndr.math.Matrix44
import org.openrndr.math.Vector2
import org.openrndr.resourceUrl
class EncodePoints : Filter(filterShaderFromUrl(resourceUrl("/shaders/gl3/encode-points.frag")))
class JumpFlood : Filter(filterShaderFromUrl(resourceUrl("/shaders/gl3/jumpflood.frag"))) {
var maxSteps: Int by parameters
var step: Int by parameters
}
class PixelDistance : Filter(filterShaderFromUrl(resourceUrl("/shaders/gl3/pixel-distance.frag")))
class ContourPoints : Filter(filterShaderFromUrl(resourceUrl("/shaders/gl3/contour-points.frag")))
class Threshold : Filter(filterShaderFromUrl(resourceUrl("/shaders/gl3/threshold.frag"))) {
var threshold by parameters
init {
threshold = 0.5
}
}
val encodePoints by lazy { EncodePoints() }
val jumpFlood by lazy { JumpFlood() }
val pixelDistance by lazy { PixelDistance() }
val contourPoints by lazy { ContourPoints() }
val threshold by lazy { Threshold() }
class JumpFlooder(val width: Int, val height: Int) {
private val dimension = Math.max(width, height)
private val exp = Math.ceil(Math.log(dimension.toDouble()) / Math.log(2.0)).toInt()
private val squareDim = Math.pow(2.0, exp.toDouble()).toInt()
private val coordinates =
listOf(colorBuffer(squareDim, squareDim, format = ColorFormat.RGB, type = ColorType.FLOAT32),
colorBuffer(squareDim, squareDim, format = ColorFormat.RGB, type = ColorType.FLOAT32))
private val final = renderTarget(width, height) {
colorBuffer(type = ColorType.FLOAT32)
}
val result: ColorBuffer get() = final.colorBuffer(0)
private val square = renderTarget(squareDim, squareDim) {
colorBuffer()
}
private var contourUsed = false
private val thresholded by lazy { colorBuffer(width, height) }
private val edges by lazy { colorBuffer(width, height) }
fun distanceToContour(drawer: Drawer, input: ColorBuffer, thresholdValue: Double = 0.5): ColorBuffer {
threshold.threshold = thresholdValue
threshold.apply(input, thresholded)
contourPoints.apply(thresholded, edges)
contourUsed = true
return jumpFlood(drawer, edges)
}
fun directions(xRange: IntProgression = 0 until width, yRange: IntProgression = 0 until height): Array<List<Vector2>> {
result.shadow.download()
return result.shadow.mapIndexed(xRange, yRange) { _, _, r, g, _, _ -> Vector2(r, g) }
}
fun jumpFlood(drawer: Drawer, input: ColorBuffer): ColorBuffer {
if (input.width != width || input.height != height) {
throw IllegalArgumentException("dimensions mismatch")
}
drawer.isolatedWithTarget(square) {
drawer.background(ColorRGBa.BLACK)
drawer.ortho(square)
drawer.view = Matrix44.IDENTITY
drawer.model = Matrix44.IDENTITY
drawer.image(input)
}
encodePoints.apply(square.colorBuffer(0), coordinates[0])
for (i in 0 until exp) {
jumpFlood.step = i
jumpFlood.apply(coordinates[i % 2], coordinates[(i + 1) % 2])
}
pixelDistance.apply( arrayOf(coordinates[exp % 2], thresholded), coordinates[exp % 2])
drawer.isolatedWithTarget(final) {
drawer.background(ColorRGBa.BLACK)
drawer.ortho(final)
drawer.view = Matrix44.IDENTITY
drawer.model = Matrix44.IDENTITY
drawer.image(coordinates[exp % 2])
}
return result
}
fun destroy(destroyFinal: Boolean = true) {
coordinates.forEach { it.destroy() }
square.colorBuffer(0).destroy()
square.detachColorBuffers()
square.destroy()
if (destroyFinal) {
final.colorBuffer(0).destroy()
}
final.detachColorBuffers()
final.destroy()
if (contourUsed) {
edges.destroy()
thresholded.destroy()
}
}
}
fun jumpFlood(drawer: Drawer, points: ColorBuffer): ColorBuffer {
val jumpFlooder = JumpFlooder(points.width, points.height)
jumpFlooder.jumpFlood(drawer, points)
val result = jumpFlooder.result
jumpFlooder.destroy(false)
return result
package org.openrndr.extra.jumpfill
import org.openrndr.color.ColorRGBa
import org.openrndr.draw.*
import org.openrndr.math.Matrix44
import org.openrndr.math.Vector2
import org.openrndr.resourceUrl
class EncodePoints : Filter(filterShaderFromUrl(resourceUrl("/shaders/gl3/encode-points.frag")))
class JumpFlood : Filter(filterShaderFromUrl(resourceUrl("/shaders/gl3/jumpflood.frag"))) {
var maxSteps: Int by parameters
var step: Int by parameters
}
class PixelDistance : Filter(filterShaderFromUrl(resourceUrl("/shaders/gl3/pixel-distance.frag")))
class ContourPoints : Filter(filterShaderFromUrl(resourceUrl("/shaders/gl3/contour-points.frag")))
class Threshold : Filter(filterShaderFromUrl(resourceUrl("/shaders/gl3/threshold.frag"))) {
var threshold by parameters
init {
threshold = 0.5
}
}
val encodePoints by lazy { EncodePoints() }
val jumpFlood by lazy { JumpFlood() }
val pixelDistance by lazy { PixelDistance() }
val contourPoints by lazy { ContourPoints() }
val threshold by lazy { Threshold() }
class JumpFlooder(val width: Int, val height: Int) {
private val dimension = Math.max(width, height)
private val exp = Math.ceil(Math.log(dimension.toDouble()) / Math.log(2.0)).toInt()
private val squareDim = Math.pow(2.0, exp.toDouble()).toInt()
private val coordinates =
listOf(colorBuffer(squareDim, squareDim, format = ColorFormat.RGB, type = ColorType.FLOAT32),
colorBuffer(squareDim, squareDim, format = ColorFormat.RGB, type = ColorType.FLOAT32))
private val final = renderTarget(width, height) {
colorBuffer(type = ColorType.FLOAT32)
}
val result: ColorBuffer get() = final.colorBuffer(0)
private val square = renderTarget(squareDim, squareDim) {
colorBuffer()
}
private var contourUsed = false
val thresholded by lazy { colorBuffer(width, height) }
val edges by lazy { colorBuffer(width, height) }
fun distanceToContour(drawer: Drawer, input: ColorBuffer, thresholdValue: Double = 0.5): ColorBuffer {
threshold.threshold = thresholdValue
threshold.apply(input, thresholded)
contourPoints.apply(thresholded, edges)
contourUsed = true
return jumpFlood(drawer, edges)
}
fun directions(xRange: IntProgression = 0 until width, yRange: IntProgression = 0 until height): Array<List<Vector2>> {
result.shadow.download()
return result.shadow.mapIndexed(xRange, yRange) { _, _, r, g, _, _ -> Vector2(r, g) }
}
fun jumpFlood(drawer: Drawer, input: ColorBuffer): ColorBuffer {
if (input.width != width || input.height != height) {
throw IllegalArgumentException("dimensions mismatch")
}
drawer.isolatedWithTarget(square) {
drawer.background(ColorRGBa.BLACK)
drawer.ortho(square)
drawer.view = Matrix44.IDENTITY
drawer.model = Matrix44.IDENTITY
drawer.image(input)
}
encodePoints.apply(square.colorBuffer(0), coordinates[0])
for (i in 0 until exp) {
jumpFlood.step = i
jumpFlood.apply(coordinates[i % 2], coordinates[(i + 1) % 2])
}
pixelDistance.apply(arrayOf(coordinates[exp % 2], thresholded), coordinates[exp % 2])
drawer.isolatedWithTarget(final) {
drawer.background(ColorRGBa.BLACK)
drawer.ortho(final)
drawer.view = Matrix44.IDENTITY
drawer.model = Matrix44.IDENTITY
drawer.image(coordinates[exp % 2])
}
return result
}
fun destroy(destroyFinal: Boolean = true) {
coordinates.forEach { it.destroy() }
square.colorBuffer(0).destroy()
square.detachColorBuffers()
square.destroy()
if (destroyFinal) {
final.colorBuffer(0).destroy()
}
final.detachColorBuffers()
final.destroy()
if (contourUsed) {
edges.destroy()
thresholded.destroy()
}
}
}
fun jumpFlood(drawer: Drawer, points: ColorBuffer): ColorBuffer {
val jumpFlooder = JumpFlooder(points.width, points.height)
jumpFlooder.jumpFlood(drawer, points)
val result = jumpFlooder.result
jumpFlooder.destroy(false)
return result
}

View File

@@ -1,16 +1,16 @@
#version 330 core
uniform sampler2D tex0;
uniform sampler2D tex1;
in vec2 v_texCoord0;
out vec4 o_color;
void main() {
vec2 size = textureSize(tex0, 0);
vec2 pixelPosition = v_texCoord0;
vec2 centroidPixelPosition = texture(tex0, v_texCoord0).xy;
vec2 pixelDistance = (centroidPixelPosition - pixelPosition) * size * vec2(1.0, -1.0);
float threshold = texture(tex1, v_texCoord0).r;
o_color = vec4(pixelDistance, threshold, 1.0);
#version 330 core
uniform sampler2D tex0;
uniform sampler2D tex1;
in vec2 v_texCoord0;
out vec4 o_color;
void main() {
vec2 size = textureSize(tex0, 0);
vec2 pixelPosition = v_texCoord0;
vec2 centroidPixelPosition = texture(tex0, v_texCoord0).xy;
vec2 pixelDistance = (centroidPixelPosition - pixelPosition) * size * vec2(1.0, -1.0);
float threshold = texture(tex1, v_texCoord0).r;
o_color = vec4(pixelDistance, threshold, 1.0);
}

View File

@@ -1,61 +1,61 @@
package org.openrndr.extras.meshgenerators
import org.openrndr.draw.VertexBuffer
import org.openrndr.math.Vector3
fun boxMesh(width: Double = 1.0, height: Double = 1.0, depth: Double = 1.0,
widthSegments: Int = 1, heightSegments: Int = 1, depthSegments: Int = 1,
invert: Boolean = false): VertexBuffer {
val vb = meshVertexBuffer(widthSegments * heightSegments * 6 * 2 +
widthSegments * depthSegments * 6 * 2 +
heightSegments * depthSegments * 6 * 2)
vb.put {
generateBox(width, height, depth,
widthSegments, heightSegments, depthSegments,
invert, bufferWriter(this))
}
return vb
}
fun generateBox(width: Double = 1.0, height: Double = 1.0, depth: Double = 1.0,
widthSegments: Int = 1, heightSegments: Int = 1, depthSegments: Int = 1,
invert: Boolean = false,
writer: VertexWriter) {
val sign = if (invert) -1.0 else 1.0
// +x -- ZY
generatePlane(Vector3(width / 2.0 * sign, 0.0, 0.0),
Vector3.UNIT_Z, Vector3.UNIT_Y, Vector3.UNIT_X,
-depth, -height,
depthSegments, heightSegments, writer)
// -x -- ZY
generatePlane(Vector3(-width / 2.0 * sign, 0.0, 0.0),
Vector3.UNIT_Z, Vector3.UNIT_Y, -Vector3.UNIT_X,
-depth, height,
depthSegments, heightSegments, writer)
// +y -- XZ
generatePlane(Vector3(0.0, height / 2.0 * sign, 0.0),
Vector3.UNIT_X, Vector3.UNIT_Z, Vector3.UNIT_Y,
width, depth,
widthSegments, depthSegments, writer)
// -y -- XZ
generatePlane(Vector3(0.0, -height / 2.0 * sign, 0.0),
Vector3.UNIT_X, Vector3.UNIT_Z, -Vector3.UNIT_Y,
width, -depth,
widthSegments, depthSegments, writer)
// +z -- XY
generatePlane(Vector3(0.0, 0.0, depth / 2.0 * sign),
Vector3.UNIT_X, Vector3.UNIT_Y, Vector3.UNIT_Z,
-width, height,
widthSegments, heightSegments, writer)
// -z -- XY
generatePlane(Vector3(0.0, 0.0, -depth / 2.0 * sign),
Vector3.UNIT_X, Vector3.UNIT_Y, -Vector3.UNIT_Z,
width, height,
widthSegments, heightSegments, writer)
package org.openrndr.extras.meshgenerators
import org.openrndr.draw.VertexBuffer
import org.openrndr.math.Vector3
fun boxMesh(width: Double = 1.0, height: Double = 1.0, depth: Double = 1.0,
widthSegments: Int = 1, heightSegments: Int = 1, depthSegments: Int = 1,
invert: Boolean = false): VertexBuffer {
val vb = meshVertexBuffer(widthSegments * heightSegments * 6 * 2 +
widthSegments * depthSegments * 6 * 2 +
heightSegments * depthSegments * 6 * 2)
vb.put {
generateBox(width, height, depth,
widthSegments, heightSegments, depthSegments,
invert, bufferWriter(this))
}
return vb
}
fun generateBox(width: Double = 1.0, height: Double = 1.0, depth: Double = 1.0,
widthSegments: Int = 1, heightSegments: Int = 1, depthSegments: Int = 1,
invert: Boolean = false,
writer: VertexWriter) {
val sign = if (invert) -1.0 else 1.0
// +x -- ZY
generatePlane(Vector3(width / 2.0 * sign, 0.0, 0.0),
Vector3.UNIT_Z, Vector3.UNIT_Y, Vector3.UNIT_X,
-depth, -height,
depthSegments, heightSegments, writer)
// -x -- ZY
generatePlane(Vector3(-width / 2.0 * sign, 0.0, 0.0),
Vector3.UNIT_Z, Vector3.UNIT_Y, -Vector3.UNIT_X,
-depth, height,
depthSegments, heightSegments, writer)
// +y -- XZ
generatePlane(Vector3(0.0, height / 2.0 * sign, 0.0),
Vector3.UNIT_X, Vector3.UNIT_Z, Vector3.UNIT_Y,
width, depth,
widthSegments, depthSegments, writer)
// -y -- XZ
generatePlane(Vector3(0.0, -height / 2.0 * sign, 0.0),
Vector3.UNIT_X, Vector3.UNIT_Z, -Vector3.UNIT_Y,
width, -depth,
widthSegments, depthSegments, writer)
// +z -- XY
generatePlane(Vector3(0.0, 0.0, depth / 2.0 * sign),
Vector3.UNIT_X, Vector3.UNIT_Y, Vector3.UNIT_Z,
-width, height,
widthSegments, heightSegments, writer)
// -z -- XY
generatePlane(Vector3(0.0, 0.0, -depth / 2.0 * sign),
Vector3.UNIT_X, Vector3.UNIT_Y, -Vector3.UNIT_Z,
width, height,
widthSegments, heightSegments, writer)
}

View File

@@ -1,101 +1,101 @@
package org.openrndr.extras.meshgenerators
import org.openrndr.math.Vector2
import org.openrndr.math.Vector3
import org.openrndr.math.transforms.rotateY
fun generateCap(sides: Int, radius: Double, enveloppe: List<Vector2> = listOf(Vector2(0.0, 0.0), Vector2(1.0, 0.0)), writer: VertexWriter) {
val maxX = enveloppe.maxBy { it.x } ?: Vector2(1.0, 0.0)
val a = maxX.x
val cleanEnveloppe = enveloppe.map { Vector2((it.x / a) * radius, it.y) }
val normals2D = enveloppe.zipWithNext().map {
val d = it.second - it.first
d.normalized.perpendicular
}
val basePositions = cleanEnveloppe.map { Vector3(it.x, it.y, 0.0) }
val baseNormals = normals2D.map { Vector3(it.x, it.y, 0.0) }
for (side in 0 until sides) {
val r0 = rotateY(360.0 / sides * side)
val r1 = rotateY(360.0 / sides * (side + 1))
val v0 = basePositions.map { (r0 * it.xyz0).xyz }
val v1 = basePositions.map { (r1 * it.xyz0).xyz }
val n0 = baseNormals.map { (r0 * it.xyz0).xyz }
val n1 = baseNormals.map { (r1 * it.xyz0).xyz }
for (segment in 0 until basePositions.size - 1) {
val p00 = v0[segment]
val p01 = v0[segment+1]
val p10 = v1[segment]
val p11 = v1[segment+1]
val nn0 = n0[segment]
val nn1 = n1[segment]
writer(p00, nn0, Vector2.ZERO)
writer(p01, nn0, Vector2.ZERO)
writer(p11, nn1, Vector2.ZERO)
writer(p11, nn1, Vector2.ZERO)
writer(p10, nn1, Vector2.ZERO)
writer(p00, nn0, Vector2.ZERO)
}
}
}
fun generateRevolve(sides: Int, length: Double, enveloppe: List<Vector2> = listOf(Vector2(1.0, 0.0), Vector2(1.0, 1.0)), writer: VertexWriter) {
val maxY = enveloppe.maxBy { it.y } ?: Vector2(0.0, 1.0)
val a = maxY.y
val cleanEnveloppe = enveloppe.map { Vector2((it.x), (it.y/a - 0.5) * length ) }
val normals2D = enveloppe.zipWithNext().map {
val d = it.second - it.first
d.normalized.perpendicular * Vector2(1.0, -1.0)
}
val extended = listOf(normals2D[0]) + normals2D + normals2D[normals2D.size-1]
// extended.zipW
println(normals2D.joinToString(", "))
val basePositions = cleanEnveloppe.map { Vector3(it.x, it.y, 0.0) }
val baseNormals = normals2D.map { Vector3(it.x, it.y, 0.0) }
for (side in 0 until sides) {
val r0 = rotateY(360.0 / sides * side)
val r1 = rotateY(360.0 / sides * (side + 1))
val v0 = basePositions.map { (r0 * it.xyz0).xyz }
val v1 = basePositions.map { (r1 * it.xyz0).xyz }
val n0 = baseNormals.map { (r0 * it.xyz0).xyz }
val n1 = baseNormals.map { (r1 * it.xyz0).xyz }
for (segment in 0 until basePositions.size - 1) {
val p00 = v0[segment]
val p01 = v0[segment+1]
val p10 = v1[segment]
val p11 = v1[segment+1]
val nn0 = n0[segment]
val nn1 = n1[segment]
writer(p00, nn0, Vector2.ZERO)
writer(p10, nn1, Vector2.ZERO)
writer(p11, nn1, Vector2.ZERO)
writer(p11, nn1, Vector2.ZERO)
writer(p01, nn0, Vector2.ZERO)
writer(p00, nn0, Vector2.ZERO)
}
}
package org.openrndr.extras.meshgenerators
import org.openrndr.math.Vector2
import org.openrndr.math.Vector3
import org.openrndr.math.transforms.rotateY
fun generateCap(sides: Int, radius: Double, enveloppe: List<Vector2> = listOf(Vector2(0.0, 0.0), Vector2(1.0, 0.0)), writer: VertexWriter) {
val maxX = enveloppe.maxBy { it.x } ?: Vector2(1.0, 0.0)
val a = maxX.x
val cleanEnveloppe = enveloppe.map { Vector2((it.x / a) * radius, it.y) }
val normals2D = enveloppe.zipWithNext().map {
val d = it.second - it.first
d.normalized.perpendicular
}
val basePositions = cleanEnveloppe.map { Vector3(it.x, it.y, 0.0) }
val baseNormals = normals2D.map { Vector3(it.x, it.y, 0.0) }
for (side in 0 until sides) {
val r0 = rotateY(360.0 / sides * side)
val r1 = rotateY(360.0 / sides * (side + 1))
val v0 = basePositions.map { (r0 * it.xyz0).xyz }
val v1 = basePositions.map { (r1 * it.xyz0).xyz }
val n0 = baseNormals.map { (r0 * it.xyz0).xyz }
val n1 = baseNormals.map { (r1 * it.xyz0).xyz }
for (segment in 0 until basePositions.size - 1) {
val p00 = v0[segment]
val p01 = v0[segment+1]
val p10 = v1[segment]
val p11 = v1[segment+1]
val nn0 = n0[segment]
val nn1 = n1[segment]
writer(p00, nn0, Vector2.ZERO)
writer(p01, nn0, Vector2.ZERO)
writer(p11, nn1, Vector2.ZERO)
writer(p11, nn1, Vector2.ZERO)
writer(p10, nn1, Vector2.ZERO)
writer(p00, nn0, Vector2.ZERO)
}
}
}
fun generateRevolve(sides: Int, length: Double, enveloppe: List<Vector2> = listOf(Vector2(1.0, 0.0), Vector2(1.0, 1.0)), writer: VertexWriter) {
val maxY = enveloppe.maxBy { it.y } ?: Vector2(0.0, 1.0)
val a = maxY.y
val cleanEnveloppe = enveloppe.map { Vector2((it.x), (it.y/a - 0.5) * length ) }
val normals2D = enveloppe.zipWithNext().map {
val d = it.second - it.first
d.normalized.perpendicular * Vector2(1.0, -1.0)
}
val extended = listOf(normals2D[0]) + normals2D + normals2D[normals2D.size-1]
// extended.zipW
println(normals2D.joinToString(", "))
val basePositions = cleanEnveloppe.map { Vector3(it.x, it.y, 0.0) }
val baseNormals = normals2D.map { Vector3(it.x, it.y, 0.0) }
for (side in 0 until sides) {
val r0 = rotateY(360.0 / sides * side)
val r1 = rotateY(360.0 / sides * (side + 1))
val v0 = basePositions.map { (r0 * it.xyz0).xyz }
val v1 = basePositions.map { (r1 * it.xyz0).xyz }
val n0 = baseNormals.map { (r0 * it.xyz0).xyz }
val n1 = baseNormals.map { (r1 * it.xyz0).xyz }
for (segment in 0 until basePositions.size - 1) {
val p00 = v0[segment]
val p01 = v0[segment+1]
val p10 = v1[segment]
val p11 = v1[segment+1]
val nn0 = n0[segment]
val nn1 = n1[segment]
writer(p00, nn0, Vector2.ZERO)
writer(p10, nn1, Vector2.ZERO)
writer(p11, nn1, Vector2.ZERO)
writer(p11, nn1, Vector2.ZERO)
writer(p01, nn0, Vector2.ZERO)
writer(p00, nn0, Vector2.ZERO)
}
}
}

View File

@@ -1,82 +1,82 @@
package org.openrndr.extras.meshgenerators
import org.openrndr.draw.VertexBuffer
import org.openrndr.math.Vector2
import org.openrndr.math.Vector3
import org.openrndr.math.mix
import org.openrndr.math.transforms.rotateZ
fun cylinderMesh(sides: Int = 16, segments: Int = 16, radius: Double = 1.0, length: Double, invert: Boolean = false): VertexBuffer {
val vertexCount = 6 * sides * segments
val vb = meshVertexBuffer(vertexCount)
vb.put {
generateCylinder(sides, segments, radius, length, invert, bufferWriter(this))
}
return vb
}
fun generateCylinder(sides: Int, segments: Int, radius: Double, length: Double, invert: Boolean = false, vertexWriter: VertexWriter) {
return generateTaperedCylinder(sides, segments, radius, radius, length, invert, vertexWriter)
}
fun generateTaperedCylinder(sides: Int, segments: Int, radiusStart: Double, radiusEnd:Double, length: Double, invert: Boolean = false, vertexWriter: VertexWriter) {
val dphi = (Math.PI * 2) / sides
val ddeg = (360.0) / sides
val invertFactor = if (invert) -1.0 else 1.0
val dr = radiusEnd - radiusStart
val baseNormal = Vector2(length, dr).normalized.perpendicular.let { Vector3(x=it.y, y=0.0, z=it.x)}
//val baseNormal = Vector3(1.0, 0.0, 0.0)
for (segment in 0 until segments) {
val radius0 = mix(radiusStart, radiusEnd, segment*1.0/segments)
val radius1 = mix(radiusStart, radiusEnd, (segment+1)*1.0/segments)
val z0 = (length / segments) * segment - length/2.0
val z1 = (length / segments) * (segment + 1) - length/2.0
for (side in 0 until sides) {
val x00 = Math.cos(side * dphi) * radius0
val x10 = Math.cos(side * dphi + dphi) * radius0
val y00 = Math.sin(side * dphi) * radius0
val y10 = Math.sin(side * dphi + dphi) * radius0
val x01 = Math.cos(side * dphi) * radius1
val x11 = Math.cos(side * dphi + dphi) * radius1
val y01 = Math.sin(side * dphi) * radius1
val y11 = Math.sin(side * dphi + dphi) * radius1
val u0 = (segment + 0.0) / segments
val u1 = (segment + 1.0) / segments
val v0 = (side + 0.0) / sides
val v1 = (side + 1.0) / sides
val n0 = (rotateZ(side * ddeg) * baseNormal.xyz0).xyz.normalized * invertFactor
val n1 = (rotateZ((side+1) * ddeg) * baseNormal.xyz0).xyz.normalized * invertFactor
if (!invert) {
vertexWriter(Vector3(x00, y00, z0), n0, Vector2(u0, v0))
vertexWriter(Vector3(x10, y10, z0), n1, Vector2(u0, v1))
vertexWriter(Vector3(x11, y11, z1), n1, Vector2(u1, v1))
vertexWriter(Vector3(x11, y11, z1), n1, Vector2(u1, v1))
vertexWriter(Vector3(x01, y01, z1), n0, Vector2(u1, v0))
vertexWriter(Vector3(x00, y00, z0), n0, Vector2(u0, v0))
} else {
vertexWriter(Vector3(x00, y00, z0), n0, Vector2(u0, v0))
vertexWriter(Vector3(x01, y01, z1), n0, Vector2(u1, v0))
vertexWriter(Vector3(x11, y11, z1), n1, Vector2(u1, v1))
vertexWriter(Vector3(x11, y11, z1), n1, Vector2(u1, v1))
vertexWriter(Vector3(x10, y10, z0), n1, Vector2(u0, v1))
vertexWriter(Vector3(x00, y00, z0), n0, Vector2(u0, v0))
}
}
}
package org.openrndr.extras.meshgenerators
import org.openrndr.draw.VertexBuffer
import org.openrndr.math.Vector2
import org.openrndr.math.Vector3
import org.openrndr.math.mix
import org.openrndr.math.transforms.rotateZ
fun cylinderMesh(sides: Int = 16, segments: Int = 16, radius: Double = 1.0, length: Double, invert: Boolean = false): VertexBuffer {
val vertexCount = 6 * sides * segments
val vb = meshVertexBuffer(vertexCount)
vb.put {
generateCylinder(sides, segments, radius, length, invert, bufferWriter(this))
}
return vb
}
fun generateCylinder(sides: Int, segments: Int, radius: Double, length: Double, invert: Boolean = false, vertexWriter: VertexWriter) {
return generateTaperedCylinder(sides, segments, radius, radius, length, invert, vertexWriter)
}
fun generateTaperedCylinder(sides: Int, segments: Int, radiusStart: Double, radiusEnd:Double, length: Double, invert: Boolean = false, vertexWriter: VertexWriter) {
val dphi = (Math.PI * 2) / sides
val ddeg = (360.0) / sides
val invertFactor = if (invert) -1.0 else 1.0
val dr = radiusEnd - radiusStart
val baseNormal = Vector2(length, dr).normalized.perpendicular.let { Vector3(x=it.y, y=0.0, z=it.x)}
//val baseNormal = Vector3(1.0, 0.0, 0.0)
for (segment in 0 until segments) {
val radius0 = mix(radiusStart, radiusEnd, segment*1.0/segments)
val radius1 = mix(radiusStart, radiusEnd, (segment+1)*1.0/segments)
val z0 = (length / segments) * segment - length/2.0
val z1 = (length / segments) * (segment + 1) - length/2.0
for (side in 0 until sides) {
val x00 = Math.cos(side * dphi) * radius0
val x10 = Math.cos(side * dphi + dphi) * radius0
val y00 = Math.sin(side * dphi) * radius0
val y10 = Math.sin(side * dphi + dphi) * radius0
val x01 = Math.cos(side * dphi) * radius1
val x11 = Math.cos(side * dphi + dphi) * radius1
val y01 = Math.sin(side * dphi) * radius1
val y11 = Math.sin(side * dphi + dphi) * radius1
val u0 = (segment + 0.0) / segments
val u1 = (segment + 1.0) / segments
val v0 = (side + 0.0) / sides
val v1 = (side + 1.0) / sides
val n0 = (rotateZ(side * ddeg) * baseNormal.xyz0).xyz.normalized * invertFactor
val n1 = (rotateZ((side+1) * ddeg) * baseNormal.xyz0).xyz.normalized * invertFactor
if (!invert) {
vertexWriter(Vector3(x00, y00, z0), n0, Vector2(u0, v0))
vertexWriter(Vector3(x10, y10, z0), n1, Vector2(u0, v1))
vertexWriter(Vector3(x11, y11, z1), n1, Vector2(u1, v1))
vertexWriter(Vector3(x11, y11, z1), n1, Vector2(u1, v1))
vertexWriter(Vector3(x01, y01, z1), n0, Vector2(u1, v0))
vertexWriter(Vector3(x00, y00, z0), n0, Vector2(u0, v0))
} else {
vertexWriter(Vector3(x00, y00, z0), n0, Vector2(u0, v0))
vertexWriter(Vector3(x01, y01, z1), n0, Vector2(u1, v0))
vertexWriter(Vector3(x11, y11, z1), n1, Vector2(u1, v1))
vertexWriter(Vector3(x11, y11, z1), n1, Vector2(u1, v1))
vertexWriter(Vector3(x10, y10, z0), n1, Vector2(u0, v1))
vertexWriter(Vector3(x00, y00, z0), n0, Vector2(u0, v0))
}
}
}
}

View File

@@ -1,172 +1,172 @@
package org.openrndr.extras.meshgenerators
import org.openrndr.draw.VertexBuffer
import org.openrndr.draw.vertexBuffer
import org.openrndr.draw.vertexFormat
import org.openrndr.math.Matrix44
import org.openrndr.math.Vector2
import org.openrndr.math.Vector3
import org.openrndr.math.transforms.rotate
import org.openrndr.math.transforms.transform
import org.openrndr.shape.Shape
import java.nio.ByteBuffer
import java.nio.ByteOrder
class GeneratorBuffer {
class VertexData(val position: Vector3, val normal: Vector3, val texCoord: Vector2)
var data = mutableListOf<VertexData>()
fun write(position: Vector3, normal: Vector3, texCoord: Vector2) {
data.add(VertexData(position, normal, texCoord))
}
fun concat(other: GeneratorBuffer) {
data.addAll(other.data)
}
fun transform(m: Matrix44) {
data = data.map {
VertexData((m * (it.position.xyz1)).xyz, (m * (it.normal.xyz0)).xyz, it.texCoord)
}.toMutableList()
}
fun toByteBuffer(): ByteBuffer {
val bb = ByteBuffer.allocateDirect(data.size * (3 * 4 + 3 * 4 + 2 * 4))
bb.order(ByteOrder.nativeOrder())
bb.rewind()
for (d in data) {
bb.putFloat(d.position.x.toFloat())
bb.putFloat(d.position.y.toFloat())
bb.putFloat(d.position.z.toFloat())
bb.putFloat(d.normal.x.toFloat())
bb.putFloat(d.normal.y.toFloat())
bb.putFloat(d.normal.z.toFloat())
bb.putFloat(d.texCoord.x.toFloat())
bb.putFloat(d.texCoord.y.toFloat())
}
return bb
}
}
fun GeneratorBuffer.sphere(sides: Int, segments: Int, radius: Double, invert: Boolean = false) {
generateSphere(sides, segments, radius, invert, this::write)
}
fun GeneratorBuffer.hemisphere(sides: Int, segments: Int, radius: Double, invert: Boolean = false) {
generateHemisphere(sides, segments, radius, invert, this::write)
}
enum class GridCoordinates {
INDEX,
UNIPOLAR,
BIPOLAR,
}
fun GeneratorBuffer.grid(width: Int, height: Int, coordinates: GridCoordinates = GridCoordinates.BIPOLAR, builder: GeneratorBuffer.(u: Double, v: Double) -> Unit) {
for (v in 0 until height) {
for (u in 0 until width) {
group {
when (coordinates) {
GridCoordinates.INDEX -> this.builder(u * 1.0, v * 1.0)
GridCoordinates.BIPOLAR -> this.builder(2 * u / (width - 1.0) - 1,
2 * v / (height - 1.0) - 1)
GridCoordinates.UNIPOLAR -> this.builder(u / (width - 1.0), v / (height - 1.0))
}
}
}
}
}
fun GeneratorBuffer.twist(degreesPerUnit: Double, start: Double, axis: Vector3 = Vector3.UNIT_Y) {
data = data.map {
val p = it.position.projectedOn(axis)
val t = if (axis.x != 0.0) p.x / axis.x else if (axis.y != 0.0) p.y / axis.y else if (axis.z != 0.0) p.z / axis.z else
throw IllegalArgumentException("0 axis")
val r = rotate(axis, t * degreesPerUnit)
GeneratorBuffer.VertexData((r * it.position.xyz1).xyz, (r * it.normal.xyz0).xyz, it.texCoord)
}.toMutableList()
}
fun GeneratorBuffer.grid(width: Int, height: Int, depth: Int, coordinates: GridCoordinates = GridCoordinates.BIPOLAR, builder: GeneratorBuffer.(u: Double, v: Double, w: Double) -> Unit) {
for (w in 0 until depth) {
for (v in 0 until height) {
for (u in 0 until width) {
group {
when (coordinates) {
GridCoordinates.INDEX -> this.builder(u * 1.0, v * 1.0, w * 1.0)
GridCoordinates.BIPOLAR -> this.builder(2 * u / (width - 1.0) - 1,
2 * v / (height - 1.0) - 1, 2 * w / (depth - 1.0) - 1)
GridCoordinates.UNIPOLAR -> this.builder(u / (width - 1.0), v / (height - 1.0), w / (depth - 1.0))
}
}
}
}
}
}
fun GeneratorBuffer.box(width: Double, height: Double, depth: Double, widthSegments: Int = 1, heightSegments: Int = 1, depthSegments: Int = 1, invert: Boolean = false) {
generateBox(width, height, depth, widthSegments, heightSegments, depthSegments, invert, this::write)
}
fun GeneratorBuffer.cylinder(sides: Int, segments: Int, radius: Double, length: Double, invert: Boolean = false) {
generateCylinder(sides, segments, radius, length, invert, this::write)
}
fun GeneratorBuffer.taperedCylinder(sides: Int, segments: Int, startRadius: Double, endRadius: Double, length: Double, invert: Boolean = false) {
generateTaperedCylinder(sides, segments, startRadius, endRadius, length, invert, this::write)
}
fun GeneratorBuffer.cap(sides: Int, radius: Double, enveloppe: List<Vector2>) {
generateCap(sides, radius, enveloppe, this::write)
}
fun GeneratorBuffer.revolve(sides:Int, length:Double, enveloppe: List<Vector2>) {
generateRevolve(sides, length, enveloppe, this::write)
}
fun GeneratorBuffer.extrudeShape(shape: Shape, length: Double, scale: Double = 1.0, distanceTolerance: Double = 0.5) {
extrudeShape(shape, -length / 2.0, length / 2.0, scale, scale, true, true, distanceTolerance, false, this::write)
}
fun meshGenerator(builder: GeneratorBuffer.() -> Unit): VertexBuffer {
val gb = GeneratorBuffer()
gb.builder()
val vb = vertexBuffer(vertexFormat {
position(3)
normal(3)
textureCoordinate(2)
}, gb.data.size)
val bb = gb.toByteBuffer()
bb.rewind()
vb.write(bb)
return vb
}
fun generator(builder: GeneratorBuffer.() -> Unit): GeneratorBuffer {
val gb = GeneratorBuffer()
gb.builder()
return gb
}
fun GeneratorBuffer.group(builder: GeneratorBuffer.() -> Unit) {
val gb = GeneratorBuffer()
gb.builder()
this.concat(gb)
}
fun main(args: Array<String>) {
val gb = generator {
box(20.0, 20.0, 20.0)
group {
box(40.0, 40.0, 40.0)
transform(transform {
translate(0.0, 20.0, 0.0)
})
}
}
package org.openrndr.extras.meshgenerators
import org.openrndr.draw.VertexBuffer
import org.openrndr.draw.vertexBuffer
import org.openrndr.draw.vertexFormat
import org.openrndr.math.Matrix44
import org.openrndr.math.Vector2
import org.openrndr.math.Vector3
import org.openrndr.math.transforms.rotate
import org.openrndr.math.transforms.transform
import org.openrndr.shape.Shape
import java.nio.ByteBuffer
import java.nio.ByteOrder
class GeneratorBuffer {
class VertexData(val position: Vector3, val normal: Vector3, val texCoord: Vector2)
var data = mutableListOf<VertexData>()
fun write(position: Vector3, normal: Vector3, texCoord: Vector2) {
data.add(VertexData(position, normal, texCoord))
}
fun concat(other: GeneratorBuffer) {
data.addAll(other.data)
}
fun transform(m: Matrix44) {
data = data.map {
VertexData((m * (it.position.xyz1)).xyz, (m * (it.normal.xyz0)).xyz, it.texCoord)
}.toMutableList()
}
fun toByteBuffer(): ByteBuffer {
val bb = ByteBuffer.allocateDirect(data.size * (3 * 4 + 3 * 4 + 2 * 4))
bb.order(ByteOrder.nativeOrder())
bb.rewind()
for (d in data) {
bb.putFloat(d.position.x.toFloat())
bb.putFloat(d.position.y.toFloat())
bb.putFloat(d.position.z.toFloat())
bb.putFloat(d.normal.x.toFloat())
bb.putFloat(d.normal.y.toFloat())
bb.putFloat(d.normal.z.toFloat())
bb.putFloat(d.texCoord.x.toFloat())
bb.putFloat(d.texCoord.y.toFloat())
}
return bb
}
}
fun GeneratorBuffer.sphere(sides: Int, segments: Int, radius: Double, invert: Boolean = false) {
generateSphere(sides, segments, radius, invert, this::write)
}
fun GeneratorBuffer.hemisphere(sides: Int, segments: Int, radius: Double, invert: Boolean = false) {
generateHemisphere(sides, segments, radius, invert, this::write)
}
enum class GridCoordinates {
INDEX,
UNIPOLAR,
BIPOLAR,
}
fun GeneratorBuffer.grid(width: Int, height: Int, coordinates: GridCoordinates = GridCoordinates.BIPOLAR, builder: GeneratorBuffer.(u: Double, v: Double) -> Unit) {
for (v in 0 until height) {
for (u in 0 until width) {
group {
when (coordinates) {
GridCoordinates.INDEX -> this.builder(u * 1.0, v * 1.0)
GridCoordinates.BIPOLAR -> this.builder(2 * u / (width - 1.0) - 1,
2 * v / (height - 1.0) - 1)
GridCoordinates.UNIPOLAR -> this.builder(u / (width - 1.0), v / (height - 1.0))
}
}
}
}
}
fun GeneratorBuffer.twist(degreesPerUnit: Double, start: Double, axis: Vector3 = Vector3.UNIT_Y) {
data = data.map {
val p = it.position.projectedOn(axis)
val t = if (axis.x != 0.0) p.x / axis.x else if (axis.y != 0.0) p.y / axis.y else if (axis.z != 0.0) p.z / axis.z else
throw IllegalArgumentException("0 axis")
val r = rotate(axis, t * degreesPerUnit)
GeneratorBuffer.VertexData((r * it.position.xyz1).xyz, (r * it.normal.xyz0).xyz, it.texCoord)
}.toMutableList()
}
fun GeneratorBuffer.grid(width: Int, height: Int, depth: Int, coordinates: GridCoordinates = GridCoordinates.BIPOLAR, builder: GeneratorBuffer.(u: Double, v: Double, w: Double) -> Unit) {
for (w in 0 until depth) {
for (v in 0 until height) {
for (u in 0 until width) {
group {
when (coordinates) {
GridCoordinates.INDEX -> this.builder(u * 1.0, v * 1.0, w * 1.0)
GridCoordinates.BIPOLAR -> this.builder(2 * u / (width - 1.0) - 1,
2 * v / (height - 1.0) - 1, 2 * w / (depth - 1.0) - 1)
GridCoordinates.UNIPOLAR -> this.builder(u / (width - 1.0), v / (height - 1.0), w / (depth - 1.0))
}
}
}
}
}
}
fun GeneratorBuffer.box(width: Double, height: Double, depth: Double, widthSegments: Int = 1, heightSegments: Int = 1, depthSegments: Int = 1, invert: Boolean = false) {
generateBox(width, height, depth, widthSegments, heightSegments, depthSegments, invert, this::write)
}
fun GeneratorBuffer.cylinder(sides: Int, segments: Int, radius: Double, length: Double, invert: Boolean = false) {
generateCylinder(sides, segments, radius, length, invert, this::write)
}
fun GeneratorBuffer.taperedCylinder(sides: Int, segments: Int, startRadius: Double, endRadius: Double, length: Double, invert: Boolean = false) {
generateTaperedCylinder(sides, segments, startRadius, endRadius, length, invert, this::write)
}
fun GeneratorBuffer.cap(sides: Int, radius: Double, enveloppe: List<Vector2>) {
generateCap(sides, radius, enveloppe, this::write)
}
fun GeneratorBuffer.revolve(sides:Int, length:Double, enveloppe: List<Vector2>) {
generateRevolve(sides, length, enveloppe, this::write)
}
fun GeneratorBuffer.extrudeShape(shape: Shape, length: Double, scale: Double = 1.0, distanceTolerance: Double = 0.5) {
extrudeShape(shape, -length / 2.0, length / 2.0, scale, scale, true, true, distanceTolerance, false, this::write)
}
fun meshGenerator(builder: GeneratorBuffer.() -> Unit): VertexBuffer {
val gb = GeneratorBuffer()
gb.builder()
val vb = vertexBuffer(vertexFormat {
position(3)
normal(3)
textureCoordinate(2)
}, gb.data.size)
val bb = gb.toByteBuffer()
bb.rewind()
vb.write(bb)
return vb
}
fun generator(builder: GeneratorBuffer.() -> Unit): GeneratorBuffer {
val gb = GeneratorBuffer()
gb.builder()
return gb
}
fun GeneratorBuffer.group(builder: GeneratorBuffer.() -> Unit) {
val gb = GeneratorBuffer()
gb.builder()
this.concat(gb)
}
fun main(args: Array<String>) {
val gb = generator {
box(20.0, 20.0, 20.0)
group {
box(40.0, 40.0, 40.0)
transform(transform {
translate(0.0, 20.0, 0.0)
})
}
}
}

View File

@@ -1,84 +1,84 @@
package org.openrndr.extras.meshgenerators
import org.openrndr.draw.VertexBuffer
import org.openrndr.math.Vector2
import org.openrndr.math.Vector3
fun planeMesh(center: Vector3,
right: Vector3,
forward: Vector3,
up: Vector3 = forward.cross(right).normalized,
width: Double = 1.0, height: Double = 1.0,
widthSegments: Int = 1, heightSegments: Int = 1): VertexBuffer {
val vertexCount = (widthSegments * heightSegments) * 6
val vb = meshVertexBuffer(vertexCount)
vb.put {
generatePlane(center, right, forward, up,
width, height, widthSegments, heightSegments, bufferWriter(this))
}
return vb
}
/**
* generates a finite plane with its center at (0,0,0) and spanning the xz-plane
*/
fun groundPlaneMesh(width: Double = 1.0,
height: Double = 1.0,
widthSegments: Int = 1,
heightSegments: Int = 1): VertexBuffer {
return planeMesh(Vector3.ZERO, Vector3.UNIT_X, Vector3.UNIT_Z, Vector3.UNIT_Y,
width, height, widthSegments, heightSegments)
}
/**
* generates a finite plane with its center at (0,0,0) and spanning the xy-plane
*/
fun wallPlaneMesh(width: Double = 1.0,
height: Double = 1.0,
widthSegments: Int = 1,
heightSegments: Int = 1): VertexBuffer {
return planeMesh(Vector3.ZERO, Vector3.UNIT_X, Vector3.UNIT_Y, Vector3.UNIT_Z,
width, height, widthSegments, heightSegments)
}
fun generatePlane(center: Vector3,
right: Vector3,
forward: Vector3,
up: Vector3 = forward.cross(right).normalized,
width: Double = 1.0, height: Double = 1.0,
widthSegments: Int = 1, heightSegments: Int = 2,
writer: VertexWriter) {
val forwardStep = forward.normalized * (height / heightSegments)
val rightStep = right.normalized * (width / widthSegments)
val corner = center - forward.normalized * (height*0.5) - right.normalized * (width * 0.5)
val step = Vector2(1.0 / width, 1.0 / height)
for (v in 0 until heightSegments) {
for (u in 0 until widthSegments) {
val uv00 = Vector2(u + 0.0, v + 0.0) * step
val uv01 = Vector2(u + 0.0, v + 0.1) * step
val uv10 = Vector2(u + 1.0, v + 0.0) * step
val uv11 = Vector2(u + 1.0, v + 0.1) * step
val c00 = corner + forwardStep * v.toDouble() + rightStep * u.toDouble()
val c01 = corner + forwardStep * (v + 1).toDouble() + rightStep * u.toDouble()
val c10 = corner + forwardStep * v.toDouble() + rightStep * (u + 1).toDouble()
val c11 = corner + forwardStep * (v + 1).toDouble() + rightStep * (u + 1).toDouble()
writer(c11, up, uv00)
writer(c10, up, uv10)
writer(c00, up, uv11)
writer(c00, up, uv11)
writer(c01, up, uv01)
writer(c11, up, uv00)
}
}
package org.openrndr.extras.meshgenerators
import org.openrndr.draw.VertexBuffer
import org.openrndr.math.Vector2
import org.openrndr.math.Vector3
fun planeMesh(center: Vector3,
right: Vector3,
forward: Vector3,
up: Vector3 = forward.cross(right).normalized,
width: Double = 1.0, height: Double = 1.0,
widthSegments: Int = 1, heightSegments: Int = 1): VertexBuffer {
val vertexCount = (widthSegments * heightSegments) * 6
val vb = meshVertexBuffer(vertexCount)
vb.put {
generatePlane(center, right, forward, up,
width, height, widthSegments, heightSegments, bufferWriter(this))
}
return vb
}
/**
* generates a finite plane with its center at (0,0,0) and spanning the xz-plane
*/
fun groundPlaneMesh(width: Double = 1.0,
height: Double = 1.0,
widthSegments: Int = 1,
heightSegments: Int = 1): VertexBuffer {
return planeMesh(Vector3.ZERO, Vector3.UNIT_X, Vector3.UNIT_Z, Vector3.UNIT_Y,
width, height, widthSegments, heightSegments)
}
/**
* generates a finite plane with its center at (0,0,0) and spanning the xy-plane
*/
fun wallPlaneMesh(width: Double = 1.0,
height: Double = 1.0,
widthSegments: Int = 1,
heightSegments: Int = 1): VertexBuffer {
return planeMesh(Vector3.ZERO, Vector3.UNIT_X, Vector3.UNIT_Y, Vector3.UNIT_Z,
width, height, widthSegments, heightSegments)
}
fun generatePlane(center: Vector3,
right: Vector3,
forward: Vector3,
up: Vector3 = forward.cross(right).normalized,
width: Double = 1.0, height: Double = 1.0,
widthSegments: Int = 1, heightSegments: Int = 2,
writer: VertexWriter) {
val forwardStep = forward.normalized * (height / heightSegments)
val rightStep = right.normalized * (width / widthSegments)
val corner = center - forward.normalized * (height*0.5) - right.normalized * (width * 0.5)
val step = Vector2(1.0 / width, 1.0 / height)
for (v in 0 until heightSegments) {
for (u in 0 until widthSegments) {
val uv00 = Vector2(u + 0.0, v + 0.0) * step
val uv01 = Vector2(u + 0.0, v + 0.1) * step
val uv10 = Vector2(u + 1.0, v + 0.0) * step
val uv11 = Vector2(u + 1.0, v + 0.1) * step
val c00 = corner + forwardStep * v.toDouble() + rightStep * u.toDouble()
val c01 = corner + forwardStep * (v + 1).toDouble() + rightStep * u.toDouble()
val c10 = corner + forwardStep * v.toDouble() + rightStep * (u + 1).toDouble()
val c11 = corner + forwardStep * (v + 1).toDouble() + rightStep * (u + 1).toDouble()
writer(c11, up, uv00)
writer(c10, up, uv10)
writer(c00, up, uv11)
writer(c00, up, uv11)
writer(c01, up, uv01)
writer(c11, up, uv00)
}
}
}

View File

@@ -1,84 +1,84 @@
package org.openrndr.extras.meshgenerators
import org.openrndr.draw.VertexBuffer
import org.openrndr.math.Spherical
import org.openrndr.math.Vector2
fun sphereMesh(sides: Int = 16, segments: Int = 16, radius: Double = 1.0, invert: Boolean = false): VertexBuffer {
val vertexCount = 2 * sides * 3 + Math.max(0, (segments - 2)) * sides * 6
val vb = meshVertexBuffer(vertexCount)
vb.put {
generateSphere(sides, segments, radius, invert, bufferWriter(this))
}
return vb
}
fun generateSphere(sides: Int, segments: Int, radius: Double = 1.0, invert: Boolean = false, writer: VertexWriter) {
val inverter = if (invert) -1.0 else 1.0
for (t in 0 until segments) {
for (s in 0 until sides) {
val st00 = Spherical(radius, s * Math.PI * 2.0 / sides, t * Math.PI / segments)
val st01 = Spherical(radius, s * Math.PI * 2.0 / sides, (t + 1) * Math.PI / segments)
val st10 = Spherical(radius, (s + 1) * Math.PI * 2.0 / sides, t * Math.PI / segments)
val st11 = Spherical(radius, (s + 1) * Math.PI * 2.0 / sides, (t + 1) * Math.PI / segments)
val thetaMax = Math.PI
val phiMax = Math.PI * 2.0
when (t) {
0 -> {
writer(st00.cartesian, st00.cartesian.normalized * inverter, Vector2(st00.phi / phiMax, st00.theta / thetaMax))
writer(st01.cartesian, st01.cartesian.normalized * inverter, Vector2(st01.phi / phiMax, st01.theta / thetaMax))
writer(st11.cartesian, st11.cartesian.normalized * inverter, Vector2(st11.phi / phiMax, st11.theta / thetaMax))
}
segments - 1 -> {
writer(st11.cartesian, st11.cartesian.normalized * inverter, Vector2(st11.phi / phiMax, st11.theta / thetaMax))
writer(st10.cartesian, st10.cartesian.normalized * inverter, Vector2(st10.phi / phiMax, st10.theta / thetaMax))
writer(st00.cartesian, st00.cartesian.normalized * inverter, Vector2(st00.phi / phiMax, st00.theta / thetaMax))
}
else -> {
writer(st00.cartesian, st00.cartesian.normalized * inverter, Vector2(st00.phi / phiMax, st00.theta / thetaMax))
writer(st01.cartesian, st01.cartesian.normalized * inverter, Vector2(st01.phi / phiMax, st01.theta / thetaMax))
writer(st11.cartesian, st11.cartesian.normalized * inverter, Vector2(st11.phi / phiMax, st11.theta / thetaMax))
writer(st11.cartesian, st11.cartesian.normalized * inverter, Vector2(st11.phi / phiMax, st11.theta / thetaMax))
writer(st10.cartesian, st10.cartesian.normalized * inverter, Vector2(st10.phi / phiMax, st10.theta / thetaMax))
writer(st00.cartesian, st00.cartesian.normalized * inverter, Vector2(st00.phi / phiMax, st00.theta / thetaMax))
}
}
}
}
}
fun generateHemisphere(sides: Int, segments: Int, radius: Double = 1.0, invert: Boolean = false, writer: VertexWriter) {
val inverter = if (invert) -1.0 else 1.0
for (t in 0 until segments) {
for (s in 0 until sides) {
val st00 = Spherical(radius, s * Math.PI * 2.0 / sides, t * Math.PI*0.5 / segments)
val st01 = Spherical(radius, s * Math.PI * 2.0 / sides, (t + 1) * Math.PI*0.5 / segments)
val st10 = Spherical(radius, (s + 1) * Math.PI * 2.0 / sides, t * Math.PI*0.5 / segments)
val st11 = Spherical(radius, (s + 1) * Math.PI * 2.0 / sides, (t + 1) * Math.PI*0.5 / segments)
val thetaMax = Math.PI * 0.5
val phiMax = Math.PI * 2.0
when (t) {
0 -> {
writer(st00.cartesian, st00.cartesian.normalized * inverter, Vector2(st00.phi / phiMax, st00.theta / thetaMax))
writer(st01.cartesian, st01.cartesian.normalized * inverter, Vector2(st01.phi / phiMax, st01.theta / thetaMax))
writer(st11.cartesian, st11.cartesian.normalized * inverter, Vector2(st11.phi / phiMax, st11.theta / thetaMax))
}
else -> {
writer(st00.cartesian, st00.cartesian.normalized * inverter, Vector2(st00.phi / phiMax, st00.theta / thetaMax))
writer(st01.cartesian, st01.cartesian.normalized * inverter, Vector2(st01.phi / phiMax, st01.theta / thetaMax))
writer(st11.cartesian, st11.cartesian.normalized * inverter, Vector2(st11.phi / phiMax, st11.theta / thetaMax))
writer(st11.cartesian, st11.cartesian.normalized * inverter, Vector2(st11.phi / phiMax, st11.theta / thetaMax))
writer(st10.cartesian, st10.cartesian.normalized * inverter, Vector2(st10.phi / phiMax, st10.theta / thetaMax))
writer(st00.cartesian, st00.cartesian.normalized * inverter, Vector2(st00.phi / phiMax, st00.theta / thetaMax))
}
}
}
}
package org.openrndr.extras.meshgenerators
import org.openrndr.draw.VertexBuffer
import org.openrndr.math.Spherical
import org.openrndr.math.Vector2
fun sphereMesh(sides: Int = 16, segments: Int = 16, radius: Double = 1.0, invert: Boolean = false): VertexBuffer {
val vertexCount = 2 * sides * 3 + Math.max(0, (segments - 2)) * sides * 6
val vb = meshVertexBuffer(vertexCount)
vb.put {
generateSphere(sides, segments, radius, invert, bufferWriter(this))
}
return vb
}
fun generateSphere(sides: Int, segments: Int, radius: Double = 1.0, invert: Boolean = false, writer: VertexWriter) {
val inverter = if (invert) -1.0 else 1.0
for (t in 0 until segments) {
for (s in 0 until sides) {
val st00 = Spherical(radius, s * Math.PI * 2.0 / sides, t * Math.PI / segments)
val st01 = Spherical(radius, s * Math.PI * 2.0 / sides, (t + 1) * Math.PI / segments)
val st10 = Spherical(radius, (s + 1) * Math.PI * 2.0 / sides, t * Math.PI / segments)
val st11 = Spherical(radius, (s + 1) * Math.PI * 2.0 / sides, (t + 1) * Math.PI / segments)
val thetaMax = Math.PI
val phiMax = Math.PI * 2.0
when (t) {
0 -> {
writer(st00.cartesian, st00.cartesian.normalized * inverter, Vector2(st00.phi / phiMax, st00.theta / thetaMax))
writer(st01.cartesian, st01.cartesian.normalized * inverter, Vector2(st01.phi / phiMax, st01.theta / thetaMax))
writer(st11.cartesian, st11.cartesian.normalized * inverter, Vector2(st11.phi / phiMax, st11.theta / thetaMax))
}
segments - 1 -> {
writer(st11.cartesian, st11.cartesian.normalized * inverter, Vector2(st11.phi / phiMax, st11.theta / thetaMax))
writer(st10.cartesian, st10.cartesian.normalized * inverter, Vector2(st10.phi / phiMax, st10.theta / thetaMax))
writer(st00.cartesian, st00.cartesian.normalized * inverter, Vector2(st00.phi / phiMax, st00.theta / thetaMax))
}
else -> {
writer(st00.cartesian, st00.cartesian.normalized * inverter, Vector2(st00.phi / phiMax, st00.theta / thetaMax))
writer(st01.cartesian, st01.cartesian.normalized * inverter, Vector2(st01.phi / phiMax, st01.theta / thetaMax))
writer(st11.cartesian, st11.cartesian.normalized * inverter, Vector2(st11.phi / phiMax, st11.theta / thetaMax))
writer(st11.cartesian, st11.cartesian.normalized * inverter, Vector2(st11.phi / phiMax, st11.theta / thetaMax))
writer(st10.cartesian, st10.cartesian.normalized * inverter, Vector2(st10.phi / phiMax, st10.theta / thetaMax))
writer(st00.cartesian, st00.cartesian.normalized * inverter, Vector2(st00.phi / phiMax, st00.theta / thetaMax))
}
}
}
}
}
fun generateHemisphere(sides: Int, segments: Int, radius: Double = 1.0, invert: Boolean = false, writer: VertexWriter) {
val inverter = if (invert) -1.0 else 1.0
for (t in 0 until segments) {
for (s in 0 until sides) {
val st00 = Spherical(radius, s * Math.PI * 2.0 / sides, t * Math.PI*0.5 / segments)
val st01 = Spherical(radius, s * Math.PI * 2.0 / sides, (t + 1) * Math.PI*0.5 / segments)
val st10 = Spherical(radius, (s + 1) * Math.PI * 2.0 / sides, t * Math.PI*0.5 / segments)
val st11 = Spherical(radius, (s + 1) * Math.PI * 2.0 / sides, (t + 1) * Math.PI*0.5 / segments)
val thetaMax = Math.PI * 0.5
val phiMax = Math.PI * 2.0
when (t) {
0 -> {
writer(st00.cartesian, st00.cartesian.normalized * inverter, Vector2(st00.phi / phiMax, st00.theta / thetaMax))
writer(st01.cartesian, st01.cartesian.normalized * inverter, Vector2(st01.phi / phiMax, st01.theta / thetaMax))
writer(st11.cartesian, st11.cartesian.normalized * inverter, Vector2(st11.phi / phiMax, st11.theta / thetaMax))
}
else -> {
writer(st00.cartesian, st00.cartesian.normalized * inverter, Vector2(st00.phi / phiMax, st00.theta / thetaMax))
writer(st01.cartesian, st01.cartesian.normalized * inverter, Vector2(st01.phi / phiMax, st01.theta / thetaMax))
writer(st11.cartesian, st11.cartesian.normalized * inverter, Vector2(st11.phi / phiMax, st11.theta / thetaMax))
writer(st11.cartesian, st11.cartesian.normalized * inverter, Vector2(st11.phi / phiMax, st11.theta / thetaMax))
writer(st10.cartesian, st10.cartesian.normalized * inverter, Vector2(st10.phi / phiMax, st10.theta / thetaMax))
writer(st00.cartesian, st00.cartesian.normalized * inverter, Vector2(st00.phi / phiMax, st00.theta / thetaMax))
}
}
}
}
}

View File

@@ -1,54 +1,54 @@
# orx-no-clear
A simple OPENRNDR Extension that provides the classical drawing-without-clearing-the-screen functionality that
OPENRNDR does not support natively.
#### Usage
```kotlin
fun main() = application {
configure {
title = "NoClearProgram"
}
program {
backgroundColor = ColorRGBa.PINK
extend(NoClear())
extend {
drawer.circle(Math.cos(seconds) * width / 2.0 + width / 2.0, Math.sin(seconds * 0.24) * height / 2.0 + height / 2.0, 20.0)
}
}
}
```
#### Usage with additional configuration
Optionally, a static `backdrop` may be setup by providing custom code.
- Example 1. Customising the backdrop with an image
```kotlin
extend(NoClear()) {
val img = loadImage("data\\backdrop.png")
backdrop = {
drawer.image(img, 0.0, 0.0, width * 1.0, height * 1.0)
}
}
```
- Example 2. Customising the backdrop with a checker-board pattern
```kotlin
extend(NoClear()) {
backdrop = {
val xw = width / 8.0
val yh = height / 8.0
drawer.fill = ColorRGBa.RED
(0..7).forEach { row ->
(0..7).forEach { col ->
if ((row + col) % 2 == 0) {
drawer.rectangle(row * xw, col * yh, xw, yh)
}
}
}
}
}
```
# orx-no-clear
A simple OPENRNDR Extension that provides the classical drawing-without-clearing-the-screen functionality that
OPENRNDR does not support natively.
#### Usage
```kotlin
fun main() = application {
configure {
title = "NoClearProgram"
}
program {
backgroundColor = ColorRGBa.PINK
extend(NoClear())
extend {
drawer.circle(Math.cos(seconds) * width / 2.0 + width / 2.0, Math.sin(seconds * 0.24) * height / 2.0 + height / 2.0, 20.0)
}
}
}
```
#### Usage with additional configuration
Optionally, a static `backdrop` may be setup by providing custom code.
- Example 1. Customising the backdrop with an image
```kotlin
extend(NoClear()) {
val img = loadImage("data\\backdrop.png")
backdrop = {
drawer.image(img, 0.0, 0.0, width * 1.0, height * 1.0)
}
}
```
- Example 2. Customising the backdrop with a checker-board pattern
```kotlin
extend(NoClear()) {
backdrop = {
val xw = width / 8.0
val yh = height / 8.0
drawer.fill = ColorRGBa.RED
(0..7).forEach { row ->
(0..7).forEach { col ->
if ((row + col) % 2 == 0) {
drawer.rectangle(row * xw, col * yh, xw, yh)
}
}
}
}
}
```
NB! any submitted _lambda expression_ must be valid within the `renderTarget` context.

View File

@@ -1,57 +1,57 @@
package org.openrndr.extra.noclear
import org.openrndr.Extension
import org.openrndr.Program
import org.openrndr.color.ColorRGBa
import org.openrndr.draw.Drawer
import org.openrndr.draw.RenderTarget
import org.openrndr.draw.isolated
import org.openrndr.draw.renderTarget
import org.openrndr.math.Matrix44
class NoClear : Extension {
override var enabled: Boolean = true
private var renderTarget: RenderTarget? = null
/**
* code-block to draw an optional custom backdrop
*/
var backdrop: (() -> Unit)? = null
override fun beforeDraw(drawer: Drawer, program: Program) {
if (program.width > 0 && program.height > 0) { // only if the window is not minimised
if (renderTarget == null || renderTarget?.width != program.width || renderTarget?.height != program.height) {
renderTarget?.let {
it.colorBuffer(0).destroy()
it.detachColorBuffers()
it.destroy()
}
renderTarget = renderTarget(program.width, program.height) {
colorBuffer()
depthBuffer()
}
renderTarget?.let {
drawer.withTarget(it) {
background(program.backgroundColor ?: ColorRGBa.TRANSPARENT)
backdrop?.invoke() // draw custom backdrop
}
}
}
}
renderTarget?.bind()
}
override fun afterDraw(drawer: Drawer, program: Program) {
renderTarget?.unbind()
renderTarget?.let {
drawer.isolated {
drawer.ortho()
drawer.view = Matrix44.IDENTITY
drawer.model = Matrix44.IDENTITY
drawer.image(it.colorBuffer(0))
}
}
}
package org.openrndr.extra.noclear
import org.openrndr.Extension
import org.openrndr.Program
import org.openrndr.color.ColorRGBa
import org.openrndr.draw.Drawer
import org.openrndr.draw.RenderTarget
import org.openrndr.draw.isolated
import org.openrndr.draw.renderTarget
import org.openrndr.math.Matrix44
class NoClear : Extension {
override var enabled: Boolean = true
private var renderTarget: RenderTarget? = null
/**
* code-block to draw an optional custom backdrop
*/
var backdrop: (() -> Unit)? = null
override fun beforeDraw(drawer: Drawer, program: Program) {
if (program.width > 0 && program.height > 0) { // only if the window is not minimised
if (renderTarget == null || renderTarget?.width != program.width || renderTarget?.height != program.height) {
renderTarget?.let {
it.colorBuffer(0).destroy()
it.detachColorBuffers()
it.destroy()
}
renderTarget = renderTarget(program.width, program.height) {
colorBuffer()
depthBuffer()
}
renderTarget?.let {
drawer.withTarget(it) {
background(program.backgroundColor ?: ColorRGBa.TRANSPARENT)
backdrop?.invoke() // draw custom backdrop
}
}
}
}
renderTarget?.bind()
}
override fun afterDraw(drawer: Drawer, program: Program) {
renderTarget?.unbind()
renderTarget?.let {
drawer.isolated {
drawer.ortho()
drawer.view = Matrix44.IDENTITY
drawer.model = Matrix44.IDENTITY
drawer.image(it.colorBuffer(0))
}
}
}
}

View File

@@ -1,123 +1,123 @@
# orx-noise
A collection of noisy functions
## Uniform random numbers
```kotlin
val sua = Double.uniform()
val sub = Double.uniform(-1.0, 1.0)
val v2ua = Vector2.uniform()
val v2ub = Vector2.uniform(-1.0, 1.0)
val v2uc = Vector2.uniform(Vector2(0.0, 0.0), Vector2(1.0, 1.0))
val v2ur = Vector2.uniformRing(0.5, 1.0)
val v3ua = Vector3.uniform()
val v3ub = Vector3.uniform(-1.0, 1.0)
val v3uc = Vector3.uniform(Vector3(0.0, 0.0, 0.0), Vector3(1.0, 1.0, 1.0))
val v3ur = Vector3.uniformRing(0.5, 1.0)
val v4ua = Vector4.uniform()
val v4ub = Vector4.uniform(-1.0, 1.0)
val v4uc = Vector4.uniform(Vector4(0.0, 0.0, 0.0, 0.0), Vector4(1.0, 1.0, 1.0, 1.0))
val v4ur = Vector4.uniformRing(0.5, 1.0)
val ringSamples = List(500) { Vector2.uniformRing() }
```
## Multi-dimensional noise
These are a mostly straight port from FastNoise-Java but have a slightly different interface.
### Perlin noise
```
// -- 2d
val v0 = perlinLinear(seed, x, y)
val v1 = perlinQuintic(seed, x, y)
val v2 = perlinHermite(seed, x, y)
// -- 3d
val v3 = perlinLinear(seed, x, y, z)
val v4 = perlinQuintic(seed, x, y, z)
val v5 = perlinHermite(seed, x, y, z)
```
### Value noise
```
// -- 2d
val v0 = valueLinear(seed, x, y)
val v1 = valueQuintic(seed, x, y)
val v2 = valueHermite(seed, x, y)
// -- 3d
val v3 = valueLinear(seed, x, y, z)
val v4 = valueQuintic(seed, x, y, z)
val v5 = valueHermite(seed, x, y ,z)
```
### Simplex noise
```
// -- 2d
val v0 = simplexLinear(seed, x, y)
val v1 = simplexQuintic(seed, x, y)
val v2 = simplexHermite(seed, x, y)
// -- 3d
val v3 = simplexLinear(seed, x, y, z)
val v4 = simplexQuintic(seed, x, y, z)
val v5 = simplexHermite(seed, x, y ,z)
```
### Cubic noise
```
// -- 2d
val v0 = cubicLinear(seed, x, y)
val v1 = cubicQuintic(seed, x, y)
val v2 = cubicHermite(seed, x, y)
// -- 3d
val v3 = cubicLinear(seed, x, y, z)
val v4 = cubicQuintic(seed, x, y, z)
val v5 = cubicHermite(seed, x, y ,z)
```
### Fractal noise
The library provides 3 functions with which fractal noise can be composed.
#### Fractal brownian motion (FBM)
```
val v0 = fbm(seed, x, y, ::perlinLinear, octaves, lacunarity, gain)
val v1 = fbm(seed, x, y, ::simplexLinear, octaves, lacunarity, gain)
val v2 = fbm(seed, x, y, ::valueLinear, octaves, lacunarity, gain)
val v3 = fbm(seed, x, y, z, ::perlinLinear, octaves, lacunarity, gain)
val v4 = fbm(seed, x, y, z, ::simplexLinear, octaves, lacunarity, gain)
val v5 = fbm(seed, x, y, z, ::valueLinear, octaves, lacunarity, gain)
```
#### Rigid
```
val v0 = rigid(seed, x, y, ::perlinLinear, octaves, lacunarity, gain)
val v1 = rigid(seed, x, y, ::simplexLinear, octaves, lacunarity, gain)
val v2 = rigid(seed, x, y, ::valueLinear, octaves, lacunarity, gain)
val v3 = rigid(seed, x, y, z, ::perlinLinear, octaves, lacunarity, gain)
val v4 = rigid(seed, x, y, z, ::simplexLinear, octaves, lacunarity, gain)
val v5 = rigid(seed, x, y, z, ::valueLinear, octaves, lacunarity, gain)
```
#### Billow
```
val v0 = billow(seed, x, y, ::perlinLinear, octaves, lacunarity, gain)
val v1 = billow(seed, x, y, ::perlinLinear, octaves, lacunarity, gain)
val v2 = billow(seed, x, y, ::perlinLinear, octaves, lacunarity, gain)
val v3 = billow(seed, x, y, z, ::perlinLinear, octaves, lacunarity, gain)
val v4 = billow(seed, x, y, z, ::perlinLinear, octaves, lacunarity, gain)
val v5 = billow(seed, x, y, z, ::perlinLinear, octaves, lacunarity, gain)
# orx-noise
A collection of noisy functions
## Uniform random numbers
```kotlin
val sua = Double.uniform()
val sub = Double.uniform(-1.0, 1.0)
val v2ua = Vector2.uniform()
val v2ub = Vector2.uniform(-1.0, 1.0)
val v2uc = Vector2.uniform(Vector2(0.0, 0.0), Vector2(1.0, 1.0))
val v2ur = Vector2.uniformRing(0.5, 1.0)
val v3ua = Vector3.uniform()
val v3ub = Vector3.uniform(-1.0, 1.0)
val v3uc = Vector3.uniform(Vector3(0.0, 0.0, 0.0), Vector3(1.0, 1.0, 1.0))
val v3ur = Vector3.uniformRing(0.5, 1.0)
val v4ua = Vector4.uniform()
val v4ub = Vector4.uniform(-1.0, 1.0)
val v4uc = Vector4.uniform(Vector4(0.0, 0.0, 0.0, 0.0), Vector4(1.0, 1.0, 1.0, 1.0))
val v4ur = Vector4.uniformRing(0.5, 1.0)
val ringSamples = List(500) { Vector2.uniformRing() }
```
## Multi-dimensional noise
These are a mostly straight port from FastNoise-Java but have a slightly different interface.
### Perlin noise
```
// -- 2d
val v0 = perlinLinear(seed, x, y)
val v1 = perlinQuintic(seed, x, y)
val v2 = perlinHermite(seed, x, y)
// -- 3d
val v3 = perlinLinear(seed, x, y, z)
val v4 = perlinQuintic(seed, x, y, z)
val v5 = perlinHermite(seed, x, y, z)
```
### Value noise
```
// -- 2d
val v0 = valueLinear(seed, x, y)
val v1 = valueQuintic(seed, x, y)
val v2 = valueHermite(seed, x, y)
// -- 3d
val v3 = valueLinear(seed, x, y, z)
val v4 = valueQuintic(seed, x, y, z)
val v5 = valueHermite(seed, x, y ,z)
```
### Simplex noise
```
// -- 2d
val v0 = simplexLinear(seed, x, y)
val v1 = simplexQuintic(seed, x, y)
val v2 = simplexHermite(seed, x, y)
// -- 3d
val v3 = simplexLinear(seed, x, y, z)
val v4 = simplexQuintic(seed, x, y, z)
val v5 = simplexHermite(seed, x, y ,z)
```
### Cubic noise
```
// -- 2d
val v0 = cubicLinear(seed, x, y)
val v1 = cubicQuintic(seed, x, y)
val v2 = cubicHermite(seed, x, y)
// -- 3d
val v3 = cubicLinear(seed, x, y, z)
val v4 = cubicQuintic(seed, x, y, z)
val v5 = cubicHermite(seed, x, y ,z)
```
### Fractal noise
The library provides 3 functions with which fractal noise can be composed.
#### Fractal brownian motion (FBM)
```
val v0 = fbm(seed, x, y, ::perlinLinear, octaves, lacunarity, gain)
val v1 = fbm(seed, x, y, ::simplexLinear, octaves, lacunarity, gain)
val v2 = fbm(seed, x, y, ::valueLinear, octaves, lacunarity, gain)
val v3 = fbm(seed, x, y, z, ::perlinLinear, octaves, lacunarity, gain)
val v4 = fbm(seed, x, y, z, ::simplexLinear, octaves, lacunarity, gain)
val v5 = fbm(seed, x, y, z, ::valueLinear, octaves, lacunarity, gain)
```
#### Rigid
```
val v0 = rigid(seed, x, y, ::perlinLinear, octaves, lacunarity, gain)
val v1 = rigid(seed, x, y, ::simplexLinear, octaves, lacunarity, gain)
val v2 = rigid(seed, x, y, ::valueLinear, octaves, lacunarity, gain)
val v3 = rigid(seed, x, y, z, ::perlinLinear, octaves, lacunarity, gain)
val v4 = rigid(seed, x, y, z, ::simplexLinear, octaves, lacunarity, gain)
val v5 = rigid(seed, x, y, z, ::valueLinear, octaves, lacunarity, gain)
```
#### Billow
```
val v0 = billow(seed, x, y, ::perlinLinear, octaves, lacunarity, gain)
val v1 = billow(seed, x, y, ::perlinLinear, octaves, lacunarity, gain)
val v2 = billow(seed, x, y, ::perlinLinear, octaves, lacunarity, gain)
val v3 = billow(seed, x, y, z, ::perlinLinear, octaves, lacunarity, gain)
val v4 = billow(seed, x, y, z, ::perlinLinear, octaves, lacunarity, gain)
val v5 = billow(seed, x, y, z, ::perlinLinear, octaves, lacunarity, gain)
```

View File

@@ -1,28 +1,28 @@
package org.openrndr.extra.noise
private const val CUBIC_2D_BOUNDING = 1 / (1.5 * 1.5).toFloat()
fun cubic(seed: Int, x: Double, y: Double): Double {
val x1 = x.fastFloor()
val y1 = y.fastFloor()
val x0 = x1 - 1
val y0 = y1 - 1
val x2 = x1 + 1
val y2 = y1 + 1
val x3 = x1 + 2
val y3 = y1 + 2
val xs = x - x1.toDouble()
val ys = y - y1.toDouble()
return cubic(
cubic(valCoord2D(seed, x0, y0), valCoord2D(seed, x1, y0), valCoord2D(seed, x2, y0), valCoord2D(seed, x3, y0),
xs),
cubic(valCoord2D(seed, x0, y1), valCoord2D(seed, x1, y1), valCoord2D(seed, x2, y1), valCoord2D(seed, x3, y1),
xs),
cubic(valCoord2D(seed, x0, y2), valCoord2D(seed, x1, y2), valCoord2D(seed, x2, y2), valCoord2D(seed, x3, y2),
xs),
cubic(valCoord2D(seed, x0, y3), valCoord2D(seed, x1, y3), valCoord2D(seed, x2, y3), valCoord2D(seed, x3, y3),
xs),
ys) * CUBIC_2D_BOUNDING
package org.openrndr.extra.noise
private const val CUBIC_2D_BOUNDING = 1 / (1.5 * 1.5).toFloat()
fun cubic(seed: Int, x: Double, y: Double): Double {
val x1 = x.fastFloor()
val y1 = y.fastFloor()
val x0 = x1 - 1
val y0 = y1 - 1
val x2 = x1 + 1
val y2 = y1 + 1
val x3 = x1 + 2
val y3 = y1 + 2
val xs = x - x1.toDouble()
val ys = y - y1.toDouble()
return cubic(
cubic(valCoord2D(seed, x0, y0), valCoord2D(seed, x1, y0), valCoord2D(seed, x2, y0), valCoord2D(seed, x3, y0),
xs),
cubic(valCoord2D(seed, x0, y1), valCoord2D(seed, x1, y1), valCoord2D(seed, x2, y1), valCoord2D(seed, x3, y1),
xs),
cubic(valCoord2D(seed, x0, y2), valCoord2D(seed, x1, y2), valCoord2D(seed, x2, y2), valCoord2D(seed, x3, y2),
xs),
cubic(valCoord2D(seed, x0, y3), valCoord2D(seed, x1, y3), valCoord2D(seed, x2, y3), valCoord2D(seed, x3, y3),
xs),
ys) * CUBIC_2D_BOUNDING
}

View File

@@ -1,51 +1,51 @@
package org.openrndr.extra.noise
private const val CUBIC_3D_BOUNDING = 1 / (1.5 * 1.5 * 1.5).toFloat()
fun cubic(seed: Int, x: Double, y: Double, z: Double): Double {
val x1 = x.fastFloor()
val y1 = y.fastFloor()
val z1 = z.fastFloor()
val x0 = x1 - 1
val y0 = y1 - 1
val z0 = z1 - 1
val x2 = x1 + 1
val y2 = y1 + 1
val z2 = z1 + 1
val x3 = x1 + 2
val y3 = y1 + 2
val z3 = z1 + 2
val xs = x - x1.toFloat()
val ys = y - y1.toFloat()
val zs = z - z1.toFloat()
return cubic(
cubic(
cubic(valCoord3D(seed, x0, y0, z0), valCoord3D(seed, x1, y0, z0), valCoord3D(seed, x2, y0, z0), valCoord3D(seed, x3, y0, z0), xs),
cubic(valCoord3D(seed, x0, y1, z0), valCoord3D(seed, x1, y1, z0), valCoord3D(seed, x2, y1, z0), valCoord3D(seed, x3, y1, z0), xs),
cubic(valCoord3D(seed, x0, y2, z0), valCoord3D(seed, x1, y2, z0), valCoord3D(seed, x2, y2, z0), valCoord3D(seed, x3, y2, z0), xs),
cubic(valCoord3D(seed, x0, y3, z0), valCoord3D(seed, x1, y3, z0), valCoord3D(seed, x2, y3, z0), valCoord3D(seed, x3, y3, z0), xs),
ys),
cubic(
cubic(valCoord3D(seed, x0, y0, z1), valCoord3D(seed, x1, y0, z1), valCoord3D(seed, x2, y0, z1), valCoord3D(seed, x3, y0, z1), xs),
cubic(valCoord3D(seed, x0, y1, z1), valCoord3D(seed, x1, y1, z1), valCoord3D(seed, x2, y1, z1), valCoord3D(seed, x3, y1, z1), xs),
cubic(valCoord3D(seed, x0, y2, z1), valCoord3D(seed, x1, y2, z1), valCoord3D(seed, x2, y2, z1), valCoord3D(seed, x3, y2, z1), xs),
cubic(valCoord3D(seed, x0, y3, z1), valCoord3D(seed, x1, y3, z1), valCoord3D(seed, x2, y3, z1), valCoord3D(seed, x3, y3, z1), xs),
ys),
cubic(
cubic(valCoord3D(seed, x0, y0, z2), valCoord3D(seed, x1, y0, z2), valCoord3D(seed, x2, y0, z2), valCoord3D(seed, x3, y0, z2), xs),
cubic(valCoord3D(seed, x0, y1, z2), valCoord3D(seed, x1, y1, z2), valCoord3D(seed, x2, y1, z2), valCoord3D(seed, x3, y1, z2), xs),
cubic(valCoord3D(seed, x0, y2, z2), valCoord3D(seed, x1, y2, z2), valCoord3D(seed, x2, y2, z2), valCoord3D(seed, x3, y2, z2), xs),
cubic(valCoord3D(seed, x0, y3, z2), valCoord3D(seed, x1, y3, z2), valCoord3D(seed, x2, y3, z2), valCoord3D(seed, x3, y3, z2), xs),
ys),
cubic(
cubic(valCoord3D(seed, x0, y0, z3), valCoord3D(seed, x1, y0, z3), valCoord3D(seed, x2, y0, z3), valCoord3D(seed, x3, y0, z3), xs),
cubic(valCoord3D(seed, x0, y1, z3), valCoord3D(seed, x1, y1, z3), valCoord3D(seed, x2, y1, z3), valCoord3D(seed, x3, y1, z3), xs),
cubic(valCoord3D(seed, x0, y2, z3), valCoord3D(seed, x1, y2, z3), valCoord3D(seed, x2, y2, z3), valCoord3D(seed, x3, y2, z3), xs),
cubic(valCoord3D(seed, x0, y3, z3), valCoord3D(seed, x1, y3, z3), valCoord3D(seed, x2, y3, z3), valCoord3D(seed, x3, y3, z3), xs),
ys),
zs) * CUBIC_3D_BOUNDING
package org.openrndr.extra.noise
private const val CUBIC_3D_BOUNDING = 1 / (1.5 * 1.5 * 1.5).toFloat()
fun cubic(seed: Int, x: Double, y: Double, z: Double): Double {
val x1 = x.fastFloor()
val y1 = y.fastFloor()
val z1 = z.fastFloor()
val x0 = x1 - 1
val y0 = y1 - 1
val z0 = z1 - 1
val x2 = x1 + 1
val y2 = y1 + 1
val z2 = z1 + 1
val x3 = x1 + 2
val y3 = y1 + 2
val z3 = z1 + 2
val xs = x - x1.toFloat()
val ys = y - y1.toFloat()
val zs = z - z1.toFloat()
return cubic(
cubic(
cubic(valCoord3D(seed, x0, y0, z0), valCoord3D(seed, x1, y0, z0), valCoord3D(seed, x2, y0, z0), valCoord3D(seed, x3, y0, z0), xs),
cubic(valCoord3D(seed, x0, y1, z0), valCoord3D(seed, x1, y1, z0), valCoord3D(seed, x2, y1, z0), valCoord3D(seed, x3, y1, z0), xs),
cubic(valCoord3D(seed, x0, y2, z0), valCoord3D(seed, x1, y2, z0), valCoord3D(seed, x2, y2, z0), valCoord3D(seed, x3, y2, z0), xs),
cubic(valCoord3D(seed, x0, y3, z0), valCoord3D(seed, x1, y3, z0), valCoord3D(seed, x2, y3, z0), valCoord3D(seed, x3, y3, z0), xs),
ys),
cubic(
cubic(valCoord3D(seed, x0, y0, z1), valCoord3D(seed, x1, y0, z1), valCoord3D(seed, x2, y0, z1), valCoord3D(seed, x3, y0, z1), xs),
cubic(valCoord3D(seed, x0, y1, z1), valCoord3D(seed, x1, y1, z1), valCoord3D(seed, x2, y1, z1), valCoord3D(seed, x3, y1, z1), xs),
cubic(valCoord3D(seed, x0, y2, z1), valCoord3D(seed, x1, y2, z1), valCoord3D(seed, x2, y2, z1), valCoord3D(seed, x3, y2, z1), xs),
cubic(valCoord3D(seed, x0, y3, z1), valCoord3D(seed, x1, y3, z1), valCoord3D(seed, x2, y3, z1), valCoord3D(seed, x3, y3, z1), xs),
ys),
cubic(
cubic(valCoord3D(seed, x0, y0, z2), valCoord3D(seed, x1, y0, z2), valCoord3D(seed, x2, y0, z2), valCoord3D(seed, x3, y0, z2), xs),
cubic(valCoord3D(seed, x0, y1, z2), valCoord3D(seed, x1, y1, z2), valCoord3D(seed, x2, y1, z2), valCoord3D(seed, x3, y1, z2), xs),
cubic(valCoord3D(seed, x0, y2, z2), valCoord3D(seed, x1, y2, z2), valCoord3D(seed, x2, y2, z2), valCoord3D(seed, x3, y2, z2), xs),
cubic(valCoord3D(seed, x0, y3, z2), valCoord3D(seed, x1, y3, z2), valCoord3D(seed, x2, y3, z2), valCoord3D(seed, x3, y3, z2), xs),
ys),
cubic(
cubic(valCoord3D(seed, x0, y0, z3), valCoord3D(seed, x1, y0, z3), valCoord3D(seed, x2, y0, z3), valCoord3D(seed, x3, y0, z3), xs),
cubic(valCoord3D(seed, x0, y1, z3), valCoord3D(seed, x1, y1, z3), valCoord3D(seed, x2, y1, z3), valCoord3D(seed, x3, y1, z3), xs),
cubic(valCoord3D(seed, x0, y2, z3), valCoord3D(seed, x1, y2, z3), valCoord3D(seed, x2, y2, z3), valCoord3D(seed, x3, y2, z3), xs),
cubic(valCoord3D(seed, x0, y3, z3), valCoord3D(seed, x1, y3, z3), valCoord3D(seed, x2, y3, z3), valCoord3D(seed, x3, y3, z3), xs),
ys),
zs) * CUBIC_3D_BOUNDING
}

View File

@@ -1,105 +1,105 @@
package org.openrndr.extra.noise
inline fun fbm(seed: Int, x: Double, y: Double, z: Double, crossinline noise: (Int, Double, Double, Double) -> Double,
octaves: Int = 8, lacunarity: Double = 0.5, gain: Double = 0.5): Double {
var sum = noise(seed, x, y, z)
var amp = 1.0
var x = x
var y = y
var z = z
for (i in 1 until octaves) {
x *= lacunarity
y *= lacunarity
z *= lacunarity
amp *= gain
sum += noise(seed + i, x, y, z) * amp
}
return sum
}
inline fun fbm(seed: Int, x: Double, y: Double, crossinline noise: (Int, Double, Double) -> Double,
octaves: Int = 8, lacunarity: Double = 0.5, gain: Double = 0.5): Double {
var sum = noise(seed, x, y)
var amp = 1.0
var x = x
var y = y
for (i in 1 until octaves) {
x *= lacunarity
y *= lacunarity
amp *= gain
sum += noise(seed + i, x, y) * amp
}
return sum
}
inline fun billow(seed: Int, x: Double, y: Double, z: Double, crossinline noise: (Int, Double, Double, Double) -> Double,
octaves: Int = 8, lacunarity: Double = 0.5, gain: Double = 0.5) : Double {
var sum = Math.abs(noise(seed, x, y, z) * 2.0 - 1.0)
var amp = 1.0
var x = x
var y = y
var z = z
for (i in 1 until octaves) {
x *= lacunarity
y *= lacunarity
z *= lacunarity
amp *= gain
sum += Math.abs(noise(seed + i, x, y, z) * 2.0 - 1.0) * amp
}
return sum
}
inline fun billow(seed: Int, x: Double, y: Double, crossinline noise: (Int, Double, Double) -> Double,
octaves: Int = 8, lacunarity: Double = 0.5, gain: Double = 0.5) : Double {
var sum = Math.abs(noise(seed, x, y) * 2.0 - 1.0)
var amp = 1.0
var x = x
var y = y
for (i in 1 until octaves) {
x *= lacunarity
y *= lacunarity
amp *= gain
sum += Math.abs(noise(seed + i, x, y) * 2.0 - 1.0) * amp
}
return sum
}
inline fun rigid(seed: Int, x: Double, y: Double, crossinline noise: (Int, Double, Double) -> Double,
octaves: Int = 8, lacunarity: Double = 0.5, gain: Double = 0.5): Double {
var sum = 1.0 - Math.abs(noise(seed, x, y))
var amp = 1.0
var x = x
var y = y
for (i in 1 until octaves) {
x *= lacunarity
y *= lacunarity
amp *= gain
sum -= (1.0 - Math.abs(noise(seed + i, x, y))) * amp
}
return sum
}
inline fun rigid(seed: Int, x: Double, y: Double, z: Double, crossinline noise: (Int, Double, Double, Double) -> Double,
octaves: Int = 8, lacunarity: Double = 0.5, gain: Double = 0.5): Double {
var sum = 1.0 - Math.abs(noise(seed, x, y, z))
var amp = 1.0
var x = x
var y = y
var z = z
for (i in 1 until octaves) {
x *= lacunarity
y *= lacunarity
z *= lacunarity
amp *= gain
sum -= (1.0 - Math.abs(noise(seed + i, x, y, z))) * amp
}
return sum
}
package org.openrndr.extra.noise
inline fun fbm(seed: Int, x: Double, y: Double, z: Double, crossinline noise: (Int, Double, Double, Double) -> Double,
octaves: Int = 8, lacunarity: Double = 0.5, gain: Double = 0.5): Double {
var sum = noise(seed, x, y, z)
var amp = 1.0
var x = x
var y = y
var z = z
for (i in 1 until octaves) {
x *= lacunarity
y *= lacunarity
z *= lacunarity
amp *= gain
sum += noise(seed + i, x, y, z) * amp
}
return sum
}
inline fun fbm(seed: Int, x: Double, y: Double, crossinline noise: (Int, Double, Double) -> Double,
octaves: Int = 8, lacunarity: Double = 0.5, gain: Double = 0.5): Double {
var sum = noise(seed, x, y)
var amp = 1.0
var x = x
var y = y
for (i in 1 until octaves) {
x *= lacunarity
y *= lacunarity
amp *= gain
sum += noise(seed + i, x, y) * amp
}
return sum
}
inline fun billow(seed: Int, x: Double, y: Double, z: Double, crossinline noise: (Int, Double, Double, Double) -> Double,
octaves: Int = 8, lacunarity: Double = 0.5, gain: Double = 0.5) : Double {
var sum = Math.abs(noise(seed, x, y, z) * 2.0 - 1.0)
var amp = 1.0
var x = x
var y = y
var z = z
for (i in 1 until octaves) {
x *= lacunarity
y *= lacunarity
z *= lacunarity
amp *= gain
sum += Math.abs(noise(seed + i, x, y, z) * 2.0 - 1.0) * amp
}
return sum
}
inline fun billow(seed: Int, x: Double, y: Double, crossinline noise: (Int, Double, Double) -> Double,
octaves: Int = 8, lacunarity: Double = 0.5, gain: Double = 0.5) : Double {
var sum = Math.abs(noise(seed, x, y) * 2.0 - 1.0)
var amp = 1.0
var x = x
var y = y
for (i in 1 until octaves) {
x *= lacunarity
y *= lacunarity
amp *= gain
sum += Math.abs(noise(seed + i, x, y) * 2.0 - 1.0) * amp
}
return sum
}
inline fun rigid(seed: Int, x: Double, y: Double, crossinline noise: (Int, Double, Double) -> Double,
octaves: Int = 8, lacunarity: Double = 0.5, gain: Double = 0.5): Double {
var sum = 1.0 - Math.abs(noise(seed, x, y))
var amp = 1.0
var x = x
var y = y
for (i in 1 until octaves) {
x *= lacunarity
y *= lacunarity
amp *= gain
sum -= (1.0 - Math.abs(noise(seed + i, x, y))) * amp
}
return sum
}
inline fun rigid(seed: Int, x: Double, y: Double, z: Double, crossinline noise: (Int, Double, Double, Double) -> Double,
octaves: Int = 8, lacunarity: Double = 0.5, gain: Double = 0.5): Double {
var sum = 1.0 - Math.abs(noise(seed, x, y, z))
var amp = 1.0
var x = x
var y = y
var z = z
for (i in 1 until octaves) {
x *= lacunarity
y *= lacunarity
z *= lacunarity
amp *= gain
sum -= (1.0 - Math.abs(noise(seed + i, x, y, z))) * amp
}
return sum
}

File diff suppressed because one or more lines are too long

View File

@@ -1,24 +1,24 @@
package org.openrndr.extra.noise
fun hermite(t: Double): Double {
return t * t * (3 - 2 * t)
}
fun quintic(t: Double): Double {
return t * t * t * (t * (t * 6 - 15) + 10)
}
fun cubic(a: Double, b: Double, c: Double, d: Double, t: Double) : Double {
val p = d - c - (a - b)
return t * t * t * p + t * t * (a - b - p) + t * (c - a) + b
}
fun linear(x: Double) : Double {
return x
}
fun lerp(left: Double, right: Double, x: Double): Double {
return left * (1.0 - x) + right * x
package org.openrndr.extra.noise
fun hermite(t: Double): Double {
return t * t * (3 - 2 * t)
}
fun quintic(t: Double): Double {
return t * t * t * (t * (t * 6 - 15) + 10)
}
fun cubic(a: Double, b: Double, c: Double, d: Double, t: Double) : Double {
val p = d - c - (a - b)
return t * t * t * p + t * t * (a - b - p) + t * (c - a) + b
}
fun linear(x: Double) : Double {
return x
}
fun lerp(left: Double, right: Double, x: Double): Double {
return left * (1.0 - x) + right * x
}

View File

@@ -1,6 +1,6 @@
package org.openrndr.extra.noise
fun Double.fastFloor(): Int {
return if (this >= 0) this.toInt() else this.toInt() - 1
package org.openrndr.extra.noise
fun Double.fastFloor(): Int {
return if (this >= 0) this.toInt() else this.toInt() - 1
}

View File

@@ -1,26 +1,26 @@
package org.openrndr.extra.noise
fun perlinLinear(seed: Int, x: Double, y: Double) = perlin(seed, x, y, ::linear)
fun perlinQuintic(seed: Int, x: Double, y: Double) = perlin(seed, x, y, ::quintic)
fun perlinHermite(seed: Int, x: Double, y: Double) = perlin(seed, x, y, ::hermite)
private fun perlin(seed: Int, x: Double, y: Double, interpolator: (Double) -> Double): Double {
val x0 = x.fastFloor()
val y0 = y.fastFloor()
val x1 = x0 + 1
val y1 = y0 + 1
val xs = interpolator(x - x0)
val ys = interpolator(y - y0)
val xd0 = x - x0
val yd0 = y - y0
val xd1 = xd0 - 1
val yd1 = yd0 - 1
val xf0 = lerp(gradCoord2D(seed, x0, y0, xd0, yd0), gradCoord2D(seed, x1, y0, xd1, yd0), xs)
val xf1 = lerp(gradCoord2D(seed, x0, y1, xd0, yd1), gradCoord2D(seed, x1, y1, xd1, yd1), xs)
return lerp(xf0, xf1, ys)
package org.openrndr.extra.noise
fun perlinLinear(seed: Int, x: Double, y: Double) = perlin(seed, x, y, ::linear)
fun perlinQuintic(seed: Int, x: Double, y: Double) = perlin(seed, x, y, ::quintic)
fun perlinHermite(seed: Int, x: Double, y: Double) = perlin(seed, x, y, ::hermite)
private fun perlin(seed: Int, x: Double, y: Double, interpolator: (Double) -> Double): Double {
val x0 = x.fastFloor()
val y0 = y.fastFloor()
val x1 = x0 + 1
val y1 = y0 + 1
val xs = interpolator(x - x0)
val ys = interpolator(y - y0)
val xd0 = x - x0
val yd0 = y - y0
val xd1 = xd0 - 1
val yd1 = yd0 - 1
val xf0 = lerp(gradCoord2D(seed, x0, y0, xd0, yd0), gradCoord2D(seed, x1, y0, xd1, yd0), xs)
val xf1 = lerp(gradCoord2D(seed, x0, y1, xd0, yd1), gradCoord2D(seed, x1, y1, xd1, yd1), xs)
return lerp(xf0, xf1, ys)
}

View File

@@ -1,35 +1,35 @@
package org.openrndr.extra.noise
fun perlinLinear(seed: Int, x: Double, y: Double, z: Double) = perlin(seed, x, y, z, ::linear)
fun perlinQuintic(seed: Int, x: Double, y: Double, z: Double) = perlin(seed, x, y, z, ::quintic)
fun perlinHermite(seed: Int, x: Double, y: Double, z: Double) = perlin(seed, x, y, z, ::hermite)
inline fun perlin(seed: Int, x: Double, y: Double, z: Double, crossinline interpolator: (Double) -> Double = ::linear): Double {
val x0 = x.fastFloor()
val y0 = y.fastFloor()
val z0 = z.fastFloor()
val x1 = x0 + 1
val y1 = y0 + 1
val z1 = z0 + 1
val xs: Double = interpolator(x - x0)
val ys: Double = interpolator(y - y0)
val zs: Double = interpolator(z - z0)
val xd0 = x - x0
val yd0 = y - y0
val zd0 = z - z0
val xd1 = xd0 - 1
val yd1 = yd0 - 1
val zd1 = zd0 - 1
val xf00 = lerp(gradCoord3D(seed, x0, y0, z0, xd0, yd0, zd0), gradCoord3D(seed, x1, y0, z0, xd1, yd0, zd0), xs)
val xf10 = lerp(gradCoord3D(seed, x0, y1, z0, xd0, yd1, zd0), gradCoord3D(seed, x1, y1, z0, xd1, yd1, zd0), xs)
val xf01 = lerp(gradCoord3D(seed, x0, y0, z1, xd0, yd0, zd1), gradCoord3D(seed, x1, y0, z1, xd1, yd0, zd1), xs)
val xf11 = lerp(gradCoord3D(seed, x0, y1, z1, xd0, yd1, zd1), gradCoord3D(seed, x1, y1, z1, xd1, yd1, zd1), xs)
val yf0 = lerp(xf00, xf10, ys)
val yf1 = lerp(xf01, xf11, ys)
return lerp(yf0, yf1, zs)
package org.openrndr.extra.noise
fun perlinLinear(seed: Int, x: Double, y: Double, z: Double) = perlin(seed, x, y, z, ::linear)
fun perlinQuintic(seed: Int, x: Double, y: Double, z: Double) = perlin(seed, x, y, z, ::quintic)
fun perlinHermite(seed: Int, x: Double, y: Double, z: Double) = perlin(seed, x, y, z, ::hermite)
inline fun perlin(seed: Int, x: Double, y: Double, z: Double, crossinline interpolator: (Double) -> Double = ::linear): Double {
val x0 = x.fastFloor()
val y0 = y.fastFloor()
val z0 = z.fastFloor()
val x1 = x0 + 1
val y1 = y0 + 1
val z1 = z0 + 1
val xs: Double = interpolator(x - x0)
val ys: Double = interpolator(y - y0)
val zs: Double = interpolator(z - z0)
val xd0 = x - x0
val yd0 = y - y0
val zd0 = z - z0
val xd1 = xd0 - 1
val yd1 = yd0 - 1
val zd1 = zd0 - 1
val xf00 = lerp(gradCoord3D(seed, x0, y0, z0, xd0, yd0, zd0), gradCoord3D(seed, x1, y0, z0, xd1, yd0, zd0), xs)
val xf10 = lerp(gradCoord3D(seed, x0, y1, z0, xd0, yd1, zd0), gradCoord3D(seed, x1, y1, z0, xd1, yd1, zd0), xs)
val xf01 = lerp(gradCoord3D(seed, x0, y0, z1, xd0, yd0, zd1), gradCoord3D(seed, x1, y0, z1, xd1, yd0, zd1), xs)
val xf11 = lerp(gradCoord3D(seed, x0, y1, z1, xd0, yd1, zd1), gradCoord3D(seed, x1, y1, z1, xd1, yd1, zd1), xs)
val yf0 = lerp(xf00, xf10, ys)
val yf1 = lerp(xf01, xf11, ys)
return lerp(yf0, yf1, zs)
}

View File

@@ -1,63 +1,63 @@
package org.openrndr.extra.noise
private val G2 = 1.0 / 4.0
private val F2 = 1.0 / 2.0
fun simplex(seed: Int, x: Double, y: Double): Double {
var t = (x + y) * F2
val i = (x + t).fastFloor()
val j = (y + t).fastFloor()
t = ((i + j) * G2)
val X0 = i - t
val Y0 = j - t
val x0 = x - X0
val y0 = y - Y0
val i1: Int
val j1: Int
if (x0 > y0) {
i1 = 1
j1 = 0
} else {
i1 = 0
j1 = 1
}
val x1 = x0 - i1 + G2
val y1 = y0 - j1 + G2
val x2 = x0 - 1 + F2
val y2 = y0 - 1 + F2
val n0: Double
val n1: Double
val n2: Double
t = 0.5 - x0 * x0 - y0 * y0
if (t < 0)
n0 = 0.0
else {
t *= t
n0 = t * t * gradCoord2D(seed, i, j, x0, y0)
}
t = 0.5 - x1 * x1 - y1 * y1
if (t < 0)
n1 = 0.0
else {
t *= t
n1 = t * t * gradCoord2D(seed, i + i1, j + j1, x1, y1)
}
t = 0.5 - x2 * x2 - y2 * y2
if (t < 0)
n2 = 0.0
else {
t *= t
n2 = t * t * gradCoord2D(seed, i + 1, j + 1, x2, y2)
}
return 50.0 * (n0 + n1 + n2)
package org.openrndr.extra.noise
private val G2 = 1.0 / 4.0
private val F2 = 1.0 / 2.0
fun simplex(seed: Int, x: Double, y: Double): Double {
var t = (x + y) * F2
val i = (x + t).fastFloor()
val j = (y + t).fastFloor()
t = ((i + j) * G2)
val X0 = i - t
val Y0 = j - t
val x0 = x - X0
val y0 = y - Y0
val i1: Int
val j1: Int
if (x0 > y0) {
i1 = 1
j1 = 0
} else {
i1 = 0
j1 = 1
}
val x1 = x0 - i1 + G2
val y1 = y0 - j1 + G2
val x2 = x0 - 1 + F2
val y2 = y0 - 1 + F2
val n0: Double
val n1: Double
val n2: Double
t = 0.5 - x0 * x0 - y0 * y0
if (t < 0)
n0 = 0.0
else {
t *= t
n0 = t * t * gradCoord2D(seed, i, j, x0, y0)
}
t = 0.5 - x1 * x1 - y1 * y1
if (t < 0)
n1 = 0.0
else {
t *= t
n1 = t * t * gradCoord2D(seed, i + i1, j + j1, x1, y1)
}
t = 0.5 - x2 * x2 - y2 * y2
if (t < 0)
n2 = 0.0
else {
t *= t
n2 = t * t * gradCoord2D(seed, i + 1, j + 1, x2, y2)
}
return 50.0 * (n0 + n1 + n2)
}

View File

@@ -1,94 +1,94 @@
package org.openrndr.extra.noise
fun simplex(seed: Int, x: Double, y: Double, z: Double): Double {
var t = (x + y + z) / 3.0
val i = (x + t).fastFloor()
val j = (y + t).fastFloor()
val k = (z + t).fastFloor()
val t2 = (i + j + k) / 6.0
val x0 = x - (i - t2)
val y0 = y - (j - t2)
val z0 = z - (k - t2)
val i1: Int
val j1: Int
val k1: Int
val i2: Int
val j2: Int
val k2: Int
if (x0 >= y0) {
when {
y0 >= z0 -> {
i1 = 1; j1 = 0; k1 = 0; i2 = 1; j2 = 1; k2 = 0; }
x0 >= z0 -> {
i1 = 1; j1 = 0; k1 = 0; i2 = 1; j2 = 0; k2 = 1; }
else -> {
i1 = 0; j1 = 0; k1 = 1; i2 = 1; j2 = 0; k2 = 1; }
}
} else {
when {
y0 < z0 -> {
i1 = 0; j1 = 0; k1 = 1; i2 = 0; j2 = 1; k2 = 1; }
x0 < z0 -> {
i1 = 0; j1 = 1; k1 = 0; i2 = 0; j2 = 1; k2 = 1; }
else -> {
i1 = 0; j1 = 1; k1 = 0; i2 = 1; j2 = 1; k2 = 0; }
}
}
val x1 = x0 - i1 + 1.0 / 6.0
val y1 = y0 - j1 + 1.0 / 6.0
val z1 = z0 - k1 + 1.0 / 6.0
val x2 = x0 - i2 + 1.0 / 3.0
val y2 = y0 - j2 + 1.0 / 3.0
val z2 = z0 - k2 + 1.0 / 3.0
val x3 = x0 + ((1.0 / 6.0) * 3.0 - 1.0)
val y3 = y0 + ((1.0 / 6.0) * 3.0 - 1.0)
val z3 = z0 + ((1.0 / 6.0) * 3.0 - 1.0)
val n0: Double
run {
var t = 0.6 * x0 * x0 - y0 * y0 - z0 * z0
if (t < 0) {
n0 = 0.0
} else {
t *= t
n0 = t * t * gradCoord3D(seed, i, j, k, x0, y0, z0)
}
}
val n1: Double
run {
var t = 0.6 * x1 * x1 - y1 * y1 - z1 * z1
if (t < 0) {
n1 = 0.0
} else {
t *= t
n1 = t * t * gradCoord3D(seed, i + i1, j + j1, k + k1, x1, y1, z1)
}
}
val n2: Double
run {
var t = 0.6 * x2 * x2 - y2 * y2 - z2 * z2
if (t < 0) {
n2 = 0.0
} else {
t *= t
n2 = t * t * gradCoord3D(seed, i + i2, j + j2, k + k2, x2, y2, z2)
}
}
val n3: Double
run {
var t = 0.6 - x3 * x3 - y3 * y3 - z3 * z3
if (t < 0)
n3 = 0.0
else {
t *= t
n3 = t * t * gradCoord3D(seed, i + 1, j + 1, k + 1, x3, y3, z3)
}
}
return 32 * (n0 + n1 + n2 + n3)
package org.openrndr.extra.noise
fun simplex(seed: Int, x: Double, y: Double, z: Double): Double {
var t = (x + y + z) / 3.0
val i = (x + t).fastFloor()
val j = (y + t).fastFloor()
val k = (z + t).fastFloor()
val t2 = (i + j + k) / 6.0
val x0 = x - (i - t2)
val y0 = y - (j - t2)
val z0 = z - (k - t2)
val i1: Int
val j1: Int
val k1: Int
val i2: Int
val j2: Int
val k2: Int
if (x0 >= y0) {
when {
y0 >= z0 -> {
i1 = 1; j1 = 0; k1 = 0; i2 = 1; j2 = 1; k2 = 0; }
x0 >= z0 -> {
i1 = 1; j1 = 0; k1 = 0; i2 = 1; j2 = 0; k2 = 1; }
else -> {
i1 = 0; j1 = 0; k1 = 1; i2 = 1; j2 = 0; k2 = 1; }
}
} else {
when {
y0 < z0 -> {
i1 = 0; j1 = 0; k1 = 1; i2 = 0; j2 = 1; k2 = 1; }
x0 < z0 -> {
i1 = 0; j1 = 1; k1 = 0; i2 = 0; j2 = 1; k2 = 1; }
else -> {
i1 = 0; j1 = 1; k1 = 0; i2 = 1; j2 = 1; k2 = 0; }
}
}
val x1 = x0 - i1 + 1.0 / 6.0
val y1 = y0 - j1 + 1.0 / 6.0
val z1 = z0 - k1 + 1.0 / 6.0
val x2 = x0 - i2 + 1.0 / 3.0
val y2 = y0 - j2 + 1.0 / 3.0
val z2 = z0 - k2 + 1.0 / 3.0
val x3 = x0 + ((1.0 / 6.0) * 3.0 - 1.0)
val y3 = y0 + ((1.0 / 6.0) * 3.0 - 1.0)
val z3 = z0 + ((1.0 / 6.0) * 3.0 - 1.0)
val n0: Double
run {
var t = 0.6 * x0 * x0 - y0 * y0 - z0 * z0
if (t < 0) {
n0 = 0.0
} else {
t *= t
n0 = t * t * gradCoord3D(seed, i, j, k, x0, y0, z0)
}
}
val n1: Double
run {
var t = 0.6 * x1 * x1 - y1 * y1 - z1 * z1
if (t < 0) {
n1 = 0.0
} else {
t *= t
n1 = t * t * gradCoord3D(seed, i + i1, j + j1, k + k1, x1, y1, z1)
}
}
val n2: Double
run {
var t = 0.6 * x2 * x2 - y2 * y2 - z2 * z2
if (t < 0) {
n2 = 0.0
} else {
t *= t
n2 = t * t * gradCoord3D(seed, i + i2, j + j2, k + k2, x2, y2, z2)
}
}
val n3: Double
run {
var t = 0.6 - x3 * x3 - y3 * y3 - z3 * z3
if (t < 0)
n3 = 0.0
else {
t *= t
n3 = t * t * gradCoord3D(seed, i + 1, j + 1, k + 1, x3, y3, z3)
}
}
return 32 * (n0 + n1 + n2 + n3)
}

View File

@@ -1,71 +1,71 @@
package org.openrndr.extra.noise
import org.openrndr.math.Vector2
import org.openrndr.math.Vector3
import org.openrndr.math.Vector4
fun Double.Companion.uniform(min: Double = -1.0, max: Double = 1.0): Double {
return (Math.random() * (max - min)) + min
}
fun Vector2.Companion.uniform(min: Vector2 = -ONE, max: Vector2 = ONE): Vector2 {
return Vector2(Double.uniform(min.x, max.x), Double.uniform(min.y, max.y))
}
fun Vector2.Companion.uniform(min: Double = -1.0, max: Double = 1.0) =
Vector2.uniform(Vector2(min, min), Vector2(max, max))
fun Vector2.Companion.uniformRing(innerRadius: Double = 0.0, outerRadius: Double = 1.0): Vector2 {
while (true) {
uniform(-outerRadius, outerRadius).let {
val squaredLength = it.squaredLength
if (squaredLength >= innerRadius * innerRadius && squaredLength < outerRadius * outerRadius) {
return it
}
}
}
}
fun Vector3.Companion.uniform(min: Double = -1.0, max: Double = 1.0): Vector3 =
Vector3.uniform(Vector3(min, min, min), Vector3(max, max, max))
fun Vector3.Companion.uniform(min: Vector3 = -ONE, max: Vector3 = ONE): Vector3 {
return Vector3(Double.uniform(min.x, max.x), Double.uniform(min.y, max.y), Double.uniform(min.z, max.z))
}
// squared length 'polyfill' for OPENRNDR 0.3.30
private val Vector3.squaredLength__: Double get() = x * x + y * y + z * z
fun Vector3.Companion.uniformRing(innerRadius: Double = 0.0, outerRadius: Double = 1.0): Vector3 {
while (true) {
uniform(-outerRadius, outerRadius).let {
val squaredLength = it.squaredLength__
if (squaredLength >= innerRadius * innerRadius && squaredLength < outerRadius * outerRadius) {
return it
}
}
}
}
// squared length 'polyfill' for OPENRNDR 0.3.30
private val Vector4.squaredLength__: Double get() = x * x + y * y + z * z + w * w
fun Vector4.Companion.uniform(min: Double = -1.0, max: Double = 1.0): Vector4 =
Vector4.uniform(Vector4(min, min, min, min), Vector4(max, max,max, max))
fun Vector4.Companion.uniform(min: Vector4 = -ONE, max: Vector4 = ONE): Vector4 {
return Vector4(Double.uniform(min.x, max.x), Double.uniform(min.y, max.y), Double.uniform(min.z, max.z), Double.uniform(min.w, max.w))
}
fun Vector4.Companion.uniformRing(innerRadius: Double = 0.0, outerRadius: Double = 1.0): Vector4 {
while (true) {
uniform(-outerRadius, outerRadius).let {
val squaredLength = it.squaredLength__
if (squaredLength >= innerRadius * innerRadius && squaredLength < outerRadius * outerRadius) {
return it
}
}
}
}
package org.openrndr.extra.noise
import org.openrndr.math.Vector2
import org.openrndr.math.Vector3
import org.openrndr.math.Vector4
fun Double.Companion.uniform(min: Double = -1.0, max: Double = 1.0): Double {
return (Math.random() * (max - min)) + min
}
fun Vector2.Companion.uniform(min: Vector2 = -ONE, max: Vector2 = ONE): Vector2 {
return Vector2(Double.uniform(min.x, max.x), Double.uniform(min.y, max.y))
}
fun Vector2.Companion.uniform(min: Double = -1.0, max: Double = 1.0) =
Vector2.uniform(Vector2(min, min), Vector2(max, max))
fun Vector2.Companion.uniformRing(innerRadius: Double = 0.0, outerRadius: Double = 1.0): Vector2 {
while (true) {
uniform(-outerRadius, outerRadius).let {
val squaredLength = it.squaredLength
if (squaredLength >= innerRadius * innerRadius && squaredLength < outerRadius * outerRadius) {
return it
}
}
}
}
fun Vector3.Companion.uniform(min: Double = -1.0, max: Double = 1.0): Vector3 =
Vector3.uniform(Vector3(min, min, min), Vector3(max, max, max))
fun Vector3.Companion.uniform(min: Vector3 = -ONE, max: Vector3 = ONE): Vector3 {
return Vector3(Double.uniform(min.x, max.x), Double.uniform(min.y, max.y), Double.uniform(min.z, max.z))
}
// squared length 'polyfill' for OPENRNDR 0.3.30
private val Vector3.squaredLength__: Double get() = x * x + y * y + z * z
fun Vector3.Companion.uniformRing(innerRadius: Double = 0.0, outerRadius: Double = 1.0): Vector3 {
while (true) {
uniform(-outerRadius, outerRadius).let {
val squaredLength = it.squaredLength__
if (squaredLength >= innerRadius * innerRadius && squaredLength < outerRadius * outerRadius) {
return it
}
}
}
}
// squared length 'polyfill' for OPENRNDR 0.3.30
private val Vector4.squaredLength__: Double get() = x * x + y * y + z * z + w * w
fun Vector4.Companion.uniform(min: Double = -1.0, max: Double = 1.0): Vector4 =
Vector4.uniform(Vector4(min, min, min, min), Vector4(max, max,max, max))
fun Vector4.Companion.uniform(min: Vector4 = -ONE, max: Vector4 = ONE): Vector4 {
return Vector4(Double.uniform(min.x, max.x), Double.uniform(min.y, max.y), Double.uniform(min.z, max.z), Double.uniform(min.w, max.w))
}
fun Vector4.Companion.uniformRing(innerRadius: Double = 0.0, outerRadius: Double = 1.0): Vector4 {
while (true) {
uniform(-outerRadius, outerRadius).let {
val squaredLength = it.squaredLength__
if (squaredLength >= innerRadius * innerRadius && squaredLength < outerRadius * outerRadius) {
return it
}
}
}
}

View File

@@ -1,21 +1,21 @@
package org.openrndr.extra.noise
fun valueLinear(seed: Int, x:Double, y:Double) = value(seed, x, y, ::linear)
fun valueQuintic(seed: Int, x:Double, y:Double) = value(seed, x, y, ::quintic)
fun valueHermite(seed: Int, x:Double, y:Double) = value(seed, x, y, ::hermite)
inline fun value(seed: Int, x: Double, y: Double, crossinline interpolation: (Double) -> Double = ::linear): Double {
val x0 = x.fastFloor()
val y0 = y.fastFloor()
val x1 = x0 + 1
val y1 = y0 + 1
val xs = interpolation(x - x0)
val ys = interpolation(y - y0)
val xf0 = lerp(valCoord2D(seed, x0, y0), valCoord2D(seed, x1, y0), xs)
val xf1 = lerp(valCoord2D(seed, x0, y1), valCoord2D(seed, x1, y1), xs)
return lerp(xf0, xf1, ys)
package org.openrndr.extra.noise
fun valueLinear(seed: Int, x:Double, y:Double) = value(seed, x, y, ::linear)
fun valueQuintic(seed: Int, x:Double, y:Double) = value(seed, x, y, ::quintic)
fun valueHermite(seed: Int, x:Double, y:Double) = value(seed, x, y, ::hermite)
inline fun value(seed: Int, x: Double, y: Double, crossinline interpolation: (Double) -> Double = ::linear): Double {
val x0 = x.fastFloor()
val y0 = y.fastFloor()
val x1 = x0 + 1
val y1 = y0 + 1
val xs = interpolation(x - x0)
val ys = interpolation(y - y0)
val xf0 = lerp(valCoord2D(seed, x0, y0), valCoord2D(seed, x1, y0), xs)
val xf1 = lerp(valCoord2D(seed, x0, y1), valCoord2D(seed, x1, y1), xs)
return lerp(xf0, xf1, ys)
}

View File

@@ -1,29 +1,29 @@
package org.openrndr.extra.noise
fun valueLinear(seed: Int, x:Double, y:Double, z:Double) = value(seed, x, y, z, ::linear)
fun valueQuintic(seed: Int, x:Double, y:Double, z:Double) = value(seed, x, y, z, ::quintic)
fun valueHermite(seed: Int, x:Double, y:Double, z:Double) = value(seed, x, y, z, ::hermite)
inline fun value(seed:Int, x: Double, y: Double, z: Double, crossinline interpolation:(Double)->Double = ::linear) : Double {
val x0 = x.fastFloor()
val y0 = y.fastFloor()
val z0 = z.fastFloor()
val x1 = x0 + 1
val y1 = y0 + 1
val z1 = z0 + 1
val xs = interpolation(x - x0)
val ys = interpolation(y - y0)
val zs = interpolation(z - z0)
val xf00 = lerp(valCoord3D(seed, x0, y0, z0), valCoord3D(seed, x1, y0, z0), xs)
val xf10 = lerp(valCoord3D(seed, x0, y1, z0), valCoord3D(seed, x1, y1, z0), xs)
val xf01 = lerp(valCoord3D(seed, x0, y0, z1), valCoord3D(seed, x1, y0, z1), xs)
val xf11 = lerp(valCoord3D(seed, x0, y1, z1), valCoord3D(seed, x1, y1, z1), xs)
val yf0 = lerp(xf00, xf10, ys)
val yf1 = lerp(xf01, xf11, ys)
return lerp(yf0, yf1, zs)
package org.openrndr.extra.noise
fun valueLinear(seed: Int, x:Double, y:Double, z:Double) = value(seed, x, y, z, ::linear)
fun valueQuintic(seed: Int, x:Double, y:Double, z:Double) = value(seed, x, y, z, ::quintic)
fun valueHermite(seed: Int, x:Double, y:Double, z:Double) = value(seed, x, y, z, ::hermite)
inline fun value(seed:Int, x: Double, y: Double, z: Double, crossinline interpolation:(Double)->Double = ::linear) : Double {
val x0 = x.fastFloor()
val y0 = y.fastFloor()
val z0 = z.fastFloor()
val x1 = x0 + 1
val y1 = y0 + 1
val z1 = z0 + 1
val xs = interpolation(x - x0)
val ys = interpolation(y - y0)
val zs = interpolation(z - z0)
val xf00 = lerp(valCoord3D(seed, x0, y0, z0), valCoord3D(seed, x1, y0, z0), xs)
val xf10 = lerp(valCoord3D(seed, x0, y1, z0), valCoord3D(seed, x1, y1, z0), xs)
val xf01 = lerp(valCoord3D(seed, x0, y0, z1), valCoord3D(seed, x1, y0, z1), xs)
val xf11 = lerp(valCoord3D(seed, x0, y1, z1), valCoord3D(seed, x1, y1, z1), xs)
val yf0 = lerp(xf00, xf10, ys)
val yf1 = lerp(xf01, xf11, ys)
return lerp(yf0, yf1, zs)
}