From c21c7ffb851cb79dd56ac09b65354f03a206e8a1 Mon Sep 17 00:00:00 2001 From: Alexapp Date: Fri, 6 Feb 2026 11:04:25 +0300 Subject: [PATCH 01/13] Add frontFace method support (#8447) --- src/extras/renderers/outline-renderer.js | 3 ++- src/platform/graphics/constants.js | 10 ++++++++++ src/platform/graphics/graphics-device.js | 17 ++++++++++++++++- .../graphics/null/null-graphics-device.js | 3 +++ .../graphics/webgl/webgl-graphics-device.js | 13 +++++++++++++ .../graphics/webgpu/webgpu-clear-renderer.js | 4 +++- .../graphics/webgpu/webgpu-graphics-device.js | 6 +++++- .../graphics/webgpu/webgpu-render-pipeline.js | 19 +++++++++++++------ src/scene/graphics/quad-render.js | 2 ++ src/scene/graphics/render-pass-quad.js | 3 ++- src/scene/graphics/render-pass-shader-quad.js | 8 +++++++- .../gsplat-unified/gsplat-interval-texture.js | 4 +++- .../gsplat-work-buffer-render-pass.js | 3 ++- src/scene/gsplat/gsplat-resolve-sh.js | 2 ++ src/scene/gsplat/gsplat-sog-data.js | 5 ++++- src/scene/materials/material.js | 18 +++++++++++++++++- src/scene/particle-system/gpu-updater.js | 3 ++- .../renderer/render-pass-cookie-renderer.js | 3 ++- src/scene/renderer/renderer.js | 3 +++ 19 files changed, 111 insertions(+), 18 deletions(-) diff --git a/src/extras/renderers/outline-renderer.js b/src/extras/renderers/outline-renderer.js index 41b0cb26bed..56f4c135911 100644 --- a/src/extras/renderers/outline-renderer.js +++ b/src/extras/renderers/outline-renderer.js @@ -4,7 +4,7 @@ import { BlendState } from '../../platform/graphics/blend-state.js'; import { ADDRESS_CLAMP_TO_EDGE, BLENDEQUATION_ADD, BLENDMODE_ONE_MINUS_SRC_ALPHA, BLENDMODE_SRC_ALPHA, CULLFACE_NONE, - FILTER_LINEAR, FILTER_LINEAR_MIPMAP_LINEAR, PIXELFORMAT_SRGBA8, + FILTER_LINEAR, FILTER_LINEAR_MIPMAP_LINEAR, FRONTFACE_CCW, PIXELFORMAT_SRGBA8, SEMANTIC_POSITION } from '../../platform/graphics/constants.js'; import { DepthState } from '../../platform/graphics/depth-state.js'; @@ -316,6 +316,7 @@ class OutlineRenderer { device.setDepthState(DepthState.NODEPTH); device.setCullMode(CULLFACE_NONE); + device.setFrontFaceMode(FRONTFACE_CCW); device.setBlendState(this.blendState); this.quadRenderer.render(); } diff --git a/src/platform/graphics/constants.js b/src/platform/graphics/constants.js index c5808cbcca1..9e8097d0264 100644 --- a/src/platform/graphics/constants.js +++ b/src/platform/graphics/constants.js @@ -344,6 +344,16 @@ export const CULLFACE_FRONT = 2; */ export const CULLFACE_FRONTANDBACK = 3; +/** + * The clock-wise winding. Specifies whether polygons are front- or back-facing by setting a winding orientation. + */ +export const FRONTFACE_CW = 0; + +/** + * The counter-clock-wise winding. Specifies whether polygons are front- or back-facing by setting a winding orientation. + */ +export const FRONTFACE_CCW = 1; + /** * Point sample filtering. * diff --git a/src/platform/graphics/graphics-device.js b/src/platform/graphics/graphics-device.js index a47edb79942..d9f999d824c 100644 --- a/src/platform/graphics/graphics-device.js +++ b/src/platform/graphics/graphics-device.js @@ -12,7 +12,8 @@ import { CLEARFLAG_COLOR, CLEARFLAG_DEPTH, PRIMITIVE_POINTS, PRIMITIVE_TRIFAN, SEMANTIC_POSITION, TYPE_FLOAT32, PIXELFORMAT_111110F, PIXELFORMAT_RGBA16F, PIXELFORMAT_RGBA32F, DISPLAYFORMAT_LDR, - semanticToLocation + semanticToLocation, + FRONTFACE_CCW } from './constants.js'; import { BlendState } from './blend-state.js'; import { DepthState } from './depth-state.js'; @@ -736,6 +737,7 @@ class GraphicsDevice extends EventHandler { this.blendState = new BlendState(); this.depthState = new DepthState(); this.cullMode = CULLFACE_BACK; + this.frontFaceMode = FRONTFACE_CCW; // Cached viewport and scissor dimensions this.vx = this.vy = this.vw = this.vh = 0; @@ -803,6 +805,19 @@ class GraphicsDevice extends EventHandler { Debug.assert(false); } + /** + * Controls how whether polygons are front- or back-facing by setting a winding + * orientation. The default frontFace mode is {@link FRONTFACE_CCW}. + * + * @param {number} frontFace - The front face mode to set. Can be: + * + * - {@link FRONTFACE_CW} + * - {@link FRONTFACE_CCW} + */ + setFrontFaceMode(frontFace) { + Debug.assert(false); + } + /** * Sets the specified render target on the device. If null is passed as a parameter, the back * buffer becomes the current target for all rendering operations. diff --git a/src/platform/graphics/null/null-graphics-device.js b/src/platform/graphics/null/null-graphics-device.js index dd935dde275..93eee999dea 100644 --- a/src/platform/graphics/null/null-graphics-device.js +++ b/src/platform/graphics/null/null-graphics-device.js @@ -126,6 +126,9 @@ class NullGraphicsDevice extends GraphicsDevice { setCullMode(cullMode) { } + setFrontFaceMode(frontFace) { + } + setAlphaToCoverage(state) { } diff --git a/src/platform/graphics/webgl/webgl-graphics-device.js b/src/platform/graphics/webgl/webgl-graphics-device.js index aa5fc4f17fa..5b5a45381b4 100644 --- a/src/platform/graphics/webgl/webgl-graphics-device.js +++ b/src/platform/graphics/webgl/webgl-graphics-device.js @@ -335,6 +335,11 @@ class WebglGraphicsDevice extends GraphicsDevice { gl.FRONT_AND_BACK ]; + this.glFrontFace = [ + gl.CW, + gl.CCW + ]; + this.glFilter = [ gl.NEAREST, gl.LINEAR, @@ -2511,6 +2516,14 @@ class WebglGraphicsDevice extends GraphicsDevice { } } + setFrontFaceMode(frontFaceMode) { + if (this.frontFaceMode !== frontFaceMode) { + const mode = this.glFrontFace[frontFaceMode]; + this.gl.frontFace(mode); + this.frontFaceMode = frontFaceMode; + } + } + /** * Sets the active shader to be used during subsequent draw calls. * diff --git a/src/platform/graphics/webgpu/webgpu-clear-renderer.js b/src/platform/graphics/webgpu/webgpu-clear-renderer.js index 88fcbacd8d9..ecc63a7f1db 100644 --- a/src/platform/graphics/webgpu/webgpu-clear-renderer.js +++ b/src/platform/graphics/webgpu/webgpu-clear-renderer.js @@ -5,7 +5,8 @@ import { CULLFACE_NONE, PRIMITIVE_TRISTRIP, SHADERLANGUAGE_WGSL, UNIFORMTYPE_FLOAT, UNIFORMTYPE_VEC4, BINDGROUP_MESH, CLEARFLAG_COLOR, CLEARFLAG_DEPTH, CLEARFLAG_STENCIL, - BINDGROUP_MESH_UB + BINDGROUP_MESH_UB, + FRONTFACE_CCW } from '../constants.js'; import { Shader } from '../shader.js'; import { DynamicBindGroup } from '../bind-group.js'; @@ -138,6 +139,7 @@ class WebgpuClearRenderer { uniformBuffer.endUpdate(); device.setCullMode(CULLFACE_NONE); + device.setFrontFaceMode(FRONTFACE_CCW); // render 4 vertices without vertex buffer device.setShader(this.shader); diff --git a/src/platform/graphics/webgpu/webgpu-graphics-device.js b/src/platform/graphics/webgpu/webgpu-graphics-device.js index e05a6283289..e0976c122bd 100644 --- a/src/platform/graphics/webgpu/webgpu-graphics-device.js +++ b/src/platform/graphics/webgpu/webgpu-graphics-device.js @@ -731,7 +731,7 @@ class WebgpuGraphicsDevice extends GraphicsDevice { // render pipeline pipeline = this.renderPipeline.get(primitive, vb0?.format, vb1?.format, indexBuffer?.format, this.shader, this.renderTarget, this.bindGroupFormats, this.blendState, this.depthState, this.cullMode, - this.stencilEnabled, this.stencilFront, this.stencilBack); + this.stencilEnabled, this.stencilFront, this.stencilBack, this.frontFaceMode); Debug.assert(pipeline); if (this.pipeline !== pipeline) { @@ -850,6 +850,10 @@ class WebgpuGraphicsDevice extends GraphicsDevice { this.cullMode = cullMode; } + setFrontFaceMode(frontFaceMode) { + this.frontFaceMode = frontFaceMode; + } + setAlphaToCoverage(state) { } diff --git a/src/platform/graphics/webgpu/webgpu-render-pipeline.js b/src/platform/graphics/webgpu/webgpu-render-pipeline.js index 9520faa7289..dc5409802ce 100644 --- a/src/platform/graphics/webgpu/webgpu-render-pipeline.js +++ b/src/platform/graphics/webgpu/webgpu-render-pipeline.js @@ -6,7 +6,7 @@ import { WebgpuVertexBufferLayout } from './webgpu-vertex-buffer-layout.js'; import { WebgpuDebug } from './webgpu-debug.js'; import { WebgpuPipeline } from './webgpu-pipeline.js'; import { DebugGraphics } from '../debug-graphics.js'; -import { bindGroupNames, PRIMITIVE_LINESTRIP, PRIMITIVE_TRISTRIP } from '../constants.js'; +import { bindGroupNames, FRONTFACE_CCW, PRIMITIVE_LINESTRIP, PRIMITIVE_TRISTRIP } from '../constants.js'; /** * @import { BindGroupFormat } from '../bind-group-format.js' @@ -72,6 +72,11 @@ const _cullModes = [ 'front' // CULLFACE_FRONT ]; +const _frontFaceModes = [ + 'cw', // FRONTFACE_CW + 'ccw' // FRONTFACE_CCW +]; + const _stencilOps = [ 'keep', // STENCILOP_KEEP 'zero', // STENCILOP_ZERO @@ -107,7 +112,7 @@ class CacheEntry { } class WebgpuRenderPipeline extends WebgpuPipeline { - lookupHashes = new Uint32Array(14); + lookupHashes = new Uint32Array(15); constructor(device) { super(device); @@ -141,11 +146,12 @@ class WebgpuRenderPipeline extends WebgpuPipeline { * @param {boolean} stencilEnabled - Whether stencil is enabled. * @param {StencilParameters} stencilFront - The stencil state for front faces. * @param {StencilParameters} stencilBack - The stencil state for back faces. + * @param {number} frontFaceMode - The front face mode. * @returns {GPURenderPipeline} Returns the render pipeline. * @private */ get(primitive, vertexFormat0, vertexFormat1, ibFormat, shader, renderTarget, bindGroupFormats, blendState, - depthState, cullMode, stencilEnabled, stencilFront, stencilBack) { + depthState, cullMode, stencilEnabled, stencilFront, stencilBack, frontFaceMode) { Debug.assert(bindGroupFormats.length <= 3); @@ -177,6 +183,7 @@ class WebgpuRenderPipeline extends WebgpuPipeline { lookupHashes[11] = stencilEnabled ? stencilFront.key : 0; lookupHashes[12] = stencilEnabled ? stencilBack.key : 0; lookupHashes[13] = ibFormat ?? 0; + lookupHashes[14] = frontFaceMode ?? FRONTFACE_CCW; // Default ccw const hash = hash32Fnv1a(lookupHashes); // cached pipeline @@ -206,7 +213,7 @@ class WebgpuRenderPipeline extends WebgpuPipeline { const cacheEntry = new CacheEntry(); cacheEntry.hashes = new Uint32Array(lookupHashes); cacheEntry.pipeline = this.create(primitiveTopology, ibFormat, shader, renderTarget, pipelineLayout, blendState, - depthState, vertexBufferLayout, cullMode, stencilEnabled, stencilFront, stencilBack); + depthState, vertexBufferLayout, cullMode, stencilEnabled, stencilFront, stencilBack, frontFaceMode); // add to cache if (cacheEntries) { @@ -313,7 +320,7 @@ class WebgpuRenderPipeline extends WebgpuPipeline { } create(primitiveTopology, ibFormat, shader, renderTarget, pipelineLayout, blendState, depthState, vertexBufferLayout, - cullMode, stencilEnabled, stencilFront, stencilBack) { + cullMode, stencilEnabled, stencilFront, stencilBack, frontFaceMode) { const wgpu = this.device.wgpu; @@ -330,7 +337,7 @@ class WebgpuRenderPipeline extends WebgpuPipeline { primitive: { topology: primitiveTopology, - frontFace: 'ccw', + frontFace: _frontFaceModes[frontFaceMode ?? FRONTFACE_CCW], // Default ccw cullMode: _cullModes[cullMode] }, diff --git a/src/scene/graphics/quad-render.js b/src/scene/graphics/quad-render.js index 1ce683d5d3d..2172321adae 100644 --- a/src/scene/graphics/quad-render.js +++ b/src/scene/graphics/quad-render.js @@ -30,6 +30,7 @@ const _dynamicBindGroup = new DynamicBindGroup(); * you should set up the following states as needed, otherwise previously set states will be used: * - Blend state via {@link GraphicsDevice#setBlendState} * - Cull mode via {@link GraphicsDevice#setCullMode} + * - FrontFace mode via {@link GraphicsDevice#setFrontFaceMode} * - Depth state via {@link GraphicsDevice#setDepthState} * - Stencil state via {@link GraphicsDevice#setStencilState} * @@ -47,6 +48,7 @@ const _dynamicBindGroup = new DynamicBindGroup(); * // Set up render states before rendering * app.graphicsDevice.setBlendState(BlendState.NOBLEND); * app.graphicsDevice.setCullMode(CULLFACE_NONE); + * app.graphicsDevice.setFrontFaceMode(FRONTFACE_CCW); * app.graphicsDevice.setDepthState(DepthState.NODEPTH); * app.graphicsDevice.setStencilState(null, null); * diff --git a/src/scene/graphics/render-pass-quad.js b/src/scene/graphics/render-pass-quad.js index a2e4899becb..4906e648f2b 100644 --- a/src/scene/graphics/render-pass-quad.js +++ b/src/scene/graphics/render-pass-quad.js @@ -1,4 +1,4 @@ -import { CULLFACE_NONE } from '../../platform/graphics/constants.js'; +import { CULLFACE_NONE, FRONTFACE_CCW } from '../../platform/graphics/constants.js'; import { DebugGraphics } from '../../platform/graphics/debug-graphics.js'; import { DepthState } from '../../platform/graphics/depth-state.js'; import { RenderPass } from '../../platform/graphics/render-pass.js'; @@ -20,6 +20,7 @@ class RenderPassQuad extends RenderPass { DebugGraphics.pushGpuMarker(device, `${this.name}:${this.quad.shader.name}`); device.setCullMode(CULLFACE_NONE); + device.setFrontFaceMode(FRONTFACE_CCW); device.setDepthState(DepthState.NODEPTH); device.setStencilState(null, null); diff --git a/src/scene/graphics/render-pass-shader-quad.js b/src/scene/graphics/render-pass-shader-quad.js index 3ffc45bee0c..cfcb9aa3614 100644 --- a/src/scene/graphics/render-pass-shader-quad.js +++ b/src/scene/graphics/render-pass-shader-quad.js @@ -1,6 +1,6 @@ import { QuadRender } from './quad-render.js'; import { BlendState } from '../../platform/graphics/blend-state.js'; -import { CULLFACE_NONE } from '../../platform/graphics/constants.js'; +import { CULLFACE_NONE, FRONTFACE_CCW } from '../../platform/graphics/constants.js'; import { DepthState } from '../../platform/graphics/depth-state.js'; import { RenderPass } from '../../platform/graphics/render-pass.js'; @@ -33,6 +33,11 @@ class RenderPassShaderQuad extends RenderPass { */ cullMode = CULLFACE_NONE; + /** + * The front face mode to use when rendering the quad. Defaults to {@link FRONTFACE_CCW}. + */ + frontFaceMode = FRONTFACE_CCW; + /** * A blend state to use when rendering the quad. Defaults to {@link BlendState.NOBLEND}. * @@ -106,6 +111,7 @@ class RenderPassShaderQuad extends RenderPass { const device = this.device; device.setBlendState(this.blendState); device.setCullMode(this.cullMode); + device.setFrontFaceMode(this.frontFaceMode); device.setDepthState(this.depthState); device.setStencilState(this.stencilFront, this.stencilBack); diff --git a/src/scene/gsplat-unified/gsplat-interval-texture.js b/src/scene/gsplat-unified/gsplat-interval-texture.js index bcac539d95b..8e4881dd8c5 100644 --- a/src/scene/gsplat-unified/gsplat-interval-texture.js +++ b/src/scene/gsplat-unified/gsplat-interval-texture.js @@ -1,7 +1,8 @@ import { Texture } from '../../platform/graphics/texture.js'; import { ADDRESS_CLAMP_TO_EDGE, FILTER_NEAREST, PIXELFORMAT_R32U, PIXELFORMAT_RG32U, CULLFACE_NONE, - SEMANTIC_POSITION + SEMANTIC_POSITION, + FRONTFACE_CCW } from '../../platform/graphics/constants.js'; import { RenderTarget } from '../../platform/graphics/render-target.js'; import { drawQuadWithShader } from '../graphics/quad-render-utils.js'; @@ -163,6 +164,7 @@ class GSplatIntervalTexture { scope.resolve('uActiveSplats').setValue(totalIntervalSplats); this.device.setCullMode(CULLFACE_NONE); + this.device.setFrontFaceMode(FRONTFACE_CCW); this.device.setBlendState(BlendState.NOBLEND); this.device.setDepthState(DepthState.NODEPTH); diff --git a/src/scene/gsplat-unified/gsplat-work-buffer-render-pass.js b/src/scene/gsplat-unified/gsplat-work-buffer-render-pass.js index a4d48cc6cdf..533fb079aca 100644 --- a/src/scene/gsplat-unified/gsplat-work-buffer-render-pass.js +++ b/src/scene/gsplat-unified/gsplat-work-buffer-render-pass.js @@ -6,7 +6,7 @@ import { RenderPass } from '../../platform/graphics/render-pass.js'; import { DebugGraphics } from '../../platform/graphics/debug-graphics.js'; import { BlendState } from '../../platform/graphics/blend-state.js'; import { DepthState } from '../../platform/graphics/depth-state.js'; -import { CULLFACE_NONE } from '../../platform/graphics/constants.js'; +import { CULLFACE_NONE, FRONTFACE_CCW } from '../../platform/graphics/constants.js'; /** * @import { GSplatInfo } from './gsplat-info.js' @@ -105,6 +105,7 @@ class GSplatWorkBufferRenderPass extends RenderPass { // Set up render state device.setBlendState(BlendState.NOBLEND); device.setCullMode(CULLFACE_NONE); + device.setFrontFaceMode(FRONTFACE_CCW); device.setDepthState(DepthState.NODEPTH); device.setStencilState(); diff --git a/src/scene/gsplat/gsplat-resolve-sh.js b/src/scene/gsplat/gsplat-resolve-sh.js index a3d9978cb8f..c513cc203e9 100644 --- a/src/scene/gsplat/gsplat-resolve-sh.js +++ b/src/scene/gsplat/gsplat-resolve-sh.js @@ -3,6 +3,7 @@ import { Mat4 } from '../../core/math/mat4.js'; import { BlendState } from '../../platform/graphics/blend-state.js'; import { CULLFACE_NONE, + FRONTFACE_CCW, PIXELFORMAT_RGBA8, SEMANTIC_POSITION } from '../../platform/graphics/constants.js'; @@ -290,6 +291,7 @@ class GSplatResolveSH { }); device.setCullMode(CULLFACE_NONE); + device.setFrontFaceMode(FRONTFACE_CCW); device.setDepthState(DepthState.NODEPTH); device.setStencilState(null, null); device.setBlendState(BlendState.NOBLEND); diff --git a/src/scene/gsplat/gsplat-sog-data.js b/src/scene/gsplat/gsplat-sog-data.js index 3b9872953ce..c3f3c7d6314 100644 --- a/src/scene/gsplat/gsplat-sog-data.js +++ b/src/scene/gsplat/gsplat-sog-data.js @@ -7,7 +7,7 @@ import { BlendState } from '../../platform/graphics/blend-state.js'; import { DepthState } from '../../platform/graphics/depth-state.js'; import { RenderTarget } from '../../platform/graphics/render-target.js'; import { Texture } from '../../platform/graphics/texture.js'; -import { CULLFACE_NONE, PIXELFORMAT_RGBA32U, PIXELFORMAT_RGBA8, SEMANTIC_POSITION } from '../../platform/graphics/constants.js'; +import { CULLFACE_NONE, FRONTFACE_CCW, PIXELFORMAT_RGBA32U, PIXELFORMAT_RGBA8, SEMANTIC_POSITION } from '../../platform/graphics/constants.js'; import { drawQuadWithShader } from '../../scene/graphics/quad-render-utils.js'; import { ShaderUtils } from '../shader-lib/shader-utils.js'; import glslGsplatSogReorderPS from '../shader-lib/glsl/chunks/gsplat/frag/gsplatSogReorder.js'; @@ -416,6 +416,7 @@ class GSplatSogData { }); device.setCullMode(CULLFACE_NONE); + device.setFrontFaceMode(FRONTFACE_CCW); device.setBlendState(BlendState.NOBLEND); device.setDepthState(DepthState.NODEPTH); @@ -476,6 +477,7 @@ class GSplatSogData { }); device.setCullMode(CULLFACE_NONE); + device.setFrontFaceMode(FRONTFACE_CCW); device.setBlendState(BlendState.NOBLEND); device.setDepthState(DepthState.NODEPTH); @@ -526,6 +528,7 @@ class GSplatSogData { }); device.setCullMode(CULLFACE_NONE); + device.setFrontFaceMode(FRONTFACE_CCW); device.setBlendState(BlendState.NOBLEND); device.setDepthState(DepthState.NODEPTH); diff --git a/src/scene/materials/material.js b/src/scene/materials/material.js index 7a0c387eb2f..ae56d540f08 100644 --- a/src/scene/materials/material.js +++ b/src/scene/materials/material.js @@ -6,7 +6,9 @@ import { BLENDEQUATION_ADD, BLENDEQUATION_REVERSE_SUBTRACT, BLENDEQUATION_MIN, BLENDEQUATION_MAX, CULLFACE_BACK, - SHADERLANGUAGE_GLSL + SHADERLANGUAGE_GLSL, + FRONTFACE_CW, + FRONTFACE_CCW } from '../../platform/graphics/constants.js'; import { BlendState } from '../../platform/graphics/blend-state.js'; import { DepthState } from '../../platform/graphics/depth-state.js'; @@ -167,6 +169,19 @@ class Material { */ cull = CULLFACE_BACK; + /** + * Controls how whether polygons are front- or back-facing by setting a winding + * orientation. Can be: + * + * - {@link FRONTFACE_CW}: The clock-wise winding. + * - {@link FRONTFACE_CCW}: The counter-clock-wise winding. + * + * Defaults to {@link FRONTFACE_CCW}. + * + * @type {number} + */ + frontFaceMode = FRONTFACE_CCW; + /** * Stencil parameters for front faces (default is null). * @@ -639,6 +654,7 @@ class Material { this._depthState.copy(source._depthState); this.cull = source.cull; + this.frontFace = source.frontFace; this.stencilFront = source.stencilFront?.clone(); if (source.stencilBack) { diff --git a/src/scene/particle-system/gpu-updater.js b/src/scene/particle-system/gpu-updater.js index 13c031d1282..55831b87627 100644 --- a/src/scene/particle-system/gpu-updater.js +++ b/src/scene/particle-system/gpu-updater.js @@ -3,7 +3,7 @@ import { Mat3 } from '../../core/math/mat3.js'; import { Mat4 } from '../../core/math/mat4.js'; import { Vec3 } from '../../core/math/vec3.js'; -import { CULLFACE_NONE } from '../../platform/graphics/constants.js'; +import { CULLFACE_NONE, FRONTFACE_CCW } from '../../platform/graphics/constants.js'; import { DebugGraphics } from '../../platform/graphics/debug-graphics.js'; import { BlendState } from '../../platform/graphics/blend-state.js'; import { DepthState } from '../../platform/graphics/depth-state.js'; @@ -96,6 +96,7 @@ class ParticleGPUUpdater { device.setBlendState(BlendState.NOBLEND); device.setDepthState(DepthState.NODEPTH); device.setCullMode(CULLFACE_NONE); + device.setFrontFaceMode(FRONTFACE_CCW); this.randomize(); diff --git a/src/scene/renderer/render-pass-cookie-renderer.js b/src/scene/renderer/render-pass-cookie-renderer.js index d72b4c60100..cb9dd927c0c 100644 --- a/src/scene/renderer/render-pass-cookie-renderer.js +++ b/src/scene/renderer/render-pass-cookie-renderer.js @@ -1,7 +1,7 @@ import { Debug } from '../../core/debug.js'; import { Vec4 } from '../../core/math/vec4.js'; import { Mat4 } from '../../core/math/mat4.js'; -import { CULLFACE_NONE, SEMANTIC_POSITION } from '../../platform/graphics/constants.js'; +import { CULLFACE_NONE, FRONTFACE_CCW, SEMANTIC_POSITION } from '../../platform/graphics/constants.js'; import { DebugGraphics } from '../../platform/graphics/debug-graphics.js'; import { LIGHTTYPE_DIRECTIONAL, LIGHTTYPE_OMNI } from '../constants.js'; import { ShaderUtils } from '../shader-lib/shader-utils.js'; @@ -165,6 +165,7 @@ class RenderPassCookieRenderer extends RenderPass { const device = this.device; device.setBlendState(BlendState.NOBLEND); device.setCullMode(CULLFACE_NONE); + device.setFrontFaceMode(FRONTFACE_CCW); device.setDepthState(DepthState.NODEPTH); device.setStencilState(); diff --git a/src/scene/renderer/renderer.js b/src/scene/renderer/renderer.js index eb05b7d3272..e6f7653f447 100644 --- a/src/scene/renderer/renderer.js +++ b/src/scene/renderer/renderer.js @@ -552,6 +552,9 @@ class Renderer { // Cull mode device.setCullMode(material.cull); + // Front face mode + device.setFrontFaceMode(material.frontFaceMode); + // Alpha test if (material.opacityMap) { this.opacityMapId.setValue(material.opacityMap); From 3c51fbc869a71d3428596c74a40a9883d58a269c Mon Sep 17 00:00:00 2001 From: Alexapp Date: Fri, 6 Feb 2026 11:11:03 +0300 Subject: [PATCH 02/13] Changed the order to conform to WebGPU specifications. --- src/platform/graphics/constants.js | 8 ++++---- src/platform/graphics/webgl/webgl-graphics-device.js | 4 ++-- src/platform/graphics/webgpu/webgpu-render-pipeline.js | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/platform/graphics/constants.js b/src/platform/graphics/constants.js index 9e8097d0264..f2b360a24d6 100644 --- a/src/platform/graphics/constants.js +++ b/src/platform/graphics/constants.js @@ -345,14 +345,14 @@ export const CULLFACE_FRONT = 2; export const CULLFACE_FRONTANDBACK = 3; /** - * The clock-wise winding. Specifies whether polygons are front- or back-facing by setting a winding orientation. + * The counter-clock-wise winding. Specifies whether polygons are front- or back-facing by setting a winding orientation. */ -export const FRONTFACE_CW = 0; +export const FRONTFACE_CCW = 0; /** - * The counter-clock-wise winding. Specifies whether polygons are front- or back-facing by setting a winding orientation. + * The clock-wise winding. Specifies whether polygons are front- or back-facing by setting a winding orientation. */ -export const FRONTFACE_CCW = 1; +export const FRONTFACE_CW = 1; /** * Point sample filtering. diff --git a/src/platform/graphics/webgl/webgl-graphics-device.js b/src/platform/graphics/webgl/webgl-graphics-device.js index 5b5a45381b4..253627b63ce 100644 --- a/src/platform/graphics/webgl/webgl-graphics-device.js +++ b/src/platform/graphics/webgl/webgl-graphics-device.js @@ -336,8 +336,8 @@ class WebglGraphicsDevice extends GraphicsDevice { ]; this.glFrontFace = [ - gl.CW, - gl.CCW + gl.CCW, + gl.CW ]; this.glFilter = [ diff --git a/src/platform/graphics/webgpu/webgpu-render-pipeline.js b/src/platform/graphics/webgpu/webgpu-render-pipeline.js index dc5409802ce..f581a58750a 100644 --- a/src/platform/graphics/webgpu/webgpu-render-pipeline.js +++ b/src/platform/graphics/webgpu/webgpu-render-pipeline.js @@ -73,8 +73,8 @@ const _cullModes = [ ]; const _frontFaceModes = [ - 'cw', // FRONTFACE_CW - 'ccw' // FRONTFACE_CCW + 'ccw', // FRONTFACE_CCW + 'cw' // FRONTFACE_CW ]; const _stencilOps = [ From d56c6e1766c4434141be6fa1060993c809d24522 Mon Sep 17 00:00:00 2001 From: Alexapp Date: Fri, 6 Feb 2026 11:20:19 +0300 Subject: [PATCH 03/13] Fix lint --- src/scene/materials/material.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/scene/materials/material.js b/src/scene/materials/material.js index ae56d540f08..bb2916318e2 100644 --- a/src/scene/materials/material.js +++ b/src/scene/materials/material.js @@ -7,7 +7,6 @@ import { BLENDEQUATION_MIN, BLENDEQUATION_MAX, CULLFACE_BACK, SHADERLANGUAGE_GLSL, - FRONTFACE_CW, FRONTFACE_CCW } from '../../platform/graphics/constants.js'; import { BlendState } from '../../platform/graphics/blend-state.js'; @@ -172,7 +171,7 @@ class Material { /** * Controls how whether polygons are front- or back-facing by setting a winding * orientation. Can be: - * + * * - {@link FRONTFACE_CW}: The clock-wise winding. * - {@link FRONTFACE_CCW}: The counter-clock-wise winding. * From 0b7aad8c568d897e3446fbf5fe2f57585d05c1ee Mon Sep 17 00:00:00 2001 From: Alexapp Date: Fri, 6 Feb 2026 12:31:25 +0300 Subject: [PATCH 04/13] Delete mode from name --- src/extras/renderers/outline-renderer.js | 2 +- src/platform/graphics/graphics-device.js | 8 ++++---- src/platform/graphics/null/null-graphics-device.js | 2 +- .../graphics/webgl/webgl-graphics-device.js | 8 ++++---- .../graphics/webgpu/webgpu-clear-renderer.js | 2 +- .../graphics/webgpu/webgpu-graphics-device.js | 6 +++--- .../graphics/webgpu/webgpu-render-pipeline.js | 14 +++++++------- src/scene/graphics/quad-render.js | 4 ++-- src/scene/graphics/render-pass-quad.js | 2 +- src/scene/graphics/render-pass-shader-quad.js | 6 +++--- .../gsplat-unified/gsplat-interval-texture.js | 2 +- .../gsplat-work-buffer-render-pass.js | 2 +- src/scene/gsplat/gsplat-resolve-sh.js | 2 +- src/scene/gsplat/gsplat-sog-data.js | 6 +++--- src/scene/materials/material.js | 2 +- src/scene/particle-system/gpu-updater.js | 2 +- src/scene/renderer/render-pass-cookie-renderer.js | 2 +- src/scene/renderer/renderer.js | 4 ++-- 18 files changed, 38 insertions(+), 38 deletions(-) diff --git a/src/extras/renderers/outline-renderer.js b/src/extras/renderers/outline-renderer.js index 56f4c135911..9a0a60b3694 100644 --- a/src/extras/renderers/outline-renderer.js +++ b/src/extras/renderers/outline-renderer.js @@ -316,7 +316,7 @@ class OutlineRenderer { device.setDepthState(DepthState.NODEPTH); device.setCullMode(CULLFACE_NONE); - device.setFrontFaceMode(FRONTFACE_CCW); + device.setFrontFace(FRONTFACE_CCW); device.setBlendState(this.blendState); this.quadRenderer.render(); } diff --git a/src/platform/graphics/graphics-device.js b/src/platform/graphics/graphics-device.js index d9f999d824c..d75bc894fa5 100644 --- a/src/platform/graphics/graphics-device.js +++ b/src/platform/graphics/graphics-device.js @@ -737,7 +737,7 @@ class GraphicsDevice extends EventHandler { this.blendState = new BlendState(); this.depthState = new DepthState(); this.cullMode = CULLFACE_BACK; - this.frontFaceMode = FRONTFACE_CCW; + this.frontFace = FRONTFACE_CCW; // Cached viewport and scissor dimensions this.vx = this.vy = this.vw = this.vh = 0; @@ -807,14 +807,14 @@ class GraphicsDevice extends EventHandler { /** * Controls how whether polygons are front- or back-facing by setting a winding - * orientation. The default frontFace mode is {@link FRONTFACE_CCW}. + * orientation. The default frontFace is {@link FRONTFACE_CCW}. * - * @param {number} frontFace - The front face mode to set. Can be: + * @param {number} frontFace - The front face to set. Can be: * * - {@link FRONTFACE_CW} * - {@link FRONTFACE_CCW} */ - setFrontFaceMode(frontFace) { + setFrontFace(frontFace) { Debug.assert(false); } diff --git a/src/platform/graphics/null/null-graphics-device.js b/src/platform/graphics/null/null-graphics-device.js index 93eee999dea..34fff37589b 100644 --- a/src/platform/graphics/null/null-graphics-device.js +++ b/src/platform/graphics/null/null-graphics-device.js @@ -126,7 +126,7 @@ class NullGraphicsDevice extends GraphicsDevice { setCullMode(cullMode) { } - setFrontFaceMode(frontFace) { + setFrontFace(frontFace) { } setAlphaToCoverage(state) { diff --git a/src/platform/graphics/webgl/webgl-graphics-device.js b/src/platform/graphics/webgl/webgl-graphics-device.js index 253627b63ce..ce3fe4bdb7a 100644 --- a/src/platform/graphics/webgl/webgl-graphics-device.js +++ b/src/platform/graphics/webgl/webgl-graphics-device.js @@ -2516,11 +2516,11 @@ class WebglGraphicsDevice extends GraphicsDevice { } } - setFrontFaceMode(frontFaceMode) { - if (this.frontFaceMode !== frontFaceMode) { - const mode = this.glFrontFace[frontFaceMode]; + setFrontFace(frontFace) { + if (this.frontFace !== frontFace) { + const mode = this.glFrontFace[frontFace]; this.gl.frontFace(mode); - this.frontFaceMode = frontFaceMode; + this.frontFace = frontFace; } } diff --git a/src/platform/graphics/webgpu/webgpu-clear-renderer.js b/src/platform/graphics/webgpu/webgpu-clear-renderer.js index ecc63a7f1db..1c5d970b240 100644 --- a/src/platform/graphics/webgpu/webgpu-clear-renderer.js +++ b/src/platform/graphics/webgpu/webgpu-clear-renderer.js @@ -139,7 +139,7 @@ class WebgpuClearRenderer { uniformBuffer.endUpdate(); device.setCullMode(CULLFACE_NONE); - device.setFrontFaceMode(FRONTFACE_CCW); + device.setFrontFace(FRONTFACE_CCW); // render 4 vertices without vertex buffer device.setShader(this.shader); diff --git a/src/platform/graphics/webgpu/webgpu-graphics-device.js b/src/platform/graphics/webgpu/webgpu-graphics-device.js index e0976c122bd..64d2351946e 100644 --- a/src/platform/graphics/webgpu/webgpu-graphics-device.js +++ b/src/platform/graphics/webgpu/webgpu-graphics-device.js @@ -731,7 +731,7 @@ class WebgpuGraphicsDevice extends GraphicsDevice { // render pipeline pipeline = this.renderPipeline.get(primitive, vb0?.format, vb1?.format, indexBuffer?.format, this.shader, this.renderTarget, this.bindGroupFormats, this.blendState, this.depthState, this.cullMode, - this.stencilEnabled, this.stencilFront, this.stencilBack, this.frontFaceMode); + this.stencilEnabled, this.stencilFront, this.stencilBack, this.frontFace); Debug.assert(pipeline); if (this.pipeline !== pipeline) { @@ -850,8 +850,8 @@ class WebgpuGraphicsDevice extends GraphicsDevice { this.cullMode = cullMode; } - setFrontFaceMode(frontFaceMode) { - this.frontFaceMode = frontFaceMode; + setFrontFace(frontFace) { + this.frontFace = frontFace; } setAlphaToCoverage(state) { diff --git a/src/platform/graphics/webgpu/webgpu-render-pipeline.js b/src/platform/graphics/webgpu/webgpu-render-pipeline.js index f581a58750a..833ac9b025a 100644 --- a/src/platform/graphics/webgpu/webgpu-render-pipeline.js +++ b/src/platform/graphics/webgpu/webgpu-render-pipeline.js @@ -72,7 +72,7 @@ const _cullModes = [ 'front' // CULLFACE_FRONT ]; -const _frontFaceModes = [ +const _frontFace = [ 'ccw', // FRONTFACE_CCW 'cw' // FRONTFACE_CW ]; @@ -146,12 +146,12 @@ class WebgpuRenderPipeline extends WebgpuPipeline { * @param {boolean} stencilEnabled - Whether stencil is enabled. * @param {StencilParameters} stencilFront - The stencil state for front faces. * @param {StencilParameters} stencilBack - The stencil state for back faces. - * @param {number} frontFaceMode - The front face mode. + * @param {number} frontFace - The front face. * @returns {GPURenderPipeline} Returns the render pipeline. * @private */ get(primitive, vertexFormat0, vertexFormat1, ibFormat, shader, renderTarget, bindGroupFormats, blendState, - depthState, cullMode, stencilEnabled, stencilFront, stencilBack, frontFaceMode) { + depthState, cullMode, stencilEnabled, stencilFront, stencilBack, frontFace) { Debug.assert(bindGroupFormats.length <= 3); @@ -183,7 +183,7 @@ class WebgpuRenderPipeline extends WebgpuPipeline { lookupHashes[11] = stencilEnabled ? stencilFront.key : 0; lookupHashes[12] = stencilEnabled ? stencilBack.key : 0; lookupHashes[13] = ibFormat ?? 0; - lookupHashes[14] = frontFaceMode ?? FRONTFACE_CCW; // Default ccw + lookupHashes[14] = frontFace ?? FRONTFACE_CCW; // Default ccw const hash = hash32Fnv1a(lookupHashes); // cached pipeline @@ -213,7 +213,7 @@ class WebgpuRenderPipeline extends WebgpuPipeline { const cacheEntry = new CacheEntry(); cacheEntry.hashes = new Uint32Array(lookupHashes); cacheEntry.pipeline = this.create(primitiveTopology, ibFormat, shader, renderTarget, pipelineLayout, blendState, - depthState, vertexBufferLayout, cullMode, stencilEnabled, stencilFront, stencilBack, frontFaceMode); + depthState, vertexBufferLayout, cullMode, stencilEnabled, stencilFront, stencilBack, frontFace); // add to cache if (cacheEntries) { @@ -320,7 +320,7 @@ class WebgpuRenderPipeline extends WebgpuPipeline { } create(primitiveTopology, ibFormat, shader, renderTarget, pipelineLayout, blendState, depthState, vertexBufferLayout, - cullMode, stencilEnabled, stencilFront, stencilBack, frontFaceMode) { + cullMode, stencilEnabled, stencilFront, stencilBack, frontFace) { const wgpu = this.device.wgpu; @@ -337,7 +337,7 @@ class WebgpuRenderPipeline extends WebgpuPipeline { primitive: { topology: primitiveTopology, - frontFace: _frontFaceModes[frontFaceMode ?? FRONTFACE_CCW], // Default ccw + frontFace: _frontFace[frontFace ?? FRONTFACE_CCW], // Default ccw cullMode: _cullModes[cullMode] }, diff --git a/src/scene/graphics/quad-render.js b/src/scene/graphics/quad-render.js index 2172321adae..38b42204a8f 100644 --- a/src/scene/graphics/quad-render.js +++ b/src/scene/graphics/quad-render.js @@ -30,7 +30,7 @@ const _dynamicBindGroup = new DynamicBindGroup(); * you should set up the following states as needed, otherwise previously set states will be used: * - Blend state via {@link GraphicsDevice#setBlendState} * - Cull mode via {@link GraphicsDevice#setCullMode} - * - FrontFace mode via {@link GraphicsDevice#setFrontFaceMode} + * - FrontFace via {@link GraphicsDevice#setFrontFace} * - Depth state via {@link GraphicsDevice#setDepthState} * - Stencil state via {@link GraphicsDevice#setStencilState} * @@ -48,7 +48,7 @@ const _dynamicBindGroup = new DynamicBindGroup(); * // Set up render states before rendering * app.graphicsDevice.setBlendState(BlendState.NOBLEND); * app.graphicsDevice.setCullMode(CULLFACE_NONE); - * app.graphicsDevice.setFrontFaceMode(FRONTFACE_CCW); + * app.graphicsDevice.setFrontFace(FRONTFACE_CCW); * app.graphicsDevice.setDepthState(DepthState.NODEPTH); * app.graphicsDevice.setStencilState(null, null); * diff --git a/src/scene/graphics/render-pass-quad.js b/src/scene/graphics/render-pass-quad.js index 4906e648f2b..13ace57045e 100644 --- a/src/scene/graphics/render-pass-quad.js +++ b/src/scene/graphics/render-pass-quad.js @@ -20,7 +20,7 @@ class RenderPassQuad extends RenderPass { DebugGraphics.pushGpuMarker(device, `${this.name}:${this.quad.shader.name}`); device.setCullMode(CULLFACE_NONE); - device.setFrontFaceMode(FRONTFACE_CCW); + device.setFrontFace(FRONTFACE_CCW); device.setDepthState(DepthState.NODEPTH); device.setStencilState(null, null); diff --git a/src/scene/graphics/render-pass-shader-quad.js b/src/scene/graphics/render-pass-shader-quad.js index cfcb9aa3614..2ed175c9b10 100644 --- a/src/scene/graphics/render-pass-shader-quad.js +++ b/src/scene/graphics/render-pass-shader-quad.js @@ -34,9 +34,9 @@ class RenderPassShaderQuad extends RenderPass { cullMode = CULLFACE_NONE; /** - * The front face mode to use when rendering the quad. Defaults to {@link FRONTFACE_CCW}. + * The front face to use when rendering the quad. Defaults to {@link FRONTFACE_CCW}. */ - frontFaceMode = FRONTFACE_CCW; + frontFace = FRONTFACE_CCW; /** * A blend state to use when rendering the quad. Defaults to {@link BlendState.NOBLEND}. @@ -111,7 +111,7 @@ class RenderPassShaderQuad extends RenderPass { const device = this.device; device.setBlendState(this.blendState); device.setCullMode(this.cullMode); - device.setFrontFaceMode(this.frontFaceMode); + device.setFrontFace(this.frontFace); device.setDepthState(this.depthState); device.setStencilState(this.stencilFront, this.stencilBack); diff --git a/src/scene/gsplat-unified/gsplat-interval-texture.js b/src/scene/gsplat-unified/gsplat-interval-texture.js index 8e4881dd8c5..c5b615cb170 100644 --- a/src/scene/gsplat-unified/gsplat-interval-texture.js +++ b/src/scene/gsplat-unified/gsplat-interval-texture.js @@ -164,7 +164,7 @@ class GSplatIntervalTexture { scope.resolve('uActiveSplats').setValue(totalIntervalSplats); this.device.setCullMode(CULLFACE_NONE); - this.device.setFrontFaceMode(FRONTFACE_CCW); + this.device.setFrontFace(FRONTFACE_CCW); this.device.setBlendState(BlendState.NOBLEND); this.device.setDepthState(DepthState.NODEPTH); diff --git a/src/scene/gsplat-unified/gsplat-work-buffer-render-pass.js b/src/scene/gsplat-unified/gsplat-work-buffer-render-pass.js index 533fb079aca..9fbfee18b0a 100644 --- a/src/scene/gsplat-unified/gsplat-work-buffer-render-pass.js +++ b/src/scene/gsplat-unified/gsplat-work-buffer-render-pass.js @@ -105,7 +105,7 @@ class GSplatWorkBufferRenderPass extends RenderPass { // Set up render state device.setBlendState(BlendState.NOBLEND); device.setCullMode(CULLFACE_NONE); - device.setFrontFaceMode(FRONTFACE_CCW); + device.setFrontFace(FRONTFACE_CCW); device.setDepthState(DepthState.NODEPTH); device.setStencilState(); diff --git a/src/scene/gsplat/gsplat-resolve-sh.js b/src/scene/gsplat/gsplat-resolve-sh.js index c513cc203e9..16932fcbc53 100644 --- a/src/scene/gsplat/gsplat-resolve-sh.js +++ b/src/scene/gsplat/gsplat-resolve-sh.js @@ -291,7 +291,7 @@ class GSplatResolveSH { }); device.setCullMode(CULLFACE_NONE); - device.setFrontFaceMode(FRONTFACE_CCW); + device.setFrontFace(FRONTFACE_CCW); device.setDepthState(DepthState.NODEPTH); device.setStencilState(null, null); device.setBlendState(BlendState.NOBLEND); diff --git a/src/scene/gsplat/gsplat-sog-data.js b/src/scene/gsplat/gsplat-sog-data.js index c3f3c7d6314..7f22bb1afa9 100644 --- a/src/scene/gsplat/gsplat-sog-data.js +++ b/src/scene/gsplat/gsplat-sog-data.js @@ -416,7 +416,7 @@ class GSplatSogData { }); device.setCullMode(CULLFACE_NONE); - device.setFrontFaceMode(FRONTFACE_CCW); + device.setFrontFace(FRONTFACE_CCW); device.setBlendState(BlendState.NOBLEND); device.setDepthState(DepthState.NODEPTH); @@ -477,7 +477,7 @@ class GSplatSogData { }); device.setCullMode(CULLFACE_NONE); - device.setFrontFaceMode(FRONTFACE_CCW); + device.setFrontFace(FRONTFACE_CCW); device.setBlendState(BlendState.NOBLEND); device.setDepthState(DepthState.NODEPTH); @@ -528,7 +528,7 @@ class GSplatSogData { }); device.setCullMode(CULLFACE_NONE); - device.setFrontFaceMode(FRONTFACE_CCW); + device.setFrontFace(FRONTFACE_CCW); device.setBlendState(BlendState.NOBLEND); device.setDepthState(DepthState.NODEPTH); diff --git a/src/scene/materials/material.js b/src/scene/materials/material.js index bb2916318e2..9083da8612c 100644 --- a/src/scene/materials/material.js +++ b/src/scene/materials/material.js @@ -179,7 +179,7 @@ class Material { * * @type {number} */ - frontFaceMode = FRONTFACE_CCW; + frontFace = FRONTFACE_CCW; /** * Stencil parameters for front faces (default is null). diff --git a/src/scene/particle-system/gpu-updater.js b/src/scene/particle-system/gpu-updater.js index 55831b87627..ba1f24e5c37 100644 --- a/src/scene/particle-system/gpu-updater.js +++ b/src/scene/particle-system/gpu-updater.js @@ -96,7 +96,7 @@ class ParticleGPUUpdater { device.setBlendState(BlendState.NOBLEND); device.setDepthState(DepthState.NODEPTH); device.setCullMode(CULLFACE_NONE); - device.setFrontFaceMode(FRONTFACE_CCW); + device.setFrontFace(FRONTFACE_CCW); this.randomize(); diff --git a/src/scene/renderer/render-pass-cookie-renderer.js b/src/scene/renderer/render-pass-cookie-renderer.js index cb9dd927c0c..87804053181 100644 --- a/src/scene/renderer/render-pass-cookie-renderer.js +++ b/src/scene/renderer/render-pass-cookie-renderer.js @@ -165,7 +165,7 @@ class RenderPassCookieRenderer extends RenderPass { const device = this.device; device.setBlendState(BlendState.NOBLEND); device.setCullMode(CULLFACE_NONE); - device.setFrontFaceMode(FRONTFACE_CCW); + device.setFrontFace(FRONTFACE_CCW); device.setDepthState(DepthState.NODEPTH); device.setStencilState(); diff --git a/src/scene/renderer/renderer.js b/src/scene/renderer/renderer.js index e6f7653f447..7515a9d71be 100644 --- a/src/scene/renderer/renderer.js +++ b/src/scene/renderer/renderer.js @@ -552,8 +552,8 @@ class Renderer { // Cull mode device.setCullMode(material.cull); - // Front face mode - device.setFrontFaceMode(material.frontFaceMode); + // Front face + device.setFrontFace(material.frontFace); // Alpha test if (material.opacityMap) { From dca04b4bc50481e1b73cf2a076d5ecdf3981b13f Mon Sep 17 00:00:00 2001 From: Alexapp Date: Fri, 13 Feb 2026 16:37:53 +0300 Subject: [PATCH 05/13] Remove default value in functions with private API --- src/platform/graphics/webgpu/webgpu-render-pipeline.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/platform/graphics/webgpu/webgpu-render-pipeline.js b/src/platform/graphics/webgpu/webgpu-render-pipeline.js index 833ac9b025a..c63d894967e 100644 --- a/src/platform/graphics/webgpu/webgpu-render-pipeline.js +++ b/src/platform/graphics/webgpu/webgpu-render-pipeline.js @@ -183,7 +183,7 @@ class WebgpuRenderPipeline extends WebgpuPipeline { lookupHashes[11] = stencilEnabled ? stencilFront.key : 0; lookupHashes[12] = stencilEnabled ? stencilBack.key : 0; lookupHashes[13] = ibFormat ?? 0; - lookupHashes[14] = frontFace ?? FRONTFACE_CCW; // Default ccw + lookupHashes[14] = frontFace; const hash = hash32Fnv1a(lookupHashes); // cached pipeline @@ -337,7 +337,7 @@ class WebgpuRenderPipeline extends WebgpuPipeline { primitive: { topology: primitiveTopology, - frontFace: _frontFace[frontFace ?? FRONTFACE_CCW], // Default ccw + frontFace: _frontFace[frontFace], // Default ccw cullMode: _cullModes[cullMode] }, From 3d626f44b7d582663a8297e6ec15d29e963b9c1c Mon Sep 17 00:00:00 2001 From: Alexapp Date: Fri, 13 Feb 2026 16:40:36 +0300 Subject: [PATCH 06/13] Remove comment --- src/platform/graphics/webgpu/webgpu-render-pipeline.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platform/graphics/webgpu/webgpu-render-pipeline.js b/src/platform/graphics/webgpu/webgpu-render-pipeline.js index c63d894967e..bbec736afa0 100644 --- a/src/platform/graphics/webgpu/webgpu-render-pipeline.js +++ b/src/platform/graphics/webgpu/webgpu-render-pipeline.js @@ -337,7 +337,7 @@ class WebgpuRenderPipeline extends WebgpuPipeline { primitive: { topology: primitiveTopology, - frontFace: _frontFace[frontFace], // Default ccw + frontFace: _frontFace[frontFace], cullMode: _cullModes[cullMode] }, From 3542c9a4dc07d64a8f66c8922c538c8762bfd8dd Mon Sep 17 00:00:00 2001 From: Alexapp Date: Sat, 14 Feb 2026 11:06:38 +0300 Subject: [PATCH 07/13] Add setupFrontFace method to renderer --- src/scene/renderer/forward-renderer.js | 1 + src/scene/renderer/renderer.js | 5 +++++ src/scene/renderer/shadow-renderer.js | 1 + 3 files changed, 7 insertions(+) diff --git a/src/scene/renderer/forward-renderer.js b/src/scene/renderer/forward-renderer.js index fdc96b28a24..b04c7fd4f4f 100644 --- a/src/scene/renderer/forward-renderer.js +++ b/src/scene/renderer/forward-renderer.js @@ -626,6 +626,7 @@ class ForwardRenderer extends Renderer { DebugGraphics.pushGpuMarker(device, `Node: ${drawCall.node.name}, Material: ${material.name}`); this.setupCullMode(camera._cullFaces, flipFactor, drawCall); + this.setupFrontFace(drawCall); const stencilFront = drawCall.stencilFront ?? material.stencilFront; const stencilBack = drawCall.stencilBack ?? material.stencilBack; diff --git a/src/scene/renderer/renderer.js b/src/scene/renderer/renderer.js index 7515a9d71be..e06cc138b87 100644 --- a/src/scene/renderer/renderer.js +++ b/src/scene/renderer/renderer.js @@ -505,6 +505,11 @@ class Renderer { } } + setupFrontFace(drawCall) { + const material = drawCall.material; + this.device.setCullMode(material.frontFace); + } + updateCameraFrustum(camera) { if (camera.xr && camera.xr.views.list.length) { diff --git a/src/scene/renderer/shadow-renderer.js b/src/scene/renderer/shadow-renderer.js index e3b196fcd03..36e2bd3e80d 100644 --- a/src/scene/renderer/shadow-renderer.js +++ b/src/scene/renderer/shadow-renderer.js @@ -330,6 +330,7 @@ class ShadowRenderer { } renderer.setupCullMode(true, flipFactor, meshInstance); + renderer.setupFrontFace(meshInstance); // Uniforms I (shadow): material material.setParameters(device); From c2db7bb9aa3183f563e496bd8ca8f463c60e1f6a Mon Sep 17 00:00:00 2001 From: Alexapp Date: Sat, 14 Feb 2026 11:07:53 +0300 Subject: [PATCH 08/13] Fix: 'FRONTFACE_CCW' is defined but never used --- src/platform/graphics/webgpu/webgpu-render-pipeline.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platform/graphics/webgpu/webgpu-render-pipeline.js b/src/platform/graphics/webgpu/webgpu-render-pipeline.js index bbec736afa0..6fe98e67f4d 100644 --- a/src/platform/graphics/webgpu/webgpu-render-pipeline.js +++ b/src/platform/graphics/webgpu/webgpu-render-pipeline.js @@ -6,7 +6,7 @@ import { WebgpuVertexBufferLayout } from './webgpu-vertex-buffer-layout.js'; import { WebgpuDebug } from './webgpu-debug.js'; import { WebgpuPipeline } from './webgpu-pipeline.js'; import { DebugGraphics } from '../debug-graphics.js'; -import { bindGroupNames, FRONTFACE_CCW, PRIMITIVE_LINESTRIP, PRIMITIVE_TRISTRIP } from '../constants.js'; +import { bindGroupNames, PRIMITIVE_LINESTRIP, PRIMITIVE_TRISTRIP } from '../constants.js'; /** * @import { BindGroupFormat } from '../bind-group-format.js' From a9ea2990a964630892e3e94b9caafb4f298e5e9b Mon Sep 17 00:00:00 2001 From: Alexapp Date: Sat, 14 Feb 2026 11:11:01 +0300 Subject: [PATCH 09/13] Bly setupFrontFace --- src/scene/renderer/renderer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/scene/renderer/renderer.js b/src/scene/renderer/renderer.js index e06cc138b87..7cd3a388832 100644 --- a/src/scene/renderer/renderer.js +++ b/src/scene/renderer/renderer.js @@ -507,7 +507,7 @@ class Renderer { setupFrontFace(drawCall) { const material = drawCall.material; - this.device.setCullMode(material.frontFace); + this.device.setFrontFace(material.frontFace); } updateCameraFrustum(camera) { From 485bd58cbd911aafd455c294eaa3da5925baf5cf Mon Sep 17 00:00:00 2001 From: Alexandr Shamarin Date: Sat, 14 Feb 2026 12:28:13 +0300 Subject: [PATCH 10/13] Update src/platform/graphics/constants.js Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/platform/graphics/constants.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/platform/graphics/constants.js b/src/platform/graphics/constants.js index f2b360a24d6..f64d48551fb 100644 --- a/src/platform/graphics/constants.js +++ b/src/platform/graphics/constants.js @@ -346,11 +346,15 @@ export const CULLFACE_FRONTANDBACK = 3; /** * The counter-clock-wise winding. Specifies whether polygons are front- or back-facing by setting a winding orientation. + * + * @category Graphics */ export const FRONTFACE_CCW = 0; /** * The clock-wise winding. Specifies whether polygons are front- or back-facing by setting a winding orientation. + * + * @category Graphics */ export const FRONTFACE_CW = 1; From 598919a998199f87aa471169338f2a9967b61829 Mon Sep 17 00:00:00 2001 From: Alexandr Shamarin Date: Sat, 14 Feb 2026 12:28:29 +0300 Subject: [PATCH 11/13] Update src/platform/graphics/constants.js Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/platform/graphics/constants.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/platform/graphics/constants.js b/src/platform/graphics/constants.js index f64d48551fb..91bf9eab598 100644 --- a/src/platform/graphics/constants.js +++ b/src/platform/graphics/constants.js @@ -345,6 +345,8 @@ export const CULLFACE_FRONT = 2; export const CULLFACE_FRONTANDBACK = 3; /** + * The counter-clock-wise winding. Specifies whether polygons are front- or back-facing by setting a winding orientation. + * * The counter-clock-wise winding. Specifies whether polygons are front- or back-facing by setting a winding orientation. * * @category Graphics @@ -355,6 +357,8 @@ export const FRONTFACE_CCW = 0; * The clock-wise winding. Specifies whether polygons are front- or back-facing by setting a winding orientation. * * @category Graphics + * + * @category Graphics */ export const FRONTFACE_CW = 1; From 227debf588574e482cf0113318c5bf3775962cd4 Mon Sep 17 00:00:00 2001 From: Alexandr Shamarin Date: Sat, 14 Feb 2026 12:28:40 +0300 Subject: [PATCH 12/13] Update src/platform/graphics/graphics-device.js Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/platform/graphics/graphics-device.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platform/graphics/graphics-device.js b/src/platform/graphics/graphics-device.js index 593ccc5c5d7..14409d8ee9c 100644 --- a/src/platform/graphics/graphics-device.js +++ b/src/platform/graphics/graphics-device.js @@ -824,7 +824,7 @@ class GraphicsDevice extends EventHandler { } /** - * Controls how whether polygons are front- or back-facing by setting a winding + * Controls whether polygons are front- or back-facing by setting a winding * orientation. The default frontFace is {@link FRONTFACE_CCW}. * * @param {number} frontFace - The front face to set. Can be: From acbf75b9765de69c006083e305378edbec3c5029 Mon Sep 17 00:00:00 2001 From: Alexandr Shamarin Date: Sat, 14 Feb 2026 12:28:50 +0300 Subject: [PATCH 13/13] Update src/scene/materials/material.js Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/scene/materials/material.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/scene/materials/material.js b/src/scene/materials/material.js index b336ebda731..20130a2b10c 100644 --- a/src/scene/materials/material.js +++ b/src/scene/materials/material.js @@ -169,7 +169,7 @@ class Material { cull = CULLFACE_BACK; /** - * Controls how whether polygons are front- or back-facing by setting a winding + * Controls whether polygons are front- or back-facing by setting a winding * orientation. Can be: * * - {@link FRONTFACE_CW}: The clock-wise winding.