Optimized materials allocation

This commit is contained in:
Alexander Nozik 2020-12-21 21:45:29 +03:00
parent eefc036dcb
commit 66ea23ad60
11 changed files with 181 additions and 91 deletions

View File

@ -11,7 +11,6 @@ import hep.dataforge.vision.solid.*
import hep.dataforge.vision.solid.Solid.Companion.GEOMETRY_KEY
import hep.dataforge.vision.solid.SolidMaterial.Companion.MATERIAL_COLOR_KEY
import hep.dataforge.vision.solid.three.*
import hep.dataforge.vision.solid.three.ThreeMaterials.getMaterial
import info.laht.threekt.core.BufferGeometry
import info.laht.threekt.core.Object3D
import info.laht.threekt.geometries.BoxBufferGeometry
@ -31,8 +30,8 @@ internal class VariableBox(xSize: Number, ySize: Number, zSize: Number) : ThreeV
scaleX = xSize
scaleY = ySize
scaleZ = zSize
getProperty(MeshThreeFactory.EDGES_ENABLED_KEY, false)
getProperty(MeshThreeFactory.WIREFRAME_ENABLED_KEY, false)
// getProperty(MeshThreeFactory.EDGES_ENABLED_KEY, inherit = false, includeStyles = false)
// getProperty(MeshThreeFactory.WIREFRAME_ENABLED_KEY, inherit = false, includeStyles = false)
}
override fun render(three: ThreePlugin): Object3D {
@ -44,9 +43,10 @@ internal class VariableBox(xSize: Number, ySize: Number, zSize: Number) : ThreeV
//JS sometimes tries to pass Geometry as BufferGeometry
@Suppress("USELESS_IS_CHECK") if (geometry !is BufferGeometry) error("BufferGeometry expected")
val mesh = Mesh(geometry, getMaterial(this@VariableBox, true)).apply {
val mesh = Mesh(geometry, ThreeMaterials.DEFAULT).apply {
updateMaterial(this@VariableBox)
applyEdges(this@VariableBox)
applyWireFrame(this@VariableBox)
//applyWireFrame(this@VariableBox)
//set position for mesh
updatePosition(this@VariableBox)
@ -69,10 +69,10 @@ internal class VariableBox(xSize: Number, ySize: Number, zSize: Number) : ThreeV
mesh.scale.set(newXSize, newYSize, newZSize)
mesh.updateMatrix()
}
name.startsWith(MeshThreeFactory.WIREFRAME_KEY) -> mesh.applyWireFrame(this@VariableBox)
//name.startsWith(MeshThreeFactory.WIREFRAME_KEY) -> mesh.applyWireFrame(this@VariableBox)
name.startsWith(MeshThreeFactory.EDGES_KEY) -> mesh.applyEdges(this@VariableBox)
name.startsWith(MATERIAL_COLOR_KEY) -> {
mesh.material = getMaterial(this, true)
mesh.updateMaterial(this)
}
else -> mesh.updateProperty(this@VariableBox, name)
}

View File

@ -4,6 +4,7 @@ import hep.dataforge.meta.MetaItem
import hep.dataforge.meta.MutableItemProvider
import hep.dataforge.meta.descriptors.Described
import hep.dataforge.meta.descriptors.NodeDescriptor
import hep.dataforge.meta.descriptors.get
import hep.dataforge.names.Name
import hep.dataforge.names.asName
import hep.dataforge.names.toName
@ -45,8 +46,8 @@ public interface Vision : Described {
*/
public fun getProperty(
name: Name,
inherit: Boolean? = null,
includeStyles: Boolean? = null,
inherit: Boolean = false,
includeStyles: Boolean = true,
includeDefaults: Boolean = true,
): MetaItem<*>?
@ -63,14 +64,15 @@ public interface Vision : Described {
* if it should include inherited properties etc.
*/
@OptIn(ExperimentalCoroutinesApi::class)
public val propertyChanges: Flow<Name> get() = callbackFlow<Name> {
coroutineScope {
onPropertyChange(this) {
send(it)
public val propertyChanges: Flow<Name>
get() = callbackFlow<Name> {
coroutineScope {
onPropertyChange(this) {
send(it)
}
awaitClose { cancel() }
}
awaitClose { cancel() }
}
}
/**
@ -93,7 +95,7 @@ public interface Vision : Described {
}
}
public fun Vision.asyncNotifyPropertyChange(propertyName: Name){
public fun Vision.asyncNotifyPropertyChange(propertyName: Name) {
scope.launch {
notifyPropertyChanged(propertyName)
}
@ -120,8 +122,8 @@ public fun Vision.allProperties(
): MutableItemProvider = object : MutableItemProvider {
override fun getItem(name: Name): MetaItem<*>? = getProperty(
name,
inherit = inherit,
includeStyles = includeStyles,
inherit = inherit ?: (descriptor?.get(name)?.inherited != false),
includeStyles = includeStyles ?: (descriptor?.get(name)?.usesStyles == true),
includeDefaults = includeDefaults
)
@ -133,8 +135,8 @@ public fun Vision.allProperties(
*/
public fun Vision.getProperty(
key: String,
inherit: Boolean? = null,
includeStyles: Boolean? = null,
inherit: Boolean = false,
includeStyles: Boolean = true,
includeDefaults: Boolean = true,
): MetaItem<*>? = getProperty(key.toName(), inherit, includeStyles, includeDefaults)

View File

@ -65,15 +65,15 @@ public open class VisionBase : Vision {
override fun getProperty(
name: Name,
inherit: Boolean?,
includeStyles: Boolean?,
inherit: Boolean,
includeStyles: Boolean,
includeDefaults: Boolean,
): MetaItem<*>? = sequence {
yield(getOwnProperty(name))
if (includeStyles ?: descriptor?.get(name)?.usesStyles != false) {
if (includeStyles) {
yieldAll(getStyleItems(name))
}
if (inherit ?: descriptor?.get(name)?.inherited == true) {
if (inherit) {
yield(parent?.getProperty(name, inherit, includeStyles, includeDefaults))
}
yield(descriptor?.get(name)?.defaultItem())

View File

@ -6,15 +6,13 @@ import hep.dataforge.values.asValue
@DslMarker
public annotation class VisionBuilder
public fun Sequence<MetaItem<*>?>.merge(): MetaItem<*>? {
return when (val first = firstOrNull { it != null }) {
null -> null
is MetaItem.ValueItem -> first //fast search for first entry if it is value
is MetaItem.NodeItem -> {
//merge nodes if first encountered node is meta
val laminate: Laminate = Laminate(mapNotNull { it.node }.toList())
MetaItem.NodeItem(laminate)
}
public fun Sequence<MetaItem<*>?>.merge(): MetaItem<*>? = when (val first = firstOrNull { it != null }) {
null -> null
is MetaItem.ValueItem -> first //fast search for first entry if it is value
is MetaItem.NodeItem -> {
//merge nodes if first encountered node is meta
val laminate: Laminate = Laminate(mapNotNull { it.node }.toList())
MetaItem.NodeItem(laminate)
}
}

View File

@ -109,8 +109,8 @@ internal class Prototypes(
override fun getProperty(
name: Name,
inherit: Boolean?,
includeStyles: Boolean?,
inherit: Boolean,
includeStyles: Boolean,
includeDefaults: Boolean,
): MetaItem<*>? = null

View File

@ -2,7 +2,6 @@ package hep.dataforge.vision.solid
import hep.dataforge.meta.*
import hep.dataforge.meta.descriptors.NodeDescriptor
import hep.dataforge.meta.descriptors.get
import hep.dataforge.names.*
import hep.dataforge.vision.*
import kotlinx.coroutines.CoroutineScope
@ -15,17 +14,17 @@ public interface SolidReference : Vision {
private fun SolidReference.getRefProperty(
name: Name,
inherit: Boolean?,
includeStyles: Boolean?,
inherit: Boolean,
includeStyles: Boolean,
includeDefaults: Boolean,
): MetaItem<*>? {
return sequence {
yield(getOwnProperty(name))
if (includeStyles ?: descriptor?.get(name)?.usesStyles != false) {
if (includeStyles) {
yieldAll(getStyleItems(name))
}
yield(prototype.getProperty(name, inherit, includeStyles, includeDefaults))
if (inherit ?: descriptor?.get(name)?.inherited == true) {
if (inherit) {
yield(parent?.getProperty(name, inherit))
}
}.merge()
@ -77,8 +76,8 @@ public class SolidReferenceGroup(
override fun getProperty(
name: Name,
inherit: Boolean?,
includeStyles: Boolean?,
inherit: Boolean,
includeStyles: Boolean,
includeDefaults: Boolean,
): MetaItem<*>? = getRefProperty(name, inherit, includeStyles, includeDefaults)
@ -108,8 +107,8 @@ public class SolidReferenceGroup(
override fun getProperty(
name: Name,
inherit: Boolean?,
includeStyles: Boolean?,
inherit: Boolean,
includeStyles: Boolean,
includeDefaults: Boolean,
): MetaItem<*>? = getRefProperty(name, inherit, includeStyles, includeDefaults)

View File

@ -9,10 +9,8 @@ import hep.dataforge.names.startsWith
import hep.dataforge.vision.solid.Solid
import hep.dataforge.vision.solid.SolidMaterial
import hep.dataforge.vision.solid.layer
import hep.dataforge.vision.solid.three.ThreeMaterials.getMaterial
import info.laht.threekt.core.BufferGeometry
import info.laht.threekt.geometries.EdgesGeometry
import info.laht.threekt.geometries.WireframeGeometry
import info.laht.threekt.objects.LineSegments
import info.laht.threekt.objects.Mesh
import kotlin.reflect.KClass
@ -36,7 +34,7 @@ public abstract class MeshThreeFactory<in T : Solid>(
//val meshMeta: Meta = obj.properties[Material3D.MATERIAL_KEY]?.node ?: Meta.empty
val mesh = Mesh(geometry, null).apply {
val mesh = Mesh(geometry, ThreeMaterials.DEFAULT).apply {
matrixAutoUpdate = false
//set position for mesh
updatePosition(obj)
@ -49,11 +47,11 @@ public abstract class MeshThreeFactory<in T : Solid>(
val oldGeometry = mesh.geometry as BufferGeometry
val newGeometry = buildGeometry(obj)
oldGeometry.attributes = newGeometry.attributes
mesh.applyWireFrame(obj)
//mesh.applyWireFrame(obj)
mesh.applyEdges(obj)
newGeometry.dispose()
}
name.startsWith(WIREFRAME_KEY) -> mesh.applyWireFrame(obj)
//name.startsWith(WIREFRAME_KEY) -> mesh.applyWireFrame(obj)
name.startsWith(EDGES_KEY) -> mesh.applyEdges(obj)
else -> mesh.updateProperty(obj, name)
}
@ -64,19 +62,19 @@ public abstract class MeshThreeFactory<in T : Solid>(
public companion object {
public val EDGES_KEY: Name = "edges".asName()
public val WIREFRAME_KEY: Name = "wireframe".asName()
//public val WIREFRAME_KEY: Name = "wireframe".asName()
public val ENABLED_KEY: Name = "enabled".asName()
public val EDGES_ENABLED_KEY: Name = EDGES_KEY + ENABLED_KEY
public val EDGES_MATERIAL_KEY: Name = EDGES_KEY + SolidMaterial.MATERIAL_KEY
public val WIREFRAME_ENABLED_KEY: Name = WIREFRAME_KEY + ENABLED_KEY
public val WIREFRAME_MATERIAL_KEY: Name = WIREFRAME_KEY + SolidMaterial.MATERIAL_KEY
//public val WIREFRAME_ENABLED_KEY: Name = WIREFRAME_KEY + ENABLED_KEY
//public val WIREFRAME_MATERIAL_KEY: Name = WIREFRAME_KEY + SolidMaterial.MATERIAL_KEY
}
}
internal fun Mesh.applyProperties(obj: Solid): Mesh = apply {
material = getMaterial(obj, true)
updateMaterial(obj)
applyEdges(obj)
applyWireFrame(obj)
//applyWireFrame(obj)
layers.enable(obj.layer)
children.forEach {
it.layers.enable(obj.layer)
@ -86,9 +84,16 @@ internal fun Mesh.applyProperties(obj: Solid): Mesh = apply {
public fun Mesh.applyEdges(obj: Solid) {
val edges = children.find { it.name == "@edges" } as? LineSegments
//inherited edges definition, enabled by default
if (obj.getProperty(MeshThreeFactory.EDGES_ENABLED_KEY).boolean != false) {
if (obj.getProperty(MeshThreeFactory.EDGES_ENABLED_KEY, inherit = true, includeStyles = true).boolean != false) {
val bufferGeometry = geometry as? BufferGeometry ?: return
val material = ThreeMaterials.getLineMaterial(obj.getProperty(MeshThreeFactory.EDGES_MATERIAL_KEY).node, true)
val material = ThreeMaterials.getLineMaterial(
obj.getProperty(
MeshThreeFactory.EDGES_MATERIAL_KEY,
inherit = true,
includeStyles = true
).node,
true
)
if (edges == null) {
add(
LineSegments(
@ -109,23 +114,23 @@ public fun Mesh.applyEdges(obj: Solid) {
}
}
public fun Mesh.applyWireFrame(obj: Solid) {
children.find { it.name == "@wireframe" }?.let {
remove(it)
(it as LineSegments).dispose()
}
//inherited wireframe definition, disabled by default
if (obj.getProperty(MeshThreeFactory.WIREFRAME_ENABLED_KEY).boolean == true) {
val bufferGeometry = geometry as? BufferGeometry ?: return
val material =
ThreeMaterials.getLineMaterial(obj.getProperty(MeshThreeFactory.WIREFRAME_MATERIAL_KEY).node, true)
add(
LineSegments(
WireframeGeometry(bufferGeometry),
material
).apply {
name = "@wireframe"
}
)
}
}
//public fun Mesh.applyWireFrame(obj: Solid) {
// children.find { it.name == "@wireframe" }?.let {
// remove(it)
// (it as LineSegments).dispose()
// }
// //inherited wireframe definition, disabled by default
// if (obj.getProperty(MeshThreeFactory.WIREFRAME_ENABLED_KEY).boolean == true) {
// val bufferGeometry = geometry as? BufferGeometry ?: return
// val material =
// ThreeMaterials.getLineMaterial(obj.getProperty(MeshThreeFactory.WIREFRAME_MATERIAL_KEY).node, true)
// add(
// LineSegments(
// WireframeGeometry(bufferGeometry),
// material
// ).apply {
// name = "@wireframe"
// }
// )
// }
//}

View File

@ -7,7 +7,6 @@ import hep.dataforge.vision.Vision
import hep.dataforge.vision.solid.*
import hep.dataforge.vision.solid.SolidMaterial.Companion.MATERIAL_KEY
import hep.dataforge.vision.solid.three.ThreeFactory.Companion.TYPE
import hep.dataforge.vision.solid.three.ThreeMaterials.getMaterial
import hep.dataforge.vision.visible
import info.laht.threekt.core.BufferGeometry
import info.laht.threekt.core.Object3D
@ -34,7 +33,7 @@ public interface ThreeFactory<in T : Vision> {
*/
public fun Object3D.updatePosition(obj: Vision) {
visible = obj.visible ?: true
if(obj is Solid) {
if (obj is Solid) {
position.set(obj.x, obj.y, obj.z)
setRotationFromEuler(obj.euler)
scale.set(obj.scaleX, obj.scaleY, obj.scaleZ)
@ -47,7 +46,7 @@ public fun Object3D.updatePosition(obj: Vision) {
*/
public fun Object3D.updateProperty(source: Vision, propertyName: Name) {
if (this is Mesh && propertyName.startsWith(MATERIAL_KEY)) {
this.material = getMaterial(source, true)
updateMaterialProperty(source, propertyName)
} else if (
propertyName.startsWith(Solid.POSITION_KEY)
|| propertyName.startsWith(Solid.ROTATION)

View File

@ -3,7 +3,6 @@ package hep.dataforge.vision.solid.three
import hep.dataforge.context.logger
import hep.dataforge.vision.solid.SolidLabel
import hep.dataforge.vision.solid.three.ThreeMaterials.getMaterial
import info.laht.threekt.core.Object3D
import info.laht.threekt.geometries.TextBufferGeometry
import info.laht.threekt.objects.Mesh
@ -23,11 +22,12 @@ public object ThreeLabelFactory : ThreeFactory<SolidLabel> {
height = 1
curveSegments = 1
})
return Mesh(textGeo, getMaterial(obj, true)).apply {
return Mesh(textGeo, ThreeMaterials.DEFAULT).apply {
updateMaterial(obj)
updatePosition(obj)
obj.onPropertyChange(three.updateScope){ _ ->
obj.onPropertyChange(three.updateScope) { _ ->
//TODO
three.logger.warn{"Label parameter change not implemented"}
three.logger.warn { "Label parameter change not implemented" }
}
}
}

View File

@ -1,23 +1,28 @@
package hep.dataforge.vision.solid.three
import hep.dataforge.meta.*
import hep.dataforge.names.Name
import hep.dataforge.values.ValueType
import hep.dataforge.values.int
import hep.dataforge.values.string
import hep.dataforge.vision.Colors
import hep.dataforge.vision.Vision
import hep.dataforge.vision.allStyles
import hep.dataforge.vision.merge
import hep.dataforge.vision.solid.SolidMaterial
import info.laht.threekt.materials.LineBasicMaterial
import info.laht.threekt.materials.Material
import info.laht.threekt.materials.MeshBasicMaterial
import info.laht.threekt.materials.MeshPhongMaterial
import info.laht.threekt.math.Color
import info.laht.threekt.objects.Mesh
public object ThreeMaterials {
public val DEFAULT_COLOR: Color = Color(Colors.darkgreen)
public val DEFAULT: MeshBasicMaterial = MeshBasicMaterial().apply {
color.set(DEFAULT_COLOR)
cached = true
}
public val DEFAULT_LINE_COLOR: Color = Color(Colors.black)
public val DEFAULT_LINE: LineBasicMaterial = LineBasicMaterial().apply {
@ -54,7 +59,7 @@ public object ThreeMaterials {
private val materialCache = HashMap<Meta, Material>()
private fun buildMaterial(meta: Meta): Material {
internal fun buildMaterial(meta: Meta): Material {
return if (meta[SolidMaterial.SPECULAR_COLOR_KEY] != null) {
MeshPhongMaterial().apply {
color = meta[SolidMaterial.COLOR_KEY]?.getColor() ?: DEFAULT_COLOR
@ -79,15 +84,26 @@ public object ThreeMaterials {
}
}
public fun getMaterial(vision3D: Vision, cache: Boolean): Material {
val meta = vision3D.getProperty(SolidMaterial.MATERIAL_KEY, inherit = true).node ?: return DEFAULT
return if (cache) {
materialCache.getOrPut(meta) { buildMaterial(meta) }
} else {
buildMaterial(meta)
internal fun cacheMeta(meta: Meta): Material = materialCache.getOrPut(meta) {
buildMaterial(meta).apply {
cached = true
}
}
// internal fun getMaterial(vision: Vision, cache: Boolean): Material {
// val meta = vision.getProperty(SolidMaterial.MATERIAL_KEY, inherit = true).node ?: return DEFAULT
// return if (cache) {
// materialCache.getOrPut(meta) {
// buildMaterial(meta).apply {
// cached = true
// }
// }
// } else {
// buildMaterial(meta)
// }
// }
}
/**
@ -111,3 +127,74 @@ public fun MetaItem<*>.getColor(): Color {
}
}
private var Material.cached: Boolean
get() = userData["cached"] == true
set(value) {
userData["cached"] = value
}
public fun Mesh.updateMaterial(vision: Vision) {
//val meta = vision.getProperty(SolidMaterial.MATERIAL_KEY, inherit = true).node
val ownMaterialMeta = vision.getOwnProperty(SolidMaterial.MATERIAL_KEY)
val stylesMaterialMeta = vision.allStyles[SolidMaterial.MATERIAL_KEY]
val parentMaterialMeta = vision.parent?.getProperty(
SolidMaterial.MATERIAL_KEY,
inherit = true,
includeStyles = false,
includeDefaults = false
)
material = when {
ownMaterialMeta == null && stylesMaterialMeta == null && parentMaterialMeta == null -> {
//use default is not material properties are defined
ThreeMaterials.DEFAULT
}
ownMaterialMeta == null && parentMaterialMeta == null -> {
//If material is style-based, use cached
ThreeMaterials.cacheMeta(stylesMaterialMeta.node ?: Meta.EMPTY)
}
else -> {
val merge = sequenceOf(ownMaterialMeta, stylesMaterialMeta, parentMaterialMeta).merge().node ?: Meta.EMPTY
ThreeMaterials.buildMaterial(merge)
}
}
}
public fun Mesh.updateMaterialProperty(vision: Vision, propertyName: Name) {
if (material.cached) {
//generate a new material since cached material should not be changed
updateMaterial(vision)
} else {
when (propertyName) {
SolidMaterial.MATERIAL_COLOR_KEY -> {
material.asDynamic().color = vision.getProperty(
SolidMaterial.MATERIAL_COLOR_KEY,
inherit = true,
includeStyles = true,
includeDefaults = false
)?.getColor() ?: ThreeMaterials.DEFAULT_COLOR
material.needsUpdate = true
}
SolidMaterial.MATERIAL_OPACITY_KEY -> {
val opacity = vision.getProperty(
SolidMaterial.MATERIAL_OPACITY_KEY,
inherit = true,
includeStyles = true,
includeDefaults = false
).double ?: 1.0
material.asDynamic().opacity = opacity
material.transparent = opacity < 1.0
material.needsUpdate = true
}
SolidMaterial.MATERIAL_WIREFRAME_KEY -> {
material.asDynamic().wireframe = vision.getProperty(
SolidMaterial.MATERIAL_WIREFRAME_KEY,
inherit = true,
includeStyles = true,
includeDefaults = false
).boolean ?: false
material.needsUpdate = true
}
else -> console.warn("Unrecognized material property: $propertyName")
}
}
}

View File

@ -111,7 +111,7 @@ open external class Material {
var visible: Boolean
var userData: Map<String, Any>
var userData: dynamic
/**
* Specifies that the material needs to be updated at the WebGL level. Set it to true if you made changes that need to be reflected in WebGL.