Skip to content

Commit

Permalink
Improve mesh generator (#301)
Browse files Browse the repository at this point in the history
Co-authored-by: Edwin Jakobs <[email protected]>
  • Loading branch information
hamoid and edwinRNDR committed Apr 19, 2023
1 parent a8c2f72 commit 03bf971
Show file tree
Hide file tree
Showing 31 changed files with 2,637 additions and 1,162 deletions.
128 changes: 77 additions & 51 deletions orx-mesh-generators/README.md
Original file line number Diff line number Diff line change
@@ -1,95 +1,121 @@
# orx-mesh-generators

Generates 3D meshes: sphere, box, cylinder, plane, dodecahedron.
Generates various types of 3D meshes.

##### usage
## Simple meshes

```kotlin
// To create simple meshes
val sphere = sphereMesh(32, 32, 4.0)
val unitSphere = sphereMesh()
val cube = boxMesh()
val box = boxMesh(2.0, 4.0, 2.0)
val cylinder = cylinderMesh(radius = 0.5, length = 1.0, center = true)
val dodecahedron = dodecahedronMesh(0.5)
val plane = planeMesh(Vector3.ZERO, Vector3.UNIT_X, Vector3.UNIT_Y)
val disk = capMesh(sides = 15, radius = 0.5)
val tube = revolveMesh(sides = 15, length = 1.0)

// To draw the generated meshes
drawer.vertexBuffer(dodecahedron, DrawPrimitive.TRIANGLES)
```

## Complex triangular mesh generation

`orx-mesh-generators` comes with `buildTriangleMesh`, which
implements a [DSL](https://en.wikipedia.org/wiki/Domain-specific_language)
to construct 3D shapes.

...
To create shapes we can call methods like `box()`, `sphere()`,
`cylinder()`, `dodecahedron()`, `plane()`, `revolve()`,
`taperedCylinder()`, `hemisphere()` and `cap()`.

```kotlin
// Create a rotated box
val mesh = buildTriangleMesh {
rotate(Vector3.UNIT_Z, 45.0)
box()
}
```

drawer.vertexBuffer(sphere, DrawPrimitive.TRIANGLES)
drawer.vertexBuffer(unitSphere, DrawPrimitive.TRIANGLES)
drawer.vertexBuffer(cube, DrawPrimitive.TRIANGLES)
drawer.vertexBuffer(box, DrawPrimitive.TRIANGLES)
We can also use methods like `translate()` and `rotate()` to create
more complex compositions. The `color` property sets the color of
the next mesh.

```kotlin
// Create a ring of boxes of various colors
val mesh = buildTriangleMesh {
repeat(12) {
// Take a small step
translate(2.0, 0.0, 0.0)
// Turn 30 degrees
rotate(Vector3.UNIT_Y, 30.0)
// Set a color
color = rgb(it / 11.0, 1.0, 1.0 - it / 11.0)
// Add a colored box
box(1.0, 1.0, 1.0)
}
}
```

## API
`isolated { ... }` can be used to encapsulate transformations and
avoid them accumulating to unpredictable values.

```kotlin
fun sphereMesh(
sides: Int = 16,
segments: Int = 16,
radius: Double = 1.0,
invert: Boolean = false): VertexBuffer

fun groundPlaneMesh(
width: Double = 1.0,
height: Double = 1.0,
widthSegments: Int = 1,
heightSegments: Int): VertexBuffer

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 mesh = buildTriangleMesh {
repeat(10) { x ->
repeat(10) { y ->
isolated {
translate(x * 1.0, y * 1.0, 0.0)
sphere(8, 8, 0.1)
}
}
}
}
```
<!-- __demos__ >
# Demos
[DemoBoxKt](src/demo/kotlin/DemoBoxKt.kt
![DemoBoxKt](https://github.com/openrndr/orx/blob/media/orx-mesh-generators/images/DemoBoxKt.png
[DemoComplex01Kt](src/demo/kotlin/DemoComplex01Kt.kt
![DemoComplex01Kt](https://github.com/openrndr/orx/blob/media/orx-mesh-generators/images/DemoComplex01Kt.png
[DemoComplex02Kt](src/demo/kotlin/DemoComplex02Kt.kt
![DemoComplex02Kt](https://github.com/openrndr/orx/blob/media/orx-mesh-generators/images/DemoComplex02Kt.png
[DemoComplex03Kt](src/demo/kotlin/DemoComplex03Kt.kt
![DemoComplex03Kt](https://github.com/openrndr/orx/blob/media/orx-mesh-generators/images/DemoComplex03Kt.png
[DemoComplex04Kt](src/demo/kotlin/DemoComplex04Kt.kt
![DemoComplex04Kt](https://github.com/openrndr/orx/blob/media/orx-mesh-generators/images/DemoComplex04Kt.png
[DemoComplex05Kt](src/demo/kotlin/DemoComplex05Kt.kt
![DemoComplex05Kt](https://github.com/openrndr/orx/blob/media/orx-mesh-generators/images/DemoComplex05Kt.png

Other available methods are:

- `grid()`: creates a tri-dimensional grid of meshes.
- `extrudeShape()`: gives depth to 2D `Shape`.
- `twist()`: post-processing effect to twist a mesh around an axis.
- `extrudeContourSteps()`: uses Parallel Transport Frames to extrude a contour along a 3D path.

The [demo folder](src/jvmDemo/kotlin) contains examples using these methods.

Check out the [source code](src/commonMain/kotlin) to learn about function arguments.

<!-- __demos__ -->
## Demos
### DemoAll
[source code](src/demo/kotlin/DemoAll.kt)
[source code](src/jvmDemo/kotlin/DemoAll.kt)

![DemoAllKt](https://raw.githubusercontent.com/openrndr/orx/media/orx-mesh-generators/images/DemoAllKt.png)

### DemoBox
[source code](src/demo/kotlin/DemoBox.kt)
[source code](src/jvmDemo/kotlin/DemoBox.kt)

![DemoBoxKt](https://raw.githubusercontent.com/openrndr/orx/media/orx-mesh-generators/images/DemoBoxKt.png)

### DemoComplex01
[source code](src/demo/kotlin/DemoComplex01.kt)
[source code](src/jvmDemo/kotlin/DemoComplex01.kt)

![DemoComplex01Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-mesh-generators/images/DemoComplex01Kt.png)

### DemoComplex02
[source code](src/demo/kotlin/DemoComplex02.kt)
[source code](src/jvmDemo/kotlin/DemoComplex02.kt)

![DemoComplex02Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-mesh-generators/images/DemoComplex02Kt.png)

### DemoComplex03
[source code](src/demo/kotlin/DemoComplex03.kt)
[source code](src/jvmDemo/kotlin/DemoComplex03.kt)

![DemoComplex03Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-mesh-generators/images/DemoComplex03Kt.png)

### DemoComplex04
[source code](src/demo/kotlin/DemoComplex04.kt)
[source code](src/jvmDemo/kotlin/DemoComplex04.kt)

![DemoComplex04Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-mesh-generators/images/DemoComplex04Kt.png)

### DemoComplex05
[source code](src/demo/kotlin/DemoComplex05.kt)
[source code](src/jvmDemo/kotlin/DemoComplex05.kt)

![DemoComplex05Kt](https://raw.githubusercontent.com/openrndr/orx/media/orx-mesh-generators/images/DemoComplex05Kt.png)
29 changes: 21 additions & 8 deletions orx-mesh-generators/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,11 +1,24 @@
plugins {
org.openrndr.extra.convention.`kotlin-jvm`
org.openrndr.extra.convention.`kotlin-multiplatform`
}

dependencies {
implementation(libs.openrndr.application)
implementation(libs.openrndr.math)
demoImplementation(project(":orx-shapes"))
demoImplementation(project(":orx-mesh-generators"))
demoImplementation(project(":orx-camera"))
}
kotlin {
sourceSets {
@Suppress("UNUSED_VARIABLE")
val commonMain by getting {
dependencies {
api(libs.openrndr.application)
api(libs.openrndr.math)
}
}

@Suppress("UNUSED_VARIABLE")
val jvmDemo by getting {
dependencies {
implementation(project(":orx-shapes"))
implementation(project(":orx-mesh-generators"))
implementation(project(":orx-camera"))
}
}
}
}
114 changes: 114 additions & 0 deletions orx-mesh-generators/src/commonMain/kotlin/Box.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package org.openrndr.extra.meshgenerators

import org.openrndr.draw.VertexBuffer
import org.openrndr.math.Vector3

/**
* Returns a Box mesh
*
* @param width the width of the box
* @param height the height of the box
* @param depth the depth of the box
* @param widthSegments the number of segments along the x-axis
* @param heightSegments the number of segments along the z-axis
* @param depthSegments the number of segments along the y-axis
* @param flipNormals generates inside-out geometry if true
* @return A vertex buffer containing the triangles to render the 3D shape
*/
fun boxMesh(
width: Double = 1.0,
height: Double = 1.0,
depth: Double = 1.0,
widthSegments: Int = 1,
heightSegments: Int = 1,
depthSegments: Int = 1,
flipNormals: 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,
flipNormals, bufferWriter(this)
)
}
return vb
}

/**
* Generate a box
*
* @param width the width of the box
* @param height the height of the box
* @param depth the depth of the box
* @param widthSegments the number of segments along the x-axis
* @param heightSegments the number of segments along the z-axis
* @param depthSegments the number of segments along the y-axis
* @param flipNormals generates inside-out geometry if true
* @param writer the vertex writer function
*/

fun generateBox(
width: Double = 1.0,
height: Double = 1.0,
depth: Double = 1.0,
widthSegments: Int = 1,
heightSegments: Int = 1,
depthSegments: Int = 1,
flipNormals: Boolean = false,
writer: VertexWriter
) {

val sign = if (flipNormals) -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
)
}
Loading

0 comments on commit 03bf971

Please sign in to comment.