@fonsecabarreto/genesis-gl-core 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/README.md +19 -2
  2. package/dist/{Camera-DY_8gx3C.d.ts → Camera-CJVYy9fH.d.ts} +13 -2
  3. package/dist/Core/classes/Material.d.ts +1 -1
  4. package/dist/Core/classes/Material.js +1 -1
  5. package/dist/Core/classes/Model.d.ts +3 -3
  6. package/dist/Core/classes/Model.js +1 -1
  7. package/dist/Core/classes/Renderer.d.ts +11 -5
  8. package/dist/Core/classes/Renderer.js +4 -4
  9. package/dist/Core/classes/Scene.d.ts +2 -2
  10. package/dist/Core/classes/Viewport.d.ts +1 -1
  11. package/dist/Core/classes/Viewport.js +1 -1
  12. package/dist/Core/index.d.ts +4 -4
  13. package/dist/Core/index.js +4 -4
  14. package/dist/Core/utils/load-glb.d.ts +3 -3
  15. package/dist/Core/utils/load-glb.js +4 -4
  16. package/dist/Core/utils/parse-obj.d.ts +2 -2
  17. package/dist/Core/utils/parse-obj.js +4 -4
  18. package/dist/Editor/index.d.ts +126 -15
  19. package/dist/Editor/index.js +471 -74
  20. package/dist/Editor/index.js.map +1 -1
  21. package/dist/Game/controls/KeyboardInput.d.ts +4 -4
  22. package/dist/Game/index.d.ts +308 -7
  23. package/dist/Game/index.js +470 -24
  24. package/dist/Game/index.js.map +1 -1
  25. package/dist/{KeyboardInput-DTsfj3tE.d.ts → KeyboardInput-1xOAabI0.d.ts} +95 -14
  26. package/dist/{Material-BGLkldxv.d.ts → Material-DhwSRbP2.d.ts} +8 -0
  27. package/dist/{Model-CQvDXd-b.d.ts → Model-BBZHnUp1.d.ts} +24 -8
  28. package/dist/{chunk-6LS6AO5H.js → chunk-L66K4AZU.js} +36 -30
  29. package/dist/chunk-L66K4AZU.js.map +1 -0
  30. package/dist/{chunk-JK2HEZAT.js → chunk-QOAQVTAB.js} +26 -22
  31. package/dist/chunk-QOAQVTAB.js.map +1 -0
  32. package/dist/{chunk-5TAAXI6S.js → chunk-XMW2MS66.js} +39 -16
  33. package/dist/chunk-XMW2MS66.js.map +1 -0
  34. package/dist/{chunk-QCQVJCSR.js → chunk-ZCJ3MJZD.js} +103 -67
  35. package/dist/chunk-ZCJ3MJZD.js.map +1 -0
  36. package/package.json +1 -1
  37. package/dist/chunk-5TAAXI6S.js.map +0 -1
  38. package/dist/chunk-6LS6AO5H.js.map +0 -1
  39. package/dist/chunk-JK2HEZAT.js.map +0 -1
  40. package/dist/chunk-QCQVJCSR.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/Core/classes/Renderer.ts","../src/Core/utils/compute-bounds.ts","../src/Core/classes/Mesh.ts","../src/Core/shaders/index.ts","../src/Core/classes/WebGLCore.ts","../src/Core/classes/Skeleton.ts","../src/Core/classes/AnimationClip.ts","../src/Core/utils/create-wire-box.ts"],"sourcesContent":["import { mat4, vec3 } from 'gl-matrix';\nimport { Material, Mesh, Scene, WebGLCore } from '.';\nimport { Viewport } from './Viewport';\nimport { Light } from './Light';\nimport { Vector3 } from '../domain/interfaces/Vectors';\nimport { Model } from './Model';\nimport { Skeleton } from './Skeleton';\nimport { createWireBox } from '../utils/create-wire-box';\n\nexport class Renderer {\n /** Set to `true` to render wireframe bounding boxes for debugging. */\n public debug = false;\n\n // Cached debug materials to avoid per-frame GPU allocations\n private _modelHitboxMaterial: Material | null = null;\n private _meshHitboxMaterial: Material | null = null;\n\n // Cache wire-box debug meshes: recreated only when the bounding box changes.\n private readonly _modelWireBoxCache = new WeakMap<Model, { mesh: Mesh; key: string }>();\n private readonly _meshWireBoxCache = new WeakMap<Mesh, { mesh: Mesh; key: string }>();\n\n /** Pre-allocated identity matrix for world-space hitbox draws. */\n private readonly _identity: mat4 = mat4.create();\n\n /** Cached `gl instanceof WebGL2RenderingContext` — computed once at construction. */\n private readonly _isWebGL2: boolean;\n\n constructor(\n private webglCore: WebGLCore,\n public viewport: Viewport,\n ) {\n const gl = webglCore.getRenderingContext();\n this._isWebGL2 = gl instanceof WebGL2RenderingContext;\n if (gl) gl.enable(gl.DEPTH_TEST);\n }\n\n /** Lazily-created material for model-level hitboxes (red). */\n private get modelHitboxMaterial(): Material {\n if (!this._modelHitboxMaterial) {\n this._modelHitboxMaterial = new Material(this.webglCore);\n this._modelHitboxMaterial.setColorHex('#ff0404ff');\n this._modelHitboxMaterial.unlit = true;\n }\n return this._modelHitboxMaterial;\n }\n\n /** Lazily-created material for mesh-level hitboxes (green). */\n private get meshHitboxMaterial(): Material {\n if (!this._meshHitboxMaterial) {\n this._meshHitboxMaterial = new Material(this.webglCore);\n this._meshHitboxMaterial.setColorHex('#04ff0cff');\n this._meshHitboxMaterial.unlit = true;\n }\n return this._meshHitboxMaterial;\n }\n\n public clearFrame(\n color: [number, number, number, number] = [0.2, 0.5, 0.95, 1],\n ) {\n const gl = this.webglCore.getRenderingContext();\n if (!gl) return;\n\n gl.clearColor(...color);\n gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);\n }\n\n public render(scene: Scene) {\n const gl = this.webglCore.getRenderingContext();\n if (!gl) return;\n\n const models = scene.getModels();\n const lights = scene.getLights();\n const camera = this.viewport.camera;\n\n const viewPosition: vec3 = camera.getComputedPosition();\n this.clearFrame();\n\n const viewMatrix = camera.getViewMatrix() as Float32Array;\n const projMatrix = camera.getProjectionMatrix() as Float32Array;\n\n let activeProgram: WebGLProgram | null = null;\n\n for (const model of models) {\n activeProgram = this.drawModel(\n gl,\n model,\n lights,\n viewPosition,\n viewMatrix,\n projMatrix,\n activeProgram,\n );\n }\n }\n\n private drawModel(\n gl: WebGLRenderingContext,\n model: Model,\n lights: Light[],\n viewPosition: vec3,\n viewMatrix: Float32Array,\n projMatrix: Float32Array,\n activeProgram: WebGLProgram | null,\n ): WebGLProgram | null {\n const modelMatrix = model.getModelMatrix();\n const hasSkeleton = model.skeleton !== null;\n\n for (const mesh of model.meshes) {\n activeProgram = this.drawMesh(\n mesh,\n lights,\n viewPosition,\n modelMatrix,\n viewMatrix,\n projMatrix,\n activeProgram,\n hasSkeleton && mesh.isSkinned ? model : null,\n );\n\n if (this.debug) {\n this.drawHitBoxForMesh(\n mesh,\n modelMatrix,\n viewMatrix,\n projMatrix,\n activeProgram,\n );\n }\n }\n\n // Debug wireframe\n if (this.debug) {\n this.drawHitBox(model, activeProgram);\n }\n\n return activeProgram;\n }\n\n private drawMesh(\n mesh: Mesh,\n lights: Light[],\n viewPosition: vec3,\n modelMatrix: mat4,\n viewMatrix: Float32Array,\n projMatrix: Float32Array,\n activeProgram: WebGLProgram | null,\n skinnedModel: Model | null = null,\n ): WebGLProgram | null {\n const gl = this.webglCore.getRenderingContext();\n if (!gl) return activeProgram;\n if (!mesh.material) return activeProgram;\n\n mesh.initBuffer(this.webglCore);\n\n const { program, uniformLocations } = mesh.material;\n\n if (program !== activeProgram) {\n gl.useProgram(program);\n activeProgram = program;\n\n if (uniformLocations['uView'])\n gl.uniformMatrix4fv(uniformLocations['uView'], false, viewMatrix);\n if (uniformLocations['uProjection'])\n gl.uniformMatrix4fv(uniformLocations['uProjection'], false, projMatrix);\n }\n\n // Use cached WebGL2 check\n const isWebGL2 = this._isWebGL2;\n\n // --- Bind VAO if available ---\n if (isWebGL2 && mesh.buffers?.vao) {\n (gl as unknown as WebGL2RenderingContext).bindVertexArray(\n mesh.buffers.vao,\n );\n } else {\n // --- WebGL1 Fallback ---\n const aPosition = mesh.material.attribLocations['aPosition'];\n if (aPosition !== -1 && mesh.buffers?.vertexBuffer) {\n gl.bindBuffer(gl.ARRAY_BUFFER, mesh.buffers.vertexBuffer);\n gl.enableVertexAttribArray(aPosition);\n gl.vertexAttribPointer(aPosition, 3, gl.FLOAT, false, 0, 0);\n }\n\n const aNormal = mesh.material.attribLocations['aNormal'];\n if (aNormal !== -1 && mesh.buffers?.normalBuffer) {\n gl.bindBuffer(gl.ARRAY_BUFFER, mesh.buffers.normalBuffer);\n gl.enableVertexAttribArray(aNormal);\n gl.vertexAttribPointer(aNormal, 3, gl.FLOAT, false, 0, 0);\n }\n\n const aTexCoord = mesh.material.attribLocations['aTexCoord'];\n if (aTexCoord !== -1 && mesh.buffers?.texCoordBuffer) {\n gl.bindBuffer(gl.ARRAY_BUFFER, mesh.buffers.texCoordBuffer);\n gl.enableVertexAttribArray(aTexCoord);\n gl.vertexAttribPointer(aTexCoord, 2, gl.FLOAT, false, 0, 0);\n }\n\n // Skinning attributes (joint indices + weights)\n const aJointIndices = mesh.material.attribLocations['aJointIndices'];\n if (aJointIndices !== -1 && mesh.buffers?.jointIndexBuffer) {\n gl.bindBuffer(gl.ARRAY_BUFFER, mesh.buffers.jointIndexBuffer);\n gl.enableVertexAttribArray(aJointIndices);\n gl.vertexAttribPointer(aJointIndices, 4, gl.FLOAT, false, 0, 0);\n }\n\n const aJointWeights = mesh.material.attribLocations['aJointWeights'];\n if (aJointWeights !== -1 && mesh.buffers?.jointWeightBuffer) {\n gl.bindBuffer(gl.ARRAY_BUFFER, mesh.buffers.jointWeightBuffer);\n gl.enableVertexAttribArray(aJointWeights);\n gl.vertexAttribPointer(aJointWeights, 4, gl.FLOAT, false, 0, 0);\n }\n }\n\n // Material uniforms\n mesh.material.apply(gl, lights, viewPosition as Vector3);\n\n // ── Skinning uniforms ─────────────────────────────────────\n const uUseSkinning = uniformLocations['uUseSkinning'];\n const skeleton: Skeleton | null =\n (skinnedModel?.skeleton as Skeleton | undefined) ?? null;\n if (skeleton && mesh.isSkinned) {\n if (uUseSkinning) gl.uniform1i(uUseSkinning, 1);\n const uJointMatrices = uniformLocations['uJointMatrices[0]'];\n if (uJointMatrices) {\n gl.uniformMatrix4fv(uJointMatrices, false, skeleton.jointMatrices);\n }\n } else {\n if (uUseSkinning) gl.uniform1i(uUseSkinning, 0);\n }\n\n // Model transform\n const uModel = mesh.material.uniformLocations['uModel'];\n if (uModel) gl.uniformMatrix4fv(uModel, false, modelMatrix as Float32Array);\n\n // Draw\n if (mesh.buffers?.indexBuffer && mesh.indices) {\n gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, mesh.buffers.indexBuffer);\n const type =\n mesh.indices instanceof Uint32Array\n ? gl.UNSIGNED_INT\n : gl.UNSIGNED_SHORT;\n gl.drawElements(mesh.mode, mesh.indices.length, type, 0);\n } else {\n gl.drawArrays(mesh.mode, 0, mesh.vertices.length / 3);\n }\n\n // Cleanup\n if (isWebGL2 && mesh.buffers?.vao) {\n (gl as unknown as WebGL2RenderingContext).bindVertexArray(null);\n } else {\n gl.bindBuffer(gl.ARRAY_BUFFER, null);\n gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);\n }\n\n return activeProgram;\n }\n\n public drawHitBox(\n model: Model,\n activeProgram: WebGLProgram | null,\n ) {\n const bbox = model.boundingBox;\n const key = `${bbox.min[0]},${bbox.min[1]},${bbox.min[2]},${bbox.max[0]},${bbox.max[1]},${bbox.max[2]}`;\n const cached = this._modelWireBoxCache.get(model);\n let hitboxMesh: Mesh;\n if (!cached || cached.key !== key) {\n hitboxMesh = createWireBox(bbox.min, bbox.max, this.modelHitboxMaterial);\n hitboxMesh.isCollidable = false;\n this._modelWireBoxCache.set(model, { mesh: hitboxMesh, key });\n } else {\n hitboxMesh = cached.mesh;\n }\n\n const camera = this.viewport.camera;\n const viewMatrix = camera.getViewMatrix() as Float32Array;\n const projMatrix = camera.getProjectionMatrix() as Float32Array;\n\n this.drawMesh(\n hitboxMesh,\n [],\n camera.position,\n this._identity,\n viewMatrix,\n projMatrix,\n activeProgram,\n );\n }\n\n private drawHitBoxForMesh(\n mesh: Mesh,\n modelMatrix: mat4,\n viewMatrix: Float32Array,\n projMatrix: Float32Array,\n activeProgram: WebGLProgram | null,\n ) {\n if (!mesh.boundingBox) return;\n\n const { min, max } = mesh.boundingBox;\n const key = `${min[0]},${min[1]},${min[2]},${max[0]},${max[1]},${max[2]}`;\n const cached = this._meshWireBoxCache.get(mesh);\n let wireBox: Mesh;\n if (!cached || cached.key !== key) {\n wireBox = createWireBox(min, max, this.meshHitboxMaterial);\n wireBox.isCollidable = false;\n this._meshWireBoxCache.set(mesh, { mesh: wireBox, key });\n } else {\n wireBox = cached.mesh;\n }\n\n const camera = this.viewport.camera;\n this.drawMesh(\n wireBox,\n [],\n camera.position,\n modelMatrix,\n viewMatrix,\n projMatrix,\n activeProgram,\n );\n }\n}\n","import { AABB } from '../domain/value-objects/Collider';\n\n/**\n * Compute the axis-aligned bounding box (AABB) from a flat Float32Array of vertices.\n * Each vertex is expected to be 3 consecutive floats: [x, y, z, ...].\n */\nexport function computeVertexBounds(vertices: Float32Array): AABB {\n if (!vertices || vertices.length === 0) {\n return { min: [0, 0, 0], max: [0, 0, 0] };\n }\n\n let minX = Infinity,\n minY = Infinity,\n minZ = Infinity;\n let maxX = -Infinity,\n maxY = -Infinity,\n maxZ = -Infinity;\n\n for (let i = 0; i < vertices.length; i += 3) {\n const x = vertices[i];\n const y = vertices[i + 1];\n const z = vertices[i + 2];\n\n if (x < minX) minX = x;\n if (y < minY) minY = y;\n if (z < minZ) minZ = z;\n\n if (x > maxX) maxX = x;\n if (y > maxY) maxY = y;\n if (z > maxZ) maxZ = z;\n }\n\n return {\n min: [minX, minY, minZ],\n max: [maxX, maxY, maxZ],\n };\n}\n","import WebGLCore from './WebGLCore';\nimport { Material } from './Material';\nimport { AABB } from '../domain/value-objects/Collider';\nimport { computeVertexBounds } from '../utils/compute-bounds';\n\n/** WebGL draw-mode constants. */\nexport const GL_LINES = 0x0001;\nexport const GL_TRIANGLES = 0x0004;\n\nexport const INITIAL_BOUNDING_BOX: AABB = { min: [0, 0, 0], max: [0, 0, 0] };\n\n/**\n * A single renderable mesh: vertex data, normals, indices, a material, and an\n * optional bounding box used for collision detection.\n *\n * For skinned meshes, set {@link jointIndices} and {@link jointWeights} to\n * enable GPU skeletal animation.\n */\nexport class Mesh {\n name: string;\n\n vertices: Float32Array;\n\n normals: Float32Array;\n\n texCoords: Float32Array;\n\n indices: Uint16Array | Uint32Array | null;\n\n mode: number;\n\n material: Material;\n\n // ── Skinning data (optional) ─────────────────────────────────\n /** Per-vertex joint indices (vec4 per vertex, 4 influences). */\n jointIndices: Float32Array | null = null;\n\n /** Per-vertex joint weights (vec4 per vertex, 4 influences). */\n jointWeights: Float32Array | null = null;\n\n // WebGL buffers aggregate\n buffers: MeshBuffers | null = null;\n\n boundingBox: AABB = INITIAL_BOUNDING_BOX;\n\n isCollidable = true;\n\n isInitialized = false;\n\n constructor(\n name: string,\n vertices: Float32Array,\n normals: Float32Array,\n material: Material,\n texCoords: Float32Array = new Float32Array(),\n indices: Uint16Array | Uint32Array | null = null,\n ) {\n this.mode = GL_TRIANGLES;\n this.name = name;\n this.normals = normals;\n this.indices = indices;\n this.vertices = vertices;\n this.texCoords = texCoords;\n this.material = material;\n\n this.computeBounds();\n }\n\n setMode(newMode: number) {\n this.mode = newMode;\n }\n\n /** Whether this mesh has skinning data for skeletal animation. */\n get isSkinned(): boolean {\n return this.jointIndices !== null && this.jointWeights !== null;\n }\n\n computeBounds() {\n this.boundingBox = computeVertexBounds(this.vertices);\n }\n\n clone(): Mesh {\n const clonedMesh = new Mesh(\n this.name,\n new Float32Array(this.vertices),\n new Float32Array(this.normals),\n this.material, // shallow ref — call Material.clone() if independent material is needed\n new Float32Array(this.texCoords),\n this.indices\n ? this.indices instanceof Uint16Array\n ? new Uint16Array(this.indices)\n : new Uint32Array(this.indices)\n : null,\n );\n clonedMesh.setMode(this.mode);\n clonedMesh.isCollidable = this.isCollidable;\n if (this.jointIndices)\n clonedMesh.jointIndices = new Float32Array(this.jointIndices);\n if (this.jointWeights)\n clonedMesh.jointWeights = new Float32Array(this.jointWeights);\n return clonedMesh;\n }\n\n /** Initialize GPU buffers safely */\n public initBuffer(webglCore: WebGLCore) {\n const gl = webglCore.getRenderingContext();\n\n if (!gl) {\n console.warn(\n `[Mesh:${this.name}] GL context not available, skipping buffer init.`,\n );\n return;\n }\n if (this.isInitialized) return;\n\n this.buffers = new MeshBuffers();\n this.buffers.init(gl as WebGL2RenderingContext, this);\n this.isInitialized = true;\n }\n\n /** Dispose of GPU buffers safely */\n public dispose(webglCore: WebGLCore) {\n const gl = webglCore.getRenderingContext();\n if (!gl || !this.buffers) return;\n\n this.buffers.dispose(gl as WebGL2RenderingContext);\n this.buffers = null;\n this.isInitialized = false;\n }\n}\n\nexport class MeshBuffers {\n vao: WebGLVertexArrayObject | null = null;\n vertexBuffer: WebGLBuffer | null = null;\n normalBuffer: WebGLBuffer | null = null;\n texCoordBuffer: WebGLBuffer | null = null;\n indexBuffer: WebGLBuffer | null = null;\n jointIndexBuffer: WebGLBuffer | null = null;\n jointWeightBuffer: WebGLBuffer | null = null;\n\n public init(gl: WebGLRenderingContext, mesh: Mesh) {\n if (!gl) throw new Error('Cannot init MeshBuffers: invalid GL context');\n\n const isWebGL2 = gl instanceof WebGL2RenderingContext;\n const gl2 = isWebGL2 ? (gl as unknown as WebGL2RenderingContext) : null;\n\n if (gl2) {\n this.vao = gl2.createVertexArray();\n gl2.bindVertexArray(this.vao);\n }\n\n // --- Attribute layout ---\n // Attribute locations must match the shader (aPosition=0, aNormal=1, aTexCoord=2).\n // Look them up from the mesh's material program so the VAO captures the bindings.\n const program = mesh.material?.program;\n const aPosition = program ? gl.getAttribLocation(program, 'aPosition') : 0;\n const aNormal = program ? gl.getAttribLocation(program, 'aNormal') : 1;\n const aTexCoord = program ? gl.getAttribLocation(program, 'aTexCoord') : 2;\n\n // Vertex buffer\n this.vertexBuffer = gl.createBuffer();\n if (!this.vertexBuffer) throw new Error('Failed to create vertex buffer');\n gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);\n gl.bufferData(gl.ARRAY_BUFFER, mesh.vertices, gl.STATIC_DRAW);\n if (aPosition !== -1) {\n gl.enableVertexAttribArray(aPosition);\n gl.vertexAttribPointer(aPosition, 3, gl.FLOAT, false, 0, 0);\n }\n\n // Normal buffer\n this.normalBuffer = gl.createBuffer();\n if (!this.normalBuffer) throw new Error('Failed to create normal buffer');\n gl.bindBuffer(gl.ARRAY_BUFFER, this.normalBuffer);\n gl.bufferData(gl.ARRAY_BUFFER, mesh.normals, gl.STATIC_DRAW);\n if (aNormal !== -1) {\n gl.enableVertexAttribArray(aNormal);\n gl.vertexAttribPointer(aNormal, 3, gl.FLOAT, false, 0, 0);\n }\n\n // TexCoord buffer\n if (mesh.texCoords.length > 0) {\n this.texCoordBuffer = gl.createBuffer();\n if (!this.texCoordBuffer)\n throw new Error('Failed to create texCoord buffer');\n gl.bindBuffer(gl.ARRAY_BUFFER, this.texCoordBuffer);\n gl.bufferData(gl.ARRAY_BUFFER, mesh.texCoords, gl.STATIC_DRAW);\n if (aTexCoord !== -1) {\n gl.enableVertexAttribArray(aTexCoord);\n gl.vertexAttribPointer(aTexCoord, 2, gl.FLOAT, false, 0, 0);\n }\n }\n\n // Index buffer\n if (mesh.indices && mesh.indices.length > 0) {\n this.indexBuffer = gl.createBuffer();\n if (!this.indexBuffer) throw new Error('Failed to create index buffer');\n gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer);\n gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, mesh.indices, gl.STATIC_DRAW);\n }\n\n // Joint indices buffer (skinning)\n const aJointIndices = program\n ? gl.getAttribLocation(program, 'aJointIndices')\n : -1;\n if (\n mesh.jointIndices &&\n mesh.jointIndices.length > 0 &&\n aJointIndices !== -1\n ) {\n this.jointIndexBuffer = gl.createBuffer();\n if (!this.jointIndexBuffer)\n throw new Error('Failed to create joint index buffer');\n gl.bindBuffer(gl.ARRAY_BUFFER, this.jointIndexBuffer);\n gl.bufferData(gl.ARRAY_BUFFER, mesh.jointIndices, gl.STATIC_DRAW);\n gl.enableVertexAttribArray(aJointIndices);\n gl.vertexAttribPointer(aJointIndices, 4, gl.FLOAT, false, 0, 0);\n }\n\n // Joint weights buffer (skinning)\n const aJointWeights = program\n ? gl.getAttribLocation(program, 'aJointWeights')\n : -1;\n if (\n mesh.jointWeights &&\n mesh.jointWeights.length > 0 &&\n aJointWeights !== -1\n ) {\n this.jointWeightBuffer = gl.createBuffer();\n if (!this.jointWeightBuffer)\n throw new Error('Failed to create joint weight buffer');\n gl.bindBuffer(gl.ARRAY_BUFFER, this.jointWeightBuffer);\n gl.bufferData(gl.ARRAY_BUFFER, mesh.jointWeights, gl.STATIC_DRAW);\n gl.enableVertexAttribArray(aJointWeights);\n gl.vertexAttribPointer(aJointWeights, 4, gl.FLOAT, false, 0, 0);\n }\n\n if (gl2) {\n gl2.bindVertexArray(null);\n }\n }\n\n public dispose(gl: WebGL2RenderingContext) {\n if (!gl) {\n console.warn('Cannot dispose MeshBuffers: invalid GL context');\n return;\n }\n\n const isWebGL2 = gl instanceof WebGL2RenderingContext;\n\n if (this.vertexBuffer) gl.deleteBuffer(this.vertexBuffer);\n if (this.normalBuffer) gl.deleteBuffer(this.normalBuffer);\n if (this.texCoordBuffer) gl.deleteBuffer(this.texCoordBuffer);\n if (this.indexBuffer) gl.deleteBuffer(this.indexBuffer);\n if (this.jointIndexBuffer) gl.deleteBuffer(this.jointIndexBuffer);\n if (this.jointWeightBuffer) gl.deleteBuffer(this.jointWeightBuffer);\n\n if (isWebGL2 && this.vao) {\n (gl as unknown as WebGL2RenderingContext).deleteVertexArray(this.vao);\n }\n\n this.vertexBuffer =\n this.normalBuffer =\n this.texCoordBuffer =\n this.indexBuffer =\n this.jointIndexBuffer =\n this.jointWeightBuffer =\n this.vao =\n null;\n }\n}\n","// GLSL shader sources inlined as TypeScript strings so the package can\n// be built with tsup without requiring Vite's ?raw loader.\n\nexport const vsSource = /* glsl */ `\n#define MAX_JOINTS 128\n\nattribute vec3 aPosition;\nattribute vec3 aNormal;\nattribute vec2 aTexCoord;\n\n// ── Skinning attributes (vec4: up to 4 joint influences per vertex) ──\nattribute vec4 aJointIndices;\nattribute vec4 aJointWeights;\n\nuniform mat4 uModel;\nuniform mat4 uView;\nuniform mat4 uProjection;\n\n// ── Skinning uniforms ──\nuniform bool uUseSkinning;\nuniform mat4 uJointMatrices[MAX_JOINTS];\n\nvarying vec3 vNormal;\nvarying vec3 vPosition;\nvarying vec2 vTexCoord;\n\nvoid main() {\n vTexCoord = aTexCoord;\n\n vec4 pos = vec4(aPosition, 1.0);\n vec3 norm = aNormal;\n\n if (uUseSkinning) {\n mat4 skinMat =\n aJointWeights.x * uJointMatrices[int(aJointIndices.x)] +\n aJointWeights.y * uJointMatrices[int(aJointIndices.y)] +\n aJointWeights.z * uJointMatrices[int(aJointIndices.z)] +\n aJointWeights.w * uJointMatrices[int(aJointIndices.w)];\n pos = skinMat * pos;\n norm = mat3(skinMat) * norm;\n }\n\n vNormal = mat3(uModel) * norm;\n\n vec4 worldPosition = uModel * pos;\n vPosition = worldPosition.xyz;\n gl_Position = uProjection * uView * worldPosition;\n}\n`;\n\nexport const fsSource = /* glsl */ `\nprecision mediump float;\n\n#define MAX_LIGHTS 5\n\nvarying vec3 vNormal;\nvarying vec3 vPosition;\nvarying vec2 vTexCoord;\n\nuniform vec3 uLightDirection[MAX_LIGHTS];\nuniform vec3 uLightColor[MAX_LIGHTS];\nuniform float uLightIntensity[MAX_LIGHTS];\nuniform int uLightType[MAX_LIGHTS]; // 0=directional, 1=point, 2=ambient\nuniform int uLightCount;\nuniform vec3 uLightPosition[MAX_LIGHTS];\nuniform float uLightConstant[MAX_LIGHTS];\nuniform float uLightLinear[MAX_LIGHTS];\nuniform float uLightQuadratic[MAX_LIGHTS];\n\nuniform vec4 uColor;\nuniform bool uUnlit;\nuniform bool uUseTexture;\nuniform sampler2D uTexture;\n\nuniform vec3 uViewPosition;\nuniform float uShininess;\nuniform vec3 uSpecularColor;\n\nvoid main() {\n vec4 baseColor = uColor;\n if (uUseTexture) {\n baseColor *= texture2D(uTexture, vTexCoord);\n }\n\n if (uUnlit) {\n gl_FragColor = baseColor;\n return;\n }\n\n vec3 norm = normalize(vNormal);\n vec3 totalDiffuse = vec3(0.0);\n vec3 totalAmbient = vec3(0.0);\n vec3 totalSpecular = vec3(0.0);\n\n vec3 viewDir = normalize(uViewPosition - vPosition);\n\n for (int i = 0; i < MAX_LIGHTS; i++) {\n if (i >= uLightCount) break;\n\n if (uLightType[i] == 2) {\n totalAmbient += uLightColor[i] * uLightIntensity[i];\n } else {\n vec3 lightDir;\n float attenuation = 1.0;\n\n if (uLightType[i] == 0) {\n // Directional light\n lightDir = normalize(-uLightDirection[i]);\n } else {\n // Point light\n vec3 lightVec = uLightPosition[i] - vPosition;\n float distance = length(lightVec);\n lightDir = normalize(lightVec);\n attenuation = 1.0 / (\n uLightConstant[i] +\n uLightLinear[i] * distance +\n uLightQuadratic[i] * distance * distance\n );\n }\n\n // Diffuse\n float diff = max(dot(norm, lightDir), 0.0);\n totalDiffuse += diff * uLightColor[i] * uLightIntensity[i] * attenuation;\n\n // Specular (Blinn-Phong) — softer, more natural highlights\n vec3 halfDir = normalize(lightDir + viewDir);\n float spec = pow(max(dot(norm, halfDir), 0.0), uShininess);\n totalSpecular += spec * uSpecularColor * uLightIntensity[i] * attenuation;\n }\n }\n\n vec3 lightingResult =\n totalAmbient * baseColor.rgb +\n totalDiffuse * baseColor.rgb +\n totalSpecular;\n\n gl_FragColor = vec4(lightingResult, baseColor.a);\n}\n`;\n","/* eslint-disable @typescript-eslint/no-unsafe-argument */\nimport { vsSource, fsSource } from '../shaders/index';\n\nexport type RenderCallback = (\n gl: WebGLRenderingContext,\n program: WebGLProgram,\n) => void;\n\nexport class WebGLCore {\n public gl: WebGL2RenderingContext | WebGLRenderingContext;\n public program: WebGLProgram;\n public canvas: HTMLCanvasElement;\n public isWebGL2: boolean;\n\n /**\n * Initialise the WebGL rendering core.\n *\n * @param canvasOrId - An `HTMLCanvasElement` or the DOM `id` of one.\n * Defaults to `'glcanvas'`.\n */\n constructor(canvasOrId: string | HTMLCanvasElement = 'glcanvas') {\n const canvas =\n typeof canvasOrId === 'string'\n ? (document.getElementById(canvasOrId) as HTMLCanvasElement | null)\n : canvasOrId;\n if (!canvas) throw new Error('Canvas not found');\n this.canvas = canvas;\n\n canvas.width = window.innerWidth;\n canvas.height = window.innerHeight;\n\n this.gl = (canvas.getContext('webgl2') ?? canvas.getContext('webgl')) as\n | WebGL2RenderingContext\n | WebGLRenderingContext;\n\n if (!this.gl) throw new Error('WebGL not supported in this browser!');\n\n this.isWebGL2 = this.gl instanceof WebGL2RenderingContext;\n this.gl.viewport(0, 0, canvas.width, canvas.height);\n\n const program = this.createProgram(this.gl);\n if (!program) throw new Error('Failed to create program');\n this.program = program as unknown as WebGLProgram;\n }\n\n public getProgram(): WebGLProgram {\n return this.program;\n }\n\n public getRenderingContext(): WebGLRenderingContext {\n return this.gl;\n }\n\n private createProgram(gl: WebGLRenderingContext): WebGLProgram | null {\n const vertexShader = this.compileShader(gl, gl.VERTEX_SHADER, vsSource);\n const fragmentShader = this.compileShader(gl, gl.FRAGMENT_SHADER, fsSource);\n\n if (!vertexShader || !fragmentShader)\n throw new Error('Failed to create shaders');\n\n const program = gl.createProgram();\n if (!program) return null;\n\n gl.attachShader(program, vertexShader);\n gl.attachShader(program, fragmentShader);\n gl.linkProgram(program);\n\n // Clean up shader objects\n gl.deleteShader(vertexShader);\n gl.deleteShader(fragmentShader);\n\n if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {\n console.error(gl.getProgramInfoLog(program));\n throw new Error(gl.getProgramInfoLog(program) || 'Program link error');\n }\n\n return program;\n }\n\n private compileShader(\n gl: WebGLRenderingContext,\n type: number,\n source: string,\n ): WebGLShader | null {\n const shader = gl.createShader(type);\n if (!shader) return null;\n\n gl.shaderSource(shader, source);\n gl.compileShader(shader);\n\n if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {\n console.error(gl.getShaderInfoLog(shader));\n gl.deleteShader(shader);\n throw new Error(gl.getShaderInfoLog(shader) || 'Shader compile error');\n }\n return shader;\n }\n\n public resize(width: number, height: number) {\n this.canvas.width = width;\n this.canvas.height = height;\n this.gl.viewport(0, 0, width, height);\n }\n\n dispose() {\n const { gl, program } = this;\n if (program) gl.deleteProgram(program);\n }\n}\n\nexport default WebGLCore;\n","import { mat4, quat, vec3 } from 'gl-matrix';\n\n/** Maximum number of joints supported per skeleton (must match shader). */\nexport const MAX_JOINTS = 128;\n\n/**\n * Describes a single joint (bone) inside a {@link Skeleton}.\n *\n * Each joint stores its default bind-pose transform (translation, rotation,\n * scale) and the inverse bind matrix used to transform vertices from model\n * space into joint-local space.\n */\nexport interface Joint {\n /** Human-readable name (e.g. \"Hips\", \"LeftArm\"). */\n name: string;\n /** Original glTF node index this joint references. */\n nodeIndex: number;\n /** Index of the parent joint in the skeleton's joint array (-1 = root). */\n parentIndex: number;\n /** Bind-pose translation. */\n localTranslation: vec3;\n /** Bind-pose rotation (quaternion). */\n localRotation: quat;\n /** Bind-pose scale. */\n localScale: vec3;\n /** Inverse bind matrix from the glTF skin. */\n inverseBindMatrix: mat4;\n}\n\n/**\n * Per-joint local transform produced by animation sampling.\n * Any undefined component falls back to the bind-pose value.\n */\nexport interface JointPose {\n t?: vec3;\n r?: quat;\n s?: vec3;\n}\n\n/**\n * A skeleton representing a hierarchy of {@link Joint}s.\n *\n * Call {@link computeJointMatrices} each frame with the current animation pose\n * to produce a flat `Float32Array` of `mat4`s ready for upload as a shader\n * uniform.\n */\nexport class Skeleton {\n /** Ordered joint list (parent always precedes child). */\n public readonly joints: Joint[];\n\n /**\n * Flat `Float32Array` of `joints.length` column-major `mat4` values.\n * Updated by {@link computeJointMatrices} — upload directly to\n * `uJointMatrices[0]`.\n */\n public readonly jointMatrices: Float32Array;\n\n /**\n * Global transform of the non-joint ancestor nodes above the skeleton\n * root. In many exported models the armature sits under a scene-root\n * node that carries scale / rotation (e.g. cm → m conversion). This\n * transform is pre-multiplied into root-joint globals so that\n * `global * inverseBindMatrix` evaluates to identity in the bind pose.\n */\n public readonly rootTransform: mat4;\n\n // ── Pre-allocated scratch buffers ──\n /** Per-joint global transform matrices, reused each frame (allocated once in constructor). */\n private readonly _globals: mat4[];\n private readonly _local: mat4 = mat4.create();\n private readonly _final: mat4 = mat4.create();\n\n constructor(joints: Joint[], rootTransform?: mat4) {\n if (joints.length > MAX_JOINTS) {\n console.warn(\n `[Skeleton] ${joints.length} joints exceeds MAX_JOINTS (${MAX_JOINTS}). ` +\n 'Extra joints will be ignored by the shader.',\n );\n }\n this.joints = joints;\n this.jointMatrices = new Float32Array(joints.length * 16);\n this.rootTransform = rootTransform ?? mat4.create();\n // Pre-allocate per-joint globals and scratch matrices — reused every frame.\n this._globals = Array.from({ length: joints.length }, () => mat4.create());\n }\n\n /** Number of joints in the skeleton. */\n get jointCount(): number {\n return this.joints.length;\n }\n\n /**\n * Walk the joint hierarchy, composing global transforms, then multiply by\n * each joint's inverse-bind matrix. The result is stored in\n * {@link jointMatrices} ready for shader upload.\n *\n * @param animatedPoses - Optional per-joint local transforms from an\n * {@link AnimationClip}. Map keys are **joint array indices** (not glTF\n * node indices).\n */\n computeJointMatrices(animatedPoses?: Map<number, JointPose>): void {\n const local = this._local;\n const final = this._final;\n const globals = this._globals;\n\n for (let i = 0; i < this.joints.length; i++) {\n const joint = this.joints[i];\n const pose = animatedPoses?.get(i);\n\n const t = pose?.t ?? joint.localTranslation;\n const r = pose?.r ?? joint.localRotation;\n const s = pose?.s ?? joint.localScale;\n\n mat4.fromRotationTranslationScale(local, r, t, s);\n\n if (joint.parentIndex >= 0) {\n mat4.multiply(globals[i], globals[joint.parentIndex], local);\n } else {\n // Root joint — include the non-joint ancestor transform\n mat4.multiply(globals[i], this.rootTransform, local);\n }\n\n // final = global * inverseBindMatrix\n mat4.multiply(final, globals[i], joint.inverseBindMatrix);\n this.jointMatrices.set(final as unknown as Float32Array, i * 16);\n }\n }\n\n /**\n * Create a deep clone of this skeleton (useful when instancing models).\n */\n clone(): Skeleton {\n const clonedJoints: Joint[] = this.joints.map((j) => ({\n name: j.name,\n nodeIndex: j.nodeIndex,\n parentIndex: j.parentIndex,\n localTranslation: vec3.clone(j.localTranslation),\n localRotation: quat.clone(j.localRotation),\n localScale: vec3.clone(j.localScale),\n inverseBindMatrix: mat4.clone(j.inverseBindMatrix),\n }));\n return new Skeleton(clonedJoints, mat4.clone(this.rootTransform));\n }\n}\n","import { quat, vec3 } from 'gl-matrix';\nimport type { JointPose } from './Skeleton';\n\n// ── Module-level scratch buffers ──\n// Reused across all AnimationClip.sample() calls. Safe because sample() is\n// synchronous and scratch values are copied into pose storage before reuse.\nconst _sv0Vec3 = vec3.create();\nconst _sv1Vec3 = vec3.create();\nconst _sOutVec3 = vec3.create();\nconst _sv0Quat = quat.create();\nconst _sv1Quat = quat.create();\nconst _sOutQuat = quat.create();\n\n// ─── Types ──────────────────────────────────────────────────────\n\n/** Interpolation mode as defined by glTF 2.0. */\nexport type InterpolationMode = 'STEP' | 'LINEAR' | 'CUBICSPLINE';\n\n/** Which transform component the channel drives. */\nexport type AnimationPath = 'translation' | 'rotation' | 'scale';\n\n/**\n * Stores the raw keyframe data for one animated property and performs\n * interpolation between keyframes.\n */\nexport interface AnimationSampler {\n /** Keyframe timestamps (seconds). */\n times: Float32Array;\n /** Flattened keyframe output values (3 or 4 components per key). */\n values: Float32Array;\n /** Interpolation mode (default `LINEAR`). */\n interpolation: InterpolationMode;\n}\n\n/**\n * Links a {@link AnimationSampler} to a specific joint property.\n */\nexport interface AnimationChannel {\n /** Index into the owning {@link Skeleton}'s joint array. */\n jointIndex: number;\n /** Transform component this channel drives. */\n path: AnimationPath;\n /** The sampler that holds keyframe data. */\n sampler: AnimationSampler;\n}\n\n// ─── AnimationClip ──────────────────────────────────────────────\n\n/**\n * A named animation clip containing one or more {@link AnimationChannel}s.\n *\n * Call {@link update} each frame to advance the playhead, then {@link sample}\n * to obtain per-joint local transforms that feed into\n * `Skeleton.computeJointMatrices()`.\n *\n * ```ts\n * clip.play();\n * // game loop:\n * clip.update(deltaTime);\n * const poses = clip.sample();\n * skeleton.computeJointMatrices(poses);\n * ```\n */\nexport class AnimationClip {\n /** Human-readable name (e.g. \"Walk\", \"Idle\"). */\n public readonly name: string;\n\n /** All channels in this clip. */\n public readonly channels: AnimationChannel[];\n\n /** Total duration in seconds (derived from the maximum keyframe time). */\n public readonly duration: number;\n\n /** Current playback time in seconds. */\n public currentTime = 0;\n\n /** Whether the clip loops when it reaches the end. */\n public loop = true;\n\n /** Playback speed multiplier (1 = normal, 2 = double, −1 = reverse). */\n public speed = 1;\n\n /** Whether the clip is currently advancing. */\n public playing = false;\n\n /**\n * Per-joint pose objects reused across frames to avoid per-frame allocations.\n * The vec3/quat inside each entry are also pre-allocated on first use.\n */\n private readonly _posePool = new Map<number, JointPose>();\n\n constructor(name: string, channels: AnimationChannel[], duration: number) {\n this.name = name;\n this.channels = channels;\n this.duration = duration;\n }\n\n /** Start / resume playback. */\n play(): void {\n this.playing = true;\n }\n\n /** Pause playback (keeps current time). */\n pause(): void {\n this.playing = false;\n }\n\n /** Stop and rewind to the beginning. */\n stop(): void {\n this.playing = false;\n this.currentTime = 0;\n }\n\n /** Rewind to the beginning without changing play state. */\n reset(): void {\n this.currentTime = 0;\n }\n\n /**\n * Advance the playhead by `deltaTime` seconds (respects {@link speed}).\n * Automatically loops or clamps depending on {@link loop}.\n */\n update(deltaTime: number): void {\n if (!this.playing || this.duration <= 0) return;\n\n this.currentTime += deltaTime * this.speed;\n\n if (this.loop) {\n this.currentTime =\n ((this.currentTime % this.duration) + this.duration) % this.duration;\n } else {\n if (this.currentTime >= this.duration) {\n this.currentTime = this.duration;\n this.playing = false;\n } else if (this.currentTime < 0) {\n this.currentTime = 0;\n this.playing = false;\n }\n }\n }\n\n /**\n * Sample every channel at the current playhead and write per-joint local\n * transforms into `out` (or a newly created map if none is provided).\n *\n * Pass a persistent `Map` to avoid allocating a new one every frame.\n */\n sample(out?: Map<number, JointPose>): Map<number, JointPose> {\n const result = out ?? new Map<number, JointPose>();\n if (out) out.clear();\n\n for (const channel of this.channels) {\n // Reuse the JointPose object for this joint from the pool.\n let entry = this._posePool.get(channel.jointIndex);\n if (!entry) {\n entry = {};\n this._posePool.set(channel.jointIndex, entry);\n }\n result.set(channel.jointIndex, entry);\n\n // sampleChannel writes into a module-level scratch buffer.\n // Copy the result into the pre-allocated fields of the pose entry.\n const value = sampleChannel(channel, this.currentTime);\n switch (channel.path) {\n case 'translation':\n if (!entry.t) entry.t = vec3.create();\n vec3.copy(entry.t, value as vec3);\n break;\n case 'rotation':\n if (!entry.r) entry.r = quat.create();\n quat.copy(entry.r, value as quat);\n break;\n case 'scale':\n if (!entry.s) entry.s = vec3.create();\n vec3.copy(entry.s, value as vec3);\n break;\n }\n }\n\n return result;\n }\n\n /**\n * Create an independent copy of this clip (shares the underlying sampler\n * data but has its own playback state).\n */\n clone(): AnimationClip {\n const copy = new AnimationClip(this.name, this.channels, this.duration);\n copy.loop = this.loop;\n copy.speed = this.speed;\n return copy;\n }\n}\n\n// ── Sampling helpers ───────────────────────────────────────────────\n\n/** Write `numComponents` floats from `values` at `index * numComponents` into `out`. */\nfunction readValueInto(\n values: Float32Array,\n index: number,\n numComponents: number,\n out: vec3 | quat,\n): void {\n const off = index * numComponents;\n out[0] = values[off];\n out[1] = values[off + 1];\n out[2] = values[off + 2];\n if (numComponents === 4) out[3] = values[off + 3];\n}\n\n/**\n * Sample a single channel at the given time. Writes the result into a\n * module-level scratch buffer and returns a reference to it.\n * The caller must copy the value before the next sampleChannel invocation.\n */\nfunction sampleChannel(channel: AnimationChannel, time: number): vec3 | quat {\n const { sampler, path } = channel;\n const { times, values, interpolation } = sampler;\n const numComponents = path === 'rotation' ? 4 : 3;\n const outScratch: vec3 | quat = numComponents === 4 ? _sOutQuat : _sOutVec3;\n\n // Edge cases — clamp to first / last keyframe\n if (times.length === 0) {\n if (numComponents === 4) quat.identity(outScratch as quat);\n else vec3.set(outScratch as vec3, 0, 0, 0);\n return outScratch;\n }\n if (time <= times[0]) {\n readValueInto(values, 0, numComponents, outScratch);\n return outScratch;\n }\n if (time >= times[times.length - 1]) {\n readValueInto(values, times.length - 1, numComponents, outScratch);\n return outScratch;\n }\n\n // Binary search for the surrounding keyframe pair\n let lo = 0;\n let hi = times.length - 1;\n while (lo < hi - 1) {\n const mid = (lo + hi) >>> 1;\n if (times[mid] <= time) {\n lo = mid;\n } else {\n hi = mid;\n }\n }\n\n const t0 = times[lo];\n const t1 = times[hi];\n const factor = (time - t0) / (t1 - t0);\n\n const v0Scratch: vec3 | quat = numComponents === 4 ? _sv0Quat : _sv0Vec3;\n const v1Scratch: vec3 | quat = numComponents === 4 ? _sv1Quat : _sv1Vec3;\n readValueInto(values, lo, numComponents, v0Scratch);\n readValueInto(values, hi, numComponents, v1Scratch);\n\n if (interpolation === 'STEP') {\n // STEP: return v0 unchanged\n readValueInto(values, lo, numComponents, outScratch);\n return outScratch;\n }\n\n // LINEAR (or fallback for CUBICSPLINE — true cubic not yet implemented)\n if (path === 'rotation') {\n quat.slerp(outScratch as quat, v0Scratch as quat, v1Scratch as quat, factor);\n return outScratch;\n }\n\n vec3.lerp(outScratch as vec3, v0Scratch as vec3, v1Scratch as vec3, factor);\n return outScratch;\n}\n","import { Material, Mesh } from '../classes';\nimport { GL_LINES } from '../classes/Mesh';\nimport { Vector3 } from '../domain/interfaces/Vectors';\n\nexport function createWireBox(\n min: Vector3,\n max: Vector3,\n material: Material,\n): Mesh {\n const [minX, minY, minZ] = min;\n const [maxX, maxY, maxZ] = max;\n\n // 8 corners of the box\n const vertices: number[] = [\n minX,\n minY,\n minZ,\n maxX,\n minY,\n minZ,\n maxX,\n maxY,\n minZ,\n minX,\n maxY,\n minZ,\n minX,\n minY,\n maxZ,\n maxX,\n minY,\n maxZ,\n maxX,\n maxY,\n maxZ,\n minX,\n maxY,\n maxZ,\n ];\n\n // edges as lines (pairs of indices)\n const indices: number[] = [\n 0,\n 1,\n 1,\n 2,\n 2,\n 3,\n 3,\n 0, // bottom face\n 4,\n 5,\n 5,\n 6,\n 6,\n 7,\n 7,\n 4, // top face\n 0,\n 4,\n 1,\n 5,\n 2,\n 6,\n 3,\n 7, // vertical edges\n ];\n\n // expand indices to vertices array (since WebGL uses gl.LINES)\n const lineVertices: number[] = [];\n for (let i = 0; i < indices.length; i++) {\n const idx = indices[i];\n lineVertices.push(\n vertices[idx * 3],\n vertices[idx * 3 + 1],\n vertices[idx * 3 + 2],\n );\n }\n\n // Normals can just be zero for wireframe\n const normals = new Float32Array(lineVertices.length);\n\n const mesh = new Mesh(\n 'hitbox',\n new Float32Array(lineVertices),\n normals,\n material,\n new Float32Array(),\n null,\n );\n mesh.setMode(GL_LINES);\n return mesh;\n}\n"],"mappings":"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","names":["mat4","quat","vec3","mat4"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fonsecabarreto/genesis-gl-core",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "GenesisGL — WebGL 3D engine for browser games",
5
5
  "author": "Lucas Fonseca Barreto",
6
6
  "license": "MIT",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/Core/classes/Camera.ts","../src/Core/controls/Mouse/MouseWheelControl.ts","../src/Core/controls/Mouse/MouseDragControl.ts","../src/Core/controls/Mouse/MousePointerLockControl.ts","../src/Core/classes/Viewport.ts"],"sourcesContent":["import { mat4, vec3, vec4 } from 'gl-matrix';\nimport { Vector3 } from '../domain/interfaces/Vectors';\n\n/**\n * Perspective camera with orbit (yaw/pitch), zoom, and viewport controls.\n */\nexport class Camera {\n public target: Vector3 = [0, 0, 0]; // camera looks at this\n\n /* Translation */\n public position: Vector3 = [0, 0, 0]; // 3D position\n\n /* Zoom */\n public zoom: number = 0.89;\n public minZoom: number = 0.5;\n public maxZoom: number = 1;\n public zoomSpeed: number = 0.06;\n\n /* Rotation */\n public yaw: number = 0; // horizontal rotation\n public pitch: number = 0.5; // vertical rotation\n\n public near = 0.01;\n public far = 1000.0;\n\n /* Render Utils */\n public lastViewMatrix?: Float32Array;\n public lastProjectionMatrix?: Float32Array;\n\n constructor(\n public viewportWidth: number,\n public viewportHeight: number,\n ) {}\n\n /** Translate the camera by a delta. */\n move(dx: number, dy: number, dz: number = 0) {\n this.position[0] += dx;\n this.position[1] += dy;\n this.position[2] += dz;\n }\n\n /** Clamp-set the zoom level. */\n setZoom(zoom: number) {\n this.zoom = Math.max(this.minZoom, Math.min(this.maxZoom, zoom));\n }\n\n /** Zoom by a signed delta (positive = zoom in). */\n zoomBy(delta: number) {\n const factor = Math.exp(delta * this.zoomSpeed);\n this.setZoom(this.zoom * factor);\n }\n\n worldToNDC(x: number, y: number): [number, number] {\n const ndcX = ((x - this.position[0]) / this.viewportWidth) * 2 * this.zoom;\n const ndcY = ((y - this.position[1]) / this.viewportHeight) * 2 * this.zoom;\n return [ndcX, ndcY];\n }\n\n worldScale(scale: [number, number]): [number, number] {\n return [\n (scale[0] / this.viewportWidth) * 2 * this.zoom,\n (scale[1] / this.viewportHeight) * 2 * this.zoom,\n ];\n }\n\n setViewport(width: number, height: number) {\n this.viewportWidth = width;\n this.viewportHeight = height;\n }\n\n /** Compute the eye position from yaw/pitch/distance to target. */\n getComputedPosition(): vec3 {\n const radius = vec3.distance(this.position, this.target);\n\n const camX =\n this.target[0] + radius * Math.cos(this.pitch) * Math.sin(this.yaw);\n const camY = this.target[1] + radius * Math.sin(this.pitch);\n const camZ =\n this.target[2] + radius * Math.cos(this.pitch) * Math.cos(this.yaw);\n\n return vec3.fromValues(camX, camY, camZ);\n }\n\n /** Build the view matrix (lookAt). */\n getViewMatrix(): mat4 {\n const view = mat4.create();\n\n const eye = this.getComputedPosition(); // Use the computed position\n const up = vec3.fromValues(0, 1, 0);\n\n mat4.lookAt(view, eye, this.target, up);\n return view;\n }\n\n /** Build the perspective projection matrix. */\n getProjectionMatrix(): mat4 {\n const projection = mat4.create();\n const aspect = this.viewportWidth / this.viewportHeight;\n const fov = Math.PI / 4 / this.zoom;\n mat4.perspective(projection, fov, aspect, this.near, this.far);\n return projection;\n }\n\n worldToNDC3D(x: number, y: number, z: number = 0): [number, number, number] {\n const world = vec4.fromValues(x, y, z, 1.0);\n const mvp = mat4.create();\n mat4.multiply(mvp, this.getProjectionMatrix(), this.getViewMatrix());\n vec4.transformMat4(world, world, mvp);\n return [world[0] / world[3], world[1] / world[3], world[2] / world[3]];\n }\n\n worldScale3D(scale: [number, number, number]): [number, number, number] {\n return [\n (scale[0] / this.viewportWidth) * 2 * this.zoom,\n (scale[1] / this.viewportHeight) * 2 * this.zoom,\n (scale[2] / ((this.viewportWidth + this.viewportHeight) / 2)) *\n 2 *\n this.zoom,\n ];\n }\n\n public minPitch: number = -Math.PI / 2 + 1.49;\n public maxPitch: number = Math.PI / 2 - 0.01;\n\n rotate(deltaYaw: number, deltaPitch: number) {\n this.yaw += deltaYaw;\n\n this.pitch -= deltaPitch; // ← add deltaPitch here\n this.pitch = Math.max(this.minPitch, Math.min(this.maxPitch, this.pitch));\n }\n}\n","import { IMouseControl } from './IMouseControl';\n\nexport type WheelDirection = 'up' | 'down';\nexport type WheelListener = (direction: WheelDirection, delta: number) => void;\n\nexport class MouseWheelControl implements IMouseControl {\n private listeners: WheelListener[] = [];\n private wheelHandler?: (e: WheelEvent) => void;\n\n public enable() {\n if (this.wheelHandler) return;\n\n this.wheelHandler = (e: WheelEvent) => {\n const direction: WheelDirection = e.deltaY < 0 ? 'up' : 'down';\n this.notify(direction, e.deltaY);\n };\n\n window.addEventListener('wheel', this.wheelHandler, { passive: true });\n }\n\n public disable() {\n if (this.wheelHandler) {\n window.removeEventListener('wheel', this.wheelHandler);\n this.wheelHandler = undefined;\n }\n }\n\n public onChange(listener: WheelListener) {\n this.listeners.push(listener);\n }\n\n /** Remove a previously registered listener. */\n public removeListener(listener: WheelListener) {\n this.listeners = this.listeners.filter((l) => l !== listener);\n }\n\n private notify(direction: WheelDirection, delta: number) {\n for (const listener of this.listeners) listener(direction, delta);\n }\n}\n","import { IMouseControl } from './IMouseControl';\n\nexport type DragListener = (dx: number, dy: number, button: number) => void;\n\nexport class MouseDragControl implements IMouseControl {\n private listeners: DragListener[] = [];\n public isDragging = false;\n\n private dragButton: number = 0; // 0 = left, 2 = right\n private lastX = 0;\n private lastY = 0;\n\n constructor(private element: HTMLElement) {}\n\n enable() {\n this.element.addEventListener('mousedown', this.onMouseDown);\n window.addEventListener('mouseup', this.onMouseUp);\n window.addEventListener('mousemove', this.onMouseMove);\n }\n\n disable() {\n this.element.removeEventListener('mousedown', this.onMouseDown);\n window.removeEventListener('mouseup', this.onMouseUp);\n window.removeEventListener('mousemove', this.onMouseMove);\n }\n\n onChange(listener: DragListener) {\n this.listeners.push(listener);\n }\n\n /** Remove a previously registered listener. */\n removeListener(listener: DragListener) {\n this.listeners = this.listeners.filter((l) => l !== listener);\n }\n\n private onMouseDown = (e: MouseEvent) => {\n this.isDragging = true;\n this.dragButton = e.button;\n\n this.lastX = e.clientX;\n this.lastY = e.clientY;\n };\n\n private onMouseUp = () => {\n this.isDragging = false;\n };\n\n private onMouseMove = (e: MouseEvent) => {\n if (!this.isDragging) return;\n\n const dx = e.clientX - this.lastX;\n const dy = e.clientY - this.lastY;\n\n this.lastX = e.clientX;\n this.lastY = e.clientY;\n\n for (const listener of this.listeners) listener(dx, dy, this.dragButton);\n };\n}\n","import { IMouseControl } from './IMouseControl';\n\nexport type MoveListener = (dx: number, dy: number) => void;\n\n/**\n * Captures mouse movement using the Pointer Lock API.\n * Ideal for camera-like controls where the cursor should not hit screen edges.\n */\nexport class MousePointerLockControl implements IMouseControl {\n private listeners: MoveListener[] = [];\n private isEnabled = false;\n private isPointerLocked = false;\n\n constructor(\n private element: HTMLElement,\n private sensitivity = 1,\n ) {}\n\n enable() {\n if (this.isEnabled) return;\n this.isEnabled = true;\n\n // Must be initiated by user click\n this.element.addEventListener('click', this.requestPointerLock);\n document.addEventListener('pointerlockchange', this.onPointerLockChange);\n document.addEventListener('mousemove', this.onMouseMove);\n }\n\n disable() {\n if (!this.isEnabled) return;\n this.isEnabled = false;\n\n this.element.removeEventListener('click', this.requestPointerLock);\n document.removeEventListener('pointerlockchange', this.onPointerLockChange);\n document.removeEventListener('mousemove', this.onMouseMove);\n\n if (this.isPointerLocked) document.exitPointerLock();\n }\n\n onChange(listener: MoveListener) {\n this.listeners.push(listener);\n }\n\n /** Remove a previously registered listener. */\n removeListener(listener: MoveListener) {\n this.listeners = this.listeners.filter((l) => l !== listener);\n }\n\n setSensitivity(value: number) {\n this.sensitivity = value;\n }\n\n private requestPointerLock = () => {\n this.element.requestPointerLock();\n };\n\n private onPointerLockChange = () => {\n this.isPointerLocked = document.pointerLockElement === this.element;\n };\n\n private onMouseMove = (e: MouseEvent) => {\n if (!this.isPointerLocked) return;\n\n const dx = e.movementX * this.sensitivity;\n const dy = e.movementY * this.sensitivity;\n\n for (const listener of this.listeners) listener(dx, dy);\n };\n}\n","import { Camera } from './Camera';\nimport {\n MouseWheelControl,\n WheelDirection,\n} from '../controls/Mouse/MouseWheelControl';\nimport { MouseDragControl } from '../controls/Mouse/MouseDragControl';\nimport { MousePointerLockControl } from '../controls/Mouse/MousePointerLockControl';\nimport WebGLCore from './WebGLCore';\n\nexport class Viewport {\n private static zoomInterval = 25;\n\n private wheelControl = new MouseWheelControl();\n private dragControl: MouseDragControl;\n private moveControl: MousePointerLockControl;\n\n private lastZoomTime: number = 0;\n private canvas: HTMLCanvasElement;\n private scale: number = 1;\n private webglCore: WebGLCore | null = null;\n private resizeHandler = () => this.resizeCanvas();\n public camera: Camera;\n\n /**\n * @param canvasOrId - An HTMLCanvasElement or the `id` attribute of one.\n * @param width - Logical viewport width.\n * @param height - Logical viewport height.\n * @param webglCore - Optional WebGLCore instance. When provided, the existing\n * GL context is reused for viewport resize instead of\n * requesting a new one.\n */\n constructor(\n canvasOrId: string | HTMLCanvasElement,\n public width: number,\n public height: number,\n webglCore?: WebGLCore,\n ) {\n this.canvas =\n typeof canvasOrId === 'string'\n ? (document.getElementById(canvasOrId) as HTMLCanvasElement)\n : canvasOrId;\n if (!this.canvas) throw new Error('Canvas not found');\n\n this.webglCore = webglCore ?? null;\n this.camera = new Camera(width, height);\n this.dragControl = new MouseDragControl(this.canvas);\n this.moveControl = new MousePointerLockControl(this.canvas, 0.25);\n\n this.resizeCanvas();\n this.setupControls();\n\n window.addEventListener('resize', this.resizeHandler);\n }\n\n private setupControls() {\n const zoomInterval = Viewport.zoomInterval;\n\n // Camera Zoom\n this.wheelControl.onChange((direction: WheelDirection) => {\n const now = performance.now();\n if (now - this.lastZoomTime < zoomInterval) return;\n\n const zoomDelta = direction === 'up' ? 1 : -1;\n this.camera.zoomBy(zoomDelta);\n\n this.lastZoomTime = now;\n });\n this.wheelControl.enable();\n\n // Drag / pan / rotate\n /* this.dragControl.onChange((dx, dy, button) => {\n const factor = 0.01 * this.camera.zoom;\n\n if (button === 2) {\n // Right click = orbit\n this.camera.rotate(-dx * factor, -dy * factor);\n } else if (button === 0) {\n this.camera.target[0] -= (dx * factor) / this.scale;\n this.camera.target[1] += (dy * factor) / this.scale;\n }\n\n });\n this.dragControl.enable(); */\n\n this.moveControl.onChange((dx, dy) => {\n const factor = 0.01 * this.camera.zoom;\n this.camera.rotate(-dx * factor, -dy * factor);\n });\n this.moveControl.enable();\n\n this.canvas.addEventListener('contextmenu', (e) => e.preventDefault());\n }\n\n public isDraggingCamera(): boolean {\n return this.dragControl.isDragging;\n }\n\n private resizeCanvas() {\n const screenWidth = window.innerWidth;\n const screenHeight = window.innerHeight;\n\n const aspectViewport = this.width / this.height;\n const aspectScreen = screenWidth / screenHeight;\n\n let canvasWidth: number, canvasHeight: number;\n\n if (aspectScreen > aspectViewport) {\n canvasHeight = screenHeight;\n canvasWidth = canvasHeight * aspectViewport;\n } else {\n canvasWidth = screenWidth;\n canvasHeight = canvasWidth / aspectViewport;\n }\n\n this.canvas.width = canvasWidth;\n this.canvas.height = canvasHeight;\n\n this.scale = canvasWidth / this.width;\n\n // Reuse the existing GL context when available, otherwise fall back.\n const gl = this.webglCore\n ? this.webglCore.getRenderingContext()\n : (this.canvas.getContext('webgl2') ?? this.canvas.getContext('webgl'));\n\n if (!gl) throw new Error('WebGL not supported');\n gl.viewport(0, 0, canvasWidth, canvasHeight);\n\n this.camera.setViewport(canvasWidth, canvasHeight);\n }\n\n getCanvas(): HTMLCanvasElement {\n return this.canvas;\n }\n\n getScale(): number {\n return this.scale;\n }\n\n /** Release event listeners. Call when the viewport is no longer needed. */\n dispose() {\n window.removeEventListener('resize', this.resizeHandler);\n this.wheelControl.disable();\n this.dragControl.disable();\n this.moveControl.disable();\n }\n}\n"],"mappings":";AAAA,SAAS,MAAM,MAAM,YAAY;AAM1B,IAAM,SAAN,MAAa;AAAA,EAuBlB,YACS,eACA,gBACP;AAFO;AACA;AAAA,EACN;AAAA,EAFM;AAAA,EACA;AAAA,EAxBF,SAAkB,CAAC,GAAG,GAAG,CAAC;AAAA;AAAA;AAAA,EAG1B,WAAoB,CAAC,GAAG,GAAG,CAAC;AAAA;AAAA;AAAA,EAG5B,OAAe;AAAA,EACf,UAAkB;AAAA,EAClB,UAAkB;AAAA,EAClB,YAAoB;AAAA;AAAA,EAGpB,MAAc;AAAA;AAAA,EACd,QAAgB;AAAA;AAAA,EAEhB,OAAO;AAAA,EACP,MAAM;AAAA;AAAA,EAGN;AAAA,EACA;AAAA;AAAA,EAQP,KAAK,IAAY,IAAY,KAAa,GAAG;AAC3C,SAAK,SAAS,CAAC,KAAK;AACpB,SAAK,SAAS,CAAC,KAAK;AACpB,SAAK,SAAS,CAAC,KAAK;AAAA,EACtB;AAAA;AAAA,EAGA,QAAQ,MAAc;AACpB,SAAK,OAAO,KAAK,IAAI,KAAK,SAAS,KAAK,IAAI,KAAK,SAAS,IAAI,CAAC;AAAA,EACjE;AAAA;AAAA,EAGA,OAAO,OAAe;AACpB,UAAM,SAAS,KAAK,IAAI,QAAQ,KAAK,SAAS;AAC9C,SAAK,QAAQ,KAAK,OAAO,MAAM;AAAA,EACjC;AAAA,EAEA,WAAW,GAAW,GAA6B;AACjD,UAAM,QAAS,IAAI,KAAK,SAAS,CAAC,KAAK,KAAK,gBAAiB,IAAI,KAAK;AACtE,UAAM,QAAS,IAAI,KAAK,SAAS,CAAC,KAAK,KAAK,iBAAkB,IAAI,KAAK;AACvE,WAAO,CAAC,MAAM,IAAI;AAAA,EACpB;AAAA,EAEA,WAAW,OAA2C;AACpD,WAAO;AAAA,MACJ,MAAM,CAAC,IAAI,KAAK,gBAAiB,IAAI,KAAK;AAAA,MAC1C,MAAM,CAAC,IAAI,KAAK,iBAAkB,IAAI,KAAK;AAAA,IAC9C;AAAA,EACF;AAAA,EAEA,YAAY,OAAe,QAAgB;AACzC,SAAK,gBAAgB;AACrB,SAAK,iBAAiB;AAAA,EACxB;AAAA;AAAA,EAGA,sBAA4B;AAC1B,UAAM,SAAS,KAAK,SAAS,KAAK,UAAU,KAAK,MAAM;AAEvD,UAAM,OACJ,KAAK,OAAO,CAAC,IAAI,SAAS,KAAK,IAAI,KAAK,KAAK,IAAI,KAAK,IAAI,KAAK,GAAG;AACpE,UAAM,OAAO,KAAK,OAAO,CAAC,IAAI,SAAS,KAAK,IAAI,KAAK,KAAK;AAC1D,UAAM,OACJ,KAAK,OAAO,CAAC,IAAI,SAAS,KAAK,IAAI,KAAK,KAAK,IAAI,KAAK,IAAI,KAAK,GAAG;AAEpE,WAAO,KAAK,WAAW,MAAM,MAAM,IAAI;AAAA,EACzC;AAAA;AAAA,EAGA,gBAAsB;AACpB,UAAM,OAAO,KAAK,OAAO;AAEzB,UAAM,MAAM,KAAK,oBAAoB;AACrC,UAAM,KAAK,KAAK,WAAW,GAAG,GAAG,CAAC;AAElC,SAAK,OAAO,MAAM,KAAK,KAAK,QAAQ,EAAE;AACtC,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,sBAA4B;AAC1B,UAAM,aAAa,KAAK,OAAO;AAC/B,UAAM,SAAS,KAAK,gBAAgB,KAAK;AACzC,UAAM,MAAM,KAAK,KAAK,IAAI,KAAK;AAC/B,SAAK,YAAY,YAAY,KAAK,QAAQ,KAAK,MAAM,KAAK,GAAG;AAC7D,WAAO;AAAA,EACT;AAAA,EAEA,aAAa,GAAW,GAAW,IAAY,GAA6B;AAC1E,UAAM,QAAQ,KAAK,WAAW,GAAG,GAAG,GAAG,CAAG;AAC1C,UAAM,MAAM,KAAK,OAAO;AACxB,SAAK,SAAS,KAAK,KAAK,oBAAoB,GAAG,KAAK,cAAc,CAAC;AACnE,SAAK,cAAc,OAAO,OAAO,GAAG;AACpC,WAAO,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,MAAM,CAAC,CAAC;AAAA,EACvE;AAAA,EAEA,aAAa,OAA2D;AACtE,WAAO;AAAA,MACJ,MAAM,CAAC,IAAI,KAAK,gBAAiB,IAAI,KAAK;AAAA,MAC1C,MAAM,CAAC,IAAI,KAAK,iBAAkB,IAAI,KAAK;AAAA,MAC3C,MAAM,CAAC,MAAM,KAAK,gBAAgB,KAAK,kBAAkB,KACxD,IACA,KAAK;AAAA,IACT;AAAA,EACF;AAAA,EAEO,WAAmB,CAAC,KAAK,KAAK,IAAI;AAAA,EAClC,WAAmB,KAAK,KAAK,IAAI;AAAA,EAExC,OAAO,UAAkB,YAAoB;AAC3C,SAAK,OAAO;AAEZ,SAAK,SAAS;AACd,SAAK,QAAQ,KAAK,IAAI,KAAK,UAAU,KAAK,IAAI,KAAK,UAAU,KAAK,KAAK,CAAC;AAAA,EAC1E;AACF;;;AC7HO,IAAM,oBAAN,MAAiD;AAAA,EAC9C,YAA6B,CAAC;AAAA,EAC9B;AAAA,EAED,SAAS;AACd,QAAI,KAAK,aAAc;AAEvB,SAAK,eAAe,CAAC,MAAkB;AACrC,YAAM,YAA4B,EAAE,SAAS,IAAI,OAAO;AACxD,WAAK,OAAO,WAAW,EAAE,MAAM;AAAA,IACjC;AAEA,WAAO,iBAAiB,SAAS,KAAK,cAAc,EAAE,SAAS,KAAK,CAAC;AAAA,EACvE;AAAA,EAEO,UAAU;AACf,QAAI,KAAK,cAAc;AACrB,aAAO,oBAAoB,SAAS,KAAK,YAAY;AACrD,WAAK,eAAe;AAAA,IACtB;AAAA,EACF;AAAA,EAEO,SAAS,UAAyB;AACvC,SAAK,UAAU,KAAK,QAAQ;AAAA,EAC9B;AAAA;AAAA,EAGO,eAAe,UAAyB;AAC7C,SAAK,YAAY,KAAK,UAAU,OAAO,CAAC,MAAM,MAAM,QAAQ;AAAA,EAC9D;AAAA,EAEQ,OAAO,WAA2B,OAAe;AACvD,eAAW,YAAY,KAAK,UAAW,UAAS,WAAW,KAAK;AAAA,EAClE;AACF;;;ACnCO,IAAM,mBAAN,MAAgD;AAAA,EAQrD,YAAoB,SAAsB;AAAtB;AAAA,EAAuB;AAAA,EAAvB;AAAA,EAPZ,YAA4B,CAAC;AAAA,EAC9B,aAAa;AAAA,EAEZ,aAAqB;AAAA;AAAA,EACrB,QAAQ;AAAA,EACR,QAAQ;AAAA,EAIhB,SAAS;AACP,SAAK,QAAQ,iBAAiB,aAAa,KAAK,WAAW;AAC3D,WAAO,iBAAiB,WAAW,KAAK,SAAS;AACjD,WAAO,iBAAiB,aAAa,KAAK,WAAW;AAAA,EACvD;AAAA,EAEA,UAAU;AACR,SAAK,QAAQ,oBAAoB,aAAa,KAAK,WAAW;AAC9D,WAAO,oBAAoB,WAAW,KAAK,SAAS;AACpD,WAAO,oBAAoB,aAAa,KAAK,WAAW;AAAA,EAC1D;AAAA,EAEA,SAAS,UAAwB;AAC/B,SAAK,UAAU,KAAK,QAAQ;AAAA,EAC9B;AAAA;AAAA,EAGA,eAAe,UAAwB;AACrC,SAAK,YAAY,KAAK,UAAU,OAAO,CAAC,MAAM,MAAM,QAAQ;AAAA,EAC9D;AAAA,EAEQ,cAAc,CAAC,MAAkB;AACvC,SAAK,aAAa;AAClB,SAAK,aAAa,EAAE;AAEpB,SAAK,QAAQ,EAAE;AACf,SAAK,QAAQ,EAAE;AAAA,EACjB;AAAA,EAEQ,YAAY,MAAM;AACxB,SAAK,aAAa;AAAA,EACpB;AAAA,EAEQ,cAAc,CAAC,MAAkB;AACvC,QAAI,CAAC,KAAK,WAAY;AAEtB,UAAM,KAAK,EAAE,UAAU,KAAK;AAC5B,UAAM,KAAK,EAAE,UAAU,KAAK;AAE5B,SAAK,QAAQ,EAAE;AACf,SAAK,QAAQ,EAAE;AAEf,eAAW,YAAY,KAAK,UAAW,UAAS,IAAI,IAAI,KAAK,UAAU;AAAA,EACzE;AACF;;;AClDO,IAAM,0BAAN,MAAuD;AAAA,EAK5D,YACU,SACA,cAAc,GACtB;AAFQ;AACA;AAAA,EACP;AAAA,EAFO;AAAA,EACA;AAAA,EANF,YAA4B,CAAC;AAAA,EAC7B,YAAY;AAAA,EACZ,kBAAkB;AAAA,EAO1B,SAAS;AACP,QAAI,KAAK,UAAW;AACpB,SAAK,YAAY;AAGjB,SAAK,QAAQ,iBAAiB,SAAS,KAAK,kBAAkB;AAC9D,aAAS,iBAAiB,qBAAqB,KAAK,mBAAmB;AACvE,aAAS,iBAAiB,aAAa,KAAK,WAAW;AAAA,EACzD;AAAA,EAEA,UAAU;AACR,QAAI,CAAC,KAAK,UAAW;AACrB,SAAK,YAAY;AAEjB,SAAK,QAAQ,oBAAoB,SAAS,KAAK,kBAAkB;AACjE,aAAS,oBAAoB,qBAAqB,KAAK,mBAAmB;AAC1E,aAAS,oBAAoB,aAAa,KAAK,WAAW;AAE1D,QAAI,KAAK,gBAAiB,UAAS,gBAAgB;AAAA,EACrD;AAAA,EAEA,SAAS,UAAwB;AAC/B,SAAK,UAAU,KAAK,QAAQ;AAAA,EAC9B;AAAA;AAAA,EAGA,eAAe,UAAwB;AACrC,SAAK,YAAY,KAAK,UAAU,OAAO,CAAC,MAAM,MAAM,QAAQ;AAAA,EAC9D;AAAA,EAEA,eAAe,OAAe;AAC5B,SAAK,cAAc;AAAA,EACrB;AAAA,EAEQ,qBAAqB,MAAM;AACjC,SAAK,QAAQ,mBAAmB;AAAA,EAClC;AAAA,EAEQ,sBAAsB,MAAM;AAClC,SAAK,kBAAkB,SAAS,uBAAuB,KAAK;AAAA,EAC9D;AAAA,EAEQ,cAAc,CAAC,MAAkB;AACvC,QAAI,CAAC,KAAK,gBAAiB;AAE3B,UAAM,KAAK,EAAE,YAAY,KAAK;AAC9B,UAAM,KAAK,EAAE,YAAY,KAAK;AAE9B,eAAW,YAAY,KAAK,UAAW,UAAS,IAAI,EAAE;AAAA,EACxD;AACF;;;AC3DO,IAAM,WAAN,MAAM,UAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAsBpB,YACE,YACO,OACA,QACP,WACA;AAHO;AACA;AAGP,SAAK,SACH,OAAO,eAAe,WACjB,SAAS,eAAe,UAAU,IACnC;AACN,QAAI,CAAC,KAAK,OAAQ,OAAM,IAAI,MAAM,kBAAkB;AAEpD,SAAK,YAAY,aAAa;AAC9B,SAAK,SAAS,IAAI,OAAO,OAAO,MAAM;AACtC,SAAK,cAAc,IAAI,iBAAiB,KAAK,MAAM;AACnD,SAAK,cAAc,IAAI,wBAAwB,KAAK,QAAQ,IAAI;AAEhE,SAAK,aAAa;AAClB,SAAK,cAAc;AAEnB,WAAO,iBAAiB,UAAU,KAAK,aAAa;AAAA,EACtD;AAAA,EAnBS;AAAA,EACA;AAAA,EAxBT,OAAe,eAAe;AAAA,EAEtB,eAAe,IAAI,kBAAkB;AAAA,EACrC;AAAA,EACA;AAAA,EAEA,eAAuB;AAAA,EACvB;AAAA,EACA,QAAgB;AAAA,EAChB,YAA8B;AAAA,EAC9B,gBAAgB,MAAM,KAAK,aAAa;AAAA,EACzC;AAAA,EAiCC,gBAAgB;AACtB,UAAM,eAAe,UAAS;AAG9B,SAAK,aAAa,SAAS,CAAC,cAA8B;AACxD,YAAM,MAAM,YAAY,IAAI;AAC5B,UAAI,MAAM,KAAK,eAAe,aAAc;AAE5C,YAAM,YAAY,cAAc,OAAO,IAAI;AAC3C,WAAK,OAAO,OAAO,SAAS;AAE5B,WAAK,eAAe;AAAA,IACtB,CAAC;AACD,SAAK,aAAa,OAAO;AAiBzB,SAAK,YAAY,SAAS,CAAC,IAAI,OAAO;AACpC,YAAM,SAAS,OAAO,KAAK,OAAO;AAClC,WAAK,OAAO,OAAO,CAAC,KAAK,QAAQ,CAAC,KAAK,MAAM;AAAA,IAC/C,CAAC;AACD,SAAK,YAAY,OAAO;AAExB,SAAK,OAAO,iBAAiB,eAAe,CAAC,MAAM,EAAE,eAAe,CAAC;AAAA,EACvE;AAAA,EAEO,mBAA4B;AACjC,WAAO,KAAK,YAAY;AAAA,EAC1B;AAAA,EAEQ,eAAe;AACrB,UAAM,cAAc,OAAO;AAC3B,UAAM,eAAe,OAAO;AAE5B,UAAM,iBAAiB,KAAK,QAAQ,KAAK;AACzC,UAAM,eAAe,cAAc;AAEnC,QAAI,aAAqB;AAEzB,QAAI,eAAe,gBAAgB;AACjC,qBAAe;AACf,oBAAc,eAAe;AAAA,IAC/B,OAAO;AACL,oBAAc;AACd,qBAAe,cAAc;AAAA,IAC/B;AAEA,SAAK,OAAO,QAAQ;AACpB,SAAK,OAAO,SAAS;AAErB,SAAK,QAAQ,cAAc,KAAK;AAGhC,UAAM,KAAK,KAAK,YACZ,KAAK,UAAU,oBAAoB,IAClC,KAAK,OAAO,WAAW,QAAQ,KAAK,KAAK,OAAO,WAAW,OAAO;AAEvE,QAAI,CAAC,GAAI,OAAM,IAAI,MAAM,qBAAqB;AAC9C,OAAG,SAAS,GAAG,GAAG,aAAa,YAAY;AAE3C,SAAK,OAAO,YAAY,aAAa,YAAY;AAAA,EACnD;AAAA,EAEA,YAA+B;AAC7B,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,WAAmB;AACjB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,UAAU;AACR,WAAO,oBAAoB,UAAU,KAAK,aAAa;AACvD,SAAK,aAAa,QAAQ;AAC1B,SAAK,YAAY,QAAQ;AACzB,SAAK,YAAY,QAAQ;AAAA,EAC3B;AACF;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/Core/utils/load-texture.ts","../src/Core/utils/parse-hex-to-rgb.ts","../src/Core/classes/Material.ts"],"sourcesContent":["export interface TextureOptions {\n /** When true, use gl.REPEAT wrap mode for tiling textures. */\n repeat?: boolean;\n}\n\nexport async function loadWebGlTexture(\n gl: WebGLRenderingContext,\n url: string,\n options: TextureOptions = {},\n): Promise<WebGLTexture> {\n return new Promise((resolve, reject) => {\n const texture = gl.createTexture();\n if (!texture) return reject(new Error('Failed to create texture'));\n\n gl.bindTexture(gl.TEXTURE_2D, texture);\n\n // Placeholder pixel (gray) until image loads\n const level = 0;\n const internalFormat = gl.RGBA;\n const width = 1;\n const height = 1;\n const border = 0;\n const srcFormat = gl.RGBA;\n const srcType = gl.UNSIGNED_BYTE;\n const pixel = new Uint8Array([128, 128, 128, 255]); // gray\n gl.texImage2D(\n gl.TEXTURE_2D,\n level,\n internalFormat,\n width,\n height,\n border,\n srcFormat,\n srcType,\n pixel,\n );\n\n const image = new Image();\n image.crossOrigin = 'anonymous';\n image.onload = () => {\n gl.bindTexture(gl.TEXTURE_2D, texture);\n gl.texImage2D(\n gl.TEXTURE_2D,\n level,\n internalFormat,\n srcFormat,\n srcType,\n image,\n );\n\n // Auto mipmaps and filtering\n if (isPowerOf2(image.width) && isPowerOf2(image.height)) {\n gl.generateMipmap(gl.TEXTURE_2D);\n gl.texParameteri(\n gl.TEXTURE_2D,\n gl.TEXTURE_MIN_FILTER,\n gl.LINEAR_MIPMAP_LINEAR,\n );\n // Wrap mode: REPEAT for tiling, otherwise default (REPEAT is GL default for POT)\n if (options.repeat) {\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);\n }\n } else {\n // Non-power-of-2: REPEAT is not supported in WebGL 1\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);\n }\n\n resolve(texture);\n };\n image.onerror = reject;\n image.src = url;\n });\n}\n\nfunction isPowerOf2(value: number) {\n return (value & (value - 1)) === 0;\n}\n","import { Vector4 } from '../domain/interfaces/Vectors';\n\nexport function parseHexToRgbArray(hex: string): Vector4 {\n // Remove leading #\n if (hex.startsWith('#')) hex = hex.slice(1);\n\n let r = 1,\n g = 1,\n b = 1,\n a = 1;\n\n if (hex.length === 6) {\n r = parseInt(hex.slice(0, 2), 16) / 255;\n g = parseInt(hex.slice(2, 4), 16) / 255;\n b = parseInt(hex.slice(4, 6), 16) / 255;\n a = 1;\n } else if (hex.length === 8) {\n r = parseInt(hex.slice(0, 2), 16) / 255;\n g = parseInt(hex.slice(2, 4), 16) / 255;\n b = parseInt(hex.slice(4, 6), 16) / 255;\n a = parseInt(hex.slice(6, 8), 16) / 255;\n } else {\n throw new Error('Invalid hex color format. Use #RRGGBB or #RRGGBBAA.');\n }\n\n return [r, g, b, a];\n}\n","import { Vector3, Vector4 } from '../domain/interfaces/Vectors';\nimport { loadWebGlTexture, TextureOptions } from '../utils/load-texture';\nimport { parseHexToRgbArray } from '../utils/parse-hex-to-rgb';\nimport { Light } from './Light';\nimport WebGLCore from './WebGLCore';\n\n/** Maximum number of lights supported per draw call (must match the shader). */\nexport const MAX_LIGHTS = 5;\n\n/**\n * Encapsulates GPU material state: shader program reference, uniform/attribute\n * caches, albedo colour, textures, and lighting properties.\n */\nexport class Material {\n // cache\n public program: WebGLProgram;\n public uniformLocations: Record<string, WebGLUniformLocation | null> = {};\n public attribLocations: Record<string, number> = {};\n\n // Attributes\n public albedoColor: Vector4 = [1, 1, 1, 1];\n public unlit: boolean = false;\n public texture?: WebGLTexture;\n public specular: Vector3 = [0.3, 0.3, 0.3];\n public shininess: number = 64;\n public doubleSided: boolean = false;\n //others\n public ambientColor: Vector3 = [0.1, 0.1, 0.1]; // Ka\n public dissolve: number = 1.0; // d or Tr\n public diffuse: Vector3 = [1, 1, 1]; // Kd\n /** Physics friction coefficient [0–1]. 0 = no resistance (ice), 1 = instant stop. */\n public friction: number = 0.3;\n\n constructor(\n private webglCore: WebGLCore,\n options: Partial<Material> = {},\n ) {\n this.program = webglCore.getProgram();\n const { gl } = webglCore;\n\n // Cache uniforms\n const names = [\n 'uColor',\n 'uUnlit',\n 'uModel',\n 'uView',\n 'uProjection',\n 'uLightCount',\n 'uUseTexture',\n 'uTexture',\n // --- LIGHTING UNIFORMS ---\n 'uViewPosition', // Camera position for specular light\n 'uShininess',\n 'uSpecularColor',\n 'uAmbientColor',\n 'uDissolve',\n 'uDiffuseColor',\n 'uLightDirection[0]',\n 'uLightColor[0]',\n 'uLightIntensity[0]',\n 'uLightType[0]',\n 'uLightPosition[0]', // NEW: For Point Lights\n 'uLightConstant[0]', // NEW: Attenuation\n 'uLightLinear[0]', // NEW: Attenuation\n 'uLightQuadratic[0]', // NEW: Attenuation\n // --- SKINNING UNIFORMS ---\n 'uUseSkinning',\n 'uJointMatrices[0]',\n ];\n for (const name of names) {\n this.uniformLocations[name] = gl.getUniformLocation(this.program, name);\n }\n\n // Cache attributes\n const attribs = [\n 'aPosition',\n 'aNormal',\n 'aTexCoord',\n 'aJointIndices',\n 'aJointWeights',\n ];\n for (const name of attribs) {\n this.attribLocations[name] = gl.getAttribLocation(this.program, name);\n }\n\n Object.assign(this, options);\n }\n\n setColorHex(hex: string) {\n this.albedoColor = parseHexToRgbArray(hex);\n }\n\n setColor(rgba: [number, number, number, number]) {\n this.albedoColor = rgba;\n this.dissolve = rgba[3];\n this.diffuse = [rgba[0], rgba[1], rgba[2]];\n }\n\n /**\n * Load an image from URL and create a WebGL texture.\n */\n async loadTexture(url: string, options?: TextureOptions): Promise<void> {\n const { gl } = this.webglCore;\n this.texture = await loadWebGlTexture(gl, url, options);\n }\n\n /**\n * Create an independent copy of this material.\n * Shares the same WebGL program but copies all mutable properties.\n * The texture reference is shared (immutable GPU resource).\n */\n clone(): Material {\n const copy = new Material(this.webglCore);\n copy.albedoColor = [...this.albedoColor];\n copy.specular = [...this.specular];\n copy.ambientColor = [...this.ambientColor];\n copy.diffuse = [...this.diffuse];\n copy.shininess = this.shininess;\n copy.dissolve = this.dissolve;\n copy.unlit = this.unlit;\n copy.doubleSided = this.doubleSided;\n copy.friction = this.friction;\n copy.texture = this.texture; // shared GPU resource\n return copy;\n }\n\n apply(gl: WebGLRenderingContext, lights: Light[], viewPosition?: Vector3) {\n if (this.uniformLocations['uColor'])\n gl.uniform4fv(this.uniformLocations['uColor'], this.albedoColor);\n\n if (this.uniformLocations['uUnlit'])\n gl.uniform1i(this.uniformLocations['uUnlit'], this.unlit ? 1 : 0);\n\n // --- NEW MATERIAL UNIFORMS ---\n if (this.uniformLocations['uShininess'])\n gl.uniform1f(this.uniformLocations['uShininess'], this.shininess);\n\n if (this.uniformLocations['uSpecularColor'])\n gl.uniform3fv(this.uniformLocations['uSpecularColor'], this.specular);\n\n if (this.uniformLocations['uDissolve'])\n gl.uniform1f(this.uniformLocations['uDissolve'], this.dissolve);\n\n if (this.uniformLocations['uAmbientColor'])\n gl.uniform3fv(this.uniformLocations['uAmbientColor'], this.ambientColor);\n\n if (this.uniformLocations['uDiffuseColor'])\n gl.uniform3fv(this.uniformLocations['uDiffuseColor'], this.diffuse);\n\n // --- NEW CAMERA UNIFORM FOR SPECULAR HIGHLIGHTS ---\n if (viewPosition && this.uniformLocations['uViewPosition']) {\n gl.uniform3fv(this.uniformLocations['uViewPosition'], viewPosition);\n }\n\n // Texture\n const hasTexture = !!this.texture;\n if (this.uniformLocations['uUseTexture'])\n gl.uniform1i(this.uniformLocations['uUseTexture'], hasTexture ? 1 : 0);\n\n if (hasTexture && this.texture) {\n gl.activeTexture(gl.TEXTURE0);\n gl.bindTexture(gl.TEXTURE_2D, this.texture);\n if (this.uniformLocations['uTexture'])\n gl.uniform1i(this.uniformLocations['uTexture'], 0);\n }\n\n // Lights (unchanged)\n const MAX = MAX_LIGHTS;\n if (!this.unlit && lights?.length) {\n const count = Math.min(lights.length, MAX);\n\n if (this.uniformLocations['uLightCount'])\n gl.uniform1i(this.uniformLocations['uLightCount'], count);\n\n const dirs: number[] = [];\n const colors: number[] = [];\n const intensities: number[] = [];\n const types: number[] = [];\n const positions: number[] = [];\n const constants: number[] = [];\n const linears: number[] = [];\n const quadratics: number[] = [];\n\n for (let i = 0; i < count; i++) {\n const light = lights[i];\n\n let typeInt = 0;\n if (light.type === 'point') typeInt = 1;\n else if (light.type === 'ambient') typeInt = 2;\n\n types.push(typeInt);\n\n dirs.push(...(light.direction ?? [0, 0, 0]));\n positions.push(...(light.position ?? [0, 0, 0]));\n colors.push(...light.color);\n intensities.push(light.intensity);\n constants.push(light.constant);\n linears.push(light.linear);\n quadratics.push(light.quadratic);\n }\n\n if (this.uniformLocations['uLightDirection[0]'])\n gl.uniform3fv(\n this.uniformLocations['uLightDirection[0]'],\n new Float32Array(dirs),\n );\n if (this.uniformLocations['uLightColor[0]'])\n gl.uniform3fv(\n this.uniformLocations['uLightColor[0]'],\n new Float32Array(colors),\n );\n if (this.uniformLocations['uLightIntensity[0]'])\n gl.uniform1fv(\n this.uniformLocations['uLightIntensity[0]'],\n new Float32Array(intensities),\n );\n if (this.uniformLocations['uLightType[0]'])\n gl.uniform1iv(\n this.uniformLocations['uLightType[0]'],\n new Int32Array(types),\n );\n if (this.uniformLocations['uLightPosition[0]'])\n gl.uniform3fv(\n this.uniformLocations['uLightPosition[0]'],\n new Float32Array(positions),\n );\n if (this.uniformLocations['uLightConstant[0]'])\n gl.uniform1fv(\n this.uniformLocations['uLightConstant[0]'],\n new Float32Array(constants),\n );\n if (this.uniformLocations['uLightLinear[0]'])\n gl.uniform1fv(\n this.uniformLocations['uLightLinear[0]'],\n new Float32Array(linears),\n );\n if (this.uniformLocations['uLightQuadratic[0]'])\n gl.uniform1fv(\n this.uniformLocations['uLightQuadratic[0]'],\n new Float32Array(quadratics),\n );\n }\n }\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/Core/classes/Model.ts","../src/Core/domain/value-objects/Collider.ts","../src/Core/utils/gltf-utils.ts"],"sourcesContent":["// classes/Model.ts\nimport { mat4 } from 'gl-matrix';\n\nimport { AABB, Collider } from '../domain/value-objects/Collider';\nimport { transformAABB } from '../utils/gltf-utils';\nimport { Vector3 } from '../domain/interfaces/Vectors';\nimport { Mesh } from './Mesh';\nimport { Skeleton } from './Skeleton';\nimport { AnimationClip } from './AnimationClip';\nimport WebGLCore from './WebGLCore';\n\n/**\n * A high-level renderable object composed of one or more {@link Mesh}es.\n * Provides transform, bounding-box, AABB collision, and optional skeletal\n * animation utilities.\n */\nexport class Model {\n public name: string;\n public meshes: Mesh[];\n public collider: Collider;\n public translation: Vector3;\n public rotation: Vector3;\n public scale: Vector3;\n public boundingBox: AABB;\n\n // ── Skeletal animation ──────────────────────────────────────\n /** The skeleton driving skinned meshes (set after GLB load if present). */\n public skeleton: Skeleton | null = null;\n\n /** Named animation clips available on this model. */\n public animations: Map<string, AnimationClip> = new Map();\n\n /** The currently playing animation clip (if any). */\n public activeAnimation: AnimationClip | null = null;\n\n private _boundsDefinedManually = false;\n private _modelMatrix: mat4 = mat4.create();\n private _matrixDirty = true;\n\n /** When `false` the model is excluded from collision detection. */\n public collidable = true;\n\n constructor(\n meshes: Mesh[],\n translation: Vector3 = [0, 0, 0],\n scale: Vector3 = [1, 1, 1],\n _name = 'model',\n skeleton: Skeleton | null = null,\n ) {\n this.meshes = meshes;\n this.translation = translation;\n this.scale = scale;\n this.rotation = [0, 0, 0];\n this.collider = new Collider('box');\n this.name = _name;\n this.skeleton = skeleton;\n this.boundingBox = this.computeBoundingBox();\n\n // Align bottom to y=0 — must dirty the matrix since we mutate translation directly\n this.translation[1] -= this.boundingBox.min[1];\n this._matrixDirty = true;\n this.updateBoundingBox();\n }\n\n // ─────────────────────────────────────────────\n // ░░░ Transform API ░░░\n // ─────────────────────────────────────────────\n /** Set absolute position. Marks matrix dirty and updates bounding box. */\n public setTranslation(x: number, y: number, z: number) {\n this.translation = [x, y, z];\n this._matrixDirty = true;\n this.updateBoundingBox();\n }\n\n /** Set absolute rotation (radians per axis). */\n public setRotation(x: number, y: number, z: number) {\n this.rotation = [x, y, z];\n this._matrixDirty = true;\n this.updateBoundingBox();\n }\n\n /** Set absolute scale. */\n public setScale(x: number, y: number, z: number) {\n this.scale = [x, y, z];\n this._matrixDirty = true;\n this.updateBoundingBox();\n }\n\n /** Translate the model by a delta. */\n public move(dx: number, dy: number, dz: number = 0) {\n this.translation[0] += dx;\n this.translation[1] += dy;\n this.translation[2] += dz;\n this._matrixDirty = true;\n this.updateBoundingBox();\n }\n\n // ─────────────────────────────────────────────\n // ░░░ Matrix Handling ░░░\n // ─────────────────────────────────────────────\n private getModelMatrix(): mat4 {\n if (this._matrixDirty) {\n mat4.identity(this._modelMatrix);\n mat4.translate(this._modelMatrix, this._modelMatrix, this.translation);\n mat4.rotateX(this._modelMatrix, this._modelMatrix, this.rotation[0]);\n mat4.rotateY(this._modelMatrix, this._modelMatrix, this.rotation[1]);\n mat4.rotateZ(this._modelMatrix, this._modelMatrix, this.rotation[2]);\n mat4.scale(this._modelMatrix, this._modelMatrix, this.scale);\n this._matrixDirty = false;\n }\n return this._modelMatrix;\n }\n\n // ─────────────────────────────────────────────\n // ░░░ Bounding Box & Collider ░░░\n // ─────────────────────────────────────────────\n /**\n * Manually define the collision bounding box relative to the model's\n * current translation.\n */\n public setBounds(\n xOffset: number,\n yOffset: number,\n zOffset: number,\n width: number,\n height: number,\n depth: number,\n ) {\n this.boundingBox.min = [\n this.translation[0] + xOffset,\n this.translation[1] + yOffset,\n this.translation[2] + zOffset,\n ];\n this.boundingBox.max = [\n this.boundingBox.min[0] + width,\n this.boundingBox.min[1] + height,\n this.boundingBox.min[2] + depth,\n ];\n this._boundsDefinedManually = true;\n }\n\n private computeBoundingBox(): AABB {\n if (this.meshes.length === 0) {\n return { min: [...this.translation], max: [...this.translation] };\n }\n\n const modelMat = this.getModelMatrix();\n\n // For skinned models the shader applies: uModel * skinMat * vertex.\n // In bind pose skinMat ≈ skeleton.rootTransform (the non-joint ancestor\n // transform that many exporters don't bake into the IBM). We must\n // include it so the bbox matches what the GPU actually renders.\n let effectiveMat = modelMat;\n if (this.skeleton) {\n effectiveMat = mat4.create();\n mat4.multiply(effectiveMat, modelMat, this.skeleton.rootTransform);\n }\n\n const min: Vector3 = [Infinity, Infinity, Infinity];\n const max: Vector3 = [-Infinity, -Infinity, -Infinity];\n\n for (const mesh of this.meshes) {\n if (!mesh.isCollidable) continue;\n\n mesh.computeBounds();\n\n // Skip degenerate meshes (helper objects, IK controls, etc. that have\n // all vertices at the local origin — they would pull the bbox to include\n // the world-space origin even when the model is far away).\n const mb = mesh.boundingBox;\n const isDegenerate =\n mb.min[0] === mb.max[0] &&\n mb.min[1] === mb.max[1] &&\n mb.min[2] === mb.max[2];\n if (isDegenerate) continue;\n\n const transformed = transformAABB(\n mesh.boundingBox.min,\n mesh.boundingBox.max,\n effectiveMat,\n );\n\n for (let i = 0; i < 3; i++) {\n min[i] = Math.min(min[i], transformed.min[i]);\n max[i] = Math.max(max[i], transformed.max[i]);\n }\n }\n\n // No collidable mesh contributed — return a unit box at the model's position\n // to avoid propagating Infinity/NaN into translation.\n if (!isFinite(min[0])) {\n const t = effectiveMat;\n const tx = t[12];\n const ty = t[13];\n const tz = t[14];\n return {\n min: [tx - 0.5, ty, tz - 0.5],\n max: [tx + 0.5, ty + 1, tz + 0.5],\n };\n }\n\n return { min, max };\n }\n\n /** Recompute the world-space AABB from mesh data (unless manually set). */\n public updateBoundingBox() {\n if (this._boundsDefinedManually) return;\n this.boundingBox = this.computeBoundingBox();\n }\n\n /** World-space centre of the bounding box. */\n public getCenter(): Vector3 {\n const { min, max } = this.boundingBox;\n return [\n (min[0] + max[0]) / 2,\n (min[1] + max[1]) / 2,\n (min[2] + max[2]) / 2,\n ];\n }\n\n /** Extents of the bounding box in each axis. */\n public getSize(): Vector3 {\n const { min, max } = this.boundingBox;\n return [max[0] - min[0], max[1] - min[1], max[2] - min[2]];\n }\n\n /** Test for intersection with another model using their colliders. */\n public intersects(other: Model): boolean {\n const a = this.collider;\n const b = other.collider;\n\n if (a.type !== 'box' || b.type !== 'box') {\n console.warn(\n `[Model] Collider types '${a.type}' / '${b.type}' not yet implemented — falling back to AABB.`,\n );\n }\n\n return this.intersectAABB(other);\n }\n\n private intersectAABB(other: Model): boolean {\n const a = this.boundingBox;\n const b = other.boundingBox;\n return !(\n a.max[0] < b.min[0] ||\n a.min[0] > b.max[0] ||\n a.max[1] < b.min[1] ||\n a.min[1] > b.max[1] ||\n a.max[2] < b.min[2] ||\n a.min[2] > b.max[2]\n );\n }\n\n // ─────────────────────────────────────────────\n // ░░░ Resource Management ░░░\n // ─────────────────────────────────────────────\n /** Release all GPU resources held by this model's meshes. */\n public dispose(glCore: WebGLCore) {\n for (const mesh of this.meshes) {\n mesh.dispose?.(glCore);\n }\n this.meshes = [];\n }\n\n // ─────────────────────────────────────────────\n // ░░░ Cloning ░░░\n // ─────────────────────────────────────────────\n /** Deep-clone the model including all meshes. Materials are shared. */\n public clone(): Model {\n const clonedMeshes = this.meshes.map((m) => (m.clone ? m.clone() : m));\n const c = new Model(\n clonedMeshes,\n [...this.translation],\n [...this.scale],\n this.name,\n this.skeleton?.clone() ?? null,\n );\n c.rotation = [...this.rotation];\n c.collider = new Collider(this.collider.type);\n c.boundingBox = {\n min: [...this.boundingBox.min],\n max: [...this.boundingBox.max],\n };\n for (const [name, clip] of this.animations) {\n c.animations.set(name, clip.clone());\n }\n return c;\n }\n\n // ─────────────────────────────────────────────\n // ░░░ Animation ░░░\n // ─────────────────────────────────────────────\n\n /**\n * Start playing a named animation clip.\n * @param name - Must match a key in {@link animations}.\n * @param loop - Whether the clip should loop (default `true`).\n */\n public playAnimation(name: string, loop = true): void {\n const clip = this.animations.get(name);\n if (!clip) {\n console.warn(`[Model:${this.name}] Animation \"${name}\" not found`);\n return;\n }\n this.activeAnimation = clip;\n clip.loop = loop;\n clip.reset();\n clip.play();\n }\n\n /** Pause the active animation without rewinding. */\n public pauseAnimation(): void {\n this.activeAnimation?.pause();\n }\n\n /** Stop the active animation and rewind. */\n public stopAnimation(): void {\n if (this.activeAnimation) {\n this.activeAnimation.stop();\n this.activeAnimation = null;\n }\n }\n\n /** List the names of all available animations. */\n public getAnimationNames(): string[] {\n return [...this.animations.keys()];\n }\n\n /**\n * Advance animation and recompute joint matrices.\n * Call this every frame from your game loop.\n *\n * @param deltaTime - Elapsed time in seconds since the last frame.\n */\n public update(deltaTime: number) {\n if (!this.activeAnimation || !this.skeleton) return;\n\n this.activeAnimation.update(deltaTime);\n const poses = this.activeAnimation.sample();\n\n // Merge with bind-pose defaults\n const merged = new Map<\n number,\n {\n t: import('gl-matrix').vec3;\n r: import('gl-matrix').quat;\n s: import('gl-matrix').vec3;\n }\n >();\n for (let i = 0; i < this.skeleton.joints.length; i++) {\n const joint = this.skeleton.joints[i];\n const pose = poses.get(i);\n merged.set(i, {\n t: pose?.t ?? joint.localTranslation,\n r: pose?.r ?? joint.localRotation,\n s: pose?.s ?? joint.localScale,\n });\n }\n\n this.skeleton.computeJointMatrices(merged);\n }\n}\n","import { Vector3 } from '../interfaces/Vectors';\n\nexport interface AABB {\n min: Vector3;\n max: Vector3;\n}\n\nexport type ColliderType = 'box' | 'sphere' | 'capsule';\nexport class Collider {\n type: ColliderType;\n offset: Vector3;\n size: Vector3;\n\n constructor(\n type: ColliderType,\n offset: Vector3 = [0, 0, 0],\n size: Vector3 = [1, 1, 1],\n ) {\n this.type = type;\n this.offset = offset;\n this.size = size;\n }\n}\n","// utils/gltf-utils.ts\nimport { mat4, vec3 } from 'gl-matrix';\n\n/** Transforma um ponto (vec3) por uma mat4 */\nexport function transformPoint(\n out: [number, number, number],\n m: mat4,\n p: [number, number, number],\n) {\n const v = vec3.fromValues(p[0], p[1], p[2]);\n vec3.transformMat4(v, v, m);\n out[0] = v[0];\n out[1] = v[1];\n out[2] = v[2];\n}\n\n/** Recebe uma AABB local (min,max) e uma mat4 e retorna AABB transformada */\nexport function transformAABB(\n min: [number, number, number],\n max: [number, number, number],\n m: mat4,\n) {\n // calcula os 8 cantos, transforma, e retorna new min/max\n const corners: [number, number, number][] = [\n [min[0], min[1], min[2]],\n [min[0], min[1], max[2]],\n [min[0], max[1], min[2]],\n [min[0], max[1], max[2]],\n [max[0], min[1], min[2]],\n [max[0], min[1], max[2]],\n [max[0], max[1], min[2]],\n [max[0], max[1], max[2]],\n ];\n const outMin: [number, number, number] = [Infinity, Infinity, Infinity];\n const outMax: [number, number, number] = [-Infinity, -Infinity, -Infinity];\n const t: [number, number, number] = [0, 0, 0];\n for (const c of corners) {\n transformPoint(t, m, c);\n for (let i = 0; i < 3; i++) {\n outMin[i] = Math.min(outMin[i], t[i]);\n outMax[i] = Math.max(outMax[i], t[i]);\n }\n }\n return { min: outMin, max: outMax };\n}\n"],"mappings":"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","names":["mat4","mat4"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/Core/classes/Renderer.ts","../src/Core/utils/compute-bounds.ts","../src/Core/classes/Mesh.ts","../src/Core/shaders/index.ts","../src/Core/classes/WebGLCore.ts","../src/Core/classes/Skeleton.ts","../src/Core/classes/AnimationClip.ts","../src/Core/utils/create-wire-box.ts"],"sourcesContent":["import { mat4, vec3 } from 'gl-matrix';\nimport { Material, Mesh, Scene, WebGLCore } from '.';\nimport { Viewport } from './Viewport';\nimport { Light } from './Light';\nimport { Vector3 } from '../domain/interfaces/Vectors';\nimport { Model } from './Model';\nimport { Skeleton } from './Skeleton';\nimport { createWireBox } from '../utils/create-wire-box';\n\nconst computeModelMatrix = (model: Model): mat4 => {\n const m = mat4.create();\n mat4.translate(m, m, model.translation);\n mat4.rotateX(m, m, model.rotation[0]);\n mat4.rotateY(m, m, model.rotation[1]);\n mat4.rotateZ(m, m, model.rotation[2]);\n mat4.scale(m, m, model.scale);\n return m;\n};\n\nexport class Renderer {\n /** Set to `true` to render wireframe bounding boxes for debugging. */\n public debug = false;\n\n // Cached debug materials to avoid per-frame GPU allocations\n private _modelHitboxMaterial: Material | null = null;\n private _meshHitboxMaterial: Material | null = null;\n\n constructor(\n private webglCore: WebGLCore,\n public viewport: Viewport,\n ) {}\n\n /** Lazily-created material for model-level hitboxes (red). */\n private get modelHitboxMaterial(): Material {\n if (!this._modelHitboxMaterial) {\n this._modelHitboxMaterial = new Material(this.webglCore);\n this._modelHitboxMaterial.setColorHex('#ff0404ff');\n this._modelHitboxMaterial.unlit = true;\n }\n return this._modelHitboxMaterial;\n }\n\n /** Lazily-created material for mesh-level hitboxes (green). */\n private get meshHitboxMaterial(): Material {\n if (!this._meshHitboxMaterial) {\n this._meshHitboxMaterial = new Material(this.webglCore);\n this._meshHitboxMaterial.setColorHex('#04ff0cff');\n this._meshHitboxMaterial.unlit = true;\n }\n return this._meshHitboxMaterial;\n }\n\n public clearFrame(\n color: [number, number, number, number] = [0.2, 0.5, 0.95, 1],\n ) {\n const gl = this.webglCore.getRenderingContext();\n if (!gl) return;\n\n gl.clearColor(...color);\n gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);\n gl.enable(gl.DEPTH_TEST);\n }\n\n public render(scene: Scene) {\n const gl = this.webglCore.getRenderingContext();\n if (!gl) return;\n\n const models = scene.getModels();\n const lights = scene.getLights();\n const camera = this.viewport.camera;\n\n const viewPosition: vec3 = camera.getComputedPosition();\n this.clearFrame();\n\n const viewMatrix = camera.getViewMatrix() as Float32Array;\n const projMatrix = camera.getProjectionMatrix() as Float32Array;\n\n const activeProgram: WebGLProgram | null = null;\n\n for (const model of models) {\n this.drawModel(\n gl,\n model,\n lights,\n viewPosition,\n viewMatrix,\n projMatrix,\n activeProgram,\n );\n }\n }\n\n private drawModel(\n gl: WebGLRenderingContext,\n model: Model,\n lights: Light[],\n viewPosition: vec3,\n viewMatrix: Float32Array,\n projMatrix: Float32Array,\n activeProgram: WebGLProgram | null,\n ) {\n const modelMatrix = computeModelMatrix(model);\n const hasSkeleton = model.skeleton !== null;\n\n for (const mesh of model.meshes) {\n activeProgram = this.drawMesh(\n mesh,\n lights,\n viewPosition,\n modelMatrix,\n viewMatrix,\n projMatrix,\n activeProgram,\n hasSkeleton && mesh.isSkinned ? model : null,\n );\n\n if (this.debug) {\n this.drawHitBoxForMesh(\n mesh,\n modelMatrix,\n viewMatrix,\n projMatrix,\n activeProgram,\n );\n }\n }\n\n // Debug wireframe\n if (this.debug) {\n this.drawHitBox(model, modelMatrix, activeProgram);\n }\n }\n\n private drawMesh(\n mesh: Mesh,\n lights: Light[],\n viewPosition: vec3,\n modelMatrix: mat4,\n viewMatrix: Float32Array,\n projMatrix: Float32Array,\n activeProgram: WebGLProgram | null,\n skinnedModel: Model | null = null,\n ): WebGLProgram | null {\n const gl = this.webglCore.getRenderingContext();\n if (!gl) return activeProgram;\n if (!mesh.material) return activeProgram;\n\n mesh.initBuffer(this.webglCore);\n\n const { program, uniformLocations } = mesh.material;\n\n if (program !== activeProgram) {\n gl.useProgram(program);\n activeProgram = program;\n\n if (uniformLocations['uView'])\n gl.uniformMatrix4fv(uniformLocations['uView'], false, viewMatrix);\n if (uniformLocations['uProjection'])\n gl.uniformMatrix4fv(uniformLocations['uProjection'], false, projMatrix);\n }\n\n // Detect WebGL2\n const isWebGL2 = gl instanceof WebGL2RenderingContext;\n\n // --- Bind VAO if available ---\n if (isWebGL2 && mesh.buffers?.vao) {\n (gl as unknown as WebGL2RenderingContext).bindVertexArray(\n mesh.buffers.vao,\n );\n } else {\n // --- WebGL1 Fallback ---\n const aPosition = mesh.material.attribLocations['aPosition'];\n if (aPosition !== -1 && mesh.buffers?.vertexBuffer) {\n gl.bindBuffer(gl.ARRAY_BUFFER, mesh.buffers.vertexBuffer);\n gl.enableVertexAttribArray(aPosition);\n gl.vertexAttribPointer(aPosition, 3, gl.FLOAT, false, 0, 0);\n }\n\n const aNormal = mesh.material.attribLocations['aNormal'];\n if (aNormal !== -1 && mesh.buffers?.normalBuffer) {\n gl.bindBuffer(gl.ARRAY_BUFFER, mesh.buffers.normalBuffer);\n gl.enableVertexAttribArray(aNormal);\n gl.vertexAttribPointer(aNormal, 3, gl.FLOAT, false, 0, 0);\n }\n\n const aTexCoord = mesh.material.attribLocations['aTexCoord'];\n if (aTexCoord !== -1 && mesh.buffers?.texCoordBuffer) {\n gl.bindBuffer(gl.ARRAY_BUFFER, mesh.buffers.texCoordBuffer);\n gl.enableVertexAttribArray(aTexCoord);\n gl.vertexAttribPointer(aTexCoord, 2, gl.FLOAT, false, 0, 0);\n }\n\n // Skinning attributes (joint indices + weights)\n const aJointIndices = mesh.material.attribLocations['aJointIndices'];\n if (aJointIndices !== -1 && mesh.buffers?.jointIndexBuffer) {\n gl.bindBuffer(gl.ARRAY_BUFFER, mesh.buffers.jointIndexBuffer);\n gl.enableVertexAttribArray(aJointIndices);\n gl.vertexAttribPointer(aJointIndices, 4, gl.FLOAT, false, 0, 0);\n }\n\n const aJointWeights = mesh.material.attribLocations['aJointWeights'];\n if (aJointWeights !== -1 && mesh.buffers?.jointWeightBuffer) {\n gl.bindBuffer(gl.ARRAY_BUFFER, mesh.buffers.jointWeightBuffer);\n gl.enableVertexAttribArray(aJointWeights);\n gl.vertexAttribPointer(aJointWeights, 4, gl.FLOAT, false, 0, 0);\n }\n }\n\n // Material uniforms\n mesh.material.apply(gl, lights, viewPosition as Vector3);\n\n // ── Skinning uniforms ─────────────────────────────────────\n const uUseSkinning = uniformLocations['uUseSkinning'];\n const skeleton: Skeleton | null =\n (skinnedModel?.skeleton as Skeleton | undefined) ?? null;\n if (skeleton && mesh.isSkinned) {\n if (uUseSkinning) gl.uniform1i(uUseSkinning, 1);\n const uJointMatrices = uniformLocations['uJointMatrices[0]'];\n if (uJointMatrices) {\n gl.uniformMatrix4fv(uJointMatrices, false, skeleton.jointMatrices);\n }\n } else {\n if (uUseSkinning) gl.uniform1i(uUseSkinning, 0);\n }\n\n // Model transform\n const uModel = mesh.material.uniformLocations['uModel'];\n if (uModel) gl.uniformMatrix4fv(uModel, false, modelMatrix as Float32Array);\n\n // Draw\n if (mesh.buffers?.indexBuffer && mesh.indices) {\n gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, mesh.buffers.indexBuffer);\n const type =\n mesh.indices instanceof Uint32Array\n ? gl.UNSIGNED_INT\n : gl.UNSIGNED_SHORT;\n gl.drawElements(mesh.mode, mesh.indices.length, type, 0);\n } else {\n gl.drawArrays(mesh.mode, 0, mesh.vertices.length / 3);\n }\n\n // Cleanup\n if (isWebGL2 && mesh.buffers?.vao) {\n (gl as unknown as WebGL2RenderingContext).bindVertexArray(null);\n } else {\n gl.bindBuffer(gl.ARRAY_BUFFER, null);\n gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);\n }\n\n return activeProgram;\n }\n\n public drawHitBox(\n model: Model,\n modelMatrix: mat4,\n activeProgram: WebGLProgram | null,\n ) {\n // Use bounding box directly — already in world coordinates\n const hitboxMesh = createWireBox(\n model.boundingBox.min,\n model.boundingBox.max,\n this.modelHitboxMaterial,\n );\n hitboxMesh.isCollidable = false;\n\n const camera = this.viewport.camera;\n const viewMatrix = camera.getViewMatrix() as Float32Array;\n const projMatrix = camera.getProjectionMatrix() as Float32Array;\n\n const identity = mat4.create();\n\n this.drawMesh(\n hitboxMesh,\n [],\n camera.position,\n identity,\n viewMatrix,\n projMatrix,\n activeProgram,\n );\n }\n\n private drawHitBoxForMesh(\n mesh: Mesh,\n modelMatrix: mat4,\n viewMatrix: Float32Array,\n projMatrix: Float32Array,\n activeProgram: WebGLProgram | null,\n ) {\n const gl = this.webglCore.getRenderingContext();\n if (!gl || !mesh.boundingBox) return;\n\n const { min, max } = mesh.boundingBox;\n\n const wireBox = createWireBox(min, max, this.meshHitboxMaterial);\n wireBox.isCollidable = false;\n\n const camera = this.viewport.camera;\n this.drawMesh(\n wireBox,\n [],\n camera.position,\n modelMatrix,\n viewMatrix,\n projMatrix,\n activeProgram,\n );\n }\n}\n","import { AABB } from '../domain/value-objects/Collider';\n\n/**\n * Compute the axis-aligned bounding box (AABB) from a flat Float32Array of vertices.\n * Each vertex is expected to be 3 consecutive floats: [x, y, z, ...].\n */\nexport function computeVertexBounds(vertices: Float32Array): AABB {\n if (!vertices || vertices.length === 0) {\n return { min: [0, 0, 0], max: [0, 0, 0] };\n }\n\n let minX = Infinity,\n minY = Infinity,\n minZ = Infinity;\n let maxX = -Infinity,\n maxY = -Infinity,\n maxZ = -Infinity;\n\n for (let i = 0; i < vertices.length; i += 3) {\n const x = vertices[i];\n const y = vertices[i + 1];\n const z = vertices[i + 2];\n\n if (x < minX) minX = x;\n if (y < minY) minY = y;\n if (z < minZ) minZ = z;\n\n if (x > maxX) maxX = x;\n if (y > maxY) maxY = y;\n if (z > maxZ) maxZ = z;\n }\n\n return {\n min: [minX, minY, minZ],\n max: [maxX, maxY, maxZ],\n };\n}\n","import WebGLCore from './WebGLCore';\nimport { Material } from './Material';\nimport { AABB } from '../domain/value-objects/Collider';\nimport { computeVertexBounds } from '../utils/compute-bounds';\n\n/** WebGL draw-mode constants. */\nexport const GL_LINES = 0x0001;\nexport const GL_TRIANGLES = 0x0004;\n\nexport const INITIAL_BOUNDING_BOX: AABB = { min: [0, 0, 0], max: [0, 0, 0] };\n\n/**\n * A single renderable mesh: vertex data, normals, indices, a material, and an\n * optional bounding box used for collision detection.\n *\n * For skinned meshes, set {@link jointIndices} and {@link jointWeights} to\n * enable GPU skeletal animation.\n */\nexport class Mesh {\n name: string;\n\n vertices: Float32Array;\n\n normals: Float32Array;\n\n texCoords: Float32Array;\n\n indices: Uint16Array | Uint32Array | null;\n\n mode: number;\n\n material: Material;\n\n // ── Skinning data (optional) ─────────────────────────────────\n /** Per-vertex joint indices (vec4 per vertex, 4 influences). */\n jointIndices: Float32Array | null = null;\n\n /** Per-vertex joint weights (vec4 per vertex, 4 influences). */\n jointWeights: Float32Array | null = null;\n\n // WebGL buffers aggregate\n buffers: MeshBuffers | null = null;\n\n boundingBox: AABB = INITIAL_BOUNDING_BOX;\n\n isCollidable = true;\n\n isInitialized = false;\n\n constructor(\n name: string,\n vertices: Float32Array,\n normals: Float32Array,\n material: Material,\n texCoords: Float32Array = new Float32Array(),\n indices: Uint16Array | Uint32Array | null = null,\n ) {\n this.mode = GL_TRIANGLES;\n this.name = name;\n this.normals = normals;\n this.indices = indices;\n this.vertices = vertices;\n this.texCoords = texCoords;\n this.material = material;\n\n this.computeBounds();\n }\n\n setMode(newMode: number) {\n this.mode = newMode;\n }\n\n /** Whether this mesh has skinning data for skeletal animation. */\n get isSkinned(): boolean {\n return this.jointIndices !== null && this.jointWeights !== null;\n }\n\n computeBounds() {\n this.boundingBox = computeVertexBounds(this.vertices);\n }\n\n clone(): Mesh {\n const clonedMesh = new Mesh(\n this.name,\n new Float32Array(this.vertices),\n new Float32Array(this.normals),\n this.material, // shallow ref — call Material.clone() if independent material is needed\n new Float32Array(this.texCoords),\n this.indices\n ? this.indices instanceof Uint16Array\n ? new Uint16Array(this.indices)\n : new Uint32Array(this.indices)\n : null,\n );\n clonedMesh.setMode(this.mode);\n clonedMesh.isCollidable = this.isCollidable;\n if (this.jointIndices)\n clonedMesh.jointIndices = new Float32Array(this.jointIndices);\n if (this.jointWeights)\n clonedMesh.jointWeights = new Float32Array(this.jointWeights);\n return clonedMesh;\n }\n\n /** Initialize GPU buffers safely */\n public initBuffer(webglCore: WebGLCore) {\n const gl = webglCore.getRenderingContext();\n\n if (!gl) {\n console.warn(\n `[Mesh:${this.name}] GL context not available, skipping buffer init.`,\n );\n return;\n }\n if (this.isInitialized) return;\n\n this.buffers = new MeshBuffers();\n this.buffers.init(gl as WebGL2RenderingContext, this);\n this.isInitialized = true;\n }\n\n /** Dispose of GPU buffers safely */\n public dispose(webglCore: WebGLCore) {\n const gl = webglCore.getRenderingContext();\n if (!gl || !this.buffers) return;\n\n this.buffers.dispose(gl as WebGL2RenderingContext);\n this.buffers = null;\n this.isInitialized = false;\n }\n}\n\nexport class MeshBuffers {\n vao: WebGLVertexArrayObject | null = null;\n vertexBuffer: WebGLBuffer | null = null;\n normalBuffer: WebGLBuffer | null = null;\n texCoordBuffer: WebGLBuffer | null = null;\n indexBuffer: WebGLBuffer | null = null;\n jointIndexBuffer: WebGLBuffer | null = null;\n jointWeightBuffer: WebGLBuffer | null = null;\n\n public init(gl: WebGLRenderingContext, mesh: Mesh) {\n if (!gl) throw new Error('Cannot init MeshBuffers: invalid GL context');\n\n const isWebGL2 = gl instanceof WebGL2RenderingContext;\n const gl2 = isWebGL2 ? (gl as unknown as WebGL2RenderingContext) : null;\n\n if (gl2) {\n this.vao = gl2.createVertexArray();\n gl2.bindVertexArray(this.vao);\n }\n\n // --- Attribute layout ---\n // Attribute locations must match the shader (aPosition=0, aNormal=1, aTexCoord=2).\n // Look them up from the mesh's material program so the VAO captures the bindings.\n const program = mesh.material?.program;\n const aPosition = program ? gl.getAttribLocation(program, 'aPosition') : 0;\n const aNormal = program ? gl.getAttribLocation(program, 'aNormal') : 1;\n const aTexCoord = program ? gl.getAttribLocation(program, 'aTexCoord') : 2;\n\n // Vertex buffer\n this.vertexBuffer = gl.createBuffer();\n if (!this.vertexBuffer) throw new Error('Failed to create vertex buffer');\n gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);\n gl.bufferData(gl.ARRAY_BUFFER, mesh.vertices, gl.STATIC_DRAW);\n if (aPosition !== -1) {\n gl.enableVertexAttribArray(aPosition);\n gl.vertexAttribPointer(aPosition, 3, gl.FLOAT, false, 0, 0);\n }\n\n // Normal buffer\n this.normalBuffer = gl.createBuffer();\n if (!this.normalBuffer) throw new Error('Failed to create normal buffer');\n gl.bindBuffer(gl.ARRAY_BUFFER, this.normalBuffer);\n gl.bufferData(gl.ARRAY_BUFFER, mesh.normals, gl.STATIC_DRAW);\n if (aNormal !== -1) {\n gl.enableVertexAttribArray(aNormal);\n gl.vertexAttribPointer(aNormal, 3, gl.FLOAT, false, 0, 0);\n }\n\n // TexCoord buffer\n if (mesh.texCoords.length > 0) {\n this.texCoordBuffer = gl.createBuffer();\n if (!this.texCoordBuffer)\n throw new Error('Failed to create texCoord buffer');\n gl.bindBuffer(gl.ARRAY_BUFFER, this.texCoordBuffer);\n gl.bufferData(gl.ARRAY_BUFFER, mesh.texCoords, gl.STATIC_DRAW);\n if (aTexCoord !== -1) {\n gl.enableVertexAttribArray(aTexCoord);\n gl.vertexAttribPointer(aTexCoord, 2, gl.FLOAT, false, 0, 0);\n }\n }\n\n // Index buffer\n if (mesh.indices && mesh.indices.length > 0) {\n this.indexBuffer = gl.createBuffer();\n if (!this.indexBuffer) throw new Error('Failed to create index buffer');\n gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer);\n gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, mesh.indices, gl.STATIC_DRAW);\n }\n\n // Joint indices buffer (skinning)\n const aJointIndices = program\n ? gl.getAttribLocation(program, 'aJointIndices')\n : -1;\n if (\n mesh.jointIndices &&\n mesh.jointIndices.length > 0 &&\n aJointIndices !== -1\n ) {\n this.jointIndexBuffer = gl.createBuffer();\n if (!this.jointIndexBuffer)\n throw new Error('Failed to create joint index buffer');\n gl.bindBuffer(gl.ARRAY_BUFFER, this.jointIndexBuffer);\n gl.bufferData(gl.ARRAY_BUFFER, mesh.jointIndices, gl.STATIC_DRAW);\n gl.enableVertexAttribArray(aJointIndices);\n gl.vertexAttribPointer(aJointIndices, 4, gl.FLOAT, false, 0, 0);\n }\n\n // Joint weights buffer (skinning)\n const aJointWeights = program\n ? gl.getAttribLocation(program, 'aJointWeights')\n : -1;\n if (\n mesh.jointWeights &&\n mesh.jointWeights.length > 0 &&\n aJointWeights !== -1\n ) {\n this.jointWeightBuffer = gl.createBuffer();\n if (!this.jointWeightBuffer)\n throw new Error('Failed to create joint weight buffer');\n gl.bindBuffer(gl.ARRAY_BUFFER, this.jointWeightBuffer);\n gl.bufferData(gl.ARRAY_BUFFER, mesh.jointWeights, gl.STATIC_DRAW);\n gl.enableVertexAttribArray(aJointWeights);\n gl.vertexAttribPointer(aJointWeights, 4, gl.FLOAT, false, 0, 0);\n }\n\n if (gl2) {\n gl2.bindVertexArray(null);\n }\n }\n\n public dispose(gl: WebGL2RenderingContext) {\n if (!gl) {\n console.warn('Cannot dispose MeshBuffers: invalid GL context');\n return;\n }\n\n const isWebGL2 = gl instanceof WebGL2RenderingContext;\n\n if (this.vertexBuffer) gl.deleteBuffer(this.vertexBuffer);\n if (this.normalBuffer) gl.deleteBuffer(this.normalBuffer);\n if (this.texCoordBuffer) gl.deleteBuffer(this.texCoordBuffer);\n if (this.indexBuffer) gl.deleteBuffer(this.indexBuffer);\n if (this.jointIndexBuffer) gl.deleteBuffer(this.jointIndexBuffer);\n if (this.jointWeightBuffer) gl.deleteBuffer(this.jointWeightBuffer);\n\n if (isWebGL2 && this.vao) {\n (gl as unknown as WebGL2RenderingContext).deleteVertexArray(this.vao);\n }\n\n this.vertexBuffer =\n this.normalBuffer =\n this.texCoordBuffer =\n this.indexBuffer =\n this.jointIndexBuffer =\n this.jointWeightBuffer =\n this.vao =\n null;\n }\n}\n","// GLSL shader sources inlined as TypeScript strings so the package can\n// be built with tsup without requiring Vite's ?raw loader.\n\nexport const vsSource = /* glsl */ `\n#define MAX_JOINTS 128\n\nattribute vec3 aPosition;\nattribute vec3 aNormal;\nattribute vec2 aTexCoord;\n\n// ── Skinning attributes (vec4: up to 4 joint influences per vertex) ──\nattribute vec4 aJointIndices;\nattribute vec4 aJointWeights;\n\nuniform mat4 uModel;\nuniform mat4 uView;\nuniform mat4 uProjection;\n\n// ── Skinning uniforms ──\nuniform bool uUseSkinning;\nuniform mat4 uJointMatrices[MAX_JOINTS];\n\nvarying vec3 vNormal;\nvarying vec3 vPosition;\nvarying vec2 vTexCoord;\n\nvoid main() {\n vTexCoord = aTexCoord;\n\n vec4 pos = vec4(aPosition, 1.0);\n vec3 norm = aNormal;\n\n if (uUseSkinning) {\n mat4 skinMat =\n aJointWeights.x * uJointMatrices[int(aJointIndices.x)] +\n aJointWeights.y * uJointMatrices[int(aJointIndices.y)] +\n aJointWeights.z * uJointMatrices[int(aJointIndices.z)] +\n aJointWeights.w * uJointMatrices[int(aJointIndices.w)];\n pos = skinMat * pos;\n norm = mat3(skinMat) * norm;\n }\n\n vNormal = mat3(uModel) * norm;\n\n vec4 worldPosition = uModel * pos;\n vPosition = worldPosition.xyz;\n gl_Position = uProjection * uView * worldPosition;\n}\n`;\n\nexport const fsSource = /* glsl */ `\nprecision mediump float;\n\n#define MAX_LIGHTS 5\n\nvarying vec3 vNormal;\nvarying vec3 vPosition;\nvarying vec2 vTexCoord;\n\nuniform vec3 uLightDirection[MAX_LIGHTS];\nuniform vec3 uLightColor[MAX_LIGHTS];\nuniform float uLightIntensity[MAX_LIGHTS];\nuniform int uLightType[MAX_LIGHTS]; // 0=directional, 1=point, 2=ambient\nuniform int uLightCount;\nuniform vec3 uLightPosition[MAX_LIGHTS];\nuniform float uLightConstant[MAX_LIGHTS];\nuniform float uLightLinear[MAX_LIGHTS];\nuniform float uLightQuadratic[MAX_LIGHTS];\n\nuniform vec4 uColor;\nuniform bool uUnlit;\nuniform bool uUseTexture;\nuniform sampler2D uTexture;\n\nuniform vec3 uViewPosition;\nuniform float uShininess;\nuniform vec3 uSpecularColor;\n\nvoid main() {\n vec4 baseColor = uColor;\n if (uUseTexture) {\n baseColor *= texture2D(uTexture, vTexCoord);\n }\n\n if (uUnlit) {\n gl_FragColor = baseColor;\n return;\n }\n\n vec3 norm = normalize(vNormal);\n vec3 totalDiffuse = vec3(0.0);\n vec3 totalAmbient = vec3(0.0);\n vec3 totalSpecular = vec3(0.0);\n\n vec3 viewDir = normalize(uViewPosition - vPosition);\n\n for (int i = 0; i < MAX_LIGHTS; i++) {\n if (i >= uLightCount) break;\n\n if (uLightType[i] == 2) {\n totalAmbient += uLightColor[i] * uLightIntensity[i];\n } else {\n vec3 lightDir;\n float attenuation = 1.0;\n\n if (uLightType[i] == 0) {\n // Directional light\n lightDir = normalize(-uLightDirection[i]);\n } else {\n // Point light\n vec3 lightVec = uLightPosition[i] - vPosition;\n float distance = length(lightVec);\n lightDir = normalize(lightVec);\n attenuation = 1.0 / (\n uLightConstant[i] +\n uLightLinear[i] * distance +\n uLightQuadratic[i] * distance * distance\n );\n }\n\n // Diffuse\n float diff = max(dot(norm, lightDir), 0.0);\n totalDiffuse += diff * uLightColor[i] * uLightIntensity[i] * attenuation;\n\n // Specular (Blinn-Phong) — softer, more natural highlights\n vec3 halfDir = normalize(lightDir + viewDir);\n float spec = pow(max(dot(norm, halfDir), 0.0), uShininess);\n totalSpecular += spec * uSpecularColor * uLightIntensity[i] * attenuation;\n }\n }\n\n vec3 lightingResult =\n totalAmbient * baseColor.rgb +\n totalDiffuse * baseColor.rgb +\n totalSpecular;\n\n gl_FragColor = vec4(lightingResult, baseColor.a);\n}\n`;\n","/* eslint-disable @typescript-eslint/no-unsafe-argument */\nimport { vsSource, fsSource } from '../shaders/index';\n\nexport type RenderCallback = (\n gl: WebGLRenderingContext,\n program: WebGLProgram,\n) => void;\n\nexport class WebGLCore {\n public gl: WebGL2RenderingContext | WebGLRenderingContext;\n public program: WebGLProgram;\n public canvas: HTMLCanvasElement;\n public isWebGL2: boolean;\n\n /**\n * Initialise the WebGL rendering core.\n *\n * @param canvasOrId - An `HTMLCanvasElement` or the DOM `id` of one.\n * Defaults to `'glcanvas'`.\n */\n constructor(canvasOrId: string | HTMLCanvasElement = 'glcanvas') {\n const canvas =\n typeof canvasOrId === 'string'\n ? (document.getElementById(canvasOrId) as HTMLCanvasElement | null)\n : canvasOrId;\n if (!canvas) throw new Error('Canvas not found');\n this.canvas = canvas;\n\n canvas.width = window.innerWidth;\n canvas.height = window.innerHeight;\n\n this.gl = (canvas.getContext('webgl2') ?? canvas.getContext('webgl')) as\n | WebGL2RenderingContext\n | WebGLRenderingContext;\n\n if (!this.gl) throw new Error('WebGL not supported in this browser!');\n\n this.isWebGL2 = this.gl instanceof WebGL2RenderingContext;\n this.gl.viewport(0, 0, canvas.width, canvas.height);\n\n const program = this.createProgram(this.gl);\n if (!program) throw new Error('Failed to create program');\n this.program = program as unknown as WebGLProgram;\n }\n\n public getProgram(): WebGLProgram {\n return this.program;\n }\n\n public getRenderingContext(): WebGLRenderingContext {\n return this.gl;\n }\n\n private createProgram(gl: WebGLRenderingContext): WebGLProgram | null {\n const vertexShader = this.compileShader(gl, gl.VERTEX_SHADER, vsSource);\n const fragmentShader = this.compileShader(gl, gl.FRAGMENT_SHADER, fsSource);\n\n if (!vertexShader || !fragmentShader)\n throw new Error('Failed to create shaders');\n\n const program = gl.createProgram();\n if (!program) return null;\n\n gl.attachShader(program, vertexShader);\n gl.attachShader(program, fragmentShader);\n gl.linkProgram(program);\n\n // Clean up shader objects\n gl.deleteShader(vertexShader);\n gl.deleteShader(fragmentShader);\n\n if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {\n console.error(gl.getProgramInfoLog(program));\n throw new Error(gl.getProgramInfoLog(program) || 'Program link error');\n }\n\n return program;\n }\n\n private compileShader(\n gl: WebGLRenderingContext,\n type: number,\n source: string,\n ): WebGLShader | null {\n const shader = gl.createShader(type);\n if (!shader) return null;\n\n gl.shaderSource(shader, source);\n gl.compileShader(shader);\n\n if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {\n console.error(gl.getShaderInfoLog(shader));\n gl.deleteShader(shader);\n throw new Error(gl.getShaderInfoLog(shader) || 'Shader compile error');\n }\n return shader;\n }\n\n public resize(width: number, height: number) {\n this.canvas.width = width;\n this.canvas.height = height;\n this.gl.viewport(0, 0, width, height);\n }\n\n dispose() {\n const { gl, program } = this;\n if (program) gl.deleteProgram(program);\n }\n}\n\nexport default WebGLCore;\n","import { mat4, quat, vec3 } from 'gl-matrix';\n\n/** Maximum number of joints supported per skeleton (must match shader). */\nexport const MAX_JOINTS = 128;\n\n/**\n * Describes a single joint (bone) inside a {@link Skeleton}.\n *\n * Each joint stores its default bind-pose transform (translation, rotation,\n * scale) and the inverse bind matrix used to transform vertices from model\n * space into joint-local space.\n */\nexport interface Joint {\n /** Human-readable name (e.g. \"Hips\", \"LeftArm\"). */\n name: string;\n /** Original glTF node index this joint references. */\n nodeIndex: number;\n /** Index of the parent joint in the skeleton's joint array (-1 = root). */\n parentIndex: number;\n /** Bind-pose translation. */\n localTranslation: vec3;\n /** Bind-pose rotation (quaternion). */\n localRotation: quat;\n /** Bind-pose scale. */\n localScale: vec3;\n /** Inverse bind matrix from the glTF skin. */\n inverseBindMatrix: mat4;\n}\n\n/**\n * Per-joint local transform produced by animation sampling.\n * Any undefined component falls back to the bind-pose value.\n */\nexport interface JointPose {\n t?: vec3;\n r?: quat;\n s?: vec3;\n}\n\n/**\n * A skeleton representing a hierarchy of {@link Joint}s.\n *\n * Call {@link computeJointMatrices} each frame with the current animation pose\n * to produce a flat `Float32Array` of `mat4`s ready for upload as a shader\n * uniform.\n */\nexport class Skeleton {\n /** Ordered joint list (parent always precedes child). */\n public readonly joints: Joint[];\n\n /**\n * Flat `Float32Array` of `joints.length` column-major `mat4` values.\n * Updated by {@link computeJointMatrices} — upload directly to\n * `uJointMatrices[0]`.\n */\n public readonly jointMatrices: Float32Array;\n\n /**\n * Global transform of the non-joint ancestor nodes above the skeleton\n * root. In many exported models the armature sits under a scene-root\n * node that carries scale / rotation (e.g. cm → m conversion). This\n * transform is pre-multiplied into root-joint globals so that\n * `global * inverseBindMatrix` evaluates to identity in the bind pose.\n */\n public readonly rootTransform: mat4;\n\n constructor(joints: Joint[], rootTransform?: mat4) {\n if (joints.length > MAX_JOINTS) {\n console.warn(\n `[Skeleton] ${joints.length} joints exceeds MAX_JOINTS (${MAX_JOINTS}). ` +\n 'Extra joints will be ignored by the shader.',\n );\n }\n this.joints = joints;\n this.jointMatrices = new Float32Array(joints.length * 16);\n this.rootTransform = rootTransform ?? mat4.create();\n }\n\n /** Number of joints in the skeleton. */\n get jointCount(): number {\n return this.joints.length;\n }\n\n /**\n * Walk the joint hierarchy, composing global transforms, then multiply by\n * each joint's inverse-bind matrix. The result is stored in\n * {@link jointMatrices} ready for shader upload.\n *\n * @param animatedPoses - Optional per-joint local transforms from an\n * {@link AnimationClip}. Map keys are **joint array indices** (not glTF\n * node indices).\n */\n computeJointMatrices(animatedPoses?: Map<number, JointPose>): void {\n const globals: mat4[] = new Array<mat4>(this.joints.length);\n const local = mat4.create();\n\n for (let i = 0; i < this.joints.length; i++) {\n const joint = this.joints[i];\n const pose = animatedPoses?.get(i);\n\n const t = pose?.t ?? joint.localTranslation;\n const r = pose?.r ?? joint.localRotation;\n const s = pose?.s ?? joint.localScale;\n\n mat4.fromRotationTranslationScale(local, r, t, s);\n\n if (joint.parentIndex >= 0) {\n globals[i] = mat4.create();\n mat4.multiply(globals[i], globals[joint.parentIndex], local);\n } else {\n // Root joint — include the non-joint ancestor transform\n globals[i] = mat4.create();\n mat4.multiply(globals[i], this.rootTransform, local);\n }\n\n // final = global * inverseBindMatrix\n const final = mat4.create();\n mat4.multiply(final, globals[i], joint.inverseBindMatrix);\n this.jointMatrices.set(final as unknown as Float32Array, i * 16);\n }\n }\n\n /**\n * Create a deep clone of this skeleton (useful when instancing models).\n */\n clone(): Skeleton {\n const clonedJoints: Joint[] = this.joints.map((j) => ({\n name: j.name,\n nodeIndex: j.nodeIndex,\n parentIndex: j.parentIndex,\n localTranslation: vec3.clone(j.localTranslation),\n localRotation: quat.clone(j.localRotation),\n localScale: vec3.clone(j.localScale),\n inverseBindMatrix: mat4.clone(j.inverseBindMatrix),\n }));\n return new Skeleton(clonedJoints, mat4.clone(this.rootTransform));\n }\n}\n","import { quat, vec3 } from 'gl-matrix';\nimport type { JointPose } from './Skeleton';\n\n// ─── Types ──────────────────────────────────────────────────────\n\n/** Interpolation mode as defined by glTF 2.0. */\nexport type InterpolationMode = 'STEP' | 'LINEAR' | 'CUBICSPLINE';\n\n/** Which transform component the channel drives. */\nexport type AnimationPath = 'translation' | 'rotation' | 'scale';\n\n/**\n * Stores the raw keyframe data for one animated property and performs\n * interpolation between keyframes.\n */\nexport interface AnimationSampler {\n /** Keyframe timestamps (seconds). */\n times: Float32Array;\n /** Flattened keyframe output values (3 or 4 components per key). */\n values: Float32Array;\n /** Interpolation mode (default `LINEAR`). */\n interpolation: InterpolationMode;\n}\n\n/**\n * Links a {@link AnimationSampler} to a specific joint property.\n */\nexport interface AnimationChannel {\n /** Index into the owning {@link Skeleton}'s joint array. */\n jointIndex: number;\n /** Transform component this channel drives. */\n path: AnimationPath;\n /** The sampler that holds keyframe data. */\n sampler: AnimationSampler;\n}\n\n// ─── AnimationClip ──────────────────────────────────────────────\n\n/**\n * A named animation clip containing one or more {@link AnimationChannel}s.\n *\n * Call {@link update} each frame to advance the playhead, then {@link sample}\n * to obtain per-joint local transforms that feed into\n * `Skeleton.computeJointMatrices()`.\n *\n * ```ts\n * clip.play();\n * // game loop:\n * clip.update(deltaTime);\n * const poses = clip.sample();\n * skeleton.computeJointMatrices(poses);\n * ```\n */\nexport class AnimationClip {\n /** Human-readable name (e.g. \"Walk\", \"Idle\"). */\n public readonly name: string;\n\n /** All channels in this clip. */\n public readonly channels: AnimationChannel[];\n\n /** Total duration in seconds (derived from the maximum keyframe time). */\n public readonly duration: number;\n\n /** Current playback time in seconds. */\n public currentTime = 0;\n\n /** Whether the clip loops when it reaches the end. */\n public loop = true;\n\n /** Playback speed multiplier (1 = normal, 2 = double, −1 = reverse). */\n public speed = 1;\n\n /** Whether the clip is currently advancing. */\n public playing = false;\n\n constructor(name: string, channels: AnimationChannel[], duration: number) {\n this.name = name;\n this.channels = channels;\n this.duration = duration;\n }\n\n /** Start / resume playback. */\n play(): void {\n this.playing = true;\n }\n\n /** Pause playback (keeps current time). */\n pause(): void {\n this.playing = false;\n }\n\n /** Stop and rewind to the beginning. */\n stop(): void {\n this.playing = false;\n this.currentTime = 0;\n }\n\n /** Rewind to the beginning without changing play state. */\n reset(): void {\n this.currentTime = 0;\n }\n\n /**\n * Advance the playhead by `deltaTime` seconds (respects {@link speed}).\n * Automatically loops or clamps depending on {@link loop}.\n */\n update(deltaTime: number): void {\n if (!this.playing || this.duration <= 0) return;\n\n this.currentTime += deltaTime * this.speed;\n\n if (this.loop) {\n this.currentTime =\n ((this.currentTime % this.duration) + this.duration) % this.duration;\n } else {\n if (this.currentTime >= this.duration) {\n this.currentTime = this.duration;\n this.playing = false;\n } else if (this.currentTime < 0) {\n this.currentTime = 0;\n this.playing = false;\n }\n }\n }\n\n /**\n * Sample every channel at the current playhead and return a map of\n * joint-index → local pose.\n */\n sample(): Map<number, JointPose> {\n const result = new Map<number, JointPose>();\n\n for (const channel of this.channels) {\n if (!result.has(channel.jointIndex)) {\n result.set(channel.jointIndex, {});\n }\n const entry = result.get(channel.jointIndex)!;\n const value = sampleChannel(channel, this.currentTime);\n\n switch (channel.path) {\n case 'translation':\n entry.t = value as vec3;\n break;\n case 'rotation':\n entry.r = value as quat;\n break;\n case 'scale':\n entry.s = value as vec3;\n break;\n }\n }\n\n return result;\n }\n\n /**\n * Create an independent copy of this clip (shares the underlying sampler\n * data but has its own playback state).\n */\n clone(): AnimationClip {\n const copy = new AnimationClip(this.name, this.channels, this.duration);\n copy.loop = this.loop;\n copy.speed = this.speed;\n return copy;\n }\n}\n\n// ─── Sampling helpers ───────────────────────────────────────────\n\n/** Read `numComponents` floats from `values` starting at `index * numComponents`. */\nfunction readValue(\n values: Float32Array,\n index: number,\n numComponents: number,\n): vec3 | quat {\n const off = index * numComponents;\n if (numComponents === 4) {\n return quat.fromValues(\n values[off],\n values[off + 1],\n values[off + 2],\n values[off + 3],\n );\n }\n return vec3.fromValues(values[off], values[off + 1], values[off + 2]);\n}\n\n/**\n * Sample a single channel at the given time using its sampler's interpolation\n * mode. Returns a `vec3` for translation/scale or a `quat` for rotation.\n */\nfunction sampleChannel(channel: AnimationChannel, time: number): vec3 | quat {\n const { sampler, path } = channel;\n const { times, values, interpolation } = sampler;\n const numComponents = path === 'rotation' ? 4 : 3;\n\n // Edge cases — clamp to first / last keyframe\n if (times.length === 0) {\n return numComponents === 4 ? quat.create() : vec3.create();\n }\n if (time <= times[0]) {\n return readValue(values, 0, numComponents);\n }\n if (time >= times[times.length - 1]) {\n return readValue(values, times.length - 1, numComponents);\n }\n\n // Binary search for the surrounding keyframe pair\n let lo = 0;\n let hi = times.length - 1;\n while (lo < hi - 1) {\n const mid = (lo + hi) >>> 1;\n if (times[mid] <= time) {\n lo = mid;\n } else {\n hi = mid;\n }\n }\n\n const t0 = times[lo];\n const t1 = times[hi];\n const factor = (time - t0) / (t1 - t0);\n\n const v0 = readValue(values, lo, numComponents);\n const v1 = readValue(values, hi, numComponents);\n\n if (interpolation === 'STEP') {\n return v0;\n }\n\n // LINEAR (or fallback for CUBICSPLINE — true cubic not yet implemented)\n if (path === 'rotation') {\n const out = quat.create();\n quat.slerp(out, v0 as quat, v1 as quat, factor);\n return out;\n }\n\n const out = vec3.create();\n vec3.lerp(out, v0 as vec3, v1 as vec3, factor);\n return out;\n}\n","import { Material, Mesh } from '../classes';\nimport { GL_LINES } from '../classes/Mesh';\nimport { Vector3 } from '../domain/interfaces/Vectors';\n\nexport function createWireBox(\n min: Vector3,\n max: Vector3,\n material: Material,\n): Mesh {\n const [minX, minY, minZ] = min;\n const [maxX, maxY, maxZ] = max;\n\n // 8 corners of the box\n const vertices: number[] = [\n minX,\n minY,\n minZ,\n maxX,\n minY,\n minZ,\n maxX,\n maxY,\n minZ,\n minX,\n maxY,\n minZ,\n minX,\n minY,\n maxZ,\n maxX,\n minY,\n maxZ,\n maxX,\n maxY,\n maxZ,\n minX,\n maxY,\n maxZ,\n ];\n\n // edges as lines (pairs of indices)\n const indices: number[] = [\n 0,\n 1,\n 1,\n 2,\n 2,\n 3,\n 3,\n 0, // bottom face\n 4,\n 5,\n 5,\n 6,\n 6,\n 7,\n 7,\n 4, // top face\n 0,\n 4,\n 1,\n 5,\n 2,\n 6,\n 3,\n 7, // vertical edges\n ];\n\n // expand indices to vertices array (since WebGL uses gl.LINES)\n const lineVertices: number[] = [];\n for (let i = 0; i < indices.length; i++) {\n const idx = indices[i];\n lineVertices.push(\n vertices[idx * 3],\n vertices[idx * 3 + 1],\n vertices[idx * 3 + 2],\n );\n }\n\n // Normals can just be zero for wireframe\n const normals = new Float32Array(lineVertices.length);\n\n const mesh = new Mesh(\n 'hitbox',\n new Float32Array(lineVertices),\n normals,\n material,\n new Float32Array(),\n null,\n );\n mesh.setMode(GL_LINES);\n return mesh;\n}\n"],"mappings":"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","names":["mat4","quat","vec3","out","mat4"]}