bloody-engine 1.0.0

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.
@@ -0,0 +1,56 @@
1
+ (function(l,C){typeof exports=="object"&&typeof module<"u"?C(exports,require("gl"),require("fs/promises"),require("path")):typeof define=="function"&&define.amd?define(["exports","gl","fs/promises","path"],C):(l=typeof globalThis<"u"?globalThis:l||self,C(l.BloodyEngine={},l.createGL,l.fs,l.path))})(this,(function(l,C,se,ie){"use strict";function q(a){const e=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(a){for(const t in a)if(t!=="default"){const r=Object.getOwnPropertyDescriptor(a,t);Object.defineProperty(e,t,r.get?r:{enumerable:!0,get:()=>a[t]})}}return e.default=a,Object.freeze(e)}const T=q(se),p=q(ie);class O{constructor(e){this.isBrowser=!0,e.canvas?this.canvas=e.canvas:(this.canvas=document.createElement("canvas"),document.body.appendChild(this.canvas)),this.width=e.width,this.height=e.height,this.canvas.width=this.width,this.canvas.height=this.height;const t={alpha:!1,...e.contextAttributes},r=this.canvas.getContext("webgl",t);if(!r)throw new Error("Failed to initialize WebGL context in browser");this.glContext=r}resize(e,t){this.width=e,this.height=t,this.canvas.width=e,this.canvas.height=t,this.glContext.viewport(0,0,e,t)}getViewport(){return{width:this.width,height:this.height}}clear(e){e&&this.glContext.clearColor(e.r,e.g,e.b,e.a),this.glContext.clear(this.glContext.COLOR_BUFFER_BIT|this.glContext.DEPTH_BUFFER_BIT)}present(){}dispose(){this.canvas.parentElement&&this.canvas.parentElement.removeChild(this.canvas)}getCanvas(){return this.canvas}}class V{constructor(e){this.isBrowser=!1,this.width=e.width,this.height=e.height;const t=C(this.width,this.height,{preserveDrawingBuffer:e.preserveDrawingBuffer??!0,...e.contextAttributes});if(!t)throw new Error("Failed to initialize WebGL context in Node.js");this.glContext=t}resize(e,t){this.width=e,this.height=t,console.warn("NodeRenderingContext: Resize requested but not supported. Consider recreating context.")}getViewport(){return{width:this.width,height:this.height}}clear(e){e&&this.glContext.clearColor(e.r,e.g,e.b,e.a),this.glContext.clear(this.glContext.COLOR_BUFFER_BIT|this.glContext.DEPTH_BUFFER_BIT)}present(){this.glContext.flush()}dispose(){this.glContext.flush()}readPixels(){const e=new Uint8Array(this.width*this.height*4);return this.glContext.readPixels(0,0,this.width,this.height,this.glContext.RGBA,this.glContext.UNSIGNED_BYTE,e),e}}class X{static isBrowserEnvironment(){return typeof window<"u"&&typeof document<"u"}static createContext(e){return this.isBrowserEnvironment()?new O(e):new V(e)}static createBrowserContext(e){return new O(e)}static createNodeContext(e){return new V(e)}}class k{constructor(e,t,r,s){this.gl=e;const i=this.injectPrecisionHeader(t,s),o=this.injectPrecisionHeader(r,s);this.vertexShader=this.compileShader(i,e.VERTEX_SHADER),this.fragmentShader=this.compileShader(o,e.FRAGMENT_SHADER),this.program=this.linkProgram(this.vertexShader,this.fragmentShader)}injectPrecisionHeader(e,t){return e.includes("#ifdef GL_ES")||e.includes("precision")?e:`#ifdef GL_ES
2
+ precision highp float;
3
+ #endif
4
+ `+e}compileShader(e,t){const r=this.gl.createShader(t);if(!r)throw new Error(`Failed to create shader of type ${t}`);if(this.gl.shaderSource(r,e),this.gl.compileShader(r),!this.gl.getShaderParameter(r,this.gl.COMPILE_STATUS)){const i=this.gl.getShaderInfoLog(r),o=t===this.gl.VERTEX_SHADER?"vertex":"fragment";throw this.gl.deleteShader(r),new Error(`Failed to compile ${o} shader:
5
+ ${i}
6
+
7
+ Source:
8
+ ${e}`)}return r}linkProgram(e,t){const r=this.gl.createProgram();if(!r)throw new Error("Failed to create shader program");if(this.gl.attachShader(r,e),this.gl.attachShader(r,t),this.gl.linkProgram(r),!this.gl.getProgramParameter(r,this.gl.LINK_STATUS)){const i=this.gl.getProgramInfoLog(r);throw this.gl.deleteProgram(r),this.gl.deleteShader(e),this.gl.deleteShader(t),new Error(`Failed to link shader program:
9
+ ${i}`)}return r}getProgram(){return this.program}getUniformLocation(e){return this.gl.getUniformLocation(this.program,e)}getAttributeLocation(e){return this.gl.getAttribLocation(this.program,e)}use(){this.gl.useProgram(this.program)}dispose(){this.gl.deleteProgram(this.program),this.gl.deleteShader(this.vertexShader),this.gl.deleteShader(this.fragmentShader)}}class H{constructor(e,t){this.context=X.createContext({width:e,height:t,preserveDrawingBuffer:!0})}getGLContext(){return this.context.glContext}getRenderingContext(){return this.context}getWidth(){return this.context.width}getHeight(){return this.context.height}getViewport(){return this.context.getViewport()}isBrowser(){return this.context.isBrowser}resize(e,t){this.context.resize(e,t)}clear(e){this.context.clear(e)}present(){this.context.present()}dispose(){this.context.dispose()}createShader(e,t){return new k(this.context.glContext,e,t,this.context.isBrowser)}}class R{constructor(e,t,r,s){this.gl=e,this.width=t,this.height=r;const i=e.createTexture();if(!i)throw new Error("Failed to create texture");this.texture=i,e.bindTexture(e.TEXTURE_2D,this.texture),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_S,e.CLAMP_TO_EDGE),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_T,e.CLAMP_TO_EDGE),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MIN_FILTER,e.LINEAR),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MAG_FILTER,e.LINEAR),s?e.texImage2D(e.TEXTURE_2D,0,e.RGBA,t,r,0,e.RGBA,e.UNSIGNED_BYTE,s):e.texImage2D(e.TEXTURE_2D,0,e.RGBA,t,r,0,e.RGBA,e.UNSIGNED_BYTE,null),e.bindTexture(e.TEXTURE_2D,null)}static createSolid(e,t,r,s,i,o,n=255){const c=t*r,h=new Uint8Array(c*4);for(let m=0;m<c;m++){const u=m*4;h[u]=s,h[u+1]=i,h[u+2]=o,h[u+3]=n}return new R(e,t,r,h)}static createCheckerboard(e,t,r,s=32){const i=new Uint8Array(t*r*4);for(let o=0;o<r;o++)for(let n=0;n<t;n++){const c=Math.floor(n/s),h=Math.floor(o/s),m=(c+h)%2===0,u=(o*t+n)*4,f=m?255:0;i[u]=f,i[u+1]=f,i[u+2]=f,i[u+3]=255}return new R(e,t,r,i)}static createGradient(e,t,r){const s=new Uint8Array(t*r*4);for(let i=0;i<r;i++)for(let o=0;o<t;o++){const n=(i*t+o)*4;s[n]=Math.floor(o/t*255),s[n+1]=Math.floor(i/r*255),s[n+2]=128,s[n+3]=255}return new R(e,t,r,s)}bind(e=0){this.gl.activeTexture(this.gl.TEXTURE0+e),this.gl.bindTexture(this.gl.TEXTURE_2D,this.texture)}unbind(){this.gl.bindTexture(this.gl.TEXTURE_2D,null)}getHandle(){return this.texture}getDimensions(){return{width:this.width,height:this.height}}dispose(){this.gl.deleteTexture(this.texture)}}class W{constructor(e,t,r=0){this.gl=e,this.stride=r;const s=r>0?r/4:3;this.vertexCount=t.length/s;const i=e.createBuffer();if(!i)throw new Error("Failed to create vertex buffer");this.buffer=i,e.bindBuffer(e.ARRAY_BUFFER,this.buffer),e.bufferData(e.ARRAY_BUFFER,t,e.STATIC_DRAW),e.bindBuffer(e.ARRAY_BUFFER,null)}bind(){this.gl.bindBuffer(this.gl.ARRAY_BUFFER,this.buffer)}unbind(){this.gl.bindBuffer(this.gl.ARRAY_BUFFER,null)}getVertexCount(){return this.vertexCount}getStride(){return this.stride}dispose(){this.gl.deleteBuffer(this.buffer)}}class j{constructor(e,t){this.gl=e,this.indexCount=t.length;const r=e.createBuffer();if(!r)throw new Error("Failed to create index buffer");this.buffer=r,e.bindBuffer(e.ELEMENT_ARRAY_BUFFER,this.buffer),e.bufferData(e.ELEMENT_ARRAY_BUFFER,t,e.STATIC_DRAW),e.bindBuffer(e.ELEMENT_ARRAY_BUFFER,null)}bind(){this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER,this.buffer)}unbind(){this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER,null)}getIndexCount(){return this.indexCount}dispose(){this.gl.deleteBuffer(this.buffer)}}const oe=Object.freeze(Object.defineProperty({__proto__:null,IndexBuffer:j,VertexBuffer:W},Symbol.toStringTag,{value:"Module"}));class ne{constructor(e,t,r=1e3){this.vertexBuffer=null,this.quads=[],this.isDirty=!1,this.verticesPerQuad=6,this.floatsPerVertex=5,this.texture=null,this.gl=e,this.shader=t,this.maxQuads=r;const s=r*this.verticesPerQuad*this.floatsPerVertex;this.vertexData=new Float32Array(s);const i=e.createBuffer();if(!i)throw new Error("Failed to create vertex buffer");this.vertexBuffer=i,e.bindBuffer(e.ARRAY_BUFFER,this.vertexBuffer),e.bufferData(e.ARRAY_BUFFER,this.vertexData.byteLength,e.DYNAMIC_DRAW),e.bindBuffer(e.ARRAY_BUFFER,null)}setTexture(e){this.texture=e}addQuad(e){if(this.quads.length>=this.maxQuads){console.warn(`Batch renderer at max capacity (${this.maxQuads})`);return}this.quads.push(e),this.isDirty=!0}clear(){this.quads=[],this.isDirty=!0}getQuadCount(){return this.quads.length}update(){if(!this.isDirty||this.quads.length===0)return;let e=0;for(const t of this.quads){const r=this.generateQuadVertices(t);for(const s of r)this.vertexData[e++]=s[0],this.vertexData[e++]=s[1],this.vertexData[e++]=s[2],this.vertexData[e++]=s[3],this.vertexData[e++]=s[4]}this.vertexBuffer&&(this.gl.bindBuffer(this.gl.ARRAY_BUFFER,this.vertexBuffer),this.gl.bufferSubData(this.gl.ARRAY_BUFFER,0,this.vertexData.subarray(0,e)),this.gl.bindBuffer(this.gl.ARRAY_BUFFER,null)),this.isDirty=!1}render(e){if(this.quads.length!==0&&(this.update(),this.shader.use(),this.vertexBuffer)){this.gl.bindBuffer(this.gl.ARRAY_BUFFER,this.vertexBuffer);const t=this.shader.getAttributeLocation("aPosition"),r=this.shader.getAttributeLocation("aTexCoord");if(t!==-1&&(this.gl.enableVertexAttribArray(t),this.gl.vertexAttribPointer(t,3,this.gl.FLOAT,!1,this.floatsPerVertex*4,0)),r!==-1&&(this.gl.enableVertexAttribArray(r),this.gl.vertexAttribPointer(r,2,this.gl.FLOAT,!1,this.floatsPerVertex*4,12)),this.texture){this.texture.bind(0);const o=this.shader.getUniformLocation("uTexture");o!==null&&this.gl.uniform1i(o,0)}const s=this.shader.getUniformLocation("uMatrix");if(s!==null){const o=e?e.getViewMatrix():new Float32Array([1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]);this.gl.uniformMatrix4fv(s,!1,o)}const i=this.quads.length*this.verticesPerQuad;this.gl.drawArrays(this.gl.TRIANGLES,0,i),this.gl.bindBuffer(this.gl.ARRAY_BUFFER,null)}}generateQuadVertices(e){const{x:t,y:r,width:s,height:i,rotation:o}=e,n=s/2,c=i/2,h=Math.cos(o),m=Math.sin(o),u=(w,b)=>[w*h-b*m,w*m+b*h],f=[[-n,-c],[n,-c],[n,c],[n,c],[-n,c],[-n,-c]],g=[[0,0],[1,0],[1,1],[1,1],[0,1],[0,0]],E=[];for(let w=0;w<f.length;w++){const[b,_]=f[w],[F,d]=u(b,_),[v,y]=g[w];E.push([t+F,r+d,0,v,y])}return E}dispose(){this.vertexBuffer&&(this.gl.deleteBuffer(this.vertexBuffer),this.vertexBuffer=null)}}class ae{constructor(e,t,r=1e3){this.vertexBuffer=null,this.quads=[],this.isDirty=!1,this.verticesPerQuad=6,this.floatsPerVertex=10,this.texture=null,this.depthTestEnabled=!0,this.gl=e,this.shader=t,this.maxQuads=r;const s=r*this.verticesPerQuad*this.floatsPerVertex;this.vertexData=new Float32Array(s);const i=e.createBuffer();if(!i)throw new Error("Failed to create vertex buffer");this.vertexBuffer=i,e.bindBuffer(e.ARRAY_BUFFER,this.vertexBuffer),e.bufferData(e.ARRAY_BUFFER,this.vertexData.byteLength,e.DYNAMIC_DRAW),e.bindBuffer(e.ARRAY_BUFFER,null)}setTexture(e){this.texture=e}addQuad(e){if(this.quads.length>=this.maxQuads){console.warn(`Sprite batch renderer at max capacity (${this.maxQuads})`);return}this.quads.push(e),this.isDirty=!0}clear(){this.quads=[],this.isDirty=!0}getQuadCount(){return this.quads.length}update(){if(!this.isDirty||this.quads.length===0)return;let e=0;for(const t of this.quads){const{x:r,y:s,z:i=0,width:o,height:n,rotation:c,color:h={r:1,g:1,b:1,a:1},uvRect:m={uMin:0,vMin:0,uMax:1,vMax:1},texIndex:u=0}=t,f=this.generateQuadVertices({x:r,y:s,z:i,width:o,height:n,rotation:c,color:h,uvRect:m,texIndex:u});for(const g of f)this.vertexData[e++]=g.x,this.vertexData[e++]=g.y,this.vertexData[e++]=g.z,this.vertexData[e++]=g.u,this.vertexData[e++]=g.v,this.vertexData[e++]=g.r,this.vertexData[e++]=g.g,this.vertexData[e++]=g.b,this.vertexData[e++]=g.a,this.vertexData[e++]=g.texIndex}this.vertexBuffer&&(this.gl.bindBuffer(this.gl.ARRAY_BUFFER,this.vertexBuffer),this.gl.bufferSubData(this.gl.ARRAY_BUFFER,0,this.vertexData.subarray(0,e)),this.gl.bindBuffer(this.gl.ARRAY_BUFFER,null)),this.isDirty=!1}setDepthTestEnabled(e){this.depthTestEnabled=e}render(e){if(this.quads.length!==0&&(this.update(),this.shader.use(),this.depthTestEnabled?(this.gl.enable(this.gl.DEPTH_TEST),this.gl.depthFunc(this.gl.LEQUAL)):this.gl.disable(this.gl.DEPTH_TEST),this.vertexBuffer)){this.gl.bindBuffer(this.gl.ARRAY_BUFFER,this.vertexBuffer);const t=this.shader.getAttributeLocation("aPosition"),r=this.shader.getAttributeLocation("aTexCoord"),s=this.shader.getAttributeLocation("aColor"),i=this.shader.getAttributeLocation("aTexIndex"),o=this.floatsPerVertex*4;if(t!==-1&&(this.gl.enableVertexAttribArray(t),this.gl.vertexAttribPointer(t,3,this.gl.FLOAT,!1,o,0)),r!==-1&&(this.gl.enableVertexAttribArray(r),this.gl.vertexAttribPointer(r,2,this.gl.FLOAT,!1,o,12)),s!==-1&&(this.gl.enableVertexAttribArray(s),this.gl.vertexAttribPointer(s,4,this.gl.FLOAT,!1,o,20)),i!==-1&&(this.gl.enableVertexAttribArray(i),this.gl.vertexAttribPointer(i,1,this.gl.FLOAT,!1,o,36)),this.texture){this.texture.bind(0);const h=this.shader.getUniformLocation("uTexture");h!==null&&this.gl.uniform1i(h,0)}const n=this.shader.getUniformLocation("uMatrix");if(n!==null){const h=e?e.getViewMatrix():new Float32Array([1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]);this.gl.uniformMatrix4fv(n,!1,h)}const c=this.quads.length*this.verticesPerQuad;this.gl.drawArrays(this.gl.TRIANGLES,0,c),this.gl.bindBuffer(this.gl.ARRAY_BUFFER,null)}}generateQuadVertices(e){const{x:t,y:r,z:s,width:i,height:o,rotation:n,color:c,uvRect:h,texIndex:m}=e,u=i/2,f=o/2,g=Math.cos(n),E=Math.sin(n),w=(d,v)=>[d*g-v*E,d*E+v*g],b=[[-u,-f],[u,-f],[u,f],[u,f],[-u,f],[-u,-f]],_=[[h.uMin,h.vMin],[h.uMax,h.vMin],[h.uMax,h.vMax],[h.uMax,h.vMax],[h.uMin,h.vMax],[h.uMin,h.vMin]],F=[];for(let d=0;d<b.length;d++){const[v,y]=b[d],[D,I]=w(v,y),[N,x]=_[d];F.push({x:t+D,y:r+I,z:s,u:N,v:x,r:c.r,g:c.g,b:c.b,a:c.a,texIndex:m})}return F}dispose(){this.vertexBuffer&&(this.gl.deleteBuffer(this.vertexBuffer),this.vertexBuffer=null)}}class A{static identity(){return new Float32Array([1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1])}static translation(e,t,r=0){return new Float32Array([1,0,0,0,0,1,0,0,0,0,1,0,e,t,r,1])}static scale(e,t,r=1){return new Float32Array([e,0,0,0,0,t,0,0,0,0,r,0,0,0,0,1])}static multiply(e,t){const r=new Float32Array(16);for(let s=0;s<4;s++)for(let i=0;i<4;i++){let o=0;for(let n=0;n<4;n++)o+=e[n*4+i]*t[s*4+n];r[s*4+i]=o}return r}static createViewMatrix(e,t,r){const s=A.translation(-e,-t,0),i=A.scale(r,r,1);return A.multiply(s,i)}}class ce{constructor(e=0,t=0,r=1){this._viewMatrix=null,this._viewMatrixDirty=!0,this._x=e,this._y=t,this._zoom=r}get x(){return this._x}set x(e){this._x=e,this._viewMatrixDirty=!0}get y(){return this._y}set y(e){this._y=e,this._viewMatrixDirty=!0}get zoom(){return this._zoom}set zoom(e){this._zoom=Math.max(.001,e),this._viewMatrixDirty=!0}setPosition(e,t){this._x=e,this._y=t,this._viewMatrixDirty=!0}move(e,t){this._x+=e,this._y+=t,this._viewMatrixDirty=!0}zoomBy(e){this._zoom=Math.max(.001,this._zoom*e),this._viewMatrixDirty=!0}reset(){this._x=0,this._y=0,this._zoom=1,this._viewMatrixDirty=!0}getViewMatrix(){return(this._viewMatrixDirty||this._viewMatrix===null)&&(this._viewMatrix=A.createViewMatrix(this._x,this._y,this._zoom),this._viewMatrixDirty=!1),this._viewMatrix}screenToWorld(e,t,r,s){const i=e-r/2,o=t-s/2,n=i/this._zoom+this._x,c=o/this._zoom+this._y;return{x:n,y:c}}worldToScreen(e,t,r,s){const i=(e-this._x)*this._zoom,o=(t-this._y)*this._zoom,n=i+r/2,c=o+s/2;return{x:n,y:c}}}class Q{constructor(e="",t=1e4){this.baseUrl=e||this.getCurrentOrigin(),this.defaultTimeout=t}getCurrentOrigin(){return typeof window<"u"?window.location.origin:"http://localhost"}resolvePath(e){try{return e.startsWith("http://")||e.startsWith("https://")?e:e.startsWith("//")?window.location.protocol+e:e.startsWith("/")?this.baseUrl+e:`${this.baseUrl}/${e}`}catch{return e}}async load(e,t){const r=this.resolvePath(e);try{const s={credentials:t?.credentials||"same-origin"};t?.headers&&(s.headers=t.headers);const i=new AbortController,o=setTimeout(()=>i.abort(),this.defaultTimeout);s.signal=i.signal;const n=await fetch(r,s);if(clearTimeout(o),!n.ok)throw new Error(`HTTP ${n.status}: ${n.statusText} for URL: ${r}`);return await n.text()}catch(s){throw s instanceof Error?s.name==="AbortError"?new Error(`Request timeout after ${this.defaultTimeout}ms for URL: ${r}`):new Error(`Failed to load resource from ${r}: ${s.message}`):new Error(`Failed to load resource from ${r}: Unknown error`)}}async loadMultiple(e,t){const r=e.map(async s=>{try{return{data:await this.load(s,t),path:s,success:!0}}catch(i){return{data:"",path:s,success:!1,error:i instanceof Error?i.message:String(i)}}});return Promise.all(r)}canLoad(e){const t=[/^https?:\/\//i,/^\/\//,/^\//,/^\.\.?\//],r=/\.[a-z0-9]+$/i.test(e);return t.some(s=>s.test(e))||r}setBaseUrl(e){this.baseUrl=e}getBaseUrl(){return this.baseUrl}setTimeout(e){this.defaultTimeout=e}}const he=Object.freeze(Object.defineProperty({__proto__:null,BrowserResourceLoader:Q},Symbol.toStringTag,{value:"Module"}));class K{constructor(e=process.cwd()){this.baseDir=e}resolvePath(e){return p.isAbsolute(e)?p.normalize(e):p.normalize(p.join(this.baseDir,e))}async load(e,t){const r=this.resolvePath(e),s=t?.encoding||"utf-8";try{return await T.readFile(r,s)}catch(i){if(i instanceof Error){const o=i.code;throw o==="ENOENT"?new Error(`File not found: ${r} (resolved from: ${e})`):o==="EACCES"?new Error(`Permission denied reading file: ${r}`):o==="EISDIR"?new Error(`Path is a directory, not a file: ${r}`):new Error(`Failed to load resource from ${r}: ${i.message}`)}throw new Error(`Failed to load resource from ${r}: Unknown error`)}}async loadMultiple(e,t){const r=e.map(async s=>{try{return{data:await this.load(s,t),path:s,success:!0}}catch(i){return{data:"",path:s,success:!1,error:i instanceof Error?i.message:String(i)}}});return Promise.all(r)}canLoad(e){return[/^\//,/^[a-zA-Z]:/,/^\.\.?\//,/^[^/\\]+\//].some(r=>r.test(e))}async exists(e){const t=this.resolvePath(e);try{return await T.access(t,T.constants.F_OK),!0}catch{return!1}}async getStats(e){const t=this.resolvePath(e);return T.stat(t)}setBaseDir(e){this.baseDir=e}getBaseDir(){return this.baseDir}async listDirectory(e,t=!1){const r=this.resolvePath(e),s=await T.readdir(r,{withFileTypes:!0}),i=[];for(const o of s){const n=p.join(r,o.name);if(o.isDirectory()&&t){const c=await this.listDirectory(n,!0);i.push(...c)}else o.isFile()&&i.push(n)}return i}}const le=Object.freeze(Object.defineProperty({__proto__:null,NodeResourceLoader:K},Symbol.toStringTag,{value:"Module"}));var P=(a=>(a.BROWSER="browser",a.NODE="node",a.UNKNOWN="unknown",a))(P||{});class S{static detectEnvironment(){return typeof window<"u"&&typeof window.document<"u"&&typeof fetch<"u"?"browser":typeof process<"u"&&process.versions!=null&&process.versions.node!=null?"node":"unknown"}static isBrowser(){return this.detectEnvironment()==="browser"}static isNode(){return this.detectEnvironment()==="node"}static async create(e){const t=e?.forceEnvironment||this.detectEnvironment();switch(t){case"browser":return await this.createBrowserLoader(e);case"node":return await this.createNodeLoader(e);case"unknown":throw new Error("Unsupported environment: Unable to determine runtime environment. Please specify forceEnvironment in options.");default:throw new Error(`Unsupported environment: ${t}`)}}static async createBrowserLoader(e){const{BrowserResourceLoader:t}=await Promise.resolve().then(()=>he);return new t(e?.baseUrl,e?.timeout)}static async createNodeLoader(e){const{NodeResourceLoader:t}=await Promise.resolve().then(()=>le);return new t(e?.baseDir)}static async createWithFallback(e,t){try{return t={...t,forceEnvironment:e},await this.create(t)}catch{return await this.create({...t,forceEnvironment:void 0})}}}async function J(a){return await S.create(a)}const ue=Object.freeze(Object.defineProperty({__proto__:null,Environment:P,ResourceLoaderFactory:S,createResourceLoader:J},Symbol.toStringTag,{value:"Module"}));class de{constructor(e=!0){this.cache=new Map,this.enabled=e}get(e){if(this.enabled)return this.cache.get(e)}set(e,t){this.enabled&&this.cache.set(e,t)}has(e){return this.enabled?this.cache.has(e):!1}clear(){this.cache.clear()}size(){return this.cache.size}enable(){this.enabled=!0}disable(){this.enabled=!1}}class Z{constructor(e,t){this.loader=e,this.concurrency=t?.concurrency??10,this.cache=new de(t?.cache??!0)}async load(e,t){const r=this.cache.get(e);if(r!==void 0)return r;const s=await this.loader.load(e,t);return this.cache.set(e,s),s}async loadBatch(e,t){const r=new Map,s=new Map;for(let i=0;i<e.length;i+=this.concurrency){const o=e.slice(i,i+this.concurrency),n=await this.loader.loadMultiple(o,t);for(const c of n)c.success?(r.set(c.path,c.data),this.cache.set(c.path,c.data)):s.set(c.path,c.error||"Unknown error")}return{succeeded:r,failed:s,total:e.length,successCount:r.size,failureCount:s.size}}async loadShader(e,t,r){const[s,i]=await Promise.all([this.load(e,r),this.load(t,r)]);return{vertex:s,fragment:i}}async loadShaders(e,t){return await Promise.all(e.map(async s=>{const i=await this.loadShader(s.vertex,s.fragment,t);return{name:s.name,...i}}))}async loadFromManifest(e,t){const r=await this.load(e,t),s=JSON.parse(r);return this.loadBatch(s.resources,t)}async preload(e,t){await this.loadBatch(e,t)}isCached(e){return this.cache.has(e)}getCached(e){return this.cache.get(e)}clearCache(){this.cache.clear()}getCacheSize(){return this.cache.size()}enableCache(){this.cache.enable()}disableCache(){this.cache.disable()}setConcurrency(e){this.concurrency=Math.max(1,e)}getLoader(){return this.loader}}async function ee(a){const{ResourceLoaderFactory:e}=await Promise.resolve().then(()=>ue),t=await e.create({baseUrl:a?.baseUrl,baseDir:a?.baseDir,timeout:a?.timeout});return new Z(t,a)}const U={width:800,height:600},M={quad:{vertices:new Float32Array([-.5,-.5,0,0,0,.5,-.5,0,1,0,.5,.5,0,1,1,.5,.5,0,1,1,-.5,.5,0,0,1,-.5,-.5,0,0,0]),stride:20}},$={size:256},z={shaders:[{name:"basic",vertex:"resources/shaders/basic.vert",fragment:"resources/shaders/basic.frag"},{name:"glow",vertex:"resources/shaders/glow.vert",fragment:"resources/shaders/glow.frag"}],resources:["resources/shaders/basic.vert","resources/shaders/basic.frag","resources/shaders/glow.vert","resources/shaders/glow.frag"]};async function te(){console.log("🩸 Bloody Engine - Resource Loader Demo"),console.log(`==========================================
10
+ `);const a=S.detectEnvironment();if(console.log(`✓ Environment detected: ${a}`),a!==P.BROWSER){console.warn("⚠ This demo is designed for browser environment");return}console.log(`
11
+ 1. Creating Resource Pipeline...`);const e=await ee({concurrency:5,cache:!0,timeout:1e4,baseUrl:window.location.origin});console.log("✓ Resource pipeline created"),console.log(" - Concurrency: 5"),console.log(" - Caching: enabled"),console.log(`
12
+ 2. Batch Loading Resources...`),console.log(`Loading ${z.resources.length} resources...`);const t=await e.loadBatch(z.resources);if(console.log("✓ Batch loading complete"),console.log(` - Succeeded: ${t.successCount}`),console.log(` - Failed: ${t.failureCount}`),t.failureCount>0){console.log(`
13
+ ❌ Failed resources:`);for(const[x,L]of t.failed)console.log(` - ${x}: ${L}`);console.log(`
14
+ ⚠️ Falling back to inline shaders...`)}console.log(`
15
+ 3. Loading Shaders...`);const r=await e.loadShaders(z.shaders);console.log(`✓ Loaded ${r.length} shaders:`);for(const x of r)console.log(` - ${x.name}:`),console.log(` Vertex: ${x.vertex.length} chars`),console.log(` Fragment: ${x.fragment.length} chars`);console.log(`
16
+ 4. Testing Cache...`);const s=e.getCacheSize();console.log(`✓ Cache contains ${s} resources`);for(const x of z.shaders){const L=e.isCached(x.vertex),Y=e.isCached(x.fragment);console.log(` - ${x.name}:`),console.log(` Vertex cached: ${L}`),console.log(` Fragment cached: ${Y}`)}console.log(`
17
+ 5. Initializing Graphics Device...`);const i=new H(U.width,U.height),o=i.getGLContext();console.log("✓ Graphics device initialized"),console.log(` - Resolution: ${U.width}x${U.height}`),console.log(`
18
+ 6. Creating Shader from Loaded Source...`);let n=r.find(x=>x.name==="glow");(!n||!n.vertex||!n.fragment)&&(console.warn("⚠️ Glow shader not loaded or empty, using inline fallback"),n={name:"glow",vertex:`attribute vec3 aPosition;
19
+ attribute vec2 aTexCoord;
20
+
21
+ varying vec2 vTexCoord;
22
+ varying float vDistance;
23
+
24
+ uniform mat4 uMatrix;
25
+
26
+ void main() {
27
+ gl_Position = uMatrix * vec4(aPosition, 1.0);
28
+ vTexCoord = aTexCoord;
29
+ vDistance = length(aTexCoord - vec2(0.5, 0.5));
30
+ }`,fragment:`precision mediump float;
31
+
32
+ varying vec2 vTexCoord;
33
+ varying float vDistance;
34
+ uniform sampler2D uTexture;
35
+ uniform vec3 uColor;
36
+ uniform float uGlowIntensity;
37
+
38
+ void main() {
39
+ vec4 texColor = texture2D(uTexture, vTexCoord);
40
+ // Better glow falloff - keeps minimum brightness
41
+ float glow = 1.0 - (vDistance * 0.7);
42
+ glow = max(0.5, glow);
43
+ vec3 glowColor = texColor.rgb * uColor * glow * uGlowIntensity;
44
+ gl_FragColor = vec4(glowColor, texColor.a);
45
+ }`});const c=i.createShader(n.vertex,n.fragment);console.log("✓ Shader compiled from loaded source code"),console.log(" - Vertex shader: compiled"),console.log(" - Fragment shader: compiled"),console.log(" - Program: linked"),console.log(`
46
+ 7. Creating Texture...`);const h=R.createGradient(o,$.size,$.size);console.log("✓ Gradient texture created"),console.log(` - Size: ${$.size}x${$.size}`),console.log(`
47
+ 8. Creating Geometry Buffers...`);const{VertexBuffer:m}=await Promise.resolve().then(()=>oe),u=new m(o,M.quad.vertices,M.quad.stride);console.log("✓ Quad buffer created"),console.log(` - Vertices: ${u.getVertexCount()}`),console.log(`
48
+ 9. Setting up Rendering...`),c.use();const f=c.getAttributeLocation("aPosition"),g=c.getAttributeLocation("aTexCoord"),E=c.getUniformLocation("uTexture"),w=c.getUniformLocation("uMatrix"),b=c.getUniformLocation("uColor"),_=c.getUniformLocation("uGlowIntensity");u.bind(),o.enableVertexAttribArray(f),o.vertexAttribPointer(f,3,o.FLOAT,!1,M.quad.stride,0),o.enableVertexAttribArray(g),o.vertexAttribPointer(g,2,o.FLOAT,!1,M.quad.stride,12),console.log("✓ Vertex attributes configured"),h.bind(0),o.uniform1i(E,0),console.log("✓ Texture bound to unit 0");const d=i.getRenderingContext().canvas;d&&(d.style.display="block",d.style.margin="0 auto",d.style.border="2px solid #333",d.style.backgroundColor="#1a1a1a"),document.body.style.margin="0",document.body.style.padding="20px",document.body.style.backgroundColor="#0a0a0a",document.body.style.fontFamily="monospace",document.body.style.color="#aaa";const v=document.createElement("h1");v.textContent="🩸 Resource Loader Demo",v.style.textAlign="center",v.style.color="#fff",d&&d.parentNode?d.parentNode.insertBefore(v,d):document.body.insertBefore(v,document.body.firstChild);const y=document.createElement("div");y.style.textAlign="center",y.style.marginTop="10px",y.style.fontSize="12px",y.innerHTML=`
49
+ <div>Environment: <strong>${a}</strong></div>
50
+ <div>Shaders loaded: <strong>${r.length}</strong></div>
51
+ <div>Cached resources: <strong>${s}</strong></div>
52
+ `,document.body.appendChild(y);let D=0;const I=Date.now();function N(){const x=Date.now(),L=(x-I)/1e3;i.clear({r:.1,g:.1,b:.1,a:1});const Y=[{x:-.3,y:.3,color:[1,.2,.2],glow:1.5},{x:.3,y:.3,color:[.2,1,.2],glow:1.8},{x:-.3,y:-.3,color:[.2,.5,1],glow:2},{x:.3,y:-.3,color:[1,1,.2],glow:1.6}];for(const B of Y){const G=fe();if(ge(G,B.x,B.y,0),xe(G,.4,.4,1),w&&o.uniformMatrix4fv(w,!1,G),b&&o.uniform3f(b,B.color[0],B.color[1],B.color[2]),_){const we=B.glow+Math.sin(L*2)*.3;o.uniform1f(_,we)}o.drawArrays(o.TRIANGLES,0,u.getVertexCount())}i.present(),D++;const re=(x-I)/1e3,me=D/re;y.innerHTML=`
53
+ <div>FPS: <strong>${me.toFixed(1)}</strong> | Frame: <strong>${D}</strong> | Elapsed: <strong>${re.toFixed(2)}s</strong></div>
54
+ <div>Environment: <strong>${a}</strong> | Shaders loaded: <strong>${r.length}</strong> | Cached: <strong>${s}</strong></div>
55
+ `,requestAnimationFrame(N)}console.log(`
56
+ ✓ Demo started! Rendering animation...`),N()}function fe(){return new Float32Array([1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1])}function ge(a,e,t,r){a[12]+=e,a[13]+=t,a[14]+=r}function xe(a,e,t,r){a[0]*=e,a[5]*=t,a[10]*=r}typeof window<"u"&&te().catch(a=>{console.error("❌ Demo failed:",a)}),l.BatchRenderer=ne,l.BrowserRenderingContext=O,l.BrowserResourceLoader=Q,l.Camera=ce,l.Environment=P,l.GraphicsDevice=H,l.IndexBuffer=j,l.Matrix4=A,l.NodeRenderingContext=V,l.NodeResourceLoader=K,l.RenderingContextFactory=X,l.ResourceLoaderFactory=S,l.ResourcePipeline=Z,l.Shader=k,l.SpriteBatchRenderer=ae,l.Texture=R,l.VertexBuffer=W,l.createResourceLoader=J,l.createResourcePipeline=ee,l.runBrowserResourceLoaderDemo=te,Object.defineProperty(l,Symbol.toStringTag,{value:"Module"})}));
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "bloody-engine",
3
+ "version": "1.0.0",
4
+ "description": "A WebGL-based 2.5D graphics engine for isometric rendering",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "main": "dist/node/index.js",
8
+ "types": "./dist/web/index.d.ts",
9
+ "files": [
10
+ "dist",
11
+ "README.md",
12
+ "LICENSE"
13
+ ],
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "https://github.com/BLooDek/bloody-engine.git"
17
+ },
18
+ "keywords": [
19
+ "webgl",
20
+ "graphics",
21
+ "engine",
22
+ "2.5d",
23
+ "isometric",
24
+ "rendering",
25
+ "game-engine",
26
+ "typescript"
27
+ ],
28
+ "bugs": {
29
+ "url": "https://github.com/BLooDek/bloody-engine/issues"
30
+ },
31
+ "homepage": "https://github.com/BLooDek/bloody-engine#readme",
32
+ "exports": {
33
+ ".": {
34
+ "types": "./dist/web/index.d.ts",
35
+ "import": "./dist/web/index.js",
36
+ "require": "./dist/node/index.js"
37
+ }
38
+ },
39
+ "scripts": {
40
+ "dev": "vite",
41
+ "build": "npm run build:web && npm run build:node",
42
+ "build:web": "vite build --config vite.config.web.ts",
43
+ "build:node": "vite build --config vite.config.node.ts",
44
+ "preview": "vite preview",
45
+ "start": "npm run build:node && node dist/node/index-node.js"
46
+ },
47
+ "devDependencies": {
48
+ "@types/gl": "^6.0.5",
49
+ "@types/node": "^20.10.6",
50
+ "typescript": "^5.3.3",
51
+ "vite": "^7.3.0"
52
+ },
53
+ "dependencies": {
54
+ "@kmamal/sdl": "^0.11.13",
55
+ "gl": "^8.1.6",
56
+ "pngjs": "^7.0.0"
57
+ }
58
+ }