From 62a49b067c50a4862198e18f96159826c2ed66af Mon Sep 17 00:00:00 2001 From: Daan Vanden Bosch Date: Fri, 2 Jun 2023 22:35:00 +0200 Subject: [PATCH] Worked on mipmapping and anisotropy. --- .../core/rendering/conversion/MeshBuilder.kt | 3 +- .../conversion/NinjaGeometryConversion.kt | 50 +++++- .../conversion/XvrTextureConversion.kt | 146 ++++++++++++++---- .../phantasmal/web/externals/three/three.kt | 22 ++- .../questEditor/loading/AreaAssetLoader.kt | 1 + .../questEditor/loading/EntityAssetLoader.kt | 37 ++++- .../web/viewer/rendering/MeshRenderer.kt | 39 ++++- .../web/viewer/rendering/TextureRenderer.kt | 115 ++++++++------ 8 files changed, 315 insertions(+), 98 deletions(-) diff --git a/web/src/main/kotlin/world/phantasmal/web/core/rendering/conversion/MeshBuilder.kt b/web/src/main/kotlin/world/phantasmal/web/core/rendering/conversion/MeshBuilder.kt index 48cf9d8e..60fac306 100644 --- a/web/src/main/kotlin/world/phantasmal/web/core/rendering/conversion/MeshBuilder.kt +++ b/web/src/main/kotlin/world/phantasmal/web/core/rendering/conversion/MeshBuilder.kt @@ -13,6 +13,7 @@ import world.phantasmal.webui.obj class MeshBuilder( private val textures: List = emptyList(), private val textureCache: UnsafeMap = UnsafeMap(), + private val anisotropy: Int = 1, ) { private val positions = mutableListOf() private val normals = mutableListOf() @@ -197,7 +198,7 @@ class MeshBuilder( if (tex == null) { tex = textures.getOrNull(group.textureIndex)?.let { xvm -> - xvrTextureToThree(xvm) + xvrTextureToThree(xvm, anisotropy = anisotropy) } textureCache.set(group.textureIndex, tex) } diff --git a/web/src/main/kotlin/world/phantasmal/web/core/rendering/conversion/NinjaGeometryConversion.kt b/web/src/main/kotlin/world/phantasmal/web/core/rendering/conversion/NinjaGeometryConversion.kt index 439f284e..b6c1d8ea 100644 --- a/web/src/main/kotlin/world/phantasmal/web/core/rendering/conversion/NinjaGeometryConversion.kt +++ b/web/src/main/kotlin/world/phantasmal/web/core/rendering/conversion/NinjaGeometryConversion.kt @@ -8,11 +8,40 @@ import world.phantasmal.core.asArray import world.phantasmal.core.isBitSet import world.phantasmal.core.jsArrayOf import world.phantasmal.core.unsafe.UnsafeMap -import world.phantasmal.psolib.fileFormats.* -import world.phantasmal.psolib.fileFormats.ninja.* +import world.phantasmal.psolib.fileFormats.AreaGeometry +import world.phantasmal.psolib.fileFormats.AreaObject +import world.phantasmal.psolib.fileFormats.AreaSection +import world.phantasmal.psolib.fileFormats.CollisionGeometry +import world.phantasmal.psolib.fileFormats.CollisionTriangle +import world.phantasmal.psolib.fileFormats.ninja.NinjaModel +import world.phantasmal.psolib.fileFormats.ninja.NinjaObject +import world.phantasmal.psolib.fileFormats.ninja.NjModel +import world.phantasmal.psolib.fileFormats.ninja.NjObject +import world.phantasmal.psolib.fileFormats.ninja.XjModel +import world.phantasmal.psolib.fileFormats.ninja.XjObject +import world.phantasmal.psolib.fileFormats.ninja.XvrTexture import world.phantasmal.web.core.dot import world.phantasmal.web.core.toQuaternion -import world.phantasmal.web.externals.three.* +import world.phantasmal.web.externals.three.Bone +import world.phantasmal.web.externals.three.BufferGeometry +import world.phantasmal.web.externals.three.Color +import world.phantasmal.web.externals.three.DoubleSide +import world.phantasmal.web.externals.three.Euler +import world.phantasmal.web.externals.three.Float32BufferAttribute +import world.phantasmal.web.externals.three.Group +import world.phantasmal.web.externals.three.InstancedMesh +import world.phantasmal.web.externals.three.Material +import world.phantasmal.web.externals.three.Matrix3 +import world.phantasmal.web.externals.three.Matrix4 +import world.phantasmal.web.externals.three.Mesh +import world.phantasmal.web.externals.three.MeshBasicMaterial +import world.phantasmal.web.externals.three.MeshLambertMaterial +import world.phantasmal.web.externals.three.Quaternion +import world.phantasmal.web.externals.three.SkinnedMesh +import world.phantasmal.web.externals.three.Texture +import world.phantasmal.web.externals.three.Uint16BufferAttribute +import world.phantasmal.web.externals.three.Vector2 +import world.phantasmal.web.externals.three.Vector3 import world.phantasmal.webui.obj import kotlin.collections.component1 import kotlin.collections.component2 @@ -86,8 +115,9 @@ fun ninjaObjectToMesh( textures: List, defaultMaterial: Material? = null, boundingVolumes: Boolean = false, + anisotropy: Int = 1, ): Mesh { - val builder = MeshBuilder(textures) + val builder = MeshBuilder(textures, anisotropy = anisotropy) defaultMaterial?.let { builder.defaultMaterial(defaultMaterial) } ninjaObjectToMeshBuilder(ninjaObject, builder) return builder.buildMesh(boundingVolumes) @@ -99,8 +129,9 @@ fun ninjaObjectToInstancedMesh( maxInstances: Int, defaultMaterial: Material? = null, boundingVolumes: Boolean = false, + anisotropy: Int = 1, ): InstancedMesh { - val builder = MeshBuilder(textures) + val builder = MeshBuilder(textures, anisotropy = anisotropy) defaultMaterial?.let { builder.defaultMaterial(defaultMaterial) } ninjaObjectToMeshBuilder(ninjaObject, builder) return builder.buildInstancedMesh(maxInstances, boundingVolumes) @@ -111,8 +142,9 @@ fun ninjaObjectToSkinnedMesh( textures: List, defaultMaterial: Material? = null, boundingVolumes: Boolean = false, + anisotropy: Int = 1, ): SkinnedMesh { - val builder = MeshBuilder(textures) + val builder = MeshBuilder(textures, anisotropy = anisotropy) defaultMaterial?.let { builder.defaultMaterial(defaultMaterial) } ninjaObjectToMeshBuilder(ninjaObject, builder) return builder.buildSkinnedMesh(boundingVolumes) @@ -129,6 +161,7 @@ fun ninjaObjectToMeshBuilder( fun renderGeometryToGroup( renderGeometry: AreaGeometry, textures: List, + anisotropy: Int = 1, processMesh: (AreaSection, AreaObject, Mesh) -> Unit = { _, _, _ -> }, ): Group { val group = Group() @@ -145,6 +178,7 @@ fun renderGeometryToGroup( section, sectionIndex, areaObj, + anisotropy, processMesh, ) ) @@ -159,6 +193,7 @@ fun renderGeometryToGroup( section, sectionIndex, areaObj, + anisotropy, processMesh, ) ) @@ -214,13 +249,14 @@ private fun areaObjectToMesh( section: AreaSection, sectionIndex: Int, areaObj: AreaObject, + anisotropy: Int, processMesh: (AreaSection, AreaObject, Mesh) -> Unit, ): Mesh { val cachedMesh = meshCache.get(areaObj.xjObject) val mesh: Mesh if (cachedMesh == null) { - val builder = MeshBuilder(textures, textureCache) + val builder = MeshBuilder(textures, textureCache, anisotropy) ninjaObjectToMeshBuilder(areaObj.xjObject, builder) builder.defaultMaterial(MeshLambertMaterial(obj { diff --git a/web/src/main/kotlin/world/phantasmal/web/core/rendering/conversion/XvrTextureConversion.kt b/web/src/main/kotlin/world/phantasmal/web/core/rendering/conversion/XvrTextureConversion.kt index f21fbe25..0fc6c212 100644 --- a/web/src/main/kotlin/world/phantasmal/web/core/rendering/conversion/XvrTextureConversion.kt +++ b/web/src/main/kotlin/world/phantasmal/web/core/rendering/conversion/XvrTextureConversion.kt @@ -6,11 +6,32 @@ import org.khronos.webgl.get import org.khronos.webgl.set import world.phantasmal.psolib.cursor.cursor import world.phantasmal.psolib.fileFormats.ninja.XvrTexture -import world.phantasmal.web.externals.three.* +import world.phantasmal.web.externals.three.CompressedPixelFormat +import world.phantasmal.web.externals.three.CompressedTexture +import world.phantasmal.web.externals.three.DataTexture +import world.phantasmal.web.externals.three.LinearFilter +import world.phantasmal.web.externals.three.Mipmap +import world.phantasmal.web.externals.three.MirroredRepeatWrapping +import world.phantasmal.web.externals.three.PixelFormat +import world.phantasmal.web.externals.three.RGBAFormat +import world.phantasmal.web.externals.three.RGBA_S3TC_DXT1_Format +import world.phantasmal.web.externals.three.RGBA_S3TC_DXT3_Format +import world.phantasmal.web.externals.three.RGBFormat +import world.phantasmal.web.externals.three.Texture +import world.phantasmal.web.externals.three.TextureDataType +import world.phantasmal.web.externals.three.TextureFilter +import world.phantasmal.web.externals.three.UnsignedShort5551Type +import world.phantasmal.web.externals.three.UnsignedShort565Type import world.phantasmal.webui.obj import kotlin.math.roundToInt -fun xvrTextureToThree(xvr: XvrTexture, filter: TextureFilter = LinearFilter): Texture = +fun xvrTextureToThree( + xvr: XvrTexture, + magFilter: TextureFilter = LinearFilter, + // TODO: Use LinearMipmapLinearFilter once we figure out mipmapping. + minFilter: TextureFilter = LinearFilter, + anisotropy: Int = 1, +): Texture = when (xvr.format.second) { // D3DFMT_R5G6B5 2 -> createDataTexture( @@ -19,7 +40,9 @@ fun xvrTextureToThree(xvr: XvrTexture, filter: TextureFilter = LinearFilter): Te xvr.height, RGBFormat, UnsignedShort565Type, - filter, + magFilter, + minFilter, + anisotropy, ) // D3DFMT_A1R5G5B5 3 -> { @@ -38,26 +61,84 @@ fun xvrTextureToThree(xvr: XvrTexture, filter: TextureFilter = LinearFilter): Te xvr.height, RGBAFormat, UnsignedShort5551Type, - filter, + magFilter, + minFilter, + anisotropy, ) } // D3DFMT_DXT1 - 6 -> createCompressedTexture( - Uint8Array(xvr.data.arrayBuffer, 0, (xvr.width * xvr.height) / 2), - xvr.width, - xvr.height, - RGBA_S3TC_DXT1_Format, - filter, - ) + 6 -> { + val mipmaps = mutableListOf() + var byteOffset = 0 + var width = xvr.width + var height = xvr.height + + while (byteOffset < xvr.data.size && width * height > 0) { + val byteSize = (width * height) / 2 + + mipmaps.add(obj { + this.data = Uint8Array(xvr.data.arrayBuffer, byteOffset, byteSize) + this.width = width + this.height = height + }) + + byteOffset += byteSize + width /= 2 + height /= 2 + + // TODO: Figure out what the problem with mipmaps is and remove this break. + // Do we interpret the XVR format incorrectly or is there a problem with + // Three.js/WebGL? + break + } + + createCompressedTexture( + mipmaps.toTypedArray(), + xvr.width, + xvr.height, + RGBA_S3TC_DXT1_Format, + magFilter, + minFilter, + anisotropy, + ) + } // D3DFMT_DXT2 // TODO: Correctly interpret this (DXT2 is basically DXT3 with premultiplied alpha). - 7 -> createCompressedTexture( - Uint8Array(xvr.data.arrayBuffer, 0, xvr.width * xvr.height), - xvr.width, - xvr.height, - RGBA_S3TC_DXT3_Format, - filter, - ) + 7 -> { + val mipmaps = mutableListOf() + var byteOffset = 0 + var width = xvr.width + var height = xvr.height + + while (byteOffset < xvr.data.size && width * height > 0) { + val byteSize = width * height + + mipmaps.add(obj { + this.data = Uint8Array(xvr.data.arrayBuffer, byteOffset, byteSize) + this.width = width + this.height = height + }) + + byteOffset += byteSize + width /= 2 + height /= 2 + + // TODO: Figure out what the problem with mipmaps is and remove this break. + // Do we interpret the XVR format incorrectly or is there a problem with + // Three.js/WebGL? + break + } + + createCompressedTexture( + mipmaps.toTypedArray(), + xvr.width, + xvr.height, + RGBA_S3TC_DXT3_Format, + magFilter, + minFilter, + anisotropy, + ) + } // 1 -> D3DFMT_A8R8G8B8 // 4 -> D3DFMT_A4R4G4B4 // 5 -> D3DFMT_P8 @@ -83,7 +164,9 @@ private fun createDataTexture( height: Int, format: PixelFormat, type: TextureDataType, - filter: TextureFilter, + magFilter: TextureFilter, + minFilter: TextureFilter, + anisotropy: Int, ): DataTexture = DataTexture( data, @@ -93,30 +176,30 @@ private fun createDataTexture( type, wrapS = MirroredRepeatWrapping, wrapT = MirroredRepeatWrapping, - magFilter = filter, - minFilter = filter, + magFilter = magFilter, + minFilter = minFilter, + anisotropy = anisotropy, ) private fun createCompressedTexture( - data: Uint8Array, + mipmaps: Array, width: Int, height: Int, format: CompressedPixelFormat, - filter: TextureFilter, + magFilter: TextureFilter, + minFilter: TextureFilter, + anisotropy: Int, ): CompressedTexture { val texture = CompressedTexture( - arrayOf(obj { - this.data = data - this.width = width - this.height = height - }), + mipmaps, width, height, format, wrapS = MirroredRepeatWrapping, wrapT = MirroredRepeatWrapping, - magFilter = filter, - minFilter = filter, + magFilter = magFilter, + minFilter = minFilter, + anisotropy = anisotropy, ) texture.needsUpdate = true return texture @@ -165,12 +248,14 @@ private fun xvrTextureToUint8Array(xvr: XvrTexture): Uint8Array { b = c0b a = 1.0 } + 1 -> { r = c1r g = c1g b = c1b a = 1.0 } + 2 -> { if (c0 > c1) { r = (2 * c0r + c1r) / 3 @@ -184,6 +269,7 @@ private fun xvrTextureToUint8Array(xvr: XvrTexture): Uint8Array { a = 1.0 } } + 3 -> { if (c0 > c1) { r = (c0r + 2 * c1r) / 3 diff --git a/web/src/main/kotlin/world/phantasmal/web/externals/three/three.kt b/web/src/main/kotlin/world/phantasmal/web/externals/three/three.kt index 8c179b4a..7559615c 100644 --- a/web/src/main/kotlin/world/phantasmal/web/externals/three/three.kt +++ b/web/src/main/kotlin/world/phantasmal/web/externals/three/three.kt @@ -7,6 +7,7 @@ package world.phantasmal.web.externals.three import org.khronos.webgl.Float32Array import org.khronos.webgl.Int32Array import org.khronos.webgl.Uint16Array +import org.khronos.webgl.Uint8Array import org.w3c.dom.HTMLCanvasElement external interface Vector @@ -235,6 +236,7 @@ open external class WebGLRenderer( var autoClearColor: Boolean var debug: WebGLDebug + var capabilities: WebGLCapabilities override fun render(scene: Object3D, camera: Camera) @@ -253,6 +255,10 @@ external interface WebGLDebug { var checkShaderErrors: Boolean } +external interface WebGLCapabilities { + fun getMaxAnisotropy(): Int +} + open external class Object3D { /** * Optional name of the object (doesn't need to be unique). @@ -686,7 +692,7 @@ external class DataTexture( wrapT: Wrapping = definedExternally, magFilter: TextureFilter = definedExternally, minFilter: TextureFilter = definedExternally, - anisotropy: Double = definedExternally, + anisotropy: Int = definedExternally, encoding: TextureEncoding = definedExternally, ) : Texture @@ -707,14 +713,10 @@ external object MirroredRepeatWrapping : Wrapping external interface TextureFilter external object NearestFilter : TextureFilter external object NearestMipmapNearestFilter : TextureFilter -external object NearestMipMapNearestFilter : TextureFilter external object NearestMipmapLinearFilter : TextureFilter -external object NearestMipMapLinearFilter : TextureFilter external object LinearFilter : TextureFilter external object LinearMipmapNearestFilter : TextureFilter -external object LinearMipMapNearestFilter : TextureFilter external object LinearMipmapLinearFilter : TextureFilter -external object LinearMipMapLinearFilter : TextureFilter external interface TextureDataType external object UnsignedByteType : TextureDataType @@ -764,7 +766,7 @@ external object RGBM16Encoding : TextureEncoding external object RGBDEncoding : TextureEncoding external class CompressedTexture( - mipmaps: Array, /* Should have data, height and width. */ + mipmaps: Array, width: Int, height: Int, format: CompressedPixelFormat = definedExternally, @@ -774,10 +776,16 @@ external class CompressedTexture( wrapT: Wrapping = definedExternally, magFilter: TextureFilter = definedExternally, minFilter: TextureFilter = definedExternally, - anisotropy: Double = definedExternally, + anisotropy: Int = definedExternally, encoding: TextureEncoding = definedExternally, ) : Texture +external interface Mipmap { + var data: Uint8Array + var width: Int + var height: Int +} + external enum class MOUSE { LEFT, MIDDLE, diff --git a/web/src/main/kotlin/world/phantasmal/web/questEditor/loading/AreaAssetLoader.kt b/web/src/main/kotlin/world/phantasmal/web/questEditor/loading/AreaAssetLoader.kt index acb4c251..4e60adce 100644 --- a/web/src/main/kotlin/world/phantasmal/web/questEditor/loading/AreaAssetLoader.kt +++ b/web/src/main/kotlin/world/phantasmal/web/questEditor/loading/AreaAssetLoader.kt @@ -174,6 +174,7 @@ class AreaAssetLoader(private val assetLoader: AssetLoader) : DisposableContaine val fix = MANUAL_FIXES[Pair(episode, areaVariant.area.id)] val sections = mutableMapOf() + // TODO: Pass anisotropy parameter. val group = renderGeometryToGroup(renderGeometry, textures) { renderSection, areaObj, mesh -> if (fix != null) { diff --git a/web/src/main/kotlin/world/phantasmal/web/questEditor/loading/EntityAssetLoader.kt b/web/src/main/kotlin/world/phantasmal/web/questEditor/loading/EntityAssetLoader.kt index 0cd24692..5125d2e9 100644 --- a/web/src/main/kotlin/world/phantasmal/web/questEditor/loading/EntityAssetLoader.kt +++ b/web/src/main/kotlin/world/phantasmal/web/questEditor/loading/EntityAssetLoader.kt @@ -7,14 +7,22 @@ import world.phantasmal.core.Success import world.phantasmal.psolib.Endianness import world.phantasmal.psolib.cursor.Cursor import world.phantasmal.psolib.cursor.cursor -import world.phantasmal.psolib.fileFormats.ninja.* +import world.phantasmal.psolib.fileFormats.ninja.NinjaObject +import world.phantasmal.psolib.fileFormats.ninja.XvrTexture +import world.phantasmal.psolib.fileFormats.ninja.parseNj +import world.phantasmal.psolib.fileFormats.ninja.parseXj +import world.phantasmal.psolib.fileFormats.ninja.parseXvm import world.phantasmal.psolib.fileFormats.quest.EntityType import world.phantasmal.psolib.fileFormats.quest.NpcType import world.phantasmal.psolib.fileFormats.quest.ObjectType import world.phantasmal.web.core.loading.AssetLoader import world.phantasmal.web.core.rendering.conversion.ninjaObjectToInstancedMesh import world.phantasmal.web.core.rendering.disposeObject3DResources -import world.phantasmal.web.externals.three.* +import world.phantasmal.web.externals.three.Color +import world.phantasmal.web.externals.three.CylinderGeometry +import world.phantasmal.web.externals.three.DoubleSide +import world.phantasmal.web.externals.three.InstancedMesh +import world.phantasmal.web.externals.three.MeshLambertMaterial import world.phantasmal.webui.DisposableContainer import world.phantasmal.webui.obj @@ -56,6 +64,7 @@ class EntityAssetLoader(private val assetLoader: AssetLoader) : DisposableContai val textures = loadTextures(type, model) + // TODO: Pass anisotropy parameter. return ninjaObjectToInstancedMesh( ninjaObject, textures, @@ -226,6 +235,7 @@ private fun entityTypeToGeometryFormat(type: EntityType): GeomFormat = else -> GeomFormat.Nj } } + is ObjectType -> { when (type) { ObjectType.EasterEgg, @@ -249,6 +259,7 @@ private fun entityTypeToGeometryFormat(type: EntityType): GeomFormat = else -> GeomFormat.Xj } } + else -> { error("$type not supported.") } @@ -272,6 +283,7 @@ private fun entityTypeToPath( GeomFormat.Nj -> "nj" GeomFormat.Xj -> "xj" } + AssetType.Texture -> "xvm" } @@ -296,26 +308,37 @@ private fun entityTypeToPath( NpcType.Hildebear2 -> entityTypeToPath(NpcType.Hildebear, assetType, suffix, model, geomFormat) + NpcType.Hildeblue2 -> entityTypeToPath(NpcType.Hildeblue, assetType, suffix, model, geomFormat) + NpcType.RagRappy2 -> entityTypeToPath(NpcType.RagRappy, assetType, suffix, model, geomFormat) + NpcType.Monest2 -> entityTypeToPath(NpcType.Monest, assetType, suffix, model, geomFormat) + NpcType.Mothmant2 -> entityTypeToPath(NpcType.Mothmant, assetType, suffix, model, geomFormat) + NpcType.PoisonLily2 -> entityTypeToPath(NpcType.PoisonLily, assetType, suffix, model, geomFormat) + NpcType.NarLily2 -> entityTypeToPath(NpcType.NarLily, assetType, suffix, model, geomFormat) + NpcType.GrassAssassin2 -> entityTypeToPath(NpcType.GrassAssassin, assetType, suffix, model, geomFormat) + NpcType.Dimenian2 -> entityTypeToPath(NpcType.Dimenian, assetType, suffix, model, geomFormat) + NpcType.LaDimenian2 -> entityTypeToPath(NpcType.LaDimenian, assetType, suffix, model, geomFormat) + NpcType.SoDimenian2 -> entityTypeToPath(NpcType.SoDimenian, assetType, suffix, model, geomFormat) + NpcType.DarkBelra2 -> entityTypeToPath(NpcType.DarkBelra, assetType, suffix, model, geomFormat) @@ -323,26 +346,35 @@ private fun entityTypeToPath( NpcType.SavageWolf2 -> entityTypeToPath(NpcType.SavageWolf, assetType, suffix, model, geomFormat) + NpcType.BarbarousWolf2 -> entityTypeToPath(NpcType.BarbarousWolf, assetType, suffix, model, geomFormat) + NpcType.PanArms2 -> entityTypeToPath(NpcType.PanArms, assetType, suffix, model, geomFormat) + NpcType.Dubchic2 -> entityTypeToPath(NpcType.Dubchic, assetType, suffix, model, geomFormat) + NpcType.Gilchic2 -> entityTypeToPath(NpcType.Gilchic, assetType, suffix, model, geomFormat) + NpcType.Garanz2 -> entityTypeToPath(NpcType.Garanz, assetType, suffix, model, geomFormat) + NpcType.Dubswitch2 -> entityTypeToPath(NpcType.Dubswitch, assetType, suffix, model, geomFormat) + NpcType.Delsaber2 -> entityTypeToPath(NpcType.Delsaber, assetType, suffix, model, geomFormat) + NpcType.ChaosSorcerer2 -> entityTypeToPath(NpcType.ChaosSorcerer, assetType, suffix, model, geomFormat) else -> "/npcs/${type.name}${fullSuffix}.$extension" } } + is ObjectType -> { when (type) { // We don't have a model for these objects. @@ -431,6 +463,7 @@ private fun entityTypeToPath( } } } + else -> { error("$type not supported.") } diff --git a/web/src/main/kotlin/world/phantasmal/web/viewer/rendering/MeshRenderer.kt b/web/src/main/kotlin/world/phantasmal/web/viewer/rendering/MeshRenderer.kt index cec43404..638acbcb 100644 --- a/web/src/main/kotlin/world/phantasmal/web/viewer/rendering/MeshRenderer.kt +++ b/web/src/main/kotlin/world/phantasmal/web/viewer/rendering/MeshRenderer.kt @@ -8,11 +8,27 @@ import world.phantasmal.psolib.fileFormats.ninja.NjMotion import world.phantasmal.psolib.fileFormats.ninja.NjObject import world.phantasmal.web.core.boundingSphere import world.phantasmal.web.core.isSkinnedMesh -import world.phantasmal.web.core.rendering.* +import world.phantasmal.web.core.rendering.DisposableThreeRenderer +import world.phantasmal.web.core.rendering.OrbitalCameraInputManager +import world.phantasmal.web.core.rendering.RenderContext import world.phantasmal.web.core.rendering.Renderer -import world.phantasmal.web.core.rendering.conversion.* +import world.phantasmal.web.core.rendering.conversion.PSO_FRAME_RATE_DOUBLE +import world.phantasmal.web.core.rendering.conversion.collisionGeometryToGroup +import world.phantasmal.web.core.rendering.conversion.createAnimationClip +import world.phantasmal.web.core.rendering.conversion.ninjaObjectToMesh +import world.phantasmal.web.core.rendering.conversion.ninjaObjectToSkinnedMesh +import world.phantasmal.web.core.rendering.conversion.renderGeometryToGroup +import world.phantasmal.web.core.rendering.disposeObject3DResources import world.phantasmal.web.core.times -import world.phantasmal.web.externals.three.* +import world.phantasmal.web.externals.three.AnimationAction +import world.phantasmal.web.externals.three.AnimationClip +import world.phantasmal.web.externals.three.AnimationMixer +import world.phantasmal.web.externals.three.Clock +import world.phantasmal.web.externals.three.LineBasicMaterial +import world.phantasmal.web.externals.three.Object3D +import world.phantasmal.web.externals.three.PerspectiveCamera +import world.phantasmal.web.externals.three.SkeletonHelper +import world.phantasmal.web.externals.three.Vector3 import world.phantasmal.web.shared.Throttle import world.phantasmal.web.viewer.stores.NinjaGeometry import world.phantasmal.web.viewer.stores.ViewerStore @@ -125,15 +141,26 @@ class MeshRenderer( val obj = ninjaGeometry.obj if (obj is NjObject) { - ninjaObjectToSkinnedMesh(obj, textures, boundingVolumes = true) + ninjaObjectToSkinnedMesh( + obj, + textures, + boundingVolumes = true, + anisotropy = threeRenderer.capabilities.getMaxAnisotropy() / 2, + ) } else { - ninjaObjectToMesh(obj, textures, boundingVolumes = true) + ninjaObjectToMesh( + obj, + textures, + boundingVolumes = true, + anisotropy = threeRenderer.capabilities.getMaxAnisotropy() / 2, + ) } } is NinjaGeometry.Render -> renderGeometryToGroup( ninjaGeometry.geometry, - textures + textures, + anisotropy = threeRenderer.capabilities.getMaxAnisotropy() / 2, ) is NinjaGeometry.Collision -> collisionGeometryToGroup(ninjaGeometry.geometry) diff --git a/web/src/main/kotlin/world/phantasmal/web/viewer/rendering/TextureRenderer.kt b/web/src/main/kotlin/world/phantasmal/web/viewer/rendering/TextureRenderer.kt index 56e01f8f..8859c31a 100644 --- a/web/src/main/kotlin/world/phantasmal/web/viewer/rendering/TextureRenderer.kt +++ b/web/src/main/kotlin/world/phantasmal/web/viewer/rendering/TextureRenderer.kt @@ -5,10 +5,21 @@ import org.khronos.webgl.Float32Array import org.khronos.webgl.Uint16Array import org.w3c.dom.HTMLCanvasElement import world.phantasmal.psolib.fileFormats.ninja.XvrTexture -import world.phantasmal.web.core.rendering.* +import world.phantasmal.web.core.rendering.DisposableThreeRenderer +import world.phantasmal.web.core.rendering.OrbitalCameraInputManager +import world.phantasmal.web.core.rendering.RenderContext import world.phantasmal.web.core.rendering.Renderer import world.phantasmal.web.core.rendering.conversion.xvrTextureToThree -import world.phantasmal.web.externals.three.* +import world.phantasmal.web.core.rendering.disposeObject3DResources +import world.phantasmal.web.externals.three.BufferGeometry +import world.phantasmal.web.externals.three.Color +import world.phantasmal.web.externals.three.Float32BufferAttribute +import world.phantasmal.web.externals.three.Mesh +import world.phantasmal.web.externals.three.MeshBasicMaterial +import world.phantasmal.web.externals.three.NearestFilter +import world.phantasmal.web.externals.three.OrthographicCamera +import world.phantasmal.web.externals.three.Uint16BufferAttribute +import world.phantasmal.web.externals.three.Vector3 import world.phantasmal.web.viewer.stores.ViewerStore import world.phantasmal.webui.obj import kotlin.math.ceil @@ -23,27 +34,31 @@ class TextureRenderer( ) : Renderer() { private var meshes = listOf() - override val context = addDisposable(RenderContext( - createCanvas(), - OrthographicCamera( - left = -400.0, - right = 400.0, - top = 300.0, - bottom = -300.0, - near = 1.0, - far = 10.0, + override val context = addDisposable( + RenderContext( + createCanvas(), + OrthographicCamera( + left = -400.0, + right = 400.0, + top = 300.0, + bottom = -300.0, + near = 1.0, + far = 10.0, + ) ) - )) + ) override val threeRenderer = addDisposable(createThreeRenderer(context.canvas)).renderer - override val inputManager = addDisposable(OrbitalCameraInputManager( - context.canvas, - context.camera, - Vector3(0.0, 0.0, 5.0), - screenSpacePanning = true, - enableRotate = false, - )) + override val inputManager = addDisposable( + OrbitalCameraInputManager( + context.canvas, + context.camera, + Vector3(0.0, 0.0, 5.0), + screenSpacePanning = true, + enableRotate = false, + ) + ) init { observeNow(store.currentTextures) { @@ -81,7 +96,7 @@ class TextureRenderer( meshes = textures.map { xvr -> val texture = try { - xvrTextureToThree(xvr, filter = NearestFilter) + xvrTextureToThree(xvr, magFilter = NearestFilter, minFilter = NearestFilter) } catch (e: Exception) { logger.error(e) { "Couldn't convert XVR texture." } null @@ -120,46 +135,56 @@ class TextureRenderer( geom.setAttribute( "position", Float32BufferAttribute( - Float32Array(arrayOf( - -halfWidth, -halfHeight, 0f, - -halfWidth, halfHeight, 0f, - halfWidth, halfHeight, 0f, - halfWidth, -halfHeight, 0f, - )), + Float32Array( + arrayOf( + -halfWidth, -halfHeight, 0f, + -halfWidth, halfHeight, 0f, + halfWidth, halfHeight, 0f, + halfWidth, -halfHeight, 0f, + ) + ), 3, ), ) geom.setAttribute( "normal", Float32BufferAttribute( - Float32Array(arrayOf( - 0f, 0f, 1f, - 0f, 0f, 1f, - 0f, 0f, 1f, - 0f, 0f, 1f, - )), + Float32Array( + arrayOf( + 0f, 0f, 1f, + 0f, 0f, 1f, + 0f, 0f, 1f, + 0f, 0f, 1f, + ) + ), 3, ), ) geom.setAttribute( "uv", Float32BufferAttribute( - Float32Array(arrayOf( - 0f, 1f, - 0f, 0f, - 1f, 0f, - 1f, 1f, - )), + Float32Array( + arrayOf( + 0f, 1f, + 0f, 0f, + 1f, 0f, + 1f, 1f, + ) + ), 2, ), ) - geom.setIndex(Uint16BufferAttribute( - Uint16Array(arrayOf( - 0, 2, 1, - 2, 0, 3, - )), - 1, - )) + geom.setIndex( + Uint16BufferAttribute( + Uint16Array( + arrayOf( + 0, 2, 1, + 2, 0, 3, + ) + ), + 1, + ) + ) geom.translate(x.toDouble(), y.toDouble(), -5.0)