Skip to content

Commit

Permalink
Feature/add instanced mesh (#69)
Browse files Browse the repository at this point in the history
  • Loading branch information
Displee committed Feb 16, 2021
1 parent 5d14e42 commit dcb8f86
Show file tree
Hide file tree
Showing 17 changed files with 361 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -473,4 +473,4 @@ class BufferAttributes : MutableMap<String, BufferAttribute> by mutableMapOf() {
//private fun getAllocationSize(elements: Int, elementShift: Int): Int {
// APIUtil.apiCheckAllocation(elements, APIUtil.apiGetBytes(elements, elementShift), 0x7FFF_FFFFL)
// return elements shl elementShift
//}
//}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ open class BufferGeometry : Cloneable, EventDispatcher by EventDispatcherImpl()

internal var drawRange = DrawRange(0, Int.MAX_VALUE / 2)

internal var _maxInstanceCount: Int? = null

fun setIndex(index: IntArray): BufferGeometry {
return setIndex(IntBufferAttribute(index, 1))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,8 @@ data class Intersection internal constructor(
var faceIndex: Int? = null,
var `object`: Object3D,
var uv: Vector2? = null,
var uv2: Vector2? = null
var uv2: Vector2? = null,
var instanceId: Int? = null
) : Comparable<Intersection> {

override fun compareTo(other: Intersection): Int {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package info.laht.threekt.objects

import info.laht.threekt.core.*
import info.laht.threekt.materials.Material
import info.laht.threekt.math.Color
import info.laht.threekt.math.Matrix4

class InstancedMesh(
geometry: BufferGeometry? = null,
materials: MutableList<Material>? = null,
var count: Int
) : Mesh(geometry, materials) {

val instanceMatrix: FloatBufferAttribute = FloatBufferAttribute(FloatArray(count * 16), 16)
var instanceColor: FloatBufferAttribute? = null
override var frustumCulled = false

fun copy(source: InstancedMesh): InstancedMesh {
super.copy(source)
instanceMatrix.copy(source.instanceMatrix)
instanceColor = source.instanceColor?.clone()
count = source.count
return this
}

fun getColorAt(index: Int, color: Color) {
val instanceColor = this.instanceColor
checkNotNull(instanceColor) { "Instance color is null." }
color.fromArray(instanceColor.buffer, index * 3)
}

fun getMatrixAt(index: Int, matrix: Matrix4) {
matrix.fromArray(instanceMatrix.buffer, index * 16)
}

fun setColorAt(index: Int, color: Color) {
if (instanceColor === null) {
instanceColor = FloatBufferAttribute(FloatArray(count * 3), 3)
}
color.toArray(instanceColor!!.buffer, index * 3)
}

fun setMatrixAt(index: Int, matrix: Matrix4) {
matrix.toArray(instanceMatrix.buffer, index * 16)
}

override fun raycast(raycaster: Raycaster, intersects: MutableList<Intersection>) {
val matrixWorld = matrixWorld
val raycastTimes = count

_mesh.geometry = geometry
_mesh.material = material

if (_mesh.materials.isEmpty()) {
return
}

for (instanceId in 0 until raycastTimes) {

// calculate the world matrix for each instance

getMatrixAt(instanceId, _instanceLocalMatrix)

_instanceWorldMatrix.multiplyMatrices(matrixWorld, _instanceLocalMatrix)

// the mesh represents this single instance

_mesh.matrixWorld.copy(_instanceWorldMatrix)

_mesh.raycast(raycaster, _instanceIntersects)

// process the result of raycast

for (intersect in _instanceIntersects) {
intersect.instanceId = instanceId
intersect.`object` = this
intersects.add(intersect)
}

_instanceIntersects.clear()
}
}

fun dispose() {
dispatchEvent("dispose", this)
}

companion object {
private val _instanceLocalMatrix = Matrix4()
private val _instanceWorldMatrix = Matrix4()
private val _instanceIntersects = mutableListOf<Intersection>()
private val _mesh = Mesh()
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package info.laht.threekt.renderers.shaders.chunk

internal val __color_pars_vertex = """
#ifdef USE_COLOR
#if defined( USE_COLOR ) || defined( USE_INSTANCING_COLOR )
varying vec3 vColor;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,21 @@ package info.laht.threekt.renderers.shaders.chunk

internal val __color_vertex = """
#if defined( USE_COLOR ) || defined( USE_INSTANCING_COLOR )
vColor = vec3( 1.0 );
#endif
#ifdef USE_COLOR
vColor.xyz = color.xyz;
vColor.xyz *= color.xyz;
#endif
#ifdef USE_INSTANCING_COLOR
vColor.xyz *= instanceColor.xyz;
#endif
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,23 @@
package info.laht.threekt.renderers.shaders.chunk

internal val __defaultnormal_vertex = """
vec3 transformedNormal = normalMatrix * objectNormal;
vec3 transformedNormal = objectNormal;
#ifdef USE_INSTANCING
// this is in lieu of a per-instance normal-matrix
// shear transforms in the instance matrix are not supported
mat3 m = mat3( instanceMatrix );
transformedNormal /= vec3( dot( m[ 0 ], m[ 0 ] ), dot( m[ 1 ], m[ 1 ] ), dot( m[ 2 ], m[ 2 ] ) );
transformedNormal = m * transformedNormal;
#endif
transformedNormal = normalMatrix * transformedNormal;
#ifdef FLIP_SIDED
Expand All @@ -12,7 +27,7 @@ vec3 transformedNormal = normalMatrix * objectNormal;
#ifdef USE_TANGENT
vec3 transformedTangent = normalMatrix * objectTangent;
vec3 transformedTangent = ( modelViewMatrix * vec4( objectTangent, 0.0 ) ).xyz;
#ifdef FLIP_SIDED
Expand All @@ -21,4 +36,5 @@ vec3 transformedNormal = normalMatrix * objectNormal;
#endif
#endif
"""
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,15 @@ package info.laht.threekt.renderers.shaders.chunk

internal val __project_vertex = """
vec4 mvPosition = modelViewMatrix * vec4( transformed, 1.0 );
vec4 mvPosition = vec4( transformed, 1.0 );
#ifdef USE_INSTANCING
mvPosition = instanceMatrix * mvPosition;
#endif
mvPosition = modelViewMatrix * mvPosition;
gl_Position = projectionMatrix * mvPosition;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
package info.laht.threekt.renderers.shaders.chunk

internal val __worldpos_vertex = """
#if defined( USE_ENVMAP ) || defined( DISTANCE ) || defined ( USE_SHADOWMAP )
vec4 worldPosition = modelMatrix * vec4( transformed, 1.0 );
vec4 worldPosition = vec4( transformed, 1.0 );
#ifdef USE_INSTANCING
worldPosition = instanceMatrix * worldPosition;
#endif
worldPosition = modelMatrix * worldPosition;
#endif
Expand Down
61 changes: 56 additions & 5 deletions core/src/jvmMain/kotlin/info/laht/threekt/renderers/GLRenderer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ class GLRenderer(
private val textures = GLTextures(state, properties, capabilities, info)
private val attributes = GLAttributes()
private val geometries = GLGeometries(attributes, info)
internal val objects = GLObjects(geometries, info)
internal val objects = GLObjects(geometries, attributes, info)
private val programCache = GLPrograms(this, capabilities)
private val renderLists = GLRenderLists()
private val renderStates = GLRenderStates()
Expand Down Expand Up @@ -229,6 +229,10 @@ class GLRenderer(

}

if (`object` is InstancedMesh) {
updateBuffers = true
}

var index = geometry.index
val position = geometry.attributes.position
var rangeFactor = 1
Expand Down Expand Up @@ -256,7 +260,7 @@ class GLRenderer(

if (updateBuffers) {

setupVertexAttributes(material, program, geometry)
setupVertexAttributes(`object`, material, program, geometry)

if (index != null) {

Expand Down Expand Up @@ -335,7 +339,13 @@ class GLRenderer(

}

renderer.render(drawStart, drawCount)
if (`object` is InstancedMesh) {
renderer.renderInstances(drawStart, drawCount, `object`.count)
} /*else if (`object` is InstancedBufferGeometry) { //TODO Implement Object3D in InstancedBufferGeometry?
renderer.renderInstances(drawStart, drawCount, `object`.maxInstancedCount)
}*/ else {
renderer.render(drawStart, drawCount)
}

}

Expand All @@ -360,7 +370,7 @@ class GLRenderer(

}

private fun setupVertexAttributes(material: Material, program: GLProgram, geometry: BufferGeometry) {
private fun setupVertexAttributes(`object`: Object3D, material: Material, program: GLProgram, geometry: BufferGeometry) {

state.initAttributes()

Expand All @@ -386,11 +396,52 @@ class GLRenderer(
val type = attribute.type

state.enableAttribute(programAttribute)

GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, buffer)
GL20.glVertexAttribPointer(programAttribute, size, type, normalized, 0, 0)


} else if (name == "instanceMatrix") {
`object` as InstancedMesh
val attribute = attributes.get(`object`.instanceMatrix)

// TODO Attribute may not be available on context restore

if (attribute == null) {
continue
}

val buffer = attribute.buffer
val type = attribute.type

state.enableAttributeAndDivisor(programAttribute + 0, 1)
state.enableAttributeAndDivisor(programAttribute + 1, 1)
state.enableAttributeAndDivisor(programAttribute + 2, 1)
state.enableAttributeAndDivisor(programAttribute + 3, 1)

GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, buffer)

GL20.glVertexAttribPointer(programAttribute + 0, 4, type, false, 64, 0)
GL20.glVertexAttribPointer(programAttribute + 1, 4, type, false, 64, 16)
GL20.glVertexAttribPointer(programAttribute + 2, 4, type, false, 64, 32)
GL20.glVertexAttribPointer(programAttribute + 3, 4, type, false, 64, 48)
} else if (name == "instanceColor") {
`object` as InstancedMesh
val attribute = attributes.get(`object`.instanceColor!!)

// TODO Attribute may not be available on context restore

if (attribute == null) {
continue
}

val buffer = attribute.buffer
val type = attribute.type

state.enableAttributeAndDivisor(programAttribute, 1)

GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, buffer)

GL20.glVertexAttribPointer(programAttribute, 3, type, false, 12, 0)
} else if (material is MaterialWithDefaultAttributeValues) {

val value = material.defaultAttributeValues[name] as FloatArray?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ internal class GLLights {
uniforms.skyColor.copy( light.color ).multiplyScalar( intensity )
uniforms.groundColor.copy( light.groundColor ).multiplyScalar( intensity )

state.hemi[ hemiLength ] = uniforms
state.hemi.safeSet(hemiLength, uniforms)

hemiLength ++
}
Expand Down Expand Up @@ -281,6 +281,7 @@ internal class GLLights {
is DirectionalLight -> DirectionalLightUniforms()
is PointLight -> PointLightUniforms()
is SpotLight -> SpotLightUniforms()
is HemisphereLight -> HemisphereLightUniforms()
else -> throw IllegalArgumentException("Unsupported light: $light")
}

Expand Down

0 comments on commit dcb8f86

Please sign in to comment.