diff --git a/.changeset/itchy-hounds-eat.md b/.changeset/itchy-hounds-eat.md new file mode 100644 index 0000000..85920d7 --- /dev/null +++ b/.changeset/itchy-hounds-eat.md @@ -0,0 +1,5 @@ +--- +'@tedengine/ted': minor +--- + +Add instance UVs for textured meshes diff --git a/packages/ted/src/renderer/frame-params.ts b/packages/ted/src/renderer/frame-params.ts index 086caa2..7f9970e 100644 --- a/packages/ted/src/renderer/frame-params.ts +++ b/packages/ted/src/renderer/frame-params.ts @@ -55,5 +55,6 @@ export interface TSerializedTexturedMaterial { type: 'textured'; options: { texture: string; + instanceUVs?: number[]; }; } diff --git a/packages/ted/src/renderer/program.ts b/packages/ted/src/renderer/program.ts index 01e0479..7e644c2 100644 --- a/packages/ted/src/renderer/program.ts +++ b/packages/ted/src/renderer/program.ts @@ -4,7 +4,7 @@ import type { IAsset } from '../core/resource-manager'; const compileShader = ( gl: WebGL2RenderingContext, shaderSource: string, - shaderType: number + shaderType: number, ): WebGLShader => { // @todo add error handling here const shader = gl.createShader(shaderType); @@ -22,7 +22,7 @@ const compileShader = ( const createProgram = ( gl: WebGL2RenderingContext, vertexShader: WebGLShader, - fragmentShader: WebGLShader + fragmentShader: WebGLShader, ) => { // @todo add error handling here const program = gl.createProgram(); @@ -73,12 +73,12 @@ export default class TProgram implements IAsset { const vertexShader = compileShader( gl, this.vertexShaderSource!, - gl.VERTEX_SHADER + gl.VERTEX_SHADER, ); const fragmentShader = compileShader( gl, this.fragmentShaderSource!, - gl.FRAGMENT_SHADER + gl.FRAGMENT_SHADER, ); this.program = createProgram(gl, vertexShader, fragmentShader); @@ -86,43 +86,52 @@ export default class TProgram implements IAsset { this.attribLocations.vertexPosition = gl.getAttribLocation( this.program, - 'aVertexPosition' + 'aVertexPosition', ); this.attribLocations.normalPosition = gl.getAttribLocation( this.program, - 'aVertexNormal' + 'aVertexNormal', ); this.attribLocations.colorPosition = gl.getAttribLocation( this.program, - 'aVertexColor' + 'aVertexColor', ); this.attribLocations.uvPosition = gl.getAttribLocation( this.program, - 'aVertexUV' + 'aVertexUV', + ); + + this.attribLocations.instanceUVPosition = gl.getAttribLocation( + this.program, + 'aVertexInstanceUV', ); this.uniformLocations.mMatrix = gl.getUniformLocation( this.program, - 'uMMatrix' + 'uMMatrix', ); this.uniformLocations.uPalette = gl.getUniformLocation( this.program, - 'uPalette' + 'uPalette', + ); + this.uniformLocations.uEnableInstanceUVs = gl.getUniformLocation( + this.program, + 'uEnableInstanceUVs', ); } public getAttributeLocations( gl: WebGL2RenderingContext, - attributes: string[] + attributes: string[], ): WebGLUniformLocation[] { return attributes.map( - (attribute) => gl.getUniformLocation(this.program!, attribute)! + (attribute) => gl.getUniformLocation(this.program!, attribute)!, ); } public getUniformLocation( gl: WebGL2RenderingContext, - name: string + name: string, ): WebGLUniformLocation { if (!this.uniformLocations[name]) { this.uniformLocations[name] = gl.getUniformLocation(this.program!, name); diff --git a/packages/ted/src/renderer/renderable-textured-mesh.ts b/packages/ted/src/renderer/renderable-textured-mesh.ts index fb51188..5ab82cc 100644 --- a/packages/ted/src/renderer/renderable-textured-mesh.ts +++ b/packages/ted/src/renderer/renderable-textured-mesh.ts @@ -19,13 +19,17 @@ export default class TRenderableTexturedMesh { private indexBuffer?: WebGLBuffer; private uvBuffer?: WebGLBuffer; + // Instance buffers, used to override the UVs of the mesh + private instanceUVBuffer?: WebGLBuffer; + private vao?: WebGLVertexArrayObject; public render( gl: WebGL2RenderingContext, texturedProgram: TTexturedProgram, texture: TRenderableTexture, - m: mat4 + m: mat4, + instanceUVs?: number[], ) { if (this.positionBuffer === undefined) { this.createBuffers(gl); @@ -54,9 +58,23 @@ export default class TRenderableTexturedMesh { gl.uniformMatrix4fv( texturedProgram.program!.uniformLocations.mMatrix, false, - m + m, + ); + + gl.uniform1f( + texturedProgram.program!.uniformLocations.uEnableInstanceUVs, + instanceUVs ? 1 : 0, ); + if (instanceUVs && this.instanceUVBuffer) { + gl.bindBuffer(gl.ARRAY_BUFFER, this.instanceUVBuffer); + gl.bufferData( + gl.ARRAY_BUFFER, + new Float32Array(instanceUVs), + gl.STATIC_DRAW, + ); + } + const vertexCount = this.indexes.length; const type = gl.UNSIGNED_SHORT; const offset = 0; @@ -65,7 +83,7 @@ export default class TRenderableTexturedMesh { } private createVAO(gl: WebGL2RenderingContext, program: TProgram) { - const { vertexPosition, normalPosition, uvPosition } = + const { vertexPosition, normalPosition, uvPosition, instanceUVPosition } = program.attribLocations; this.vao = gl.createVertexArray()!; @@ -85,7 +103,7 @@ export default class TRenderableTexturedMesh { type, normalize, stride, - offset + offset, ); gl.enableVertexAttribArray(vertexPosition); @@ -106,14 +124,14 @@ export default class TRenderableTexturedMesh { type, normalize, stride, - offset + offset, ); gl.enableVertexAttribArray(normalPosition); } if (uvPosition !== -1) { - // Color buffer + // UV buffer const numComponents = 2; const type = gl.FLOAT; @@ -127,11 +145,32 @@ export default class TRenderableTexturedMesh { type, normalize, stride, - offset + offset, ); gl.enableVertexAttribArray(uvPosition); } + + if (instanceUVPosition !== -1) { + // Instance UV buffer + + const numComponents = 2; + const type = gl.FLOAT; + const normalize = false; + const stride = 0; + const offset = 0; + gl.bindBuffer(gl.ARRAY_BUFFER, this.instanceUVBuffer!); + gl.vertexAttribPointer( + instanceUVPosition, + numComponents, + type, + normalize, + stride, + offset, + ); + + gl.enableVertexAttribArray(instanceUVPosition); + } } /** @@ -143,7 +182,7 @@ export default class TRenderableTexturedMesh { gl.bufferData( gl.ARRAY_BUFFER, new Float32Array(this.positions), - gl.STATIC_DRAW + gl.STATIC_DRAW, ); this.normalBuffer = gl.createBuffer()!; @@ -151,7 +190,7 @@ export default class TRenderableTexturedMesh { gl.bufferData( gl.ARRAY_BUFFER, new Float32Array(this.normals), - gl.STATIC_DRAW + gl.STATIC_DRAW, ); this.indexBuffer = gl.createBuffer()!; @@ -159,11 +198,14 @@ export default class TRenderableTexturedMesh { gl.bufferData( gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(this.indexes), - gl.STATIC_DRAW + gl.STATIC_DRAW, ); this.uvBuffer = gl.createBuffer()!; gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(this.uvs), gl.STATIC_DRAW); + + // Data will be buffered at render time if provided + this.instanceUVBuffer = gl.createBuffer()!; } } diff --git a/packages/ted/src/renderer/renderer.ts b/packages/ted/src/renderer/renderer.ts index 310599a..b6c6d0f 100644 --- a/packages/ted/src/renderer/renderer.ts +++ b/packages/ted/src/renderer/renderer.ts @@ -153,7 +153,13 @@ export default class TRenderer { const mesh = this.registeredTexturedMeshes[task.uuid]; const texture = this.registeredTextures[task.material.options.texture]; - mesh.render(gl, this.texturedProgram!, texture, task.transform); + mesh.render( + gl, + this.texturedProgram!, + texture, + task.transform, + task.material.options.instanceUVs, + ); } } @@ -171,7 +177,13 @@ export default class TRenderer { for (const task of layer) { const mesh = this.registeredTexturedMeshes[task.uuid]; const texture = this.registeredTextures[task.material.options.texture]; - mesh.render(gl, this.texturedProgram!, texture, task.transform); + mesh.render( + gl, + this.texturedProgram!, + texture, + task.transform, + task.material.options.instanceUVs, + ); } } } diff --git a/packages/ted/src/shaders/textured.program b/packages/ted/src/shaders/textured.program index f1a9e6b..9bbcd38 100644 --- a/packages/ted/src/shaders/textured.program +++ b/packages/ted/src/shaders/textured.program @@ -3,6 +3,9 @@ in vec4 aVertexPosition; in vec2 aVertexUV; +uniform float uEnableInstanceUVs; +in vec2 aVertexInstanceUV; + uniform mat4 uMMatrix; uniform Settings { @@ -14,7 +17,7 @@ out highp vec2 vUV; void main() { gl_Position = uVPMatrix * uMMatrix * aVertexPosition; - vUV = aVertexUV; + vUV = aVertexInstanceUV * uEnableInstanceUVs + aVertexUV * (1.0 - uEnableInstanceUVs); } ---- #version 300 es