mirror of
https://github.com/DaanVandenBosch/phantasmal-world.git
synced 2025-04-05 07:18:29 +08:00
Upgraded to three.js 0.126.0 and improved performance of quest renderer.
This commit is contained in:
parent
e6a7a5c3ed
commit
f57de99577
@ -42,7 +42,7 @@ dependencies {
|
|||||||
implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.1.1")
|
implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.1.1")
|
||||||
implementation(npm("golden-layout", "^1.5.9"))
|
implementation(npm("golden-layout", "^1.5.9"))
|
||||||
implementation(npm("monaco-editor", "0.20.0"))
|
implementation(npm("monaco-editor", "0.20.0"))
|
||||||
implementation(npm("three", "^0.122.0"))
|
implementation(npm("three", "^0.126.0"))
|
||||||
implementation(npm("javascript-lp-solver", "0.4.17"))
|
implementation(npm("javascript-lp-solver", "0.4.17"))
|
||||||
|
|
||||||
implementation(devNpm("file-loader", "^6.0.0"))
|
implementation(devNpm("file-loader", "^6.0.0"))
|
||||||
|
@ -77,6 +77,7 @@ private fun createThreeRenderer(canvas: HTMLCanvasElement): DisposableThreeRende
|
|||||||
})
|
})
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
renderer.debug.checkShaderErrors = false
|
||||||
renderer.setPixelRatio(window.devicePixelRatio)
|
renderer.setPixelRatio(window.devicePixelRatio)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,8 +3,8 @@ package world.phantasmal.web.core.rendering
|
|||||||
import world.phantasmal.web.externals.three.Object3D
|
import world.phantasmal.web.externals.three.Object3D
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Recursively disposes any geometries/materials/textures/skeletons attached to the given [Object3D]
|
* Recursively disposes the given object and any geometries/materials/textures/skeletons attached to
|
||||||
* and its children.
|
* it and its children.
|
||||||
*/
|
*/
|
||||||
fun disposeObject3DResources(obj: Object3D) {
|
fun disposeObject3DResources(obj: Object3D) {
|
||||||
val dynObj = obj.asDynamic()
|
val dynObj = obj.asDynamic()
|
||||||
@ -22,6 +22,10 @@ fun disposeObject3DResources(obj: Object3D) {
|
|||||||
dynObj.material.dispose()
|
dynObj.material.dispose()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (dynObj.dispose != null) {
|
||||||
|
dynObj.dispose()
|
||||||
|
}
|
||||||
|
|
||||||
for (child in obj.children) {
|
for (child in obj.children) {
|
||||||
disposeObject3DResources(child)
|
disposeObject3DResources(child)
|
||||||
}
|
}
|
||||||
|
@ -125,7 +125,6 @@ class MeshBuilder {
|
|||||||
check(positions.size == normals.size)
|
check(positions.size == normals.size)
|
||||||
check(uvs.isEmpty() || positions.size == uvs.size)
|
check(uvs.isEmpty() || positions.size == uvs.size)
|
||||||
|
|
||||||
// Per-buffer attributes.
|
|
||||||
val positions = Float32Array(3 * positions.size)
|
val positions = Float32Array(3 * positions.size)
|
||||||
val normals = Float32Array(3 * normals.size)
|
val normals = Float32Array(3 * normals.size)
|
||||||
val uvs = if (uvs.isEmpty()) null else Float32Array(2 * uvs.size)
|
val uvs = if (uvs.isEmpty()) null else Float32Array(2 * uvs.size)
|
||||||
@ -153,9 +152,6 @@ class MeshBuilder {
|
|||||||
geom.setAttribute("normal", Float32BufferAttribute(normals, 3))
|
geom.setAttribute("normal", Float32BufferAttribute(normals, 3))
|
||||||
uvs?.let { geom.setAttribute("uv", Float32BufferAttribute(uvs, 2)) }
|
uvs?.let { geom.setAttribute("uv", Float32BufferAttribute(uvs, 2)) }
|
||||||
|
|
||||||
// Per group/material attributes.
|
|
||||||
val indices = Uint16Array(indexCount)
|
|
||||||
|
|
||||||
if (skinning) {
|
if (skinning) {
|
||||||
check(this.positions.size == boneIndices.size / 4)
|
check(this.positions.size == boneIndices.size / 4)
|
||||||
check(this.positions.size == boneWeights.size / 4)
|
check(this.positions.size == boneWeights.size / 4)
|
||||||
@ -174,6 +170,8 @@ class MeshBuilder {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val indices = Uint16Array(indexCount)
|
||||||
|
|
||||||
var offset = 0
|
var offset = 0
|
||||||
val texCache = mutableMapOf<Int, Texture?>()
|
val texCache = mutableMapOf<Int, Texture?>()
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
package world.phantasmal.web.externals.three
|
package world.phantasmal.web.externals.three
|
||||||
|
|
||||||
import org.khronos.webgl.Float32Array
|
import org.khronos.webgl.Float32Array
|
||||||
|
import org.khronos.webgl.Int32Array
|
||||||
import org.khronos.webgl.Uint16Array
|
import org.khronos.webgl.Uint16Array
|
||||||
import org.w3c.dom.HTMLCanvasElement
|
import org.w3c.dom.HTMLCanvasElement
|
||||||
|
|
||||||
@ -123,7 +124,7 @@ external class Quaternion(
|
|||||||
/**
|
/**
|
||||||
* Inverts this quaternion.
|
* Inverts this quaternion.
|
||||||
*/
|
*/
|
||||||
fun inverse(): Quaternion
|
fun invert(): Quaternion
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Multiplies this quaternion by [q].
|
* Multiplies this quaternion by [q].
|
||||||
@ -218,6 +219,7 @@ open external class WebGLRenderer(
|
|||||||
override val domElement: HTMLCanvasElement
|
override val domElement: HTMLCanvasElement
|
||||||
|
|
||||||
var autoClearColor: Boolean
|
var autoClearColor: Boolean
|
||||||
|
var debug: WebGLDebug
|
||||||
|
|
||||||
override fun render(scene: Object3D, camera: Camera)
|
override fun render(scene: Object3D, camera: Camera)
|
||||||
|
|
||||||
@ -232,6 +234,10 @@ open external class WebGLRenderer(
|
|||||||
fun dispose()
|
fun dispose()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
external interface WebGLDebug {
|
||||||
|
var checkShaderErrors: Boolean
|
||||||
|
}
|
||||||
|
|
||||||
open external class Object3D {
|
open external class Object3D {
|
||||||
/**
|
/**
|
||||||
* Optional name of the object (doesn't need to be unique).
|
* Optional name of the object (doesn't need to be unique).
|
||||||
@ -299,47 +305,25 @@ open external class Object3D {
|
|||||||
external class Group : Object3D
|
external class Group : Object3D
|
||||||
|
|
||||||
open external class Mesh(
|
open external class Mesh(
|
||||||
geometry: Geometry = definedExternally,
|
geometry: BufferGeometry = definedExternally,
|
||||||
material: Material = definedExternally,
|
material: Material = definedExternally,
|
||||||
) : Object3D {
|
) : Object3D {
|
||||||
constructor(
|
|
||||||
geometry: Geometry,
|
|
||||||
material: Array<Material>,
|
|
||||||
)
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
geometry: BufferGeometry = definedExternally,
|
|
||||||
material: Material = definedExternally,
|
|
||||||
)
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
geometry: BufferGeometry,
|
geometry: BufferGeometry,
|
||||||
material: Array<Material>,
|
material: Array<Material>,
|
||||||
)
|
)
|
||||||
|
|
||||||
var geometry: Any /* Geometry | BufferGeometry */
|
var geometry: BufferGeometry
|
||||||
var material: Any /* Material | Material[] */
|
var material: Any /* Material | Material[] */
|
||||||
|
|
||||||
fun translateY(distance: Double): Mesh
|
fun translateY(distance: Double): Mesh
|
||||||
}
|
}
|
||||||
|
|
||||||
external class SkinnedMesh(
|
external class SkinnedMesh(
|
||||||
geometry: Geometry = definedExternally,
|
geometry: BufferGeometry = definedExternally,
|
||||||
material: Material = definedExternally,
|
material: Material = definedExternally,
|
||||||
useVertexTexture: Boolean = definedExternally,
|
useVertexTexture: Boolean = definedExternally,
|
||||||
) : Mesh {
|
) : Mesh {
|
||||||
constructor(
|
|
||||||
geometry: Geometry,
|
|
||||||
material: Array<Material>,
|
|
||||||
useVertexTexture: Boolean = definedExternally,
|
|
||||||
)
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
geometry: BufferGeometry = definedExternally,
|
|
||||||
material: Material = definedExternally,
|
|
||||||
useVertexTexture: Boolean = definedExternally,
|
|
||||||
)
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
geometry: BufferGeometry,
|
geometry: BufferGeometry,
|
||||||
material: Array<Material>,
|
material: Array<Material>,
|
||||||
@ -352,22 +336,10 @@ external class SkinnedMesh(
|
|||||||
}
|
}
|
||||||
|
|
||||||
external class InstancedMesh(
|
external class InstancedMesh(
|
||||||
geometry: Geometry,
|
geometry: BufferGeometry,
|
||||||
material: Material,
|
material: Material,
|
||||||
count: Int,
|
count: Int,
|
||||||
) : Mesh {
|
) : Mesh {
|
||||||
constructor(
|
|
||||||
geometry: Geometry,
|
|
||||||
material: Array<Material>,
|
|
||||||
count: Int,
|
|
||||||
)
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
geometry: BufferGeometry,
|
|
||||||
material: Material,
|
|
||||||
count: Int,
|
|
||||||
)
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
geometry: BufferGeometry,
|
geometry: BufferGeometry,
|
||||||
material: Array<Material>,
|
material: Array<Material>,
|
||||||
@ -379,6 +351,7 @@ external class InstancedMesh(
|
|||||||
|
|
||||||
fun getMatrixAt(index: Int, matrix: Matrix4)
|
fun getMatrixAt(index: Int, matrix: Matrix4)
|
||||||
fun setMatrixAt(index: Int, matrix: Matrix4)
|
fun setMatrixAt(index: Int, matrix: Matrix4)
|
||||||
|
fun dispose()
|
||||||
}
|
}
|
||||||
|
|
||||||
external class Bone : Object3D {
|
external class Bone : Object3D {
|
||||||
@ -502,53 +475,6 @@ external class Color() {
|
|||||||
fun setHSL(h: Double, s: Double, l: Double): Color
|
fun setHSL(h: Double, s: Double, l: Double): Color
|
||||||
}
|
}
|
||||||
|
|
||||||
open external class Geometry : EventDispatcher {
|
|
||||||
var boundingBox: Box3?
|
|
||||||
var boundingSphere: Sphere?
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The array of vertices hold every position of points of the model.
|
|
||||||
* To signal an update in this array, Geometry.verticesNeedUpdate needs to be set to true.
|
|
||||||
*/
|
|
||||||
var vertices: Array<Vector3>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Array of triangles or/and quads.
|
|
||||||
* The array of faces describe how each vertex in the model is connected with each other.
|
|
||||||
* To signal an update in this array, Geometry.elementsNeedUpdate needs to be set to true.
|
|
||||||
*/
|
|
||||||
var faces: Array<Face3>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Array of face UV layers.
|
|
||||||
* Each UV layer is an array of UV matching order and number of vertices in faces.
|
|
||||||
* To signal an update in this array, Geometry.uvsNeedUpdate needs to be set to true.
|
|
||||||
*/
|
|
||||||
var faceVertexUvs: Array<Array<Array<Vector2>>>
|
|
||||||
|
|
||||||
fun translate(x: Double, y: Double, z: Double): Geometry
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Computes bounding box of the geometry, updating {@link Geometry.boundingBox} attribute.
|
|
||||||
*/
|
|
||||||
fun computeBoundingBox()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Computes bounding sphere of the geometry, updating Geometry.boundingSphere attribute.
|
|
||||||
* Neither bounding boxes or bounding spheres are computed by default. They need to be explicitly computed, otherwise they are null.
|
|
||||||
*/
|
|
||||||
fun computeBoundingSphere()
|
|
||||||
|
|
||||||
fun dispose()
|
|
||||||
}
|
|
||||||
|
|
||||||
external class PlaneGeometry(
|
|
||||||
width: Double = definedExternally,
|
|
||||||
height: Double = definedExternally,
|
|
||||||
widthSegments: Double = definedExternally,
|
|
||||||
heightSegments: Double = definedExternally,
|
|
||||||
) : Geometry
|
|
||||||
|
|
||||||
open external class BufferGeometry : EventDispatcher {
|
open external class BufferGeometry : EventDispatcher {
|
||||||
var boundingBox: Box3?
|
var boundingBox: Box3?
|
||||||
var boundingSphere: Sphere?
|
var boundingSphere: Sphere?
|
||||||
@ -569,6 +495,13 @@ open external class BufferGeometry : EventDispatcher {
|
|||||||
fun dispose()
|
fun dispose()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
external class PlaneGeometry(
|
||||||
|
width: Double = definedExternally,
|
||||||
|
height: Double = definedExternally,
|
||||||
|
widthSegments: Double = definedExternally,
|
||||||
|
heightSegments: Double = definedExternally,
|
||||||
|
) : BufferGeometry
|
||||||
|
|
||||||
external class CylinderBufferGeometry(
|
external class CylinderBufferGeometry(
|
||||||
radiusTop: Double = definedExternally,
|
radiusTop: Double = definedExternally,
|
||||||
radiusBottom: Double = definedExternally,
|
radiusBottom: Double = definedExternally,
|
||||||
@ -586,6 +519,12 @@ open external class BufferAttribute {
|
|||||||
fun copyAt(index1: Int, bufferAttribute: BufferAttribute, index2: Int): BufferAttribute
|
fun copyAt(index1: Int, bufferAttribute: BufferAttribute, index2: Int): BufferAttribute
|
||||||
}
|
}
|
||||||
|
|
||||||
|
external class Int32BufferAttribute(
|
||||||
|
array: Int32Array,
|
||||||
|
itemSize: Int,
|
||||||
|
normalize: Boolean = definedExternally,
|
||||||
|
) : BufferAttribute
|
||||||
|
|
||||||
external class Uint16BufferAttribute(
|
external class Uint16BufferAttribute(
|
||||||
array: Uint16Array,
|
array: Uint16Array,
|
||||||
itemSize: Int,
|
itemSize: Int,
|
||||||
|
@ -1,7 +1,12 @@
|
|||||||
package world.phantasmal.web.questEditor.loading
|
package world.phantasmal.web.questEditor.loading
|
||||||
|
|
||||||
import org.khronos.webgl.ArrayBuffer
|
import org.khronos.webgl.ArrayBuffer
|
||||||
|
import org.khronos.webgl.Float32Array
|
||||||
|
import org.khronos.webgl.Uint16Array
|
||||||
|
import world.phantasmal.core.JsArray
|
||||||
|
import world.phantasmal.core.asArray
|
||||||
import world.phantasmal.core.asJsArray
|
import world.phantasmal.core.asJsArray
|
||||||
|
import world.phantasmal.core.jsArrayOf
|
||||||
import world.phantasmal.lib.Endianness
|
import world.phantasmal.lib.Endianness
|
||||||
import world.phantasmal.lib.Episode
|
import world.phantasmal.lib.Episode
|
||||||
import world.phantasmal.lib.cursor.cursor
|
import world.phantasmal.lib.cursor.cursor
|
||||||
@ -81,8 +86,7 @@ class AreaAssetLoader(private val assetLoader: AssetLoader) : DisposableContaine
|
|||||||
|
|
||||||
private fun addSectionsToCollisionGeometry(collisionGeom: Object3D, renderGeom: Object3D) {
|
private fun addSectionsToCollisionGeometry(collisionGeom: Object3D, renderGeom: Object3D) {
|
||||||
for (collisionArea in collisionGeom.children) {
|
for (collisionArea in collisionGeom.children) {
|
||||||
val origin =
|
val origin = ((collisionArea as Mesh).geometry).boundingBox!!.getCenter(tmpVec)
|
||||||
((collisionArea as Mesh).geometry as Geometry).boundingBox!!.getCenter(tmpVec)
|
|
||||||
|
|
||||||
// Cast a ray downward from the center of the section.
|
// Cast a ray downward from the center of the section.
|
||||||
raycaster.set(origin, DOWN)
|
raycaster.set(origin, DOWN)
|
||||||
@ -319,16 +323,14 @@ private fun areaCollisionGeometryToObject3D(
|
|||||||
episode: Episode,
|
episode: Episode,
|
||||||
areaVariant: AreaVariantModel,
|
areaVariant: AreaVariantModel,
|
||||||
): Object3D {
|
): Object3D {
|
||||||
val obj3d = Group()
|
val group = Group()
|
||||||
obj3d.name = "Collision Geometry $episode-${areaVariant.area.id}-${areaVariant.id}"
|
group.name = "Collision Geometry $episode-${areaVariant.area.id}-${areaVariant.id}"
|
||||||
|
|
||||||
for (collisionMesh in obj.meshes) {
|
for (collisionMesh in obj.meshes) {
|
||||||
// Use Geometry instead of BufferGeometry for better raycaster performance.
|
val positions = jsArrayOf<Float>()
|
||||||
val geom = Geometry()
|
val normals = jsArrayOf<Float>()
|
||||||
|
val materialGroups = mutableMapOf<Int, JsArray<Short>>()
|
||||||
geom.vertices = Array(collisionMesh.vertices.size) {
|
var index: Short = 0
|
||||||
vec3ToThree(collisionMesh.vertices[it])
|
|
||||||
}
|
|
||||||
|
|
||||||
for (triangle in collisionMesh.triangles) {
|
for (triangle in collisionMesh.triangles) {
|
||||||
val isSectionTransition = (triangle.flags and 0b1000000) != 0
|
val isSectionTransition = (triangle.flags and 0b1000000) != 0
|
||||||
@ -343,29 +345,47 @@ private fun areaCollisionGeometryToObject3D(
|
|||||||
|
|
||||||
// Filter out walls.
|
// Filter out walls.
|
||||||
if (materialIndex != 0) {
|
if (materialIndex != 0) {
|
||||||
geom.faces.asDynamic().push(
|
val p1 = collisionMesh.vertices[triangle.index1]
|
||||||
Face3(
|
val p2 = collisionMesh.vertices[triangle.index2]
|
||||||
triangle.index1,
|
val p3 = collisionMesh.vertices[triangle.index3]
|
||||||
triangle.index2,
|
positions.push(p1.x, p1.y, p1.z, p2.x, p2.y, p2.z, p3.x, p3.y, p3.z)
|
||||||
triangle.index3,
|
|
||||||
vec3ToThree(triangle.normal),
|
val n = triangle.normal
|
||||||
materialIndex = materialIndex,
|
normals.push(n.x, n.y, n.z, n.x, n.y, n.z, n.x, n.y, n.z)
|
||||||
)
|
|
||||||
)
|
val indices = materialGroups.getOrPut(materialIndex) { jsArrayOf() }
|
||||||
|
indices.push(index++, index++, index++)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (geom.faces.isNotEmpty()) {
|
if (index > 0) {
|
||||||
|
val geom = BufferGeometry()
|
||||||
|
geom.setAttribute(
|
||||||
|
"position", Float32BufferAttribute(Float32Array(positions.asArray()), 3),
|
||||||
|
)
|
||||||
|
geom.setAttribute(
|
||||||
|
"normal", Float32BufferAttribute(Float32Array(normals.asArray()), 3),
|
||||||
|
)
|
||||||
|
val indices = Uint16Array(index.toInt())
|
||||||
|
var offset = 0
|
||||||
|
|
||||||
|
for ((materialIndex, vertexIndices) in materialGroups) {
|
||||||
|
indices.set(vertexIndices.asArray(), offset)
|
||||||
|
geom.addGroup(offset, vertexIndices.length, materialIndex)
|
||||||
|
offset += vertexIndices.length
|
||||||
|
}
|
||||||
|
|
||||||
|
geom.setIndex(Uint16BufferAttribute(indices, 1))
|
||||||
geom.computeBoundingBox()
|
geom.computeBoundingBox()
|
||||||
geom.computeBoundingSphere()
|
geom.computeBoundingSphere()
|
||||||
|
|
||||||
obj3d.add(
|
group.add(
|
||||||
Mesh(geom, COLLISION_MATERIALS).apply {
|
Mesh(geom, COLLISION_MATERIALS).apply {
|
||||||
renderOrder = 1
|
renderOrder = 1
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
obj3d.add(
|
group.add(
|
||||||
Mesh(geom, COLLISION_WIREFRAME_MATERIALS).apply {
|
Mesh(geom, COLLISION_WIREFRAME_MATERIALS).apply {
|
||||||
renderOrder = 2
|
renderOrder = 2
|
||||||
}
|
}
|
||||||
@ -373,5 +393,5 @@ private fun areaCollisionGeometryToObject3D(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return obj3d
|
return group
|
||||||
}
|
}
|
||||||
|
@ -159,7 +159,7 @@ abstract class QuestEntityModel<Type : EntityType, Entity : QuestEntity<Type>>(
|
|||||||
} else {
|
} else {
|
||||||
q1.setFromEuler(rot)
|
q1.setFromEuler(rot)
|
||||||
q2.setFromEuler(section.rotation)
|
q2.setFromEuler(section.rotation)
|
||||||
q2.inverse()
|
q2.invert()
|
||||||
q1 *= q2
|
q1 *= q2
|
||||||
floorModEuler(q1.toEuler())
|
floorModEuler(q1.toEuler())
|
||||||
}
|
}
|
||||||
|
@ -17,5 +17,5 @@ class SectionModel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val inverseRotation: Euler = rotation.toQuaternion().inverse().toEuler()
|
val inverseRotation: Euler = rotation.toQuaternion().invert().toEuler()
|
||||||
}
|
}
|
||||||
|
@ -55,7 +55,7 @@ class EntityImageRenderer(
|
|||||||
scene.add(light, mesh)
|
scene.add(light, mesh)
|
||||||
|
|
||||||
// Compute camera position.
|
// Compute camera position.
|
||||||
val bSphere = (mesh.geometry as BufferGeometry).boundingSphere!!
|
val bSphere = mesh.geometry.boundingSphere!!
|
||||||
camera.position.copy(cameraPos)
|
camera.position.copy(cameraPos)
|
||||||
camera.position *= bSphere.radius * cameraDistFactor
|
camera.position *= bSphere.radius * cameraDistFactor
|
||||||
camera.lookAt(bSphere.center)
|
camera.lookAt(bSphere.center)
|
||||||
@ -69,7 +69,6 @@ class EntityImageRenderer(
|
|||||||
mesh.material = origMaterial
|
mesh.material = origMaterial
|
||||||
threeRenderer.render(scene, camera)
|
threeRenderer.render(scene, camera)
|
||||||
|
|
||||||
threeRenderer.render(scene, camera)
|
|
||||||
return threeRenderer.domElement.toDataURL()
|
return threeRenderer.domElement.toDataURL()
|
||||||
} finally {
|
} finally {
|
||||||
// Ensure we dispose the original material and not the background material.
|
// Ensure we dispose the original material and not the background material.
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package world.phantasmal.web.questEditor.rendering
|
package world.phantasmal.web.questEditor.rendering
|
||||||
|
|
||||||
|
import world.phantasmal.core.disposable.TrackedDisposable
|
||||||
|
import world.phantasmal.web.core.rendering.disposeObject3DResources
|
||||||
import world.phantasmal.web.externals.three.InstancedMesh
|
import world.phantasmal.web.externals.three.InstancedMesh
|
||||||
import world.phantasmal.web.questEditor.models.QuestEntityModel
|
import world.phantasmal.web.questEditor.models.QuestEntityModel
|
||||||
|
|
||||||
@ -15,13 +17,18 @@ class EntityInstancedMesh(
|
|||||||
* [EntityInstancedMesh].
|
* [EntityInstancedMesh].
|
||||||
*/
|
*/
|
||||||
private val modelChanged: (QuestEntityModel<*, *>) -> Unit,
|
private val modelChanged: (QuestEntityModel<*, *>) -> Unit,
|
||||||
) {
|
) : TrackedDisposable() {
|
||||||
private val instances: MutableList<EntityInstance> = mutableListOf()
|
private val instances: MutableList<EntityInstance> = mutableListOf()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
mesh.userData = this
|
mesh.userData = this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun dispose() {
|
||||||
|
disposeObject3DResources(mesh)
|
||||||
|
super.dispose()
|
||||||
|
}
|
||||||
|
|
||||||
fun getInstance(entity: QuestEntityModel<*, *>): EntityInstance? =
|
fun getInstance(entity: QuestEntityModel<*, *>): EntityInstance? =
|
||||||
instances.find { it.entity == entity }
|
instances.find { it.entity == entity }
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@ class EntityMeshManager(
|
|||||||
add(entity)
|
add(entity)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
{ /* Nothing to dispose. */ },
|
EntityInstancedMesh::dispose,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package world.phantasmal.web.questEditor.widgets
|
package world.phantasmal.web.questEditor.widgets
|
||||||
|
|
||||||
|
import kotlinx.browser.window
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.w3c.dom.Node
|
import org.w3c.dom.Node
|
||||||
import world.phantasmal.core.disposable.Disposable
|
import world.phantasmal.core.disposable.Disposable
|
||||||
@ -8,6 +9,7 @@ import world.phantasmal.core.math.degToRad
|
|||||||
import world.phantasmal.core.math.radToDeg
|
import world.phantasmal.core.math.radToDeg
|
||||||
import world.phantasmal.lib.fileFormats.quest.EntityPropType
|
import world.phantasmal.lib.fileFormats.quest.EntityPropType
|
||||||
import world.phantasmal.observable.value.Val
|
import world.phantasmal.observable.value.Val
|
||||||
|
import world.phantasmal.observable.value.mutableVal
|
||||||
import world.phantasmal.web.core.widgets.UnavailableWidget
|
import world.phantasmal.web.core.widgets.UnavailableWidget
|
||||||
import world.phantasmal.web.questEditor.controllers.EntityInfoController
|
import world.phantasmal.web.questEditor.controllers.EntityInfoController
|
||||||
import world.phantasmal.web.questEditor.models.QuestEntityPropModel
|
import world.phantasmal.web.questEditor.models.QuestEntityPropModel
|
||||||
@ -90,9 +92,21 @@ class EntityInfoWidget(private val ctrl: EntityInfoController) : Widget(enabled
|
|||||||
tr {
|
tr {
|
||||||
className = COORD_CLASS
|
className = COORD_CLASS
|
||||||
|
|
||||||
|
val inputValue = mutableVal(value.value)
|
||||||
|
var timeout = -1
|
||||||
|
|
||||||
|
observe(value) {
|
||||||
|
if (timeout == -1) {
|
||||||
|
timeout = window.setTimeout({
|
||||||
|
inputValue.value = value.value
|
||||||
|
timeout = -1
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val input = DoubleInput(
|
val input = DoubleInput(
|
||||||
enabled = ctrl.enabled,
|
enabled = ctrl.enabled,
|
||||||
value = value,
|
value = inputValue,
|
||||||
onChange = onChange,
|
onChange = onChange,
|
||||||
label = label,
|
label = label,
|
||||||
roundTo = 3,
|
roundTo = 3,
|
||||||
|
@ -103,8 +103,7 @@ class MeshRenderer(
|
|||||||
|
|
||||||
if (resetCamera) {
|
if (resetCamera) {
|
||||||
// Compute camera position.
|
// Compute camera position.
|
||||||
val geom = mesh.geometry as BufferGeometry
|
val bSphere = mesh.geometry.boundingSphere!!
|
||||||
val bSphere = geom.boundingSphere!!
|
|
||||||
val cameraDistFactor =
|
val cameraDistFactor =
|
||||||
1.5 / tan(degToRad((context.camera as PerspectiveCamera).fov) / 2)
|
1.5 / tan(degToRad((context.camera as PerspectiveCamera).fov) / 2)
|
||||||
val cameraPos = CAMERA_POS * (bSphere.radius * cameraDistFactor)
|
val cameraPos = CAMERA_POS * (bSphere.radius * cameraDistFactor)
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package world.phantasmal.web.viewer.rendering
|
package world.phantasmal.web.viewer.rendering
|
||||||
|
|
||||||
import mu.KotlinLogging
|
import mu.KotlinLogging
|
||||||
|
import org.khronos.webgl.Float32Array
|
||||||
|
import org.khronos.webgl.Uint16Array
|
||||||
import org.w3c.dom.HTMLCanvasElement
|
import org.w3c.dom.HTMLCanvasElement
|
||||||
import world.phantasmal.lib.fileFormats.ninja.XvrTexture
|
import world.phantasmal.lib.fileFormats.ninja.XvrTexture
|
||||||
import world.phantasmal.web.core.rendering.*
|
import world.phantasmal.web.core.rendering.*
|
||||||
@ -51,8 +53,8 @@ class TextureRenderer(
|
|||||||
|
|
||||||
private fun texturesChanged(textures: List<XvrTexture>) {
|
private fun texturesChanged(textures: List<XvrTexture>) {
|
||||||
meshes.forEach { mesh ->
|
meshes.forEach { mesh ->
|
||||||
disposeObject3DResources(mesh)
|
|
||||||
context.scene.remove(mesh)
|
context.scene.remove(mesh)
|
||||||
|
disposeObject3DResources(mesh)
|
||||||
}
|
}
|
||||||
|
|
||||||
inputManager.resetCamera()
|
inputManager.resetCamera()
|
||||||
@ -109,21 +111,59 @@ class TextureRenderer(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createQuad(x: Int, y: Int, width: Int, height: Int): PlaneGeometry {
|
private fun createQuad(x: Int, y: Int, width: Int, height: Int): BufferGeometry {
|
||||||
val quad = PlaneGeometry(
|
val halfWidth = width / 2f
|
||||||
width.toDouble(),
|
val halfHeight = height / 2f
|
||||||
height.toDouble(),
|
|
||||||
widthSegments = 1.0,
|
val geom = BufferGeometry()
|
||||||
heightSegments = 1.0,
|
|
||||||
|
geom.setAttribute(
|
||||||
|
"position",
|
||||||
|
Float32BufferAttribute(
|
||||||
|
Float32Array(arrayOf(
|
||||||
|
-halfWidth, -halfHeight, 0f,
|
||||||
|
-halfWidth, halfHeight, 0f,
|
||||||
|
halfWidth, halfHeight, 0f,
|
||||||
|
halfWidth, -halfHeight, 0f,
|
||||||
|
)),
|
||||||
|
3,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
quad.faceVertexUvs = arrayOf(
|
geom.setAttribute(
|
||||||
arrayOf(
|
"normal",
|
||||||
arrayOf(Vector2(0.0, 0.0), Vector2(0.0, 1.0), Vector2(1.0, 0.0)),
|
Float32BufferAttribute(
|
||||||
arrayOf(Vector2(0.0, 1.0), Vector2(1.0, 1.0), Vector2(1.0, 0.0)),
|
Float32Array(arrayOf(
|
||||||
)
|
0f, 0f, 1f,
|
||||||
|
0f, 0f, 1f,
|
||||||
|
0f, 0f, 1f,
|
||||||
|
0f, 0f, 1f,
|
||||||
|
)),
|
||||||
|
3,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
quad.translate(x.toDouble(), y.toDouble(), -5.0)
|
geom.setAttribute(
|
||||||
return quad
|
"uv",
|
||||||
|
Float32BufferAttribute(
|
||||||
|
Float32Array(arrayOf(
|
||||||
|
0f, 1f,
|
||||||
|
0f, 0f,
|
||||||
|
1f, 0f,
|
||||||
|
1f, 1f,
|
||||||
|
)),
|
||||||
|
2,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
geom.setIndex(Uint16BufferAttribute(
|
||||||
|
Uint16Array(arrayOf(
|
||||||
|
0, 2, 1,
|
||||||
|
2, 0, 3,
|
||||||
|
)),
|
||||||
|
1,
|
||||||
|
))
|
||||||
|
|
||||||
|
geom.translate(x.toDouble(), y.toDouble(), -5.0)
|
||||||
|
|
||||||
|
return geom
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
Loading…
Reference in New Issue
Block a user