Replace light model for 3ds

This commit is contained in:
Alexander Nozik 2022-01-27 12:10:00 +03:00
parent db5064f6ed
commit f828f86e29
No known key found for this signature in database
GPG Key ID: F7FCF2DD25C71357
27 changed files with 340 additions and 157 deletions

View File

@ -4,6 +4,8 @@
### Added
### Changed
- Naming of Canvas3D options
- Lights are added to the scene instead of 3D options
### Deprecated

View File

@ -8,7 +8,7 @@ val fxVersion by extra("11")
allprojects{
group = "space.kscience"
version = "0.2.0"
version = "0.2.1-dev-1"
}
subprojects {

View File

@ -1,6 +1,5 @@
import kotlinx.browser.document
import kotlinx.css.*
import react.child
import react.dom.render
import ringui.SmartTabs
import ringui.Tab
@ -8,6 +7,7 @@ import space.kscience.dataforge.context.Context
import space.kscience.plotly.models.Trace
import space.kscience.plotly.scatter
import space.kscience.visionforge.Application
import space.kscience.visionforge.Colors
import space.kscience.visionforge.VisionClient
import space.kscience.visionforge.plotly.PlotlyPlugin
import space.kscience.visionforge.ring.ThreeCanvasWithControls
@ -73,6 +73,9 @@ private class JsPlaygroundApp : Application {
attrs {
context = playgroundContext
solid {
ambientLight {
color(Colors.white)
}
repeat(100) {
sphere(5, name = "sphere[$it]") {
x = random.nextDouble(-300.0, 300.0)

View File

@ -7,6 +7,7 @@ import react.fc
import space.kscience.dataforge.context.Context
import space.kscience.plotly.layout
import space.kscience.plotly.models.Trace
import space.kscience.visionforge.Colors
import space.kscience.visionforge.markup.VisionOfMarkup
import space.kscience.visionforge.react.flexRow
import space.kscience.visionforge.ring.ThreeCanvasWithControls
@ -41,6 +42,11 @@ val GravityDemo = fc<DemoProps> { props ->
attrs {
context = props.context
solid {
pointLight(200, 200, 200, name = "light"){
color(Colors.white)
}
ambientLight()
sphere(5.0, "ball") {
detail = 16
color("red")

View File

@ -17,12 +17,13 @@ import react.fc
import react.useMemo
import react.useState
import space.kscience.dataforge.context.Context
import space.kscience.dataforge.meta.invoke
import space.kscience.dataforge.names.Name
import space.kscience.visionforge.react.flexColumn
import space.kscience.visionforge.react.flexRow
import space.kscience.visionforge.ring.ThreeCanvasWithControls
import space.kscience.visionforge.ring.tab
import space.kscience.visionforge.solid.specifications.Camera
import space.kscience.visionforge.solid.ambientLight
import space.kscience.visionforge.solid.specifications.Canvas3DOptions
import space.kscience.visionforge.solid.three.edges
import styled.css
@ -42,17 +43,19 @@ val MMApp = fc<MMAppProps>("Muon monitor") { props ->
val mmOptions = useMemo {
Canvas3DOptions {
camera = Camera {
camera {
distance = 2100.0
latitude = PI / 6
azimuth = PI + PI / 6
}
}
}
val root = useMemo(props.model) {
props.model.root.apply {
edges()
ambientLight()
}
}

View File

@ -18,7 +18,11 @@ fun VisionLayout<Solid>.demo(name: String, title: String = name, block: SolidGro
val meta = Meta {
"title" put title
}
val vision = SolidGroup(block)
val vision = SolidGroup(block).apply {
ambientLight{
color(Colors.white)
}
}
render(Name.parse(name), vision, meta)
}
@ -39,6 +43,7 @@ val canvasOptions = Canvas3DOptions {
@OptIn(DelicateCoroutinesApi::class)
fun VisionLayout<Solid>.showcase() {
demo("shapes", "Basic shapes") {
ambientLight()
box(100.0, 100.0, 100.0) {
z = -110.0
color("teal")

View File

@ -59,7 +59,7 @@ box(10, 10, 10, name = "small box"){
rotation = Point3D(0, 0, 0)
}
```
![](../docs/images/small-box.png)
![](../images/small-box.png)
The `big box` will have properties with custom values.
```kotlin
@ -72,7 +72,7 @@ box(40, 40, 40, name = "big box"){
rotation = Point3D(60, 80, 0)
}
```
![](../docs/images/big-rotated-box.png)
![](../images/big-rotated-box.png)
If we compare these boxes, we will see all differences.
Here is the function `main` with both boxes.
@ -111,8 +111,8 @@ fun main(){
}
}
```
![](../docs/images/two-boxes-1.png)
![](../docs/images/two-boxes-2.png)
![](../images/two-boxes-1.png)
![](../images/two-boxes-2.png)
***There is plenty of other properties, especially those, which you can create by yourself. Here we mention just a small part.***
@ -142,8 +142,8 @@ polyline(Point3D(30, 20, 10), Point3D(30, -100, 30), Point3D(30, -100, 30), Poin
}
```
![](../docs/images/polyline-points.png)
![](../docs/images/polyline-points-2.png)
![](../images/polyline-points.png)
![](../images/polyline-points-2.png)
### 2) Box
@ -165,7 +165,7 @@ Let's create just usual `box` with equal ribs.
color("pink")
}
```
![](../docs/images/box.png)
![](../images/box.png)
Now, let's make `box` with bigger `y` value.
```kotlin
@ -175,7 +175,7 @@ Now, let's make `box` with bigger `y` value.
```
As you can see, only the rib of `y-axis` differs from other ribs.
![](../docs/images/high-box.png)
![](../images/high-box.png)
For a final trial, let's create a `box` with a bigger `x` value.
@ -189,7 +189,7 @@ For a final trial, let's create a `box` with a bigger `x` value.
```
Predictably, only the `x-axis` rib is bigger than other ribs.
![](../docs/images/wide-box.png)
![](../images/wide-box.png)
### 3) Sphere
@ -206,7 +206,7 @@ As for `radius`, it has `Float` type, and, as you can guess, it sets the radius
color("blue")
}
```
![](../docs/images/sphere.png)
![](../images/sphere.png)
### 4) Hexagon
@ -220,7 +220,7 @@ It is solid which has six edges. It is set by eight values: `node1`,..., `node8`
5) Edge with vertices `node1`, `node5`, `node8`, `node4`
6) Edge with vertices `node8`, `node5`, `node6`, `node7`
![](../docs/images/scheme.png)
![](../images/scheme.png)
As the hexagon takes in specific points, we understand that this solid cannot be moved, it is fixed in space, and it can't make pivots.
@ -239,7 +239,7 @@ Let's make classic parallelepiped.
color("green")
}
```
![](../docs/images/classic-hexagon.png)
![](../images/classic-hexagon.png)
Now, let's make a custom hexagon.
@ -258,7 +258,7 @@ hexagon(
color("brown")
}
```
![](../docs/images/custom-hexagon.png)
![](../images/custom-hexagon.png)
### 3) Cone
It takes in six values: `bottomRadius`, `height`, `upperRadius`, `startAngle`, `angle`, and `name`.
@ -274,8 +274,8 @@ Let's build a classic cone:
color("beige")
}
```
![](../docs/images/cone-1.png)
![](../docs/images/cone-2.png)
![](../images/cone-1.png)
![](../images/cone-2.png)
First of all, we have to try to build a frustum cone:
```kotlin
@ -283,7 +283,7 @@ cone(60, 80, name = "cone") {
color(0u, 40u, 0u)
}
```
![](../docs/images/frustum-cone.png)
![](../images/frustum-cone.png)
Now, we need to make a try to build a cone segment:
@ -292,8 +292,8 @@ cone(60, 80, angle = PI, name = "cone") {
color(0u, 0u, 200u)
}
```
![](../docs/images/cone-segment-1.png)
![](../docs/images/cone-segment-2.png)
![](../images/cone-segment-1.png)
![](../images/cone-segment-2.png)
Finally, the segment of frustum cone is left for a try:
```kotlin
@ -301,7 +301,7 @@ cone(60, 100, 20, PI*3/4, angle = PI/3, name = "cone") {
color(190u, 0u, 0u)
}
```
![](../docs/images/frustum-cone-segment.png)
![](../images/frustum-cone-segment.png)
### 4) Cone Surface
This solid is set by seven values:`bottomOuterRadius`, `bottomInnerRadius`, `height`, `topOuterRadius`, `topInnerRadius`, `startAngle`, and `angle`.
@ -318,8 +318,8 @@ Let's build usual cone surface with almost all properties set:
rotation = Point3D(2, 50, -9)
}
```
![](../docs/images/cone-surface-1.png)
![](../docs/images/cone-surface-2.png)
![](../images/cone-surface-1.png)
![](../images/cone-surface-2.png)
Now, let's create a cone surface and set all it's properties:
@ -329,8 +329,8 @@ coneSurface(30, 25, 10, 10, 8,0f, pi*3/4, name = "cone surface") {
rotation = Point3D(2, 50, -9)
}
```
![](../docs/images/cone-surface-fragment.png)
![](../docs/images/cone-surface-fragment-2.png)
![](../images/cone-surface-fragment.png)
![](../images/cone-surface-fragment-2.png)
### 5) Cylinder
@ -344,8 +344,8 @@ cylinder(40, 100, "cylinder"){
color("indigo")
}
```
![](../docs/images/cylinder-1.png)
![](../docs/images/cylinder-2.png)
![](../images/cylinder-1.png)
![](../images/cylinder-2.png)
### 6) Tube
`tube` takes in `radius`, `height`, `innerRadius`, `startAngle`, `angle`, and `name`. *All values are familiar from `cone`, and `coneSurface` solids.*
@ -356,7 +356,7 @@ tube(50, 40, 20, name = "usual tube"){
opacity = 0.4
}
```
![](../docs/images/tube.png)
![](../images/tube.png)
This is an example of tube fragment:
@ -365,7 +365,7 @@ tube(50, 40, 20, 0f, PI, name = "fragmented tube"){
color("white")
}
```
![](../docs/images/tube-fragment.png)
![](../images/tube-fragment.png)
### 7) Extruded
`extruded` is set by two values: `shape`, and `layer`.

View File

@ -38,6 +38,10 @@ public fun ThreeCanvasWithControlsProps.solid(block: SolidGroup.() -> Unit) {
}
}
public fun ThreeCanvasWithControlsProps.options(block: Canvas3DOptions.() -> Unit){
options = Canvas3DOptions(block)
}
public fun ThreeCanvasWithControlsProps.tab(title: String, block: RBuilder.() -> Unit) {
additionalTabs = (additionalTabs ?: emptyMap()) + (title to block)
}

View File

@ -17,7 +17,7 @@ import kotlin.math.PI
import kotlin.math.cos
import kotlin.math.max
import kotlin.math.sin
import space.kscience.visionforge.solid.specifications.Camera as CameraSpec
import space.kscience.visionforge.solid.specifications.CameraScheme as CameraSpec
public class OrbitControls internal constructor(camera: Camera, canvas: SubScene, spec: CameraSpec) {

View File

@ -0,0 +1,44 @@
package space.kscience.visionforge.solid
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
import space.kscience.dataforge.names.asName
import space.kscience.visionforge.VisionBuilder
import space.kscience.visionforge.VisionContainerBuilder
import space.kscience.visionforge.numberProperty
import space.kscience.visionforge.set
@Serializable
public abstract class LightSource : SolidBase() {
@Transient
public val color: ColorAccessor = ColorAccessor(meta, "color".asName())
public var intensity: Number by numberProperty(includeStyles = false) { 1.0 }
}
@Serializable
@SerialName("solid.light.ambient")
public class AmbientLightSource : LightSource()
@VisionBuilder
public fun VisionContainerBuilder<Solid>.ambientLight(
name: String? = "@ambientLight",
block: AmbientLightSource.() -> Unit = {},
): AmbientLightSource = AmbientLightSource().apply(block).also { set(name, it) }
@Serializable
@SerialName("solid.light.point")
public class PointLightSource : LightSource()
@VisionBuilder
public fun VisionContainerBuilder<Solid>.pointLight(
x: Number,
y: Number,
z: Number,
name: String? = null,
block: PointLightSource.() -> Unit = {},
): PointLightSource = PointLightSource().apply(block).also {
it.position = Point3D(x, y, z)
set(name, it)
}

View File

@ -72,9 +72,7 @@ public class SolidGroup : VisionGroupBase(), Solid, PrototypeHolder {
}
@Suppress("FunctionName")
public fun SolidGroup(block: SolidGroup.() -> Unit): SolidGroup {
return SolidGroup().apply(block)
}
public fun SolidGroup(block: SolidGroup.() -> Unit): SolidGroup = SolidGroup().apply(block)
@VisionBuilder
public fun VisionContainerBuilder<Vision>.group(

View File

@ -27,7 +27,7 @@ public class SolidMaterial : Scheme() {
*/
public val specularColor: ColorAccessor = ColorAccessor(meta, SPECULAR_COLOR_KEY)
public val emissiveColor: ColorAccessor = ColorAccessor(meta, "emissiveColor".asName())
public val emissiveColor: ColorAccessor = ColorAccessor(meta, EMISSIVE_COLOR_KEY)
/**
* Opacity
@ -43,12 +43,15 @@ public class SolidMaterial : Scheme() {
public val MATERIAL_KEY: Name = "material".asName()
public val COLOR_KEY: Name = "color".asName()
public val MATERIAL_COLOR_KEY: Name = MATERIAL_KEY + COLOR_KEY
public val TYPE_KEY: Name = "type".asName()
public val SPECULAR_COLOR_KEY: Name = "specularColor".asName()
public val MATERIAL_SPECULAR_COLOR_KEY: Name = MATERIAL_KEY + SPECULAR_COLOR_KEY
public val EMISSIVE_COLOR_KEY: Name = "emissiveColor".asName()
public val OPACITY_KEY: Name = "opacity".asName()
public val MATERIAL_OPACITY_KEY: Name = MATERIAL_KEY + OPACITY_KEY
public val WIREFRAME_KEY: Name = "wireframe".asName()
public val MATERIAL_COLOR_KEY: Name = MATERIAL_KEY + COLOR_KEY
public val MATERIAL_EMISSIVE_COLOR_KEY: Name = MATERIAL_KEY + EMISSIVE_COLOR_KEY
public val MATERIAL_SPECULAR_COLOR_KEY: Name = MATERIAL_KEY + SPECULAR_COLOR_KEY
public val MATERIAL_WIREFRAME_KEY: Name = MATERIAL_KEY + WIREFRAME_KEY
public override val descriptor: MetaDescriptor by lazy {
@ -56,6 +59,12 @@ public class SolidMaterial : Scheme() {
MetaDescriptor {
inherited = true
value(TYPE_KEY, ValueType.STRING){
inherited = true
allowedValues = listOf("default".asValue(), "simple".asValue())
default("default")
}
value(COLOR_KEY, ValueType.STRING, ValueType.NUMBER) {
inherited = true
widgetType = "color"
@ -67,6 +76,12 @@ public class SolidMaterial : Scheme() {
hide()
}
value(EMISSIVE_COLOR_KEY, ValueType.STRING, ValueType.NUMBER) {
inherited = true
widgetType = "color"
hide()
}
value(OPACITY_KEY, ValueType.NUMBER) {
inherited = true
default(1.0)

View File

@ -39,6 +39,9 @@ public class Solids(meta: Meta) : VisionPlugin(meta) {
subclass(PolyLine.serializer())
subclass(SolidLabel.serializer())
subclass(Sphere.serializer())
subclass(AmbientLightSource.serializer())
subclass(PointLightSource.serializer())
}
public val serializersModuleForSolids: SerializersModule = SerializersModule {

View File

@ -7,24 +7,24 @@ import space.kscience.dataforge.meta.descriptors.MetaDescriptor
import space.kscience.dataforge.meta.descriptors.value
import space.kscience.dataforge.meta.double
public class Axes : Scheme() {
public class AxesScheme : Scheme() {
public var visible: Boolean by boolean(false)
public var size: Double by double(AXIS_SIZE)
public var width: Double by double(AXIS_WIDTH)
public companion object : SchemeSpec<Axes>(::Axes) {
public companion object : SchemeSpec<AxesScheme>(::AxesScheme) {
public const val AXIS_SIZE: Double = 1000.0
public const val AXIS_WIDTH: Double = 3.0
override val descriptor: MetaDescriptor by lazy {
MetaDescriptor {
value(Axes::visible){
value(AxesScheme::visible){
default(false)
}
value(Axes::size){
value(AxesScheme::size){
default(AXIS_SIZE)
}
value(Axes::width){
value(AxesScheme::width){
default(AXIS_WIDTH)
}
}

View File

@ -8,7 +8,7 @@ import space.kscience.dataforge.meta.double
import space.kscience.dataforge.meta.int
import kotlin.math.PI
public class Camera : Scheme() {
public class CameraScheme : Scheme() {
public var fov: Int by int(FIELD_OF_VIEW)
//var aspect by double(1.0)
@ -19,7 +19,7 @@ public class Camera : Scheme() {
public var azimuth: Double by double(INITIAL_AZIMUTH)
public var latitude: Double by double(INITIAL_LATITUDE)
public companion object : SchemeSpec<Camera>(::Camera) {
public companion object : SchemeSpec<CameraScheme>(::CameraScheme) {
public const val INITIAL_DISTANCE: Double = 300.0
public const val INITIAL_AZIMUTH: Double = 0.0
public const val INITIAL_LATITUDE: Double = PI / 6
@ -29,22 +29,22 @@ public class Camera : Scheme() {
override val descriptor: MetaDescriptor by lazy {
MetaDescriptor {
value(Camera::fov) {
value(CameraScheme::fov) {
default(FIELD_OF_VIEW)
}
value(Camera::nearClip) {
value(CameraScheme::nearClip) {
default(NEAR_CLIP)
}
value(Camera::farClip) {
value(CameraScheme::farClip) {
default(FAR_CLIP)
}
value(Camera::distance) {
value(CameraScheme::distance) {
default(INITIAL_DISTANCE)
}
value(Camera::azimuth) {
value(CameraScheme::azimuth) {
default(INITIAL_AZIMUTH)
}
value(Camera::latitude) {
value(CameraScheme::latitude) {
default(INITIAL_LATITUDE)
}
}
@ -52,4 +52,4 @@ public class Camera : Scheme() {
}
}
public val Camera.zenith: Double get() = PI / 2 - latitude
public val CameraScheme.zenith: Double get() = PI / 2 - latitude

View File

@ -8,28 +8,24 @@ import space.kscience.dataforge.names.Name
import space.kscience.visionforge.hide
import space.kscience.visionforge.widgetType
public class Clipping : Scheme() {
public var x: Double? by double()
public var y: Double? by double()
public var z: Double? by double()
public companion object : SchemeSpec<Clipping>(::Clipping) {
public object Clipping : SchemeSpec<PointScheme>(::PointScheme) {
override val descriptor: MetaDescriptor = MetaDescriptor {
value(Clipping::x) {
value(PointScheme::x) {
widgetType = "range"
attributes["min"] = 0.0
attributes["max"] = 1.0
attributes["step"] = 0.01
default(1.0)
}
value(Clipping::y) {
value(PointScheme::y) {
widgetType = "range"
attributes["min"] = 0.0
attributes["max"] = 1.0
attributes["step"] = 0.01
default(1.0)
}
value(Clipping::z) {
value(PointScheme::z) {
widgetType = "range"
attributes["min"] = 0.0
attributes["max"] = 1.0
@ -38,7 +34,7 @@ public class Clipping : Scheme() {
}
}
}
}
public class CanvasSize : Scheme() {
public var minSize: Int by int(400)
@ -62,16 +58,15 @@ public class CanvasSize : Scheme() {
}
public class Canvas3DOptions : Scheme() {
public var axes: Axes by spec(Axes)
public var light: Light by spec(Light)
public var camera: Camera by spec(Camera)
public var controls: Controls by spec(Controls)
public var axes: AxesScheme by spec(AxesScheme)
public var camera: CameraScheme by spec(CameraScheme)
public var controls: ControlsScheme by spec(ControlsScheme)
public var size: CanvasSize by spec(CanvasSize)
public var layers: List<Number> by numberList(0)
public var clipping: Clipping by spec(Clipping)
public var clipping: PointScheme by spec(Clipping)
public var onSelect: ((Name?) -> Unit)? = null
@ -79,7 +74,7 @@ public class Canvas3DOptions : Scheme() {
public companion object : SchemeSpec<Canvas3DOptions>(::Canvas3DOptions) {
override val descriptor: MetaDescriptor by lazy {
MetaDescriptor {
scheme(Canvas3DOptions::axes, Axes)
scheme(Canvas3DOptions::axes, AxesScheme)
value(Canvas3DOptions::layers) {
multiple = true
@ -90,15 +85,11 @@ public class Canvas3DOptions : Scheme() {
scheme(Canvas3DOptions::clipping, Clipping)
scheme(Canvas3DOptions::light, Light){
scheme(Canvas3DOptions::camera, CameraScheme) {
hide()
}
scheme(Canvas3DOptions::camera, Camera) {
hide()
}
scheme(Canvas3DOptions::controls, Controls) {
scheme(Canvas3DOptions::controls, ControlsScheme) {
hide()
}

View File

@ -4,6 +4,6 @@ import space.kscience.dataforge.meta.Scheme
import space.kscience.dataforge.meta.SchemeSpec
public class Controls : Scheme() {
public companion object : SchemeSpec<Controls>(::Controls)
public class ControlsScheme : Scheme() {
public companion object : SchemeSpec<ControlsScheme>(::ControlsScheme)
}

View File

@ -1,8 +0,0 @@
package space.kscience.visionforge.solid.specifications
import space.kscience.dataforge.meta.Scheme
import space.kscience.dataforge.meta.SchemeSpec
public class Light : Scheme() {
public companion object : SchemeSpec<Light>(::Light)
}

View File

@ -0,0 +1,19 @@
package space.kscience.visionforge.solid.specifications
import space.kscience.dataforge.meta.Scheme
import space.kscience.dataforge.meta.SchemeSpec
import space.kscience.dataforge.meta.double
public class PointScheme: Scheme(){
public var x: Double? by double()
public var y: Double? by double()
public var z: Double? by double()
public companion object: SchemeSpec<PointScheme>(::PointScheme)
}
public operator fun PointScheme.invoke(x: Number?, y: Number?, z: Number?){
this.x = x?.toDouble()
this.y = y?.toDouble()
this.z = z?.toDouble()
}

View File

@ -1,6 +1,7 @@
package space.kscience.visionforge.solid
import space.kscience.dataforge.names.Name
import space.kscience.visionforge.Colors
import space.kscience.visionforge.MutableVisionGroup
import space.kscience.visionforge.get
import kotlin.test.Test
@ -55,4 +56,18 @@ class SerializationTest {
assertEquals(group["cube"]?.meta, reconstructed["cube"]?.meta)
}
@Test
fun lightSerialization(){
val group = SolidGroup {
ambientLight {
color(Colors.white)
intensity = 100.0
}
}
val serialized = Solids.encodeToString(group)
val reconstructed = Solids.decodeFromString(serialized) as SolidGroup
assertEquals(100.0, (reconstructed["@ambientLight"] as AmbientLightSource).intensity.toDouble())
}
}

View File

@ -10,6 +10,7 @@ import space.kscience.dataforge.names.asName
import space.kscience.dataforge.names.plus
import space.kscience.dataforge.names.startsWith
import space.kscience.dataforge.values.boolean
import space.kscience.visionforge.VisionBuilder
import space.kscience.visionforge.computePropertyNode
import space.kscience.visionforge.onPropertyChange
import space.kscience.visionforge.setProperty
@ -75,6 +76,7 @@ public abstract class MeshThreeFactory<in T : Solid>(
}
}
@VisionBuilder
public fun Solid.edges(enabled: Boolean = true, block: SolidMaterial.() -> Unit = {}) {
setProperty(EDGES_ENABLED_KEY, enabled)
meta.getOrCreate(EDGES_MATERIAL_KEY).updateWith(SolidMaterial, block)

View File

@ -0,0 +1,19 @@
package space.kscience.visionforge.solid.three
import info.laht.threekt.lights.AmbientLight
import info.laht.threekt.math.Color
import space.kscience.visionforge.solid.AmbientLightSource
import kotlin.reflect.KClass
public object ThreeAmbientLightFactory : ThreeFactory<AmbientLightSource> {
override val type: KClass<in AmbientLightSource> get() = AmbientLightSource::class
override fun invoke(three: ThreePlugin, obj: AmbientLightSource): AmbientLight {
val res = AmbientLight().apply {
color = obj.color.threeColor() ?: Color(0x404040)
intensity = obj.intensity.toDouble()
}
return res
}
}

View File

@ -8,12 +8,8 @@ import info.laht.threekt.external.controls.OrbitControls
import info.laht.threekt.external.controls.TrackballControls
import info.laht.threekt.geometries.EdgesGeometry
import info.laht.threekt.helpers.AxesHelper
import info.laht.threekt.lights.AmbientLight
import info.laht.threekt.materials.LineBasicMaterial
import info.laht.threekt.math.Box3
import info.laht.threekt.math.Plane
import info.laht.threekt.math.Vector2
import info.laht.threekt.math.Vector3
import info.laht.threekt.math.*
import info.laht.threekt.objects.LineSegments
import info.laht.threekt.objects.Mesh
import info.laht.threekt.scenes.Scene
@ -35,7 +31,7 @@ import kotlin.math.cos
import kotlin.math.sin
/**
*
* A canvas for three-js rendering
*/
public class ThreeCanvas(
public val three: ThreePlugin,
@ -60,19 +56,19 @@ public class ThreeCanvas(
add(axesObject)
}
//Set up light
options.useProperty(Canvas3DOptions::light, this) { lightConfig ->
//remove old light if present
getObjectByName(LIGHT_NAME)?.let { remove(it) }
//add new light
val lightObject = buildLight(lightConfig)
lightObject.name = LIGHT_NAME
add(lightObject)
}
// //Set up light
// options.useProperty(Canvas3DOptions::light, this) { lightConfig ->
// //remove old light if present
// getObjectByName(LIGHT_NAME)?.let { remove(it) }
// //add new light
// val lightObject = buildLight(lightConfig)
// lightObject.name = LIGHT_NAME
// add(lightObject)
// }
}
private fun buildCamera(spec: Camera) = PerspectiveCamera(
private fun buildCamera(spec: CameraScheme) = PerspectiveCamera(
spec.fov,
1.0,
spec.nearClip,
@ -231,9 +227,24 @@ public class ThreeCanvas(
}
}
private fun buildLight(spec: Light?): info.laht.threekt.lights.Light = AmbientLight(0x404040)
// private fun buildLight(spec: AmbientLightScheme?): info.laht.threekt.lights.Light = when (spec?.type) {
// AmbientLightScheme.Type.POINT -> PointLight().apply {
// position.x = spec.position.x ?: 0.0
// position.y = spec.position.y ?: 0.0
// position.z = spec.position.z ?: 0.0
// }
// else -> AmbientLight().apply {
//
// }
// }.apply {
// this.color = spec?.color?.threeColor() ?: Color(0x404040)
//
// spec?.intensity?.coerceIn(0.0, 1.0)?.let {
// this.intensity = it
// }
// }
private fun addControls(element: Node, controls: Controls) {
private fun addControls(element: Node, controls: ControlsScheme) {
when (controls.meta["type"].string) {
"trackball" -> TrackballControls(camera, element)
else -> OrbitControls(camera, element)
@ -311,7 +322,7 @@ public class ThreeCanvas(
private const val SELECT_NAME = "@select"
private const val LIGHT_NAME = "@light"
private const val AXES_NAME = "@axes"
private const val CLIP_HELPER_NAME = "@clipping"
//private const val CLIP_HELPER_NAME = "@clipping"
}
}

View File

@ -8,7 +8,6 @@ import space.kscience.dataforge.misc.Type
import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.startsWith
import space.kscience.visionforge.Vision
import space.kscience.visionforge.computeProperty
import space.kscience.visionforge.solid.*
import space.kscience.visionforge.solid.SolidMaterial.Companion.MATERIAL_KEY
import space.kscience.visionforge.solid.three.ThreeFactory.Companion.TYPE
@ -58,7 +57,7 @@ public fun Object3D.updatePosition(obj: Vision) {
* Update non-position non-geometry property
*/
public fun Object3D.updateProperty(source: Vision, propertyName: Name) {
console.log("$source updated $propertyName with ${source.computeProperty(propertyName)}")
// console.log("$source updated $propertyName with ${source.computeProperty(propertyName)}")
if (this is Mesh && propertyName.startsWith(MATERIAL_KEY)) {
updateMaterialProperty(source, propertyName)
} else if (

View File

@ -3,39 +3,52 @@ package space.kscience.visionforge.solid.three
import info.laht.threekt.materials.LineBasicMaterial
import info.laht.threekt.materials.Material
import info.laht.threekt.materials.MeshBasicMaterial
import info.laht.threekt.materials.MeshStandardMaterial
import info.laht.threekt.math.Color
import info.laht.threekt.objects.Mesh
import space.kscience.dataforge.meta.*
import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.asName
import space.kscience.dataforge.names.plus
import space.kscience.dataforge.values.*
import space.kscience.visionforge.Colors
import space.kscience.visionforge.Vision
import space.kscience.visionforge.computePropertyNode
import space.kscience.visionforge.getStyleNodes
import space.kscience.visionforge.solid.ColorAccessor
import space.kscience.visionforge.solid.SolidMaterial
import space.kscience.visionforge.solid.SolidReference
public object ThreeMaterials {
public val DEFAULT_COLOR: Color = Color(Colors.darkgreen)
public val DEFAULT: MeshBasicMaterial = MeshBasicMaterial().apply {
public val DEFAULT: MeshStandardMaterial = MeshStandardMaterial().apply {
color.set(DEFAULT_COLOR)
cached = true
}
public val DEFAULT_LINE_COLOR: Color = Color(Colors.black)
public val BLACK_COLOR: Color = Color(Colors.black)
public val DEFAULT_EMISSIVE_COLOR: Color = BLACK_COLOR
public val DEFAULT_LINE_COLOR: Color get() = BLACK_COLOR
public val DEFAULT_LINE: LineBasicMaterial = LineBasicMaterial().apply {
color.set(DEFAULT_LINE_COLOR)
cached = true
}
public val SELECTED_MATERIAL: LineBasicMaterial = LineBasicMaterial().apply {
color.set(Colors.ivory)
linewidth = 8.0
cached = true
}
public val HIGHLIGHT_MATERIAL: LineBasicMaterial = LineBasicMaterial().apply {
color.set(Colors.blue)
linewidth = 8.0
cached = true
}
private val lineMaterialCache = HashMap<Meta, LineBasicMaterial>()
@ -58,34 +71,21 @@ public object ThreeMaterials {
private val materialCache = HashMap<Meta, Material>()
internal fun buildMaterial(meta: Meta): Material = MeshBasicMaterial().apply {
internal fun buildMaterial(meta: Meta): Material = when (meta[SolidMaterial.TYPE_KEY]?.string) {
"simple" -> MeshBasicMaterial().apply {
color = meta[SolidMaterial.COLOR_KEY]?.threeColor() ?: DEFAULT_COLOR
wireframe = meta[SolidMaterial.WIREFRAME_KEY].boolean ?: false
}
else -> MeshStandardMaterial().apply {
color = meta[SolidMaterial.COLOR_KEY]?.threeColor() ?: DEFAULT_COLOR
emissive = meta[SolidMaterial.EMISSIVE_COLOR_KEY]?.threeColor() ?: DEFAULT_EMISSIVE_COLOR
wireframe = meta[SolidMaterial.WIREFRAME_KEY].boolean ?: false
}
}.apply {
opacity = meta[SolidMaterial.OPACITY_KEY]?.double ?: 1.0
transparent = opacity < 1.0
wireframe = meta[SolidMaterial.WIREFRAME_KEY].boolean ?: false
needsUpdate = true
}
// val material = SolidMaterial.read(meta)
// return meta[SolidMaterial.SPECULAR_COLOR_KEY]?.let { specularColor ->
// MeshPhongMaterial().apply {
// color = meta[SolidMaterial.COLOR_KEY]?.threeColor() ?: DEFAULT_COLOR
// specular = specularColor.threeColor()
// emissive = material.emissiveColor.threeColor() ?: specular
// reflectivity = 0.5
// refractionRatio = 1.0
// shininess = 100.0
// opacity = meta[SolidMaterial.OPACITY_KEY]?.double ?: 1.0
// transparent = opacity < 1.0
// wireframe = meta[SolidMaterial.WIREFRAME_KEY].boolean ?: false
// needsUpdate = true
// }
// } ?: MeshBasicMaterial().apply {
// color = meta[SolidMaterial.COLOR_KEY]?.threeColor() ?: DEFAULT_COLOR
// opacity = meta[SolidMaterial.OPACITY_KEY]?.double ?: 1.0
// transparent = opacity < 1.0
// wireframe = meta[SolidMaterial.WIREFRAME_KEY].boolean ?: false
// needsUpdate = true
// }
internal fun cacheMaterial(meta: Meta): Material = materialCache.getOrPut(meta) {
buildMaterial(meta).apply {
@ -115,6 +115,16 @@ public fun Meta.threeColor(): Color? {
}
}
public fun ColorAccessor.threeColor(): Color? {
val value = value
return when {
value == null -> null
value === Null -> null
value.type == ValueType.NUMBER -> Color(value.int)
else -> Color(value.string)
}
}
private var Material.cached: Boolean
get() = userData["cached"] == true
set(value) {
@ -139,7 +149,11 @@ public fun Mesh.updateMaterial(vision: Vision) {
}
public fun Mesh.updateMaterialProperty(vision: Vision, propertyName: Name) {
if (material.cached || propertyName == SolidMaterial.MATERIAL_KEY) {
if (
material.cached
|| propertyName == SolidMaterial.MATERIAL_KEY
|| propertyName == SolidMaterial.MATERIAL_KEY + SolidMaterial.TYPE_KEY
) {
//generate a new material since cached material should not be changed
updateMaterial(vision)
} else {
@ -149,6 +163,16 @@ public fun Mesh.updateMaterialProperty(vision: Vision, propertyName: Name) {
?: ThreeMaterials.DEFAULT_COLOR
material.needsUpdate = true
}
SolidMaterial.SPECULAR_COLOR_KEY -> {
material.asDynamic().specular = vision.computePropertyNode(SolidMaterial.SPECULAR_COLOR_KEY)?.threeColor()
?: ThreeMaterials.DEFAULT_COLOR
material.needsUpdate = true
}
SolidMaterial.MATERIAL_EMISSIVE_COLOR_KEY -> {
material.asDynamic().emissive = vision.computePropertyNode(SolidMaterial.MATERIAL_EMISSIVE_COLOR_KEY)?.threeColor()
?: ThreeMaterials.BLACK_COLOR
material.needsUpdate = true
}
SolidMaterial.MATERIAL_OPACITY_KEY -> {
val opacity = vision.getPropertyValue(
SolidMaterial.MATERIAL_OPACITY_KEY,

View File

@ -1,7 +1,6 @@
package space.kscience.visionforge.solid.three
import info.laht.threekt.core.Object3D
import kotlinx.coroutines.CoroutineScope
import org.w3c.dom.Element
import org.w3c.dom.HTMLElement
import space.kscience.dataforge.context.*
@ -27,8 +26,7 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer {
private val objectFactories = HashMap<KClass<out Solid>, ThreeFactory<*>>()
private val compositeFactory = ThreeCompositeFactory(this)
//TODO generate a separate supervisor update scope
internal val updateScope: CoroutineScope get() = context
// internal val updateScope: CoroutineScope get() = context
init {
//Add specialized factories here
@ -38,6 +36,8 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer {
objectFactories[ConeSegment::class] = ThreeConeFactory
objectFactories[PolyLine::class] = ThreeLineFactory
objectFactories[SolidLabel::class] = ThreeCanvasLabelFactory
objectFactories[AmbientLightSource::class] = ThreeAmbientLightFactory
objectFactories[PointLightSource::class] = ThreePointLightFactory
}
@Suppress("UNCHECKED_CAST")

View File

@ -0,0 +1,28 @@
package space.kscience.visionforge.solid.three
import info.laht.threekt.lights.PointLight
import info.laht.threekt.math.Color
import space.kscience.visionforge.onPropertyChange
import space.kscience.visionforge.solid.PointLightSource
import kotlin.reflect.KClass
public object ThreePointLightFactory : ThreeFactory<PointLightSource> {
override val type: KClass<in PointLightSource> get() = PointLightSource::class
override fun invoke(three: ThreePlugin, obj: PointLightSource): PointLight {
val res = PointLight().apply {
matrixAutoUpdate = false
color = obj.color.threeColor() ?: Color(0x404040)
intensity = obj.intensity.toDouble()
updatePosition(obj)
}
obj.onPropertyChange { name ->
when {
else -> res.updateProperty(obj, name)
}
}
return res
}
}