diff --git a/_templates/material/new/shader.frag.ejs.t b/_templates/material/new/shader.frag.ejs.t index 767e597c..0ee10a70 100644 --- a/_templates/material/new/shader.frag.ejs.t +++ b/_templates/material/new/shader.frag.ejs.t @@ -7,7 +7,7 @@ precision highp float; in vec2 vUv; -out vec4 fragData[3]; +out vec4 fragData[2]; uniform sampler2D tMap; uniform vec3 uColor; diff --git a/ogl-engine/OglCanvas.tsx b/ogl-engine/OglCanvas.tsx index d4c59931..242f324e 100644 --- a/ogl-engine/OglCanvas.tsx +++ b/ogl-engine/OglCanvas.tsx @@ -40,6 +40,7 @@ export const OglCanvas: React.FC = React.memo(({ engine }) => { React.useEffect(() => { if (errorCount > 10) { + // eslint-disable-next-line no-console console.error("Too many errors, stopping rendering"); cancelAnimationFrame(frameIdRef.current); } diff --git a/ogl-engine/builders/Asteroids.ts b/ogl-engine/builders/Asteroids.ts index 03c9df48..3bb7b0b1 100644 --- a/ogl-engine/builders/Asteroids.ts +++ b/ogl-engine/builders/Asteroids.ts @@ -122,7 +122,7 @@ export class Asteroids extends Transform { ); const material = new AsteroidDustMaterial(this.engine, "prop/smoke", { - alpha: 0.25, + alpha: 0.14, color: "#ffffff", emissive: 0.0, }); diff --git a/ogl-engine/engine/BaseMesh.ts b/ogl-engine/engine/BaseMesh.ts index 80b70f2c..ada8a6b2 100644 --- a/ogl-engine/engine/BaseMesh.ts +++ b/ogl-engine/engine/BaseMesh.ts @@ -6,6 +6,7 @@ import type { Destroyable } from "@ogl-engine/types"; import type { Engine3D } from "./engine3d"; import { Light } from "./Light"; import { BoundingBox } from "./BoundingBox"; +import { RenderLayer } from "./Renderer"; const tempMat4 = new Mat4(); const tempWorldMatrix = new Mat4(); @@ -19,6 +20,7 @@ export class BaseMesh material: TMaterial; tangents = true; onDestroyCallbacks: (() => void)[] = []; + layer: RenderLayer = RenderLayer.default; constructor( engine: Engine3D, diff --git a/ogl-engine/engine/Renderer.ts b/ogl-engine/engine/Renderer.ts new file mode 100644 index 00000000..23417f0c --- /dev/null +++ b/ogl-engine/engine/Renderer.ts @@ -0,0 +1,90 @@ +import type { Mesh, Transform } from "ogl"; +import { Renderer as BaseRenderer, Vec3 } from "ogl"; + +const tempVec3 = new Vec3(); + +export const RenderLayer = { + default: 0, + ui: 1, +} as const; +// eslint-disable-next-line no-redeclare +export type RenderLayer = (typeof RenderLayer)[keyof typeof RenderLayer]; + +function isDrawableMesh(obj: Transform): obj is Mesh { + return (obj as Mesh).draw !== undefined; +} + +export class Renderer extends BaseRenderer { + private currentLayer: RenderLayer = RenderLayer.default; + + setRenderLayer(layer: RenderLayer) { + this.currentLayer = layer; + } + + getRenderList({ + scene, + camera, + frustumCull, + sort, + }: Parameters[0]) { + let renderList: Mesh[] = []; + + if (camera && frustumCull) camera.updateFrustum(); + + // Get visible + scene.traverse((node) => { + if (!node.visible) return true; + if (!isDrawableMesh(node)) return undefined; + + if (frustumCull && node.frustumCulled && camera) { + if (!camera.frustumIntersectsMesh(node)) return undefined; + } + + if ( + (node as any).layer === this.currentLayer || + (this.currentLayer === RenderLayer.default && + (node as any).layer === undefined) + ) { + renderList.push(node); + } + + return undefined; + }); + + if (sort) { + const opaque: Mesh[] = []; + const transparent: Mesh[] = []; // depthTest true + const ui: Mesh[] = []; // depthTest false + + renderList.forEach((node) => { + // Split into the 3 render groups + if (!node.program.transparent) { + opaque.push(node); + } else if (node.program.depthTest) { + transparent.push(node); + } else { + ui.push(node); + } + + (node as any).zDepth = 0; + + // Only calculate z-depth if renderOrder unset and depthTest is true + if (node.renderOrder !== 0 || !node.program.depthTest || !camera) + return; + + // update z-depth + node.worldMatrix.getTranslation(tempVec3); + tempVec3.applyMatrix4(camera.projectionViewMatrix); + (node as any).zDepth = tempVec3.z; + }); + + opaque.sort(this.sortOpaque); + transparent.sort(this.sortTransparent); + ui.sort(this.sortUI); + + renderList = opaque.concat(transparent, ui); + } + + return renderList; + } +} diff --git a/ogl-engine/engine/Scene.ts b/ogl-engine/engine/Scene.ts index 4a05ad68..2a1b09d3 100644 --- a/ogl-engine/engine/Scene.ts +++ b/ogl-engine/engine/Scene.ts @@ -114,8 +114,19 @@ export class TacticalMapScene extends Scene { label: "God Rays Exposure", } ); + this.pane.addBinding(this.engine.kawase, "iterations", { + label: "Bloom Iterations", + min: 2, + max: 5, + step: 1, + }); + this.pane.addBinding(this.engine.kawase, "samplePosMult", { + label: "Bloom Spread", + min: 0, + max: 5, + }); this.pane.addBinding( - this.engine.uniforms.env.postProcessing.bloom.uBloomStrength, + this.engine.uniforms.env.postProcessing.bloom.uGain, "value", { label: "Bloom Strength", @@ -187,13 +198,6 @@ export class TacticalMapScene extends Scene { max: 1, } ); - this.pane - .addButton({ - title: "Toggle UI", - }) - .on("click", () => - this.engine.togglePostProcessingPass("composite", "ui") - ); } destroy() { diff --git a/ogl-engine/engine/engine.ts b/ogl-engine/engine/engine.ts index 5b29b4b8..dca4cd62 100644 --- a/ogl-engine/engine/engine.ts +++ b/ogl-engine/engine/engine.ts @@ -1,13 +1,18 @@ -import { Renderer } from "ogl"; import { PubSub } from "@core/utils/pubsub"; import type { Scene } from "./Scene"; import type { Camera } from "./Camera"; +import { Renderer } from "./Renderer"; type InitEvent = { type: "init" }; type UpdateEvent = { type: "update"; delta: number }; type ErrorEvent = { type: "error"; error: Error }; type EngineEvent = InitEvent | UpdateEvent | ErrorEvent; +export type RenderingContext = WebGL2RenderingContext & { + renderer: Renderer; + canvas: HTMLCanvasElement; +}; + export abstract class Engine { public hooks: PubSub; @@ -40,6 +45,7 @@ export abstract class Engine { canvas: canvas as any, // Works for both HTMLCanvasElement and OffscreenCanvas dpr: this.dpr, antialias: true, + webgl: 2, }); } @@ -86,7 +92,7 @@ export abstract class Engine { } get gl() { - return this.renderer.gl; + return this.renderer.gl as RenderingContext; } get delta() { diff --git a/ogl-engine/engine/engine2d.ts b/ogl-engine/engine/engine2d.ts index fcfc372f..959ff5d5 100644 --- a/ogl-engine/engine/engine2d.ts +++ b/ogl-engine/engine/engine2d.ts @@ -1,9 +1,10 @@ -import { Mesh, Plane, Program, Renderer } from "ogl"; +import { Mesh, Plane, Program } from "ogl"; import settings from "@core/settings"; import { gameStore } from "@ui/state/game"; import { Scene, StrategicMapScene } from "./Scene"; import { Camera } from "./Camera"; import { Engine } from "./engine"; +import { Renderer } from "./Renderer"; // It's not really 2D, but it's going to be used as 2D export class Engine2D extends Engine { diff --git a/ogl-engine/engine/engine3d.ts b/ogl-engine/engine/engine3d.ts index 3f7ce96f..149a2233 100644 --- a/ogl-engine/engine/engine3d.ts +++ b/ogl-engine/engine/engine3d.ts @@ -1,4 +1,4 @@ -import type { Pass, Transform } from "ogl"; +import type { Pass, RenderTargetOptions, Transform } from "ogl"; import { Post, Texture, Vec2, Vec3, RenderTarget } from "ogl"; import settings from "@core/settings"; import { EntityMesh } from "@ui/components/TacticalMap/EntityMesh"; @@ -6,28 +6,41 @@ import { gameStore } from "@ui/state/game"; import { sortBy } from "@fxts/core"; import { getPane } from "@ui/context/Pane"; import brightPassFragment from "../post/brightPass.frag.glsl"; -import blurFragment from "../post/blur.frag.glsl"; import fxaaFragment from "../post/fxaa.frag.glsl"; import vignetteFragment from "../post/vignette.frag.glsl"; -import uiFragment from "../post/ui.frag.glsl"; import godraysFragment from "../post/godrays.frag.glsl"; import tonemappingFragment from "../post/tonemapping.frag.glsl"; import compositeFragment from "../post/composite.frag.glsl"; import type { Light } from "./Light"; import { dummyLight } from "./Light"; import { Camera } from "./Camera"; +import type { RenderingContext } from "./engine"; import { Engine } from "./engine"; import { TacticalMapScene, type Scene } from "./Scene"; import { OnBeforeRenderTask } from "./task"; import { RenderingPerformance } from "./performance"; +import { createEngine3DUniforms, type Engine3DUniforms } from "./uniforms3d"; +import { DualKawasePost } from "./post/dualKawase"; +import { RenderLayer } from "./Renderer"; -const bloomSize = 1.2; const lightsNum = 16; -const bloomPasses = 8; -const bloomDpr = 4; // 1/4 of the canvas size const tempVec3 = new Vec3(); +function createHiDefRenderTarget( + gl: RenderingContext, + opts: Partial = {} +) { + return new RenderTarget(gl, { + type: gl.HALF_FLOAT, + format: gl.RGBA, + internalFormat: gl.RGBA16F, + minFilter: gl.LINEAR, + magFilter: gl.LINEAR, + ...opts, + }); +} + export class Engine3D extends Engine { canvas: HTMLCanvasElement; postProcessing = true; @@ -38,42 +51,8 @@ export class Engine3D extends Engine { fxOwners: Record = {}; - uniforms: { - env: { - camera: { - scale: { value: number }; - }; - ambient: { value: Vec3 }; - lights: Light["uniforms"][]; - tEnvMap: { value: Texture }; - postProcessing: { - godrays: { - uDensity: { value: number }; - uWeight: { value: number }; - uDecay: { value: number }; - uExposure: { value: number }; - }; - bloom: { - uBloomStrength: { value: number }; - }; - vignette: { - uStrength: { value: number }; - uSmoothness: { value: number }; - uOffset: { value: number }; - }; - tonemapping: { - uGamma: { value: number }; - uSaturation: { value: number }; - uContrast: { value: number }; - uExposure: { value: number }; - uMap: { value: number }; - }; - }; - }; - resolution: { base: { value: Vec2 }; bloom: { value: Vec2 } }; - uTime: { value: number }; - uSeed: { value: number }; - }; + uniforms: Engine3DUniforms; + kawase = new DualKawasePost(); private lights: Light[] = []; private postProcessingLayers: Record< @@ -94,7 +73,7 @@ export class Engine3D extends Engine { constructor() { super(); - this.initUniforms(); + this.uniforms = createEngine3DUniforms(); this.initPane(); } @@ -106,22 +85,13 @@ export class Engine3D extends Engine { this.gl.getExtension("OES_texture_half_float"); this.gl.getExtension("OES_texture_half_float_linear"); - const gl = this.renderer.gl; this.camera = new Camera(this); this.camera.position.set(50, 50, 50); this.camera.lookAt([0, 0, 0]); this.camera.near = settings.camera.near; this.camera.far = settings.camera.far; - this.renderTarget = new RenderTarget(gl, { - // Color, bloom and UI - color: 3, - // @ts-expect-error type resolution fails for some reason - type: gl.HALF_FLOAT, - format: gl.RGBA, - // @ts-expect-error type resolution fails for some reason - internalFormat: gl.RGBA16F, - }); + this.renderTarget = createHiDefRenderTarget(this.gl, { color: 2 }); this.uniforms.env.tEnvMap.value = new Texture(this.renderer.gl); @@ -132,7 +102,6 @@ export class Engine3D extends Engine { }; initPane() { - console.log("Initializing pane"); const folder = getPane().addOrReplaceFolder({ title: "Renderer", expanded: true, @@ -168,61 +137,17 @@ export class Engine3D extends Engine { }); } - private initUniforms() { - this.uniforms = { - env: { - ambient: { value: new Vec3(0) }, - camera: { - scale: { value: 1 }, - }, - lights: [], - tEnvMap: { value: null! }, - postProcessing: { - godrays: { - uDensity: { value: 0.8 }, - uWeight: { value: 1 }, - uDecay: { value: 0.86 }, - uExposure: { value: 0.9 }, - }, - bloom: { - uBloomStrength: { value: 0.4 }, - }, - vignette: { - uStrength: { value: 0.48 }, - uSmoothness: { value: 0.19 }, - uOffset: { value: -0.41 }, - }, - tonemapping: { - uGamma: { value: 1.08 }, - uContrast: { value: 1 }, - uSaturation: { value: 1 }, - uExposure: { value: 1.03 }, - uMap: { value: 1 }, - }, - }, - }, - resolution: { - base: { value: new Vec2() }, - bloom: { value: new Vec2() }, - }, - uTime: { value: 0 }, - uSeed: { value: Math.random() }, - }; - } - private initPostProcessing = () => { - const gl = this.renderer.gl; - this.postProcessingLayers = { composite: { - post: new Post(gl, { + post: new Post(this.gl, { dpr: this.dpr, }), passes: {}, }, bloom: { - post: new Post(gl, { - dpr: this.dpr / bloomDpr, + post: new Post(this.gl, { + dpr: this.dpr, targetOnly: true, depth: false, }), @@ -230,52 +155,45 @@ export class Engine3D extends Engine { }, }; + this.kawase.init(this.postProcessingLayers.bloom.post); + + this.postProcessingLayers.composite.post.fbo.read = createHiDefRenderTarget( + this.gl, + { depth: false } + ); + this.postProcessingLayers.composite.post.fbo.write = + createHiDefRenderTarget(this.gl, { depth: false }); + + this.postProcessingLayers.bloom.post.fbo.read = createHiDefRenderTarget( + this.gl, + { depth: false } + ); + this.postProcessingLayers.bloom.post.fbo.write = createHiDefRenderTarget( + this.gl, + { depth: false } + ); + + this.postProcessingLayers.composite.post.resize(); + this.postProcessingLayers.bloom.post.resize(); + this.postProcessingLayers.bloom.passes.bright = { pass: this.postProcessingLayers.bloom.post.addPass({ fragment: brightPassFragment, uniforms: { uThreshold: { value: 0.995 }, - tEmissive: { value: new Texture(gl) }, + tEmissive: { value: null }, }, }), enabled: true, }; - this.postProcessingLayers.bloom.passes.horizontalBloom = { - enabled: true, - pass: this.postProcessingLayers.bloom.post.addPass({ - fragment: blurFragment, - uniforms: { - uResolution: this.uniforms.resolution.bloom, - uDirection: { value: new Vec2(bloomSize * 3, 0) }, - }, - }), - }; - this.postProcessingLayers.bloom.passes.verticalBloom = { - enabled: true, - pass: this.postProcessingLayers.bloom.post.addPass({ - fragment: blurFragment, - uniforms: { - uResolution: this.uniforms.resolution.bloom, - uDirection: { value: new Vec2(0, bloomSize) }, - }, - }), - }; - for (let i = 0; i < bloomPasses; i++) { - this.postProcessingLayers.bloom.post.passes.push( - this.postProcessingLayers.bloom.passes.horizontalBloom.pass, - this.postProcessingLayers.bloom.passes.verticalBloom.pass - ); - } - this.postProcessingLayers.composite.passes.composite = { enabled: true, pass: this.postProcessingLayers.composite.post.addPass({ fragment: compositeFragment, uniforms: { - uResolution: this.uniforms.resolution.base, tBloom: this.postProcessingLayers.bloom.post.uniform, - uBloomStrength: this.uniforms.env.postProcessing.bloom.uBloomStrength, + uBloomStrength: this.uniforms.env.postProcessing.bloom.uGain, }, }), }; @@ -298,7 +216,10 @@ export class Engine3D extends Engine { enabled: true, pass: this.postProcessingLayers.composite.post.addPass({ fragment: tonemappingFragment, - uniforms: this.uniforms.env.postProcessing.tonemapping, + uniforms: { + ...this.uniforms.env.postProcessing.tonemapping, + uTime: this.uniforms.uTime, + }, }), }; this.postProcessingLayers.composite.passes.fxaa = { @@ -322,15 +243,6 @@ export class Engine3D extends Engine { }, }), }; - this.postProcessingLayers.composite.passes.ui = { - enabled: true, - pass: this.postProcessingLayers.composite.post.addPass({ - fragment: uiFragment, - uniforms: { - tUi: this.renderTarget.textures[2], - }, - }), - }; }; private get vignettePass() { @@ -377,6 +289,7 @@ export class Engine3D extends Engine { } private renderSimple = () => { + this.renderer.setRenderLayer(RenderLayer.default); this.renderer.render({ scene: this.scene, camera: this.camera, @@ -384,6 +297,8 @@ export class Engine3D extends Engine { }; private renderComposite() { + this.renderer.setRenderLayer(RenderLayer.default); + // Disable compositePass pass, so this post will just render the scene for now for (const pass of this.postProcessingLayers.composite.post.passes) { pass.enabled = false; @@ -404,11 +319,11 @@ export class Engine3D extends Engine { pass.uniforms.tEmissive.value = this.renderTarget.textures[1]; } } - this.postProcessingLayers.composite.passes.ui.pass.uniforms.tUi.value = - this.renderTarget.textures[2]; this.postProcessingLayers.bloom.post.render({ texture: this.renderTarget.textures[0], }); + this.kawase.render(this.postProcessingLayers.bloom.post); + // Re-enable composite pass this.compositePass.pass.enabled = true; this.postProcessingLayers.composite.passes.tonemapping.pass.enabled = @@ -417,8 +332,6 @@ export class Engine3D extends Engine { this.fxaaPass.pass.enabled = this.fxaa; this.vignettePass.pass.enabled = this.postProcessingLayers.composite.passes.vignette.enabled; - this.postProcessingLayers.composite.passes.ui.pass.enabled = - this.postProcessingLayers.composite.passes.ui.enabled; // Allow post to render to canvas upon its last pass this.postProcessingLayers.composite.post.targetOnly = false; @@ -427,6 +340,13 @@ export class Engine3D extends Engine { this.postProcessingLayers.composite.post.render({ texture: this.renderTarget.textures[0], }); + + this.renderer.setRenderLayer(RenderLayer.ui); + this.renderer.render({ + scene: this.scene, + camera: this.camera, + clear: false, + }); } resize = () => { diff --git a/ogl-engine/engine/post/dualKawase/downsample.frag.glsl b/ogl-engine/engine/post/dualKawase/downsample.frag.glsl new file mode 100644 index 00000000..639cabcf --- /dev/null +++ b/ogl-engine/engine/post/dualKawase/downsample.frag.glsl @@ -0,0 +1,21 @@ +#version 300 es +precision highp float; + +in vec2 vUv; +out vec4 fragColor; + +uniform sampler2D tMap; +uniform vec2 uTexel; +uniform float uOffset; + +void main() { + vec2 halfpixel = uTexel * 0.5f; + + vec4 sum = texture(tMap, vUv) * 4.0f; + sum += texture(tMap, vUv - halfpixel.xy * uOffset); + sum += texture(tMap, vUv + halfpixel.xy * uOffset); + sum += texture(tMap, vUv + vec2(halfpixel.x, -halfpixel.y) * uOffset); + sum += texture(tMap, vUv - vec2(halfpixel.x, -halfpixel.y) * uOffset); + + fragColor = sum / 8.0f; +} \ No newline at end of file diff --git a/ogl-engine/engine/post/dualKawase/index.ts b/ogl-engine/engine/post/dualKawase/index.ts new file mode 100644 index 00000000..6d1d5534 --- /dev/null +++ b/ogl-engine/engine/post/dualKawase/index.ts @@ -0,0 +1,124 @@ +import type { Post, Texture } from "ogl"; +import { Vec2, RenderTarget, Mesh, Program } from "ogl"; +import type { Uniforms } from "@ogl-engine/materials/material"; +import type { RenderingContext } from "@ogl-engine/engine/engine"; +import vertex from "./shader.vert.glsl"; +import downsampleFragment from "./downsample.frag.glsl"; +import upsampleFragment from "./upsample.frag.glsl"; + +export type BloomUniforms = Uniforms<{ + tMap: Texture; + uTexel: Vec2; + uOffset: number; +}>; + +const maxIterations = 6; + +export class DualKawasePost { + downsample: Program; + upsample: Program; + iterations = 4; + mesh: Mesh; + levels: RenderTarget[] = []; + temps: RenderTarget[] = []; + samplePosMult = 1.625; + width = 0; + height = 0; + + init(post: Post) { + const gl = post.gl as RenderingContext; + + const uniforms: BloomUniforms = { + tMap: { value: null as any }, + uTexel: { value: new Vec2() }, + uOffset: { value: this.samplePosMult }, + }; + + this.downsample = new Program(gl, { + vertex, + fragment: downsampleFragment, + uniforms, + }); + this.upsample = new Program(gl, { + vertex, + fragment: upsampleFragment, + uniforms, + }); + + this.mesh = new Mesh(gl, { + geometry: post.geometry, + }); + + for (let i = 0; i < maxIterations; i++) { + const opts = { + type: gl.HALF_FLOAT, + format: gl.RGBA, + internalFormat: gl.RGBA16F, + depth: false, + }; + this.levels.push(new RenderTarget(gl, opts)); + this.temps.push(new RenderTarget(gl, opts)); + } + } + + resize(post: Post) { + this.width = post.fbo.read.width; + this.height = post.fbo.read.height; + + for (let i = 0; i < maxIterations; i++) { + this.levels[i].setSize(this.width >> (i + 1), this.height >> (i + 1)); + this.temps[i].setSize(this.width >> (i + 1), this.height >> (i + 1)); + } + } + + swap(i: number) { + const t = this.levels[i]; + this.levels[i] = this.temps[i]; + this.temps[i] = t; + } + + render(post: Post) { + if ( + this.width !== post.fbo.read.width || + this.height !== post.fbo.read.height + ) { + this.resize(post); + } + + this.mesh.program = this.downsample; + for (let i = 0; i < this.iterations; i++) { + const tMap = i === 0 ? post.fbo.read : this.levels[i - 1]; + this.downsample.uniforms.tMap.value = tMap.texture; + this.downsample.uniforms.uOffset.value = this.samplePosMult; + this.downsample.uniforms.uTexel.value.set( + 1 / tMap.width, + 1 / tMap.height + ); + post.gl.renderer.render({ + scene: this.mesh, + target: this.levels[i], + clear: true, + }); + } + + this.mesh.program = this.upsample; + for (let i = this.iterations - 2; i >= 0; i--) { + this.upsample.uniforms.tMap.value = this.levels[i + 1].texture; + this.upsample.uniforms.uOffset.value = this.samplePosMult; + this.upsample.uniforms.uTexel.value.set( + 1 / this.levels[i].width, + 1 / this.levels[i].height + ); + post.gl.renderer.render({ + scene: this.mesh, + target: i === 0 ? post.fbo.write : this.temps[i], + clear: true, + }); + + this.swap(i); + } + + post.fbo.swap(); + post.uniform.value = post.fbo.read.texture; + } +} diff --git a/ogl-engine/engine/post/dualKawase/shader.vert.glsl b/ogl-engine/engine/post/dualKawase/shader.vert.glsl new file mode 100644 index 00000000..49952bdd --- /dev/null +++ b/ogl-engine/engine/post/dualKawase/shader.vert.glsl @@ -0,0 +1,12 @@ +#version 300 es +precision highp float; + +in vec2 uv; +in vec2 position; + +out vec2 vUv; + +void main() { + vUv = uv; + gl_Position = vec4(position, 0.f, 1.f); +} \ No newline at end of file diff --git a/ogl-engine/engine/post/dualKawase/upsample.frag.glsl b/ogl-engine/engine/post/dualKawase/upsample.frag.glsl new file mode 100644 index 00000000..b11516cf --- /dev/null +++ b/ogl-engine/engine/post/dualKawase/upsample.frag.glsl @@ -0,0 +1,29 @@ +#version 300 es +precision highp float; + +in vec2 vUv; +out vec4 fragColor; + +uniform vec2 uTexel; +uniform float uOffset; +uniform sampler2D tMap; + +void main() { + + vec2 halfpixel = uTexel * 0.5f; + vec2 o = halfpixel * uOffset; + + vec4 color = vec4(0.0f); + + color += texture(tMap, vUv + vec2(-o.x * 2.0f, 0.0f)); + color += texture(tMap, vUv + vec2(o.x * 2.0f, 0.0f)); + color += texture(tMap, vUv + vec2(0.0f, -o.y * 2.0f)); + color += texture(tMap, vUv + vec2(0.0f, o.y * 2.0f)); + + color += texture(tMap, vUv + vec2(-o.x, o.y)) * 2.0f; + color += texture(tMap, vUv + vec2(o.x, o.y)) * 2.0f; + color += texture(tMap, vUv + vec2(-o.x, -o.y)) * 2.0f; + color += texture(tMap, vUv + vec2(o.x, -o.y)) * 2.0f; + + fragColor = (color / 12.0f); +} \ No newline at end of file diff --git a/ogl-engine/engine/uniforms3d.ts b/ogl-engine/engine/uniforms3d.ts new file mode 100644 index 00000000..037f333d --- /dev/null +++ b/ogl-engine/engine/uniforms3d.ts @@ -0,0 +1,82 @@ +import type { Texture } from "ogl"; +import { Vec2, Vec3 } from "ogl"; +import type { Light } from "./Light"; + +export type Engine3DUniforms = { + env: { + camera: { + scale: { value: number }; + }; + ambient: { value: Vec3 }; + lights: Light["uniforms"][]; + tEnvMap: { value: Texture }; + postProcessing: { + godrays: { + uDensity: { value: number }; + uWeight: { value: number }; + uDecay: { value: number }; + uExposure: { value: number }; + }; + bloom: { + uGain: { value: number }; + }; + vignette: { + uStrength: { value: number }; + uSmoothness: { value: number }; + uOffset: { value: number }; + }; + tonemapping: { + uGamma: { value: number }; + uSaturation: { value: number }; + uContrast: { value: number }; + uExposure: { value: number }; + uMap: { value: number }; + }; + }; + }; + resolution: { base: { value: Vec2 }; bloom: { value: Vec2 } }; + uTime: { value: number }; + uSeed: { value: number }; +}; + +export function createEngine3DUniforms(): Engine3DUniforms { + return { + env: { + ambient: { value: new Vec3(0) }, + camera: { + scale: { value: 1 }, + }, + lights: [], + tEnvMap: { value: null! }, + postProcessing: { + godrays: { + uDensity: { value: 0.8 }, + uWeight: { value: 1 }, + uDecay: { value: 0.86 }, + uExposure: { value: 0.9 }, + }, + bloom: { + uGain: { value: 0.85 }, + }, + vignette: { + uStrength: { value: 0.51 }, + uSmoothness: { value: 0.45 }, + uOffset: { value: -0.53 }, + }, + tonemapping: { + uGamma: { value: 1.08 }, + uContrast: { value: 1 }, + uSaturation: { value: 1 }, + uExposure: { value: 1.03 }, + uMap: { value: 1 }, + }, + }, + }, + resolution: { + base: { value: new Vec2() }, + bloom: { value: new Vec2() }, + }, + uTime: { value: 0 }, + uSeed: { value: Math.random() }, + }; +} diff --git a/ogl-engine/materials/AsteroidNew/shader.frag.glsl b/ogl-engine/materials/AsteroidNew/shader.frag.glsl index 7b060846..feda3455 100644 --- a/ogl-engine/materials/AsteroidNew/shader.frag.glsl +++ b/ogl-engine/materials/AsteroidNew/shader.frag.glsl @@ -24,7 +24,7 @@ uniform Light lights[16]; uniform samplerCube tEnvMap; uniform float uCameraScale; -out vec4 fragData[3]; +out vec4 fragData[2]; #define EPSILON 0.001f #pragma glslify: pbr = require("./ogl-engine/shader/pbr", lights = lights, cameraPosition = cameraPosition, worldPosition = worldPosition); diff --git a/ogl-engine/materials/AsteroidRock/shader.frag.glsl b/ogl-engine/materials/AsteroidRock/shader.frag.glsl index 6dc687ff..084d5a41 100644 --- a/ogl-engine/materials/AsteroidRock/shader.frag.glsl +++ b/ogl-engine/materials/AsteroidRock/shader.frag.glsl @@ -21,7 +21,7 @@ uniform Light lights[16]; uniform samplerCube tEnvMap; uniform float uCameraScale; -out vec4 fragData[3]; +out vec4 fragData[2]; #define EPSILON 0.001f #pragma glslify: pbr = require("./ogl-engine/shader/pbr", lights = lights, cameraPosition = cameraPosition, worldPosition = worldPosition); diff --git a/ogl-engine/materials/Grid/shader.frag.glsl b/ogl-engine/materials/Grid/shader.frag.glsl index 0e4e2abe..12a6f975 100644 --- a/ogl-engine/materials/Grid/shader.frag.glsl +++ b/ogl-engine/materials/Grid/shader.frag.glsl @@ -4,7 +4,7 @@ precision highp float; in vec2 vUv; in vec3 vPosition; -out vec4 fragData[3]; +out vec4 fragData[2]; uniform vec3 cameraPosition; uniform mat4 viewMatrix; diff --git a/ogl-engine/materials/asteroidDust/shader.frag.glsl b/ogl-engine/materials/asteroidDust/shader.frag.glsl index 09f2d8a7..7da9e4a1 100644 --- a/ogl-engine/materials/asteroidDust/shader.frag.glsl +++ b/ogl-engine/materials/asteroidDust/shader.frag.glsl @@ -7,7 +7,7 @@ in vec2 vUv; in float vFragDepth; flat in float vInstanceIndex; -out vec4 fragData[3]; +out vec4 fragData[2]; uniform sampler2D tMap; uniform vec3 uColor; @@ -24,7 +24,7 @@ void main() { float l = max(0.f, value - 0.1f); float alpha = uAlpha * l * clamp(triangle(uTime * (mod(vInstanceIndex, 9.f) + 0.1f) * animSpeed + vInstanceIndex * 0.01f), 0.15f, 0.9f); - if(alpha < 1.f / 255.f) + if(alpha < 0.1f / 255.f) discard; fragData[0] = vec4(uColor, alpha); diff --git a/ogl-engine/materials/atmosphere/shader.frag.glsl b/ogl-engine/materials/atmosphere/shader.frag.glsl index e1a28827..7c6e424a 100644 --- a/ogl-engine/materials/atmosphere/shader.frag.glsl +++ b/ogl-engine/materials/atmosphere/shader.frag.glsl @@ -9,7 +9,7 @@ in vec3 vWorldNormal; in vec3 worldPosition; in float vFragDepth; -out vec4 fragData[3]; +out vec4 fragData[2]; uniform vec3 cameraPosition; uniform float uCameraScale; diff --git a/ogl-engine/materials/base.vert.glsl b/ogl-engine/materials/base.vert.glsl index 528f9fea..8623ee0f 100644 --- a/ogl-engine/materials/base.vert.glsl +++ b/ogl-engine/materials/base.vert.glsl @@ -1,29 +1,47 @@ #version 300 es precision highp float; +#pragma defines + in vec2 uv; in vec3 position; in vec3 normal; +#ifdef USE_INSTANCING +in mat4 instanceMatrix; +in mat3 instanceNormalMatrix; +in float instanceIndex; +#endif + out vec2 vUv; out float vFragDepth; out vec3 worldPosition; out vec3 vNormal; out vec3 vViewDirection; +#ifdef USE_INSTANCING +out flat float vInstanceIndex; +#endif + uniform mat4 modelMatrix; uniform mat4 viewMatrix; uniform mat4 projectionMatrix; uniform vec3 cameraPosition; void main() { - vec4 mPosition = modelMatrix * vec4(position, 1.0f); - worldPosition = mPosition.xyz; + #ifdef USE_INSTANCING + mat4 toWorldPosition = modelMatrix * instanceMatrix; + vInstanceIndex = instanceIndex; + #else + mat4 toWorldPosition = modelMatrix; + #endif + vec4 wPosition = toWorldPosition * vec4(position, 1.0f); + worldPosition = wPosition.xyz; vUv = uv; - vNormal = normalize(modelMatrix * vec4(normal, 0.0f)).xyz; + vNormal = normalize(toWorldPosition * vec4(normal, 0.0f)).xyz; vViewDirection = normalize(cameraPosition - worldPosition.xyz); - gl_Position = projectionMatrix * viewMatrix * mPosition; + gl_Position = projectionMatrix * viewMatrix * wPosition; vFragDepth = 1.f + gl_Position.w; } diff --git a/ogl-engine/materials/color/color.ts b/ogl-engine/materials/color/color.ts index a7607e51..2cd1f7fa 100644 --- a/ogl-engine/materials/color/color.ts +++ b/ogl-engine/materials/color/color.ts @@ -29,9 +29,6 @@ export class ColorMaterial extends Material { super(engine); const defines: Record = {}; - if (opts.layer) { - defines.LAYER = opts.layer; - } this.createProgram(vertex, fragment, defines, { cullFace: false, diff --git a/ogl-engine/materials/color/shader.frag.glsl b/ogl-engine/materials/color/shader.frag.glsl index 7a110e98..7a100f6e 100644 --- a/ogl-engine/materials/color/shader.frag.glsl +++ b/ogl-engine/materials/color/shader.frag.glsl @@ -2,9 +2,6 @@ precision highp float; #pragma defines -#ifndef LAYER -#define LAYER 0 -#endif #pragma glslify: blinnPhongSpec = require(glsl-specular-blinn-phong) #pragma glslify: Light = require("./ogl-engine/shader/light"); @@ -13,7 +10,7 @@ in vec3 worldPosition; in vec3 vNormal; in float vFragDepth; -out vec4 fragData[3]; +out vec4 fragData[2]; uniform vec3 uColor; uniform bool bShaded; @@ -51,13 +48,13 @@ void main() { } } - fragData[LAYER].rgb = (diffuse + ambient + specular) * uColor; + fragData[0].rgb = (diffuse + ambient + specular) * uColor; } else { - fragData[LAYER] = vec4(uColor, 1.f); + fragData[0] = vec4(uColor, 1.f); } - fragData[LAYER].a = 1.0f; - fragData[1] = vec4(fragData[LAYER].xyz, fEmissive); + fragData[0].a = 1.0f; + fragData[1] = vec4(fragData[0].xyz, fEmissive); gl_FragDepth = log2(vFragDepth) / uCameraScale; } \ No newline at end of file diff --git a/ogl-engine/materials/engineTrail/shader.frag.glsl b/ogl-engine/materials/engineTrail/shader.frag.glsl index e3e5e1b6..2f7ef289 100644 --- a/ogl-engine/materials/engineTrail/shader.frag.glsl +++ b/ogl-engine/materials/engineTrail/shader.frag.glsl @@ -4,7 +4,7 @@ precision highp float; in vec2 vUv; in float vFragDepth; -out vec4 fragData[3]; +out vec4 fragData[2]; uniform vec3 uColor; uniform float uCameraScale; diff --git a/ogl-engine/materials/entityIndicator/shader.frag.glsl b/ogl-engine/materials/entityIndicator/shader.frag.glsl index 50d008c9..76054c4d 100644 --- a/ogl-engine/materials/entityIndicator/shader.frag.glsl +++ b/ogl-engine/materials/entityIndicator/shader.frag.glsl @@ -4,7 +4,7 @@ precision highp float; in vec2 vUv; in float vDist; -out vec4 fragData[3]; +out vec4 fragData; uniform mat4 modelMatrix; uniform vec3 cameraPosition; @@ -100,5 +100,5 @@ void main() { alpha = 1.f; } - fragData[2] = vec4(color, clamp(alpha, 0.f, 1.f)); + fragData = vec4(color, clamp(alpha, 0.f, 1.f)); } \ No newline at end of file diff --git a/ogl-engine/materials/kineticGun/shader.frag.glsl b/ogl-engine/materials/kineticGun/shader.frag.glsl index 8ebd0bf6..38faed66 100644 --- a/ogl-engine/materials/kineticGun/shader.frag.glsl +++ b/ogl-engine/materials/kineticGun/shader.frag.glsl @@ -7,7 +7,7 @@ in vec2 vUv; in float vT; in float vFragDepth; -out vec4 fragData[3]; +out vec4 fragData[2]; uniform vec4 uColor; uniform float uCameraScale; diff --git a/ogl-engine/materials/laser/shader.frag.glsl b/ogl-engine/materials/laser/shader.frag.glsl index 0f2ddd30..4116c5b6 100644 --- a/ogl-engine/materials/laser/shader.frag.glsl +++ b/ogl-engine/materials/laser/shader.frag.glsl @@ -7,7 +7,7 @@ precision highp float; in vec2 vUv; in float vFragDepth; -out vec4 fragData[3]; +out vec4 fragData[2]; uniform vec3 uColor; uniform float uCameraScale; diff --git a/ogl-engine/materials/laserImpact/shader.frag.glsl b/ogl-engine/materials/laserImpact/shader.frag.glsl index 46102226..0844ca1e 100644 --- a/ogl-engine/materials/laserImpact/shader.frag.glsl +++ b/ogl-engine/materials/laserImpact/shader.frag.glsl @@ -6,7 +6,7 @@ in float vFragDepth; in vec3 vNormal; in vec3 vViewDirection; -out vec4 fragData[3]; +out vec4 fragData[2]; uniform vec3 uColor; uniform float uCameraScale; diff --git a/ogl-engine/materials/missing/missing.ts b/ogl-engine/materials/missing/missing.ts index 6d5b255f..40debe2d 100644 --- a/ogl-engine/materials/missing/missing.ts +++ b/ogl-engine/materials/missing/missing.ts @@ -1,4 +1,3 @@ -import { Program } from "ogl"; import type { Engine3D } from "@ogl-engine/engine/engine3d"; import { Material } from "../material"; import fragment from "./shader.frag.glsl"; @@ -8,9 +7,6 @@ export class MissingMaterial extends Material { constructor(engine: Engine3D) { super(engine); - this.program = new Program(engine.gl, { - vertex, - fragment, - }); + this.createProgram(vertex, fragment); } } diff --git a/ogl-engine/materials/missing/shader.frag.glsl b/ogl-engine/materials/missing/shader.frag.glsl index 389e6391..951e5fbc 100644 --- a/ogl-engine/materials/missing/shader.frag.glsl +++ b/ogl-engine/materials/missing/shader.frag.glsl @@ -3,7 +3,7 @@ precision highp float; in float vFragDepth; -out vec4 fragData[3]; +out vec4 fragData[2]; uniform float uCameraScale; diff --git a/ogl-engine/materials/orb/shader.frag.glsl b/ogl-engine/materials/orb/shader.frag.glsl index fc33f87a..b083958b 100644 --- a/ogl-engine/materials/orb/shader.frag.glsl +++ b/ogl-engine/materials/orb/shader.frag.glsl @@ -7,7 +7,7 @@ in vec2 vUv; in float vT; in float vFragDepth; -out vec4 fragData[3]; +out vec4 fragData[2]; uniform vec4 uStart; uniform vec4 uEnd; diff --git a/ogl-engine/materials/particleCloud/shader.frag.glsl b/ogl-engine/materials/particleCloud/shader.frag.glsl index c9ea885d..a72ee619 100644 --- a/ogl-engine/materials/particleCloud/shader.frag.glsl +++ b/ogl-engine/materials/particleCloud/shader.frag.glsl @@ -9,7 +9,7 @@ in float vFragDepth; flat in float vInstanceIndex; flat in float vT; -out vec4 fragData[3]; +out vec4 fragData[2]; uniform sampler2D tMap; uniform vec3 uColor; diff --git a/ogl-engine/materials/pbr/shader.frag.glsl b/ogl-engine/materials/pbr/shader.frag.glsl index 4ff7c3d5..c0eee5ac 100644 --- a/ogl-engine/materials/pbr/shader.frag.glsl +++ b/ogl-engine/materials/pbr/shader.frag.glsl @@ -33,7 +33,7 @@ uniform float uRoughness; uniform sampler2D tEmissive; #endif -out vec4 fragData[3]; +out vec4 fragData[2]; #define EPSILON 0.001f #pragma glslify: pbr = require("./ogl-engine/shader/pbr", lights = lights, cameraPosition = cameraPosition, worldPosition = worldPosition); diff --git a/ogl-engine/materials/selectionBox/shader.frag.glsl b/ogl-engine/materials/selectionBox/shader.frag.glsl index fd38f3d9..2892b86f 100644 --- a/ogl-engine/materials/selectionBox/shader.frag.glsl +++ b/ogl-engine/materials/selectionBox/shader.frag.glsl @@ -3,7 +3,7 @@ precision highp float; in vec2 vUv; -out vec4 fragData[3]; +out vec4 fragData[2]; uniform float lineWidthX; uniform float lineWidthY; diff --git a/ogl-engine/materials/skybox/shader.frag.glsl b/ogl-engine/materials/skybox/shader.frag.glsl index 7e0355b5..cbea2efe 100644 --- a/ogl-engine/materials/skybox/shader.frag.glsl +++ b/ogl-engine/materials/skybox/shader.frag.glsl @@ -7,7 +7,7 @@ uniform float uCameraScale; in vec3 vDir; in float vFragDepth; -out vec4 fragData[3]; +out vec4 fragData[2]; void main() { fragData[0] = texture(tMap, vDir); diff --git a/ogl-engine/materials/skybox/skybox.ts b/ogl-engine/materials/skybox/skybox.ts index 003eea11..cf703fc6 100644 --- a/ogl-engine/materials/skybox/skybox.ts +++ b/ogl-engine/materials/skybox/skybox.ts @@ -76,6 +76,7 @@ export class Skybox extends Mesh implements Destroyable { tMap: { value: new Texture(engine.gl, { target: engine.gl.TEXTURE_CUBE_MAP, + internalFormat: engine.gl.SRGB8_ALPHA8, }), }, uCameraScale: { value: Math.log2(engine.camera.far) }, diff --git a/ogl-engine/materials/spritesheet/shader.frag.glsl b/ogl-engine/materials/spritesheet/shader.frag.glsl index 06069fc6..f656411e 100644 --- a/ogl-engine/materials/spritesheet/shader.frag.glsl +++ b/ogl-engine/materials/spritesheet/shader.frag.glsl @@ -4,7 +4,7 @@ precision highp float; in vec3 worldPosition; in vec2 vUv; -out vec4 fragData[3]; +out vec4 fragData[2]; uniform sampler2D tDiffuse; uniform float fAlpha; diff --git a/ogl-engine/materials/star/shader.frag.glsl b/ogl-engine/materials/star/shader.frag.glsl index e804b0af..0da363f2 100644 --- a/ogl-engine/materials/star/shader.frag.glsl +++ b/ogl-engine/materials/star/shader.frag.glsl @@ -15,7 +15,7 @@ in vec3 vWorldNormal; in vec3 vViewDirection; in float vFragDepth; -out vec4 fragData[3]; +out vec4 fragData[2]; uniform float uTime; uniform vec3 cameraPosition; diff --git a/ogl-engine/materials/starCorona/shader.frag.glsl b/ogl-engine/materials/starCorona/shader.frag.glsl index ba0dd3dc..5e775e0c 100644 --- a/ogl-engine/materials/starCorona/shader.frag.glsl +++ b/ogl-engine/materials/starCorona/shader.frag.glsl @@ -7,7 +7,7 @@ precision highp float; in vec2 vUv; in float vFragDepth; -out vec4 fragData[3]; +out vec4 fragData[2]; uniform vec3 uColor; uniform float uTime; diff --git a/ogl-engine/materials/tintedTexture/shader.frag.glsl b/ogl-engine/materials/tintedTexture/shader.frag.glsl index 7b68f371..5b3aebef 100644 --- a/ogl-engine/materials/tintedTexture/shader.frag.glsl +++ b/ogl-engine/materials/tintedTexture/shader.frag.glsl @@ -4,7 +4,7 @@ precision highp float; in vec2 vUv; in float vFragDepth; -out vec4 fragData[3]; +out vec4 fragData[2]; uniform sampler2D tMap; uniform vec3 uColor; diff --git a/ogl-engine/post/blur.frag.glsl b/ogl-engine/post/blur.frag.glsl deleted file mode 100644 index 8aedbdba..00000000 --- a/ogl-engine/post/blur.frag.glsl +++ /dev/null @@ -1,13 +0,0 @@ -precision highp float; - -#pragma glslify: blur13 = require(glsl-fast-gaussian-blur) - -uniform sampler2D tMap; -uniform vec2 uDirection; -uniform vec2 uResolution; - -varying vec2 vUv; - -void main() { - gl_FragColor = blur13(tMap, vUv, uResolution, uDirection); -} \ No newline at end of file diff --git a/ogl-engine/post/composite.frag.glsl b/ogl-engine/post/composite.frag.glsl index 65e848ec..4f0bbf5b 100644 --- a/ogl-engine/post/composite.frag.glsl +++ b/ogl-engine/post/composite.frag.glsl @@ -2,7 +2,6 @@ precision highp float; uniform sampler2D tMap; uniform sampler2D tBloom; -uniform vec2 uResolution; uniform float uBloomStrength; varying vec2 vUv; diff --git a/ogl-engine/post/tonemapping.frag.glsl b/ogl-engine/post/tonemapping.frag.glsl index d5db5456..5b1f67c1 100644 --- a/ogl-engine/post/tonemapping.frag.glsl +++ b/ogl-engine/post/tonemapping.frag.glsl @@ -3,25 +3,49 @@ precision highp float; #pragma glslify: aces = require("glsl-tone-map/aces"); uniform sampler2D tMap; -uniform float uGamma; uniform float uSaturation; uniform float uContrast; uniform float uExposure; uniform float uMap; +uniform int uTime; varying vec2 vUv; +vec3 linearToSRGB(vec3 x) { + vec3 lo = 12.92 * x; + vec3 hi = 1.055 * pow(max(x, 0.0), vec3(1.0 / 2.4)) - 0.055; + return mix(lo, hi, step(0.0031308, x)); +} + +// tiny hash + TPDF +float h(vec2 p) { + return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453); +} +// Output range is -1...1 +float tpdf(vec2 p) { + return h(p * 0.067) + h(p * 0.139) - 1.0; +} + +const float pivot = 0.18; + void main() { - vec4 color = texture2D(tMap, vUv); - color.rgb *= uExposure; + vec3 color = texture2D(tMap, vUv).rgb; + color *= uExposure; if(uMap > 0.5) { - color.rgb = aces(color.rgb); + color = aces(color); } - color.rgb = pow(color.rgb, vec3(1.0 / uGamma)); - color.rgb = (color.rgb - 0.5) * uContrast + 0.5; - float luma = dot(color.rgb, vec3(0.299, 0.587, 0.114)); + color = (color - pivot) * uContrast + pivot; + + vec3 srgb = linearToSRGB(clamp(color, 0.0, 1.0)); + + float luma = dot(srgb, vec3(0.299, 0.587, 0.114)); + srgb = mix(vec3(luma), srgb, uSaturation); + + vec2 p = gl_FragCoord.xy + float(uTime); + vec3 n = vec3(tpdf(p), tpdf(p + 37.0), tpdf(p + 91.0)) / 255.0; + srgb += n; - gl_FragColor = mix(vec4(luma, luma, luma, 1.), color, uSaturation); -} \ No newline at end of file + gl_FragColor = vec4(clamp(srgb, 0.0, 1.0), 1.0); +} diff --git a/ogl-engine/post/ui.frag.glsl b/ogl-engine/post/ui.frag.glsl deleted file mode 100644 index edc5635c..00000000 --- a/ogl-engine/post/ui.frag.glsl +++ /dev/null @@ -1,11 +0,0 @@ -precision highp float; - -uniform sampler2D tMap; -uniform sampler2D tUi; - -varying vec2 vUv; - -void main() { - vec4 ui = texture2D(tUi, vUv); - gl_FragColor = mix(clamp(texture2D(tMap, vUv), 0., 1.), ui, ui.a); -} \ No newline at end of file diff --git a/ogl-engine/utils/AxesHelper.ts b/ogl-engine/utils/AxesHelper.ts index aa218f2f..9828fd16 100644 --- a/ogl-engine/utils/AxesHelper.ts +++ b/ogl-engine/utils/AxesHelper.ts @@ -29,7 +29,7 @@ precision highp float; in vec3 vColor; in float vFragDepth; -out vec4 fragData[3]; +out vec4 fragData[2]; uniform float uCameraScale; diff --git a/ui/components/TacticalMap/EntityMesh.ts b/ui/components/TacticalMap/EntityMesh.ts index d566025c..6d7e49f0 100644 --- a/ui/components/TacticalMap/EntityMesh.ts +++ b/ui/components/TacticalMap/EntityMesh.ts @@ -20,6 +20,7 @@ import { materials } from "@ogl-engine/materials"; import { LaserWeaponEffect } from "@ogl-engine/builders/LaserWeapon"; import type { Turret } from "@core/archetypes/turret"; import { transport3D } from "@core/systems/transport3d"; +import { RenderLayer } from "@ogl-engine/engine/Renderer"; const tempVec3 = new Vec3(); export const distanceScale = 250; @@ -28,13 +29,13 @@ export class EntityIndicator extends BaseMesh { name = "EntityIndicator"; parent: EntityMesh; nameMesh: BaseMesh | null = null; + layer = RenderLayer.ui; private task: OnBeforeRenderTask; constructor(engine: Engine3D) { super(engine, { geometry: new Plane(engine.gl), material: new EntityIndicatorMaterial(engine), - frustumCulled: false, }); } @@ -70,7 +71,6 @@ export class EntityIndicator extends BaseMesh { id: { size: 1, data: text.buffers.id }, index: { data: text.buffers.index }, }), - frustumCulled: false, }); const texture = new Texture(this.engine.gl, { generateMipmaps: false, @@ -90,6 +90,7 @@ export class EntityIndicator extends BaseMesh { ) < 10 ); }); + this.nameMesh.layer = RenderLayer.ui; this.nameMesh.setParent(this); } diff --git a/ui/components/TacticalMap/TacticalMap.tsx b/ui/components/TacticalMap/TacticalMap.tsx index 6c7f0cf6..adab1e53 100644 --- a/ui/components/TacticalMap/TacticalMap.tsx +++ b/ui/components/TacticalMap/TacticalMap.tsx @@ -186,7 +186,7 @@ export class TacticalMap extends React.PureComponent { } async onEngineUpdate() { - if (!(assetLoader.ready && this.engine.isFocused())) return; + if (!(assetLoader.ready && this.engine.isFocused() && this.control)) return; if (this.dragStart) { const normalisedDragStart = new Vec2(