From bacf72ecb01a927dfb21831821fcd55d87f7dc67 Mon Sep 17 00:00:00 2001 From: Giuseppe Barbieri Date: Fri, 26 May 2023 21:05:42 +0200 Subject: [PATCH 1/3] - added kool library dependency - switched code in `Geometry::recalculateNormals` and `Icosphere` constructor to pointers - added some pointers and typealias utils - using kool constructors and methods to allocate buffers --- build.gradle.kts | 3 + .../graphics/scenery/FullscreenObject.kt | 7 ++- src/main/kotlin/graphics/scenery/Icosphere.kt | 62 +++++++++++-------- src/main/kotlin/graphics/scenery/Node.kt | 2 +- .../scenery/attribute/geometry/Geometry.kt | 38 ++++-------- .../kotlin/graphics/scenery/utils/geometry.kt | 3 + .../kotlin/graphics/scenery/utils/pointers.kt | 25 ++++++++ .../compute/CustomVolumeManagerExample.kt | 3 +- .../scenery/tests/unit/BufferUtilsTests.kt | 3 +- 9 files changed, 88 insertions(+), 58 deletions(-) create mode 100644 src/main/kotlin/graphics/scenery/utils/geometry.kt create mode 100644 src/main/kotlin/graphics/scenery/utils/pointers.kt diff --git a/build.gradle.kts b/build.gradle.kts index 58e2057d3..28685612a 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -22,6 +22,7 @@ repositories { mavenCentral() maven("https://maven.scijava.org/content/groups/public") maven("https://jitpack.io") + maven("https://raw.githubusercontent.com/kotlin-graphics/mary/master") // mavenLocal() } @@ -133,6 +134,8 @@ dependencies { implementation("org.jfree:jfreechart:1.5.0") implementation("net.imagej:imagej-ops:0.45.5") + + implementation("kotlin.graphics:kool:0.9.79") } val isRelease: Boolean diff --git a/src/main/kotlin/graphics/scenery/FullscreenObject.kt b/src/main/kotlin/graphics/scenery/FullscreenObject.kt index 3e79b7970..3c8c5cbec 100644 --- a/src/main/kotlin/graphics/scenery/FullscreenObject.kt +++ b/src/main/kotlin/graphics/scenery/FullscreenObject.kt @@ -2,6 +2,8 @@ package graphics.scenery import graphics.scenery.geometry.GeometryType import graphics.scenery.attribute.material.Material +import kool.floatBufferOf +import kool.toFloatBuffer /** * @@ -12,12 +14,11 @@ class FullscreenObject : Mesh("FullscreenObject") { init { geometry { // fake geometry - this.vertices = BufferUtils.allocateFloatAndPut( - floatArrayOf( + this.vertices = floatBufferOf( -1.0f, -1.0f, 0.0f, 1.0f, -1.0f, 0.0f, 1.0f, 1.0f, 0.0f, - -1.0f, 1.0f, 0.0f)) + -1.0f, 1.0f, 0.0f) this.normals = BufferUtils.allocateFloatAndPut( floatArrayOf( diff --git a/src/main/kotlin/graphics/scenery/Icosphere.kt b/src/main/kotlin/graphics/scenery/Icosphere.kt index 0e787ee42..178735f85 100644 --- a/src/main/kotlin/graphics/scenery/Icosphere.kt +++ b/src/main/kotlin/graphics/scenery/Icosphere.kt @@ -1,10 +1,19 @@ package graphics.scenery +import graphics.scenery.utils.Face import graphics.scenery.utils.extensions.minus import graphics.scenery.utils.extensions.plus import graphics.scenery.utils.extensions.times import graphics.scenery.utils.extensions.xy +import graphics.scenery.utils.inc +import graphics.scenery.utils.set +import kool.FloatBuffer +import kool.IntBuffer +import kool.adr +import kool.toPtr +import org.joml.Vector2f import org.joml.Vector3f +import org.lwjgl.system.MemoryUtil import java.util.* import kotlin.math.* @@ -16,15 +25,15 @@ import kotlin.math.* * @param[subdivisions] Number of subdivisions of the base icosahedron */ open class Icosphere(val radius: Float, val subdivisions: Int) : Mesh("Icosphere") { - fun MutableList.addVertex(vararg v: Float) { - this.add(Vector3f(v)) + fun MutableList.addVertex(x: Float, y: Float, z: Float) { + this += Vector3f(x, y, z) } - fun MutableList>.addFace(i: Int, j: Int, k: Int) { - this.add(kotlin.Triple(i, j, k)) + fun MutableList.addFace(i: Int, j: Int, k: Int) { + this += Triple(i, j, k) } - protected fun createBaseVertices(vertices: MutableList, indices: MutableList>) { + protected fun createBaseVertices(vertices: MutableList, indices: MutableList) { val s = sqrt((5.0f - sqrt(5.0f)) / 10.0f) val t = sqrt((5.0f + sqrt(5.0f)) / 10.0f) @@ -73,11 +82,11 @@ open class Icosphere(val radius: Float, val subdivisions: Int) : Mesh("Icosphere protected fun refineTriangles(recursionLevel: Int, vertices: MutableList, - indices: MutableList>): MutableList> { + indices: MutableList): MutableList { // refine triangles var faces = indices - (0 until recursionLevel).forEach { - val faces2 = ArrayList>(indices.size * 3) + repeat(recursionLevel) { + val faces2 = ArrayList(indices.size * 3) for (triangle in faces) { // replace triangle by 4 triangles @@ -124,7 +133,7 @@ open class Icosphere(val radius: Float, val subdivisions: Int) : Mesh("Icosphere val i = this.addVertex(middle) // store it, return index - middlePointIndexCache.put(key, i) + middlePointIndexCache[key] = i return i } @@ -136,17 +145,20 @@ open class Icosphere(val radius: Float, val subdivisions: Int) : Mesh("Icosphere init { val vertexBuffer = ArrayList() - val indexBuffer = ArrayList>() + val indexBuffer = ArrayList() createBaseVertices(vertexBuffer, indexBuffer) val faces = refineTriangles(subdivisions, vertexBuffer, indexBuffer) geometry { - vertices = BufferUtils.allocateFloat(faces.size * 3 * 3) - normals = BufferUtils.allocateFloat(faces.size * 3 * 3) - texcoords = BufferUtils.allocateFloat(faces.size * 3 * 2) - indices = BufferUtils.allocateInt(0) + vertices = FloatBuffer(faces.size * 3 * 3) + normals = FloatBuffer(faces.size * 3 * 3) + texcoords = FloatBuffer(faces.size * 3 * 2) + indices = IntBuffer(0) + var pVtx = vertices.adr.toPtr() + var pNorm = normals.adr.toPtr() + var pTxc = texcoords.adr.toPtr() faces.forEach { f -> val v1 = vertexBuffer[f.first] @@ -156,13 +168,13 @@ open class Icosphere(val radius: Float, val subdivisions: Int) : Mesh("Icosphere val uv2 = vertexToUV(v2.normalize()) val uv3 = vertexToUV(v3.normalize()) - (v1 * radius).get(vertices).position(vertices.position() + 3) - (v2 * radius).get(vertices).position(vertices.position() + 3) - (v3 * radius).get(vertices).position(vertices.position() + 3) + pVtx++[0] = v1 * radius + pVtx++[0] = v2 * radius + pVtx++[0] = v3 * radius - v1.get(normals).position(normals.position() + 3) - v2.get(normals).position(normals.position() + 3) - v3.get(normals).position(normals.position() + 3) + pNorm++[0] = v1 + pNorm++[0] = v2 + pNorm++[0] = v3 val uvNormal = (uv2 - uv1).cross(uv3 - uv1) if(uvNormal.z() < 0.0f) { @@ -177,14 +189,10 @@ open class Icosphere(val radius: Float, val subdivisions: Int) : Mesh("Icosphere } } - uv1.xy().get(texcoords).position(texcoords.position() + 2) - uv2.xy().get(texcoords).position(texcoords.position() + 2) - uv3.xy().get(texcoords).position(texcoords.position() + 2) + pTxc++[0] = uv1.xy() + pTxc++[0] = uv2.xy() + pTxc++[0] = uv3.xy() } - - vertices.flip() - normals.flip() - texcoords.flip() } boundingBox = generateBoundingBox() diff --git a/src/main/kotlin/graphics/scenery/Node.kt b/src/main/kotlin/graphics/scenery/Node.kt index f91b9114d..a83f3d904 100644 --- a/src/main/kotlin/graphics/scenery/Node.kt +++ b/src/main/kotlin/graphics/scenery/Node.kt @@ -91,7 +91,7 @@ interface Node : Networkable { @Throws(IllegalStateException::class) fun getAttribute(attributeType: Class) : U { - return getAttributeOrNull(attributeType) ?: throw IllegalStateException("Node doesn't have attribute named " + attributeType) + return getAttributeOrNull(attributeType) ?: throw IllegalStateException("Node doesn't have attribute named $attributeType") } fun ifSpatial(block: Spatial.() -> Unit): Spatial? { diff --git a/src/main/kotlin/graphics/scenery/attribute/geometry/Geometry.kt b/src/main/kotlin/graphics/scenery/attribute/geometry/Geometry.kt index 915e823fa..917901173 100644 --- a/src/main/kotlin/graphics/scenery/attribute/geometry/Geometry.kt +++ b/src/main/kotlin/graphics/scenery/attribute/geometry/Geometry.kt @@ -3,6 +3,10 @@ package graphics.scenery.attribute.geometry import graphics.scenery.* import graphics.scenery.geometry.GeometryType import graphics.scenery.utils.extensions.minus +import graphics.scenery.utils.get +import graphics.scenery.utils.inc +import graphics.scenery.utils.set +import kool.* import org.joml.Vector3f import java.io.Serializable import java.nio.FloatBuffer @@ -41,38 +45,22 @@ interface Geometry : Serializable { * STL's facet storage format into account. */ fun recalculateNormals() { - val vertexBufferView = vertices.asReadOnlyBuffer() - var i = 0 - val normals = ArrayList() + var pVtx = vertices.adr.toPtr() + var pNorm = normals.adr.toPtr() - while (i < vertexBufferView.limit() - 1) { - val v1 = Vector3f(vertexBufferView[i], vertexBufferView[i + 1], vertexBufferView[i + 2]) - i += 3 - - val v2 = Vector3f(vertexBufferView[i], vertexBufferView[i + 1], vertexBufferView[i + 2]) - i += 3 - - val v3 = Vector3f(vertexBufferView[i], vertexBufferView[i + 1], vertexBufferView[i + 2]) - i += 3 + for (i in vertices.indices step 3) { + val v1 = pVtx++[0] + val v2 = pVtx++[0] + val v3 = pVtx++[0] val a = v2 - v1 val b = v3 - v1 val n = a.cross(b).normalize() - normals.add(n.x()) - normals.add(n.y()) - normals.add(n.z()) - - normals.add(n.x()) - normals.add(n.y()) - normals.add(n.z()) - - normals.add(n.x()) - normals.add(n.y()) - normals.add(n.z()) + pNorm++[0] = n + pNorm++[0] = n + pNorm++[0] = n } - - this.normals = BufferUtils.allocateFloatAndPut(normals.toFloatArray()) } } diff --git a/src/main/kotlin/graphics/scenery/utils/geometry.kt b/src/main/kotlin/graphics/scenery/utils/geometry.kt new file mode 100644 index 000000000..03a68852a --- /dev/null +++ b/src/main/kotlin/graphics/scenery/utils/geometry.kt @@ -0,0 +1,3 @@ +package graphics.scenery.utils + +typealias Face = Triple diff --git a/src/main/kotlin/graphics/scenery/utils/pointers.kt b/src/main/kotlin/graphics/scenery/utils/pointers.kt new file mode 100644 index 000000000..ef1498e33 --- /dev/null +++ b/src/main/kotlin/graphics/scenery/utils/pointers.kt @@ -0,0 +1,25 @@ +package graphics.scenery.utils + +import kool.Ptr +import kool.unsafe +import org.joml.Vector2f +import org.joml.Vector3f + +operator fun Ptr.get(index: Int): Vector3f { + val ofs = adr.toLong() + (index shl 2) + return Vector3f(unsafe.getFloat(ofs), unsafe.getFloat(ofs + 4), unsafe.getFloat(ofs + 8)) +} +operator fun Ptr.set(index: Int, v: Vector3f) { + val ofs = adr.toLong() + index shl 2 + unsafe.putFloat(ofs, v.x) + unsafe.putFloat(ofs + 4, v.y) + unsafe.putFloat(ofs + 8, v.z) +} +operator fun Ptr.set(index: Int, v: Vector2f) { + val ofs = adr.toLong() + index shl 2 + unsafe.putFloat(ofs, v.x) + unsafe.putFloat(ofs + 4, v.y) +} + +operator fun Ptr.inc() = Ptr(adr.toLong() + 12) +operator fun Ptr.inc() = Ptr(adr.toLong() + 8) diff --git a/src/test/kotlin/graphics/scenery/tests/examples/compute/CustomVolumeManagerExample.kt b/src/test/kotlin/graphics/scenery/tests/examples/compute/CustomVolumeManagerExample.kt index 72ac81772..15a3362c0 100644 --- a/src/test/kotlin/graphics/scenery/tests/examples/compute/CustomVolumeManagerExample.kt +++ b/src/test/kotlin/graphics/scenery/tests/examples/compute/CustomVolumeManagerExample.kt @@ -11,6 +11,7 @@ import graphics.scenery.volumes.Volume import graphics.scenery.volumes.VolumeManager import ij.IJ import ij.ImagePlus +import kool.ByteBuffer import net.imglib2.img.Img import net.imglib2.img.display.imagej.ImageJFunctions import net.imglib2.type.numeric.integer.UnsignedShortType @@ -40,7 +41,7 @@ class CustomVolumeManagerExample : SceneryBase("CustomVolumeManagerExample") { )) volumeManager.customTextures.add("OutputRender") - val outputBuffer = MemoryUtil.memCalloc(1280*720*4) + val outputBuffer = ByteBuffer(1280*720*4) val outputTexture = Texture.fromImage(Image(outputBuffer, 1280, 720), usage = hashSetOf(Texture.UsageType.LoadStoreImage, Texture.UsageType.Texture)) volumeManager.material().textures["OutputRender"] = outputTexture diff --git a/src/test/kotlin/graphics/scenery/tests/unit/BufferUtilsTests.kt b/src/test/kotlin/graphics/scenery/tests/unit/BufferUtilsTests.kt index bdb99ed5b..afc63fc46 100644 --- a/src/test/kotlin/graphics/scenery/tests/unit/BufferUtilsTests.kt +++ b/src/test/kotlin/graphics/scenery/tests/unit/BufferUtilsTests.kt @@ -2,6 +2,7 @@ package graphics.scenery.tests.unit import graphics.scenery.BufferUtils import graphics.scenery.utils.lazyLogger +import kool.FloatBuffer import org.junit.Test import java.nio.ByteOrder import kotlin.test.assertEquals @@ -23,7 +24,7 @@ class BufferUtilsTests { fun testAllocateFloat() { logger.info("Testing allocation of a new direct float buffer ...") val size = kotlin.random.Random.nextInt(1, 10000) - val floatBuf = BufferUtils.allocateFloat(size) + val floatBuf = FloatBuffer(size) assertEquals(size, floatBuf.capacity(), "Float buffer capacity was expected to be $size, but is ${floatBuf.capacity()}") assertEquals (ByteOrder.nativeOrder(), floatBuf.order(), "Float buffer was expected to be in ${ByteOrder.nativeOrder()}, but is ${floatBuf.order()}") From 91212c299d7b09e27f46792139071bfffa3c3303 Mon Sep 17 00:00:00 2001 From: Giuseppe Barbieri Date: Fri, 26 May 2023 21:09:42 +0200 Subject: [PATCH 2/3] - `shl` has lower priority than multiplication (this is a drawback of the still missing bitwise operators in Kotlin), fixing with parenthesis --- src/main/kotlin/graphics/scenery/utils/pointers.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/graphics/scenery/utils/pointers.kt b/src/main/kotlin/graphics/scenery/utils/pointers.kt index ef1498e33..33354f5e0 100644 --- a/src/main/kotlin/graphics/scenery/utils/pointers.kt +++ b/src/main/kotlin/graphics/scenery/utils/pointers.kt @@ -10,13 +10,13 @@ operator fun Ptr.get(index: Int): Vector3f { return Vector3f(unsafe.getFloat(ofs), unsafe.getFloat(ofs + 4), unsafe.getFloat(ofs + 8)) } operator fun Ptr.set(index: Int, v: Vector3f) { - val ofs = adr.toLong() + index shl 2 + val ofs = adr.toLong() + (index shl 2) unsafe.putFloat(ofs, v.x) unsafe.putFloat(ofs + 4, v.y) unsafe.putFloat(ofs + 8, v.z) } operator fun Ptr.set(index: Int, v: Vector2f) { - val ofs = adr.toLong() + index shl 2 + val ofs = adr.toLong() + (index shl 2) unsafe.putFloat(ofs, v.x) unsafe.putFloat(ofs + 4, v.y) } From ce2d78306fcecfeb3ce1b753be079a8ddf738ed9 Mon Sep 17 00:00:00 2001 From: Giuseppe Barbieri Date: Tue, 30 May 2023 07:44:19 +0200 Subject: [PATCH 3/3] - fixed signature operators name clashing --- src/main/kotlin/graphics/scenery/utils/pointers.kt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/kotlin/graphics/scenery/utils/pointers.kt b/src/main/kotlin/graphics/scenery/utils/pointers.kt index 33354f5e0..7700cd77a 100644 --- a/src/main/kotlin/graphics/scenery/utils/pointers.kt +++ b/src/main/kotlin/graphics/scenery/utils/pointers.kt @@ -5,21 +5,27 @@ import kool.unsafe import org.joml.Vector2f import org.joml.Vector3f +@JvmName("Vector3fGet") operator fun Ptr.get(index: Int): Vector3f { val ofs = adr.toLong() + (index shl 2) return Vector3f(unsafe.getFloat(ofs), unsafe.getFloat(ofs + 4), unsafe.getFloat(ofs + 8)) } + +@JvmName("Vector3fSet") operator fun Ptr.set(index: Int, v: Vector3f) { val ofs = adr.toLong() + (index shl 2) unsafe.putFloat(ofs, v.x) unsafe.putFloat(ofs + 4, v.y) unsafe.putFloat(ofs + 8, v.z) } +@JvmName("Vector2fSet") operator fun Ptr.set(index: Int, v: Vector2f) { val ofs = adr.toLong() + (index shl 2) unsafe.putFloat(ofs, v.x) unsafe.putFloat(ofs + 4, v.y) } +@JvmName("Vector3fInc") operator fun Ptr.inc() = Ptr(adr.toLong() + 12) +@JvmName("Vector2fInc") operator fun Ptr.inc() = Ptr(adr.toLong() + 8)