diff --git a/src/main/kotlin/graphics/scenery/Box.kt b/src/main/kotlin/graphics/scenery/Box.kt index c20a7b9f7..143bc43d1 100644 --- a/src/main/kotlin/graphics/scenery/Box.kt +++ b/src/main/kotlin/graphics/scenery/Box.kt @@ -7,6 +7,8 @@ import org.joml.Vector3f import java.lang.IllegalArgumentException import kotlin.jvm.JvmOverloads +inline fun Box(sizes: Vector3f = Vector3f(1.0f, 1.0f, 1.0f), insideNormals: Boolean = false, block: Box.() -> Unit): Box = Box(sizes, insideNormals).apply(block) + /** * Constructs a Box [Node] with the dimensions given in [sizes] * diff --git a/src/main/kotlin/graphics/scenery/DetachedHeadCamera.kt b/src/main/kotlin/graphics/scenery/DetachedHeadCamera.kt index 78d39ee72..0cf59f879 100644 --- a/src/main/kotlin/graphics/scenery/DetachedHeadCamera.kt +++ b/src/main/kotlin/graphics/scenery/DetachedHeadCamera.kt @@ -13,6 +13,8 @@ import kotlin.math.PI import kotlin.math.atan import kotlin.reflect.KProperty +inline fun DetachedHeadCamera(block: DetachedHeadCamera.() -> Unit): DetachedHeadCamera = DetachedHeadCamera().apply(block) + /** * Detached Head Camera is a Camera subclass that tracks the head orientation * in addition to general orientation - useful for HMDs diff --git a/src/main/kotlin/graphics/scenery/Node.kt b/src/main/kotlin/graphics/scenery/Node.kt index db4a5b648..cd1eb1f26 100644 --- a/src/main/kotlin/graphics/scenery/Node.kt +++ b/src/main/kotlin/graphics/scenery/Node.kt @@ -21,6 +21,7 @@ import java.util.concurrent.CopyOnWriteArrayList import java.util.concurrent.locks.ReentrantLock import java.util.function.Consumer import kotlin.collections.ArrayList +import kotlin.collections.Collection interface Node : Networkable { var name: String @@ -137,6 +138,23 @@ interface Node : Networkable { /** Unique ID of the Node */ fun getUuid(): UUID + /** + * Attaches a child node to this node. + * + * @param[child] The child to attach to this node. + */ + operator fun plusAssign(child: Node) = addChild(child) + + /** + * Attaches the given children nodes to this node. + * + * @param[children] The children to attach to this node. + */ + operator fun plusAssign(children: Collection) { + for (child in children) + addChild(child) + } + /** * Attaches a child node to this node. * @@ -185,12 +203,12 @@ interface Node : Networkable { */ fun generateBoundingBox(): OrientedBoundingBox? { val geometry = geometryOrNull() - if(geometry == null) { + return if(geometry == null) { logger.warn("$name: Assuming 3rd party BB generation") - return boundingBox + boundingBox } else { boundingBox = geometry.generateBoundingBox(children) - return boundingBox + boundingBox } } diff --git a/src/main/kotlin/graphics/scenery/PointLight.kt b/src/main/kotlin/graphics/scenery/PointLight.kt index b9e5506c3..44aca23b3 100644 --- a/src/main/kotlin/graphics/scenery/PointLight.kt +++ b/src/main/kotlin/graphics/scenery/PointLight.kt @@ -6,6 +6,8 @@ import graphics.scenery.utils.extensions.xyz import org.joml.Vector3f import org.joml.Vector4f +inline fun PointLight(radius: Float = 5.0f, block: PointLight.() -> Unit): PointLight = PointLight(radius).apply(block) + /** * Point light class. * diff --git a/src/main/kotlin/graphics/scenery/utils.kt b/src/main/kotlin/graphics/scenery/utils.kt new file mode 100644 index 000000000..06e960493 --- /dev/null +++ b/src/main/kotlin/graphics/scenery/utils.kt @@ -0,0 +1,7 @@ +package graphics.scenery + +inline operator fun M.invoke(init: M.() -> Unit): M { + apply(init) + return this +} + diff --git a/src/main/kotlin/graphics/scenery/utils/Image.kt b/src/main/kotlin/graphics/scenery/utils/Image.kt index 7367a77f0..e1f65cdd8 100644 --- a/src/main/kotlin/graphics/scenery/utils/Image.kt +++ b/src/main/kotlin/graphics/scenery/utils/Image.kt @@ -1,6 +1,9 @@ package graphics.scenery.utils +import graphics.scenery.textures.Texture import graphics.scenery.volumes.Colormap +import net.imglib2.type.numeric.integer.UnsignedByteType +import org.joml.Vector3i import org.lwjgl.system.MemoryUtil import java.awt.Color import java.awt.color.ColorSpace @@ -22,6 +25,17 @@ import javax.imageio.ImageIO */ open class Image(val contents: ByteBuffer, val width: Int, val height: Int, val depth: Int = 1) { + fun toTexture( + repeatUVW: Triple = Texture.RepeatMode.Repeat.all(), + borderColor: Texture.BorderColor = Texture.BorderColor.OpaqueBlack, + normalized: Boolean = true, + mipmap: Boolean = true, + minFilter: Texture.FilteringMode = Texture.FilteringMode.Linear, + maxFilter: Texture.FilteringMode = Texture.FilteringMode.Linear, + usage: HashSet = hashSetOf(Texture.UsageType.Texture) + ): Texture { + return Texture(Vector3i(width, height, depth),4, UnsignedByteType(), contents, repeatUVW, borderColor, normalized, mipmap, usageType = usage, minFilter = minFilter, maxFilter = maxFilter) + } companion object { protected val logger by lazyLogger() @@ -98,6 +112,14 @@ open class Image(val contents: ByteBuffer, val width: Int, val height: Int, val return Image(imageData, bi.width, bi.height) } + /** + * Creates an Image from a resource given in [path], with [baseClass] as basis for the search path. + * [path] is expected to end in an extension (e.g., ".png"), such that the file type can be determined. + */ + inline fun fromResource(path: String): Image { + return fromStream(K::class.java.getResourceAsStream(path), path.substringAfterLast(".").lowercase()) + } + /** * Creates an Image from a resource given in [path], with [baseClass] as basis for the search path. * [path] is expected to end in an extension (e.g., ".png"), such that the file type can be determined. diff --git a/src/test/kotlin/graphics/scenery/tests/examples/basic/ArcballExample.kt b/src/test/kotlin/graphics/scenery/tests/examples/basic/ArcballExample.kt index 18208ff72..29126dfc0 100644 --- a/src/test/kotlin/graphics/scenery/tests/examples/basic/ArcballExample.kt +++ b/src/test/kotlin/graphics/scenery/tests/examples/basic/ArcballExample.kt @@ -19,8 +19,7 @@ class ArcballExample : SceneryBase("ArcballExample") { override fun init() { renderer = hub.add(Renderer.createRenderer(hub, applicationName, scene, 1024, 1024)) - val cam: Camera = DetachedHeadCamera() - with(cam) { + val cam: Camera = DetachedHeadCamera { spatial { position = Vector3f(0.0f, 0.0f, 2.5f) } @@ -28,51 +27,48 @@ class ArcballExample : SceneryBase("ArcballExample") { targeted = true target = Vector3f(0.0f, 0.0f, 0.0f) - - scene.addChild(this) } + scene += cam - val camlight = PointLight(3.0f) - camlight.intensity = 5.0f - cam.addChild(camlight) - - val box = Box(Vector3f(1.0f, 1.0f, 1.0f)) + val camlight = PointLight(3.0f) { + intensity = 5.0f + } + cam += camlight - with(box) { + val box = Box(Vector3f(1.0f, 1.0f, 1.0f)) { spatial { position = Vector3f(0.0f, 0.0f, 0.0f) } material { ambient = Vector3f(1.0f, 0.0f, 0.0f) diffuse = Vector3f(0.0f, 1.0f, 0.0f) - textures["diffuse"] = Texture.fromImage(Image.fromResource("textures/helix.png", TexturedCubeExample::class.java)) + textures["diffuse"] = Image.fromResource("textures/helix.png").toTexture() specular = Vector3f(1.0f, 1.0f, 1.0f) } - - scene.addChild(this) } + scene += box - val lights = (0..2).map { - PointLight(radius = 15.0f) - }.map { light -> - light.spatial { - position = Random.random3DVectorFromRange(-3.0f, 3.0f) + val lights = List(3) { + PointLight(radius = 15.0f) { + spatial { + position = Random.random3DVectorFromRange(-3.0f, 3.0f) + } + emissionColor = Random.random3DVectorFromRange(0.2f, 0.8f) + intensity = Random.randomFromRange(0.1f, 0.8f) } - light.emissionColor = Random.random3DVectorFromRange(0.2f, 0.8f) - light.intensity = Random.randomFromRange(0.1f, 0.8f) - light } - val floor = Box(Vector3f(500.0f, 0.05f, 500.0f)) - floor.spatial { - position = Vector3f(0.0f, -1.0f, 0.0f) - } - floor.material { - diffuse = Vector3f(1.0f, 1.0f, 1.0f) + val floor = Box(Vector3f(500.0f, 0.05f, 500.0f)) { + spatial { + position = Vector3f(0.0f, -1.0f, 0.0f) + } + material { + diffuse = Vector3f(1.0f, 1.0f, 1.0f) + } } - scene.addChild(floor) + scene += floor - lights.forEach(scene::addChild) + scene += lights } override fun inputSetup() {