diff --git a/src/lib/effects/billboard/gpu/gpu-textures-state.ts b/src/lib/effects/billboard/gpu/gpu-textures-state.ts index ffe2400c..f88841f0 100644 --- a/src/lib/effects/billboard/gpu/gpu-textures-state.ts +++ b/src/lib/effects/billboard/gpu/gpu-textures-state.ts @@ -1,4 +1,4 @@ -import { createFullscreenQuad } from "../../../helpers/fullscreen-quad"; +import { createFullscreenQuad } from '../../../helpers/fullscreen-quad'; import * as THREE from '../../../libs/three-usage'; type UniformType = 'sampler2D' | 'float' | 'vec2' | 'vec3' | 'vec4'; @@ -39,7 +39,7 @@ class GpuTexturesState { this.textureNames = params.textureNames; - this.fullscreenQuad = createFullscreenQuad("aPosition"); + this.fullscreenQuad = createFullscreenQuad('aPosition'); const vertexShader = ` in vec2 aPosition; diff --git a/src/lib/helpers/customizable-texture.ts b/src/lib/helpers/customizable-texture.ts index d73197b1..b9dcf1f7 100644 --- a/src/lib/helpers/customizable-texture.ts +++ b/src/lib/helpers/customizable-texture.ts @@ -1,6 +1,6 @@ -import * as THREE from "../libs/three-usage"; +import * as THREE from '../libs/three-usage'; -import { createFullscreenQuad } from "./fullscreen-quad"; +import { createFullscreenQuad } from './fullscreen-quad'; type Parameters = { readonly width: number; @@ -22,12 +22,13 @@ class CustomizableTexture { private readonly renderTarget: THREE.WebGLRenderTarget; private readonly fakeCamera = new THREE.PerspectiveCamera(); - private readonly fullscreenQuad = createFullscreenQuad("aPosition"); + private readonly fullscreenQuad = createFullscreenQuad('aPosition'); private readonly applyLayer: { readonly shader: THREE.RawShaderMaterial; readonly uniforms: { readonly layer: THREE.IUniform; readonly color: THREE.IUniform; + readonly flipY: THREE.IUniform; }; }; @@ -36,11 +37,15 @@ class CustomizableTexture { const layers = new Map(); for (const [name, texture] of params.additionalTextures.entries()) { - layers.set(name, { texture, color: new THREE.Color(0xFFFFFF) }); + layers.set(name, { texture, color: new THREE.Color(0xffffff) }); } this.layers = layers; this.renderTarget = new THREE.WebGLRenderTarget(params.width, params.height, { + wrapS: this.baseTexture.wrapS, + wrapT: this.baseTexture.wrapT, + magFilter: this.baseTexture.magFilter, + // minFilter: this.baseTexture.minFilter, depthBuffer: false, }); const texture = this.renderTarget.textures[0]; @@ -51,23 +56,36 @@ class CustomizableTexture { const uniforms = { layer: { value: null }, - color: { value: new THREE.Color(0xFFFFFF) }, + color: { value: new THREE.Color(0xffffff) }, + flipY: { value: 0 }, }; const shader = new THREE.RawShaderMaterial({ - glslVersion: "300 es", + glslVersion: '300 es', + depthTest: false, + blending: THREE.CustomBlending, + blendSrc: THREE.SrcAlphaFactor, + blendDst: THREE.OneMinusSrcAlphaFactor, + blendSrcAlpha: THREE.ZeroFactor, + blendDstAlpha: THREE.OneFactor, uniforms: { uLayerTexture: uniforms.layer, uLayerColor: uniforms.color, + uFlipY: uniforms.flipY, }, vertexShader: ` +uniform float uFlipY; + in vec2 aPosition; out vec2 vUv; void main() { - vUv = aPosition; gl_Position = vec4(2.0 * aPosition - 1.0, 0, 1); + vUv = vec2( + aPosition.x, + mix(aPosition.y, 1.0 - aPosition.y, uFlipY) + ); }`, fragmentShader: ` precision mediump float; @@ -77,15 +95,17 @@ uniform vec3 uLayerColor; in vec2 vUv; -(layout location = 0) out vec4 fragColor; +layout(location = 0) out vec4 fragColor; void main() { - vec4 sample = texture(uLayerTexture, vUv); - // sample.rgb *= uLayerColor; - fragColor = sample + vec4(0, 1, 0, 1); + vec4 sampled = texture(uLayerTexture, vUv); + if (sampled.a < 0.5) discard; + sampled.rgb *= uLayerColor; + fragColor = sampled; } `, }); + this.fullscreenQuad.material = shader; this.applyLayer = { shader, uniforms }; } @@ -94,7 +114,7 @@ void main() { const layer = this.layers.get(layerName); if (!layer) { const layerNames = Array.from(this.layers.keys()); - throw new Error(`Unknown layer name "${layerName}". Layer names are: ${layerNames.join("; ")}.`); + throw new Error(`Unknown layer name "${layerName}". Layer names are: ${layerNames.join('; ')}.`); } if (layer.color.equals(color)) { @@ -114,20 +134,24 @@ void main() { }; renderer.setRenderTarget(this.renderTarget); - renderer.setClearColor(0xFF0000, 0); + renderer.setClearColor(0x000000, 0); renderer.autoClear = false; renderer.autoClearColor = false; renderer.clear(true); this.applyLayer.uniforms.layer.value = this.baseTexture; - this.applyLayer.uniforms.color.value = new THREE.Color(0xFFFFFF); + this.applyLayer.uniforms.color.value = new THREE.Color(0xffffff); + this.applyLayer.uniforms.flipY.value = Number(this.baseTexture.flipY); + this.applyLayer.shader.uniformsNeedUpdate = true; renderer.render(this.fullscreenQuad, this.fakeCamera); - // for (const layer of this.layers.values()) { - // this.applyLayer.uniforms.layer.value = layer.texture; - // this.applyLayer.uniforms.color.value = layer.color; - // renderer.render(this.fullscreenQuad, this.fakeCamera); - // } + for (const layer of this.layers.values()) { + this.applyLayer.uniforms.layer.value = layer.texture; + this.applyLayer.uniforms.color.value = layer.color; + this.applyLayer.uniforms.flipY.value = Number(layer.texture.flipY); + this.applyLayer.shader.uniformsNeedUpdate = true; + renderer.render(this.fullscreenQuad, this.fakeCamera); + } renderer.setRenderTarget(previousState.renderTarget); renderer.setClearColor(previousState.clearColor); @@ -137,7 +161,4 @@ void main() { } } -export { - CustomizableTexture -}; - +export { CustomizableTexture }; diff --git a/src/lib/helpers/fullscreen-quad.ts b/src/lib/helpers/fullscreen-quad.ts index c46a6d81..d1545485 100644 --- a/src/lib/helpers/fullscreen-quad.ts +++ b/src/lib/helpers/fullscreen-quad.ts @@ -1,4 +1,4 @@ -import * as THREE from "../libs/three-usage"; +import * as THREE from '../libs/three-usage'; function createFullscreenQuad(attributeName: string): THREE.Mesh { const fullscreenQuadGeometry = new THREE.BufferGeometry(); @@ -9,6 +9,4 @@ function createFullscreenQuad(attributeName: string): THREE.Mesh { return fullscreenQuad; } -export { - createFullscreenQuad, -}; \ No newline at end of file +export { createFullscreenQuad }; diff --git a/src/lib/index.ts b/src/lib/index.ts index e38d432e..e52aee79 100644 --- a/src/lib/index.ts +++ b/src/lib/index.ts @@ -12,11 +12,11 @@ export { type ILocalMapData, type IVoxelMap, type IVoxelMaterial, - type VoxelsChunkSize + type VoxelsChunkSize, } from './terrain/voxelmap/i-voxelmap'; export { VoxelmapViewerAutonomous, - type VoxelmapViewerAutonomousOptions + type VoxelmapViewerAutonomousOptions, } from './terrain/voxelmap/viewer/autonomous/voxelmap-viewer-autonomous'; export { EComputationMethod, @@ -24,7 +24,7 @@ export { type ComputationOptions, type ComputationStatus, type VoxelmapViewerOptions, - type VoxelsChunkData + type VoxelsChunkData, } from './terrain/voxelmap/viewer/simple/voxelmap-viewer'; export { VoxelmapVisibilityComputer } from './terrain/voxelmap/voxelmap-visibility-computer'; export { type CheckerboardType } from './terrain/voxelmap/voxelsRenderable/voxelsRenderableFactory/voxels-renderable-factory-base'; @@ -36,4 +36,4 @@ export { Rain } from './effects/weather/rain'; export { Snow } from './effects/weather/snow'; export { GpuInstancedBillboard } from './effects/weather/weather-particles-base'; -export { CustomizableTexture } from "./helpers/customizable-texture"; +export { CustomizableTexture } from './helpers/customizable-texture'; diff --git a/src/lib/libs/three-usage.ts b/src/lib/libs/three-usage.ts index d803793d..f7cab21e 100644 --- a/src/lib/libs/three-usage.ts +++ b/src/lib/libs/three-usage.ts @@ -4,6 +4,7 @@ export { Box3, BufferGeometry, Color, + CustomBlending, DataTexture, Float32BufferAttribute, Group, @@ -18,6 +19,8 @@ export { MeshPhongMaterial, NoBlending, NormalBlending, + OneFactor, + OneMinusSrcAlphaFactor, PerspectiveCamera, PlaneGeometry, RawShaderMaterial, @@ -26,6 +29,7 @@ export { RGBAFormat, ShaderMaterial, Sphere, + SrcAlphaFactor, Texture, TextureLoader, Uint32BufferAttribute, @@ -34,6 +38,7 @@ export { Vector3, Vector4, WebGLRenderTarget, + ZeroFactor, type Frustum, type IUniform, type Material, diff --git a/src/lib/terrain/voxelmap/i-voxelmap.ts b/src/lib/terrain/voxelmap/i-voxelmap.ts index 836a2f08..022f6e13 100644 --- a/src/lib/terrain/voxelmap/i-voxelmap.ts +++ b/src/lib/terrain/voxelmap/i-voxelmap.ts @@ -26,7 +26,7 @@ interface ILocalMapData { * Each element in the array represent a coordinate in the map and stores the data of the voxel at these coordinates. * Each element should be encoded as follows: * - bit 0: 0 if the voxel is empty, 1 otherwise - * - bit 1: 1 if the voxel should be displayed as cheesserboard, 0 otherwise + * - bit 1: 1 if the voxel should be displayed as checkerboard, 0 otherwise * - bits 2-13: ID of the material * Use the helper "voxelmapDataPacking" to do this encoding and be future-proof. * diff --git a/src/test/main.ts b/src/test/main.ts index 346c509c..7c15571b 100644 --- a/src/test/main.ts +++ b/src/test/main.ts @@ -14,7 +14,7 @@ function createVoxelMap(): VoxelMap { const mapScaleY = 200; const mapSeed = 'fixed_seed'; const includeTreesInLod = false; - + return new VoxelMap(mapScaleXZ, mapScaleY, mapSeed, includeTreesInLod); } diff --git a/src/test/test-texture-customization.ts b/src/test/test-texture-customization.ts index a66ef2fe..781316a6 100644 --- a/src/test/test-texture-customization.ts +++ b/src/test/test-texture-customization.ts @@ -11,7 +11,6 @@ class TestTextureCustomization extends TestBase { private readonly parameters = { color1: 0xff0000, color2: 0x00ff00, - color3: 0x0000ff, }; private customizableTexture: CustomizableTexture | null = null; @@ -26,14 +25,15 @@ class TestTextureCustomization extends TestBase { gridHelper.position.setY(-0.01); this.scene.add(gridHelper); - const ambientLight = new THREE.AmbientLight(0xCCCCCC); + const ambientLight = new THREE.AmbientLight(0xffffff); this.scene.add(ambientLight); - const enforceColors = () => { this.enforceColors(); }; + const enforceColors = () => { + this.enforceColors(); + }; this.gui = new GUI(); - this.gui.addColor(this.parameters, "color1").onChange(enforceColors); - this.gui.addColor(this.parameters, "color2").onChange(enforceColors); - this.gui.addColor(this.parameters, "color3").onChange(enforceColors); + this.gui.addColor(this.parameters, 'color1').onChange(enforceColors); + this.gui.addColor(this.parameters, 'color2').onChange(enforceColors); enforceColors(); const gltfLoader = new THREE.GLTFLoader(); @@ -42,7 +42,11 @@ class TestTextureCustomization extends TestBase { dracoLoader.setDecoderConfig({ type: 'js' }); gltfLoader.setDRACOLoader(dracoLoader); - gltfLoader.load("/resources/character/primemachin.glb", gltf => { + Promise.all([ + gltfLoader.loadAsync('/resources/character/iop_male.glb'), + new THREE.TextureLoader().loadAsync('/resources/character/color_01.png'), + new THREE.TextureLoader().loadAsync('/resources/character/color_02.png'), + ]).then(([gltf, color1Texture, color2Texture]) => { this.scene.add(gltf.scene); gltf.scene.traverse(child => { @@ -50,33 +54,33 @@ class TestTextureCustomization extends TestBase { const childMesh = child as THREE.Mesh; const childMaterial = childMesh.material as THREE.MeshPhongMaterial; - setTimeout(() => { - const childTexture = childMaterial.map; - if (!childTexture) { - throw new Error("No base texture"); - } - this.customizableTexture = new CustomizableTexture({ - width: 100, - height: 100, - baseTexture: childTexture, - additionalTextures: new Map(), - }); - this.customizableTexture.update(this.renderer); - childMaterial.map = this.customizableTexture.texture; - }, 2000); + const childTexture = childMaterial.map; + if (!childTexture) { + throw new Error('No base texture'); + } + + this.customizableTexture = new CustomizableTexture({ + width: 128, + height: 256, + baseTexture: childTexture, + additionalTextures: new Map([ + ['color1', color1Texture], + ['color2', color2Texture], + ]), + }); + this.enforceColors(); + childMaterial.map = this.customizableTexture.texture; } - }) + }); }); } - protected override update(): void { - } + protected override update(): void {} private enforceColors(): void { if (this.customizableTexture) { - // this.customizableTexture.setLayerColor("color1", new THREE.Color(this.parameters.color1)); - // this.customizableTexture.setLayerColor("color2", new THREE.Color(this.parameters.color2)); - // this.customizableTexture.setLayerColor("color3", new THREE.Color(this.parameters.color3)); + this.customizableTexture.setLayerColor('color1', new THREE.Color(this.parameters.color1)); + this.customizableTexture.setLayerColor('color2', new THREE.Color(this.parameters.color2)); this.customizableTexture.update(this.renderer); } } diff --git a/test/resources/character/char01male.png b/test/resources/character/char01male.png new file mode 100644 index 00000000..3b3bf885 Binary files /dev/null and b/test/resources/character/char01male.png differ diff --git a/test/resources/character/color_01.png b/test/resources/character/color_01.png new file mode 100644 index 00000000..4f8e6bf1 Binary files /dev/null and b/test/resources/character/color_01.png differ diff --git a/test/resources/character/color_02.png b/test/resources/character/color_02.png new file mode 100644 index 00000000..ae0d6340 Binary files /dev/null and b/test/resources/character/color_02.png differ diff --git a/test/resources/character/iop_male.glb b/test/resources/character/iop_male.glb new file mode 100644 index 00000000..3d64d407 Binary files /dev/null and b/test/resources/character/iop_male.glb differ diff --git a/test/resources/character/primemachin.glb b/test/resources/character/primemachin.glb deleted file mode 100644 index a5a03b22..00000000 Binary files a/test/resources/character/primemachin.glb and /dev/null differ