@cjcoding-git/gamelib 0.0.0-stage → 1.1.3

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 (136) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.md +38 -0
  2. package/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
  3. package/.github/dependabot.yml +6 -0
  4. package/.github/workflows/build.yml +54 -0
  5. package/.github/workflows/release.yml +79 -0
  6. package/LICENSE +21 -0
  7. package/README.md +6 -2
  8. package/assets/test-frog.gltf +1921 -0
  9. package/assets/test-frog.gml +0 -0
  10. package/buildtime.txt +1 -0
  11. package/buildversion.txt +1 -0
  12. package/dist/GameLib.js +71599 -0
  13. package/dist/GameLib.min.js +8 -0
  14. package/docs/2D/API.md +1 -0
  15. package/docs/2D/GETTING_STARTED.md +1 -0
  16. package/docs/3D/API.md +1 -0
  17. package/docs/3D/GETTING_STARTED.md +23 -0
  18. package/docs/Core/API.md +1 -0
  19. package/docs/Core/GETTING_STARTED.md +1 -0
  20. package/docs/README.md +32 -0
  21. package/package.json +21 -4
  22. package/scripts/gltf2gml.html +312 -0
  23. package/src/2D/Box.js +42 -0
  24. package/src/2D/Coord.js +17 -0
  25. package/src/2D/GameLib.js +317 -0
  26. package/src/2D/Renderable.js +31 -0
  27. package/src/2D/Rotation.js +42 -0
  28. package/src/2D/Size.js +17 -0
  29. package/src/2D/Sprite.js +56 -0
  30. package/src/2D/UI/Button.js +52 -0
  31. package/src/2D/UI/TextInput.js +77 -0
  32. package/src/2D/UI/UIElement.js +10 -0
  33. package/src/2D/UI/index.js +15 -0
  34. package/src/2D/index.js +24 -0
  35. package/src/3D/Camera.js +25 -0
  36. package/src/3D/GameLib.js +202 -0
  37. package/src/3D/Geometry/Box.js +39 -0
  38. package/src/3D/Geometry/Cone.js +45 -0
  39. package/src/3D/Geometry/ModelLoaders/GLBLoader.js +12 -0
  40. package/src/3D/Geometry/ModelLoaders/GLTFLoader.js +12 -0
  41. package/src/3D/Geometry/ModelLoaders/GMLLoader.js +114 -0
  42. package/src/3D/Geometry/ModelLoaders/OBJLoader.js +12 -0
  43. package/src/3D/Geometry/ModelLoaders/index.js +17 -0
  44. package/src/3D/Geometry/Pyramid.js +55 -0
  45. package/src/3D/Geometry/Sphere.js +37 -0
  46. package/src/3D/Geometry/index.js +19 -0
  47. package/src/3D/Lighting/AmbientLight.js +17 -0
  48. package/src/3D/Lighting/DirectionalLight.js +17 -0
  49. package/src/3D/Lighting/PointLight.js +17 -0
  50. package/src/3D/Lighting/index.js +15 -0
  51. package/src/3D/Material.js +181 -0
  52. package/src/3D/Mesh.js +142 -0
  53. package/src/3D/Object.js +13 -0
  54. package/src/3D/Position.js +15 -0
  55. package/src/3D/Rotation.js +21 -0
  56. package/src/3D/Scale.js +10 -0
  57. package/src/3D/ShaderFactory/FunctionParam.js +14 -0
  58. package/src/3D/ShaderFactory/Modifiers/Variable.js +85 -0
  59. package/src/3D/ShaderFactory/Modifiers/index.js +11 -0
  60. package/src/3D/ShaderFactory/Node.js +25 -0
  61. package/src/3D/ShaderFactory/NodeTypes/A.js +9 -0
  62. package/src/3D/ShaderFactory/NodeTypes/Add.js +13 -0
  63. package/src/3D/ShaderFactory/NodeTypes/AssignVariable.js +11 -0
  64. package/src/3D/ShaderFactory/NodeTypes/Clamp.js +14 -0
  65. package/src/3D/ShaderFactory/NodeTypes/Constant.js +9 -0
  66. package/src/3D/ShaderFactory/NodeTypes/Define.js +11 -0
  67. package/src/3D/ShaderFactory/NodeTypes/Divide.js +13 -0
  68. package/src/3D/ShaderFactory/NodeTypes/Dot.js +14 -0
  69. package/src/3D/ShaderFactory/NodeTypes/Exp2.js +9 -0
  70. package/src/3D/ShaderFactory/NodeTypes/Function.js +36 -0
  71. package/src/3D/ShaderFactory/NodeTypes/Group.js +9 -0
  72. package/src/3D/ShaderFactory/NodeTypes/Header.js +12 -0
  73. package/src/3D/ShaderFactory/NodeTypes/Length.js +9 -0
  74. package/src/3D/ShaderFactory/NodeTypes/MainFunction.js +7 -0
  75. package/src/3D/ShaderFactory/NodeTypes/Max.js +14 -0
  76. package/src/3D/ShaderFactory/NodeTypes/Mix.js +14 -0
  77. package/src/3D/ShaderFactory/NodeTypes/Multiply.js +13 -0
  78. package/src/3D/ShaderFactory/NodeTypes/Negative.js +9 -0
  79. package/src/3D/ShaderFactory/NodeTypes/NodeTypes.js +9 -0
  80. package/src/3D/ShaderFactory/NodeTypes/Normalize.js +10 -0
  81. package/src/3D/ShaderFactory/NodeTypes/Precision.js +11 -0
  82. package/src/3D/ShaderFactory/NodeTypes/RGB.js +9 -0
  83. package/src/3D/ShaderFactory/NodeTypes/Subtract.js +13 -0
  84. package/src/3D/ShaderFactory/NodeTypes/Symbol.js +9 -0
  85. package/src/3D/ShaderFactory/NodeTypes/Texture2D.js +10 -0
  86. package/src/3D/ShaderFactory/NodeTypes/Variable.js +22 -0
  87. package/src/3D/ShaderFactory/NodeTypes/Vector.js +15 -0
  88. package/src/3D/ShaderFactory/NodeTypes/XYZ.js +9 -0
  89. package/src/3D/ShaderFactory/NodeTypes/index.js +32 -0
  90. package/src/3D/ShaderFactory/PrecisionTypes.js +8 -0
  91. package/src/3D/ShaderFactory/Program.js +41 -0
  92. package/src/3D/ShaderFactory/VariableTypes.js +49 -0
  93. package/src/3D/ShaderFactory/index.js +45 -0
  94. package/src/3D/Texture.js +59 -0
  95. package/src/3D/TextureFactory.js +22 -0
  96. package/src/3D/Transform.js +35 -0
  97. package/src/3D/UI/UIImage.js +24 -0
  98. package/src/3D/UI/UILoading.js +41 -0
  99. package/src/3D/UI/UIObject.js +16 -0
  100. package/src/3D/UI/UIPath.js +25 -0
  101. package/src/3D/UI/UIRenderer.js +207 -0
  102. package/src/3D/UI/UIText.js +35 -0
  103. package/src/3D/UI/index.js +19 -0
  104. package/src/3D/Utils.js +9 -0
  105. package/src/3D/index.js +43 -0
  106. package/src/Core/AssetManager.js +116 -0
  107. package/src/Core/AudioManager.js +151 -0
  108. package/src/Core/Error.js +9 -0
  109. package/src/Core/FullscreenManager.js +49 -0
  110. package/src/Core/GameLibBase.js +29 -0
  111. package/src/Core/InputManager.js +195 -0
  112. package/src/Core/Loading.js +111 -0
  113. package/src/Core/Matrix/index.js +11 -0
  114. package/src/Core/Matrix/mat4.js +178 -0
  115. package/src/Core/Multiplayer.js +54 -0
  116. package/src/Core/Network/PeerNetwork.js +410 -0
  117. package/src/Core/Network/index.js +11 -0
  118. package/src/Core/Storage.js +45 -0
  119. package/src/Core/Store.js +45 -0
  120. package/src/Core/Utils.js +34 -0
  121. package/src/Core/index.js +30 -0
  122. package/src/Lib/Ammo.js +72 -0
  123. package/src/Lib/Matter.js +1 -0
  124. package/src/Shaders/Material/Fragment/GL1.glsl +49 -0
  125. package/src/Shaders/Material/Fragment/GL2.glsl +51 -0
  126. package/src/Shaders/Material/Vertex/GL1.glsl +19 -0
  127. package/src/Shaders/Material/Vertex/GL2.glsl +20 -0
  128. package/src/Shaders/UI/Fragment/GL1.glsl +5 -0
  129. package/src/Shaders/UI/Fragment/GL2.glsl +7 -0
  130. package/src/Shaders/UI/Vertex/GL1.glsl +10 -0
  131. package/src/Shaders/UI/Vertex/GL2.glsl +11 -0
  132. package/src/Shaders/UITexture/Fragment/GL1.glsl +6 -0
  133. package/src/Shaders/UITexture/Fragment/GL2.glsl +8 -0
  134. package/src/Shaders/UITexture/Vertex/GL1.glsl +10 -0
  135. package/src/Shaders/UITexture/Vertex/GL2.glsl +11 -0
  136. package/src/index.js +19 -0
package/docs/2D/API.md ADDED
@@ -0,0 +1 @@
1
+ Nothing here yet.
@@ -0,0 +1 @@
1
+ Nothing here yet.
package/docs/3D/API.md ADDED
@@ -0,0 +1 @@
1
+ Nothing here yet.
@@ -0,0 +1,23 @@
1
+ # Getting Started
2
+ This guide is to help you get started with the GameLib 3D engine.
3
+
4
+ ## 1. Create a Canvas
5
+ To begin, you need to create a canvas. It is easiest to add it directly to the HTML code of your page:
6
+ ```html
7
+ <canvas width="500" height="500" id="yourCanvas"></canvas>
8
+ ```
9
+ You can replace width and height with your desired dimensions.
10
+
11
+ ## 2. Get the Context
12
+ Next, you need to get the rendering context of your canvas, to use it in your JavaScript code. To do this, first you need to obtain a reference to the element.
13
+ ```javascript
14
+ var canvas = document.querySelector("#yourCanvas");
15
+ ```
16
+ Then, you need to get the WebGL rendering context from the canvas. To do this, GameLib provides a function to get the context from your canvas:
17
+ ```javascript
18
+ var ctx = GameLib3D.GameLib.getContext(canvas, options);
19
+ ```
20
+ Where **canvas** is your canvas reference and **options** is an object. Available options are:
21
+ - **webGL2:** boolean
22
+
23
+ **webGL2** controls whether or not the context will be configured to use WebGL1 or WebGL2.
@@ -0,0 +1 @@
1
+ Nothing here yet.
@@ -0,0 +1 @@
1
+ Nothing here yet.
package/docs/README.md ADDED
@@ -0,0 +1,32 @@
1
+ # GameLib Documentation
2
+ GameLib has multiple components, each with a different API.
3
+
4
+ ## Loading GameLib
5
+ GameLib can be hosted from your own server, inlined in the page, or loaded from a CDN. There are two different versions provided:
6
+ - **Unminified:** Good for writing a game, still has all variable and parameter names intact.
7
+ - **Minified:** Use this for release builds. The exposed API remains the same but it has a smaller file size, and internal variables have been renamed.
8
+ To get the code yourself, download the latest release at https://github.com/cjCoding-git/GameLib/releases. To load from a CDN, add this snippet to the top of your page:
9
+ ```html
10
+ <script src="https://cdn.jsdelivr.net/gh/cjCoding-git/GameLib@latest"></script>
11
+ ```
12
+
13
+ ## Core Engine
14
+ The core engine includes common code like input and audio.
15
+
16
+ [Getting Started](Core/GETTING_STARTED.md)
17
+
18
+ [API Refrence](Core/API.md)
19
+
20
+ ## 2D Engine
21
+ The 2D engine is the part of the game engine for creating graphics with the 2D canvas API.
22
+
23
+ [Getting Started](2D/GETTING_STARTED.md)
24
+
25
+ [API Refrence](2D/API.md)
26
+
27
+ ## 3D Engine
28
+ The 3D engine makes WebGL development significantly easier.
29
+
30
+ [Getting Started](3D/GETTING_STARTED.md)
31
+
32
+ [API Refrence](3D/API.md)
package/package.json CHANGED
@@ -1,6 +1,23 @@
1
1
  {
2
2
  "name": "@cjcoding-git/gamelib",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "1.1.3",
4
+ "description": "A minimal game engine.",
5
+ "main": "src/index.js",
6
+ "type": "module",
7
+ "scripts": {},
8
+ "keywords": [
9
+ "game engine"
10
+ ],
11
+ "author": "Charles Jager <admin@cjcoding.com>",
12
+ "dependencies": {},
13
+ "devDependencies": {
14
+ "esbuild": "0.28.2",
15
+ "terser": "5.51.2"
16
+ },
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "git+https://github.com/cjCoding-git/GameLib.git"
20
+ },
21
+ "license": "MIT",
22
+ "jsdelivr": "dist/GameLib.min.js"
23
+ }
@@ -0,0 +1,312 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <title>glTF to GML converter</title>
7
+ <style>
8
+ :root { color-scheme: light dark; font: 16px system-ui, sans-serif; }
9
+ body { max-width: 680px; margin: 3rem auto; padding: 0 1rem; }
10
+ h1 { margin-bottom: .35rem; }
11
+ p { line-height: 1.5; }
12
+ .panel { border: 1px solid #8886; border-radius: 10px; padding: 1.25rem; }
13
+ label { display: block; font-weight: 600; margin-bottom: .5rem; }
14
+ input, button { font: inherit; }
15
+ input[type=file] { display: block; max-width: 100%; margin: .7rem 0 1rem; }
16
+ button { padding: .55rem .9rem; cursor: pointer; }
17
+ #status { min-height: 1.5em; margin-top: .8rem; }
18
+ .hint { opacity: .8; font-size: .92rem; }
19
+ </style>
20
+ </head>
21
+ <body>
22
+ <h1>glTF to GML</h1>
23
+ <p>Convert static glTF 2.0 meshes to GML1 binary format.</p>
24
+ <section class="panel">
25
+ <label for="files">Choose a .gltf or .glb file</label>
26
+ <input id="files" type="file" accept=".gltf,.glb,model/gltf+json,model/gltf-binary" multiple>
27
+ <p class="hint">For .gltf files with external .bin buffers, select the .bin files at the same time. Images and materials' textures are not included in GML1.</p>
28
+ <button id="convert" type="button">Convert and download .gml</button>
29
+ <div id="status" role="status" aria-live="polite"></div>
30
+ </section>
31
+ <script>
32
+ (() => {
33
+ const fileInput = document.getElementById('files');
34
+ const button = document.getElementById('convert');
35
+ const status = document.getElementById('status');
36
+ const MAGIC = 'GML1';
37
+ function fail(message) { throw new Error(message); }
38
+ function readU32(view, offset) { return view.getUint32(offset, true); }
39
+ function parseGLB(buffer) {
40
+ const view = new DataView(buffer);
41
+ if (view.byteLength < 20 || readU32(view, 0) !== 0x46546c67) fail('Invalid or truncated GLB file.');
42
+ if (readU32(view, 4) !== 2) fail('Only GLB version 2 is supported.');
43
+ if (readU32(view, 8) !== view.byteLength) fail('GLB declared length does not match the file size.');
44
+ let offset = 12, json = null, binary = null;
45
+ while (offset < view.byteLength) {
46
+ if (offset + 8 > view.byteLength) fail('Truncated GLB chunk header.');
47
+ const length = readU32(view, offset), type = readU32(view, offset + 4);
48
+ offset += 8;
49
+ if (offset + length > view.byteLength) fail('GLB chunk extends beyond the file.');
50
+ const chunk = buffer.slice(offset, offset + length);
51
+ if (type === 0x4e4f534a) {
52
+ if (json) fail('GLB contains multiple JSON chunks.');
53
+ json = JSON.parse(new TextDecoder().decode(chunk).replace(/[\u0000\u0020]+$/g, ''));
54
+ } else if (type === 0x004e4942 && !binary) {
55
+ binary = chunk;
56
+ }
57
+ offset += length;
58
+ }
59
+ if (!json) fail('GLB has no JSON chunk.');
60
+ return { json, binary };
61
+ }
62
+ function dataUriBytes(uri) {
63
+ const comma = uri.indexOf(',');
64
+ if (comma < 0) fail('Malformed data URI in glTF buffer.');
65
+ const metadata = uri.slice(0, comma), payload = uri.slice(comma + 1);
66
+ if (/;base64/i.test(metadata)) {
67
+ const raw = atob(payload);
68
+ const bytes = new Uint8Array(raw.length);
69
+ for (let i = 0; i < raw.length; i++) bytes[i] = raw.charCodeAt(i);
70
+ return bytes.buffer;
71
+ }
72
+ return new TextEncoder().encode(decodeURIComponent(payload)).buffer;
73
+ }
74
+ async function getGltf(file, selectedFiles) {
75
+ const bytes = await file.arrayBuffer();
76
+ if (/\.glb$/i.test(file.name)) {
77
+ const parsed = parseGLB(bytes);
78
+ const buffers = [];
79
+ for (let i = 0; i < (parsed.json.buffers || []).length; i++) {
80
+ const def = parsed.json.buffers[i];
81
+ if (def.uri) {
82
+ if (/^data:/i.test(def.uri)) buffers.push(dataUriBytes(def.uri));
83
+ else {
84
+ const wanted = decodeURIComponent(def.uri.split('/').pop());
85
+ const match = selectedFiles.find(candidate => candidate.name === wanted);
86
+ if (!match) fail(`Select external buffer "${wanted}" along with the glTF file.`);
87
+ buffers.push(await match.arrayBuffer());
88
+ }
89
+ } else if (i === 0 && parsed.binary) buffers.push(parsed.binary);
90
+ else fail(`No data found for glTF buffer ${i}.`);
91
+ }
92
+ return { json: parsed.json, buffers };
93
+ }
94
+ let json;
95
+ try { json = JSON.parse(new TextDecoder().decode(bytes)); }
96
+ catch { fail('The selected .gltf file is not valid JSON.'); }
97
+ const buffers = [];
98
+ for (const [i, def] of (json.buffers || []).entries()) {
99
+ if (!def.uri) fail(`glTF buffer ${i} has no URI (select a .glb file for embedded binary data).`);
100
+ if (/^data:/i.test(def.uri)) buffers.push(dataUriBytes(def.uri));
101
+ else {
102
+ const wanted = decodeURIComponent(def.uri.split('/').pop());
103
+ const match = selectedFiles.find(candidate => candidate.name === wanted);
104
+ if (!match) fail(`Select external buffer "${wanted}" along with the glTF file.`);
105
+ buffers.push(await match.arrayBuffer());
106
+ }
107
+ }
108
+ return { json, buffers };
109
+ }
110
+ const COMPONENTS = { 5120: [1, 'getInt8'], 5121: [1, 'getUint8'], 5122: [2, 'getInt16'], 5123: [2, 'getUint16'], 5125: [4, 'getUint32'], 5126: [4, 'getFloat32'] };
111
+ const ITEM_COUNTS = { SCALAR: 1, VEC2: 2, VEC3: 3, VEC4: 4, MAT2: 4, MAT3: 9, MAT4: 16 };
112
+ function makeAccessorReader(json, buffers) {
113
+ return function accessor(index) {
114
+ const def = json.accessors?.[index];
115
+ if (!def) fail(`Missing glTF accessor ${index}.`);
116
+ if (def.sparse) fail('Sparse glTF accessors are not supported by this converter.');
117
+ const info = COMPONENTS[def.componentType], itemCount = ITEM_COUNTS[def.type];
118
+ if (!info || !itemCount || !Number.isInteger(def.count) || def.count < 0) fail(`Unsupported or invalid glTF accessor ${index}.`);
119
+ if (def.bufferView === undefined) fail(`Accessor ${index} has no bufferView (zero-initialized accessors are not supported).`);
120
+ const bv = json.bufferViews?.[def.bufferView];
121
+ const buffer = buffers[bv?.buffer];
122
+ if (!bv || !buffer) fail(`Accessor ${index} references a missing bufferView or buffer.`);
123
+ const [size, getter] = info, elementSize = size * itemCount;
124
+ const stride = bv.byteStride || elementSize;
125
+ if (stride < elementSize || stride % size !== 0) fail(`Invalid byteStride in accessor ${index}.`);
126
+ const start = (bv.byteOffset || 0) + (def.byteOffset || 0);
127
+ const lastEnd = def.count ? start + (def.count - 1) * stride + elementSize : start;
128
+ if (start < (bv.byteOffset || 0) || lastEnd > (bv.byteOffset || 0) + bv.byteLength || lastEnd > buffer.byteLength) fail(`Accessor ${index} data lies outside its bufferView.`);
129
+ const view = new DataView(buffer);
130
+ const values = new Array(def.count * itemCount);
131
+ for (let i = 0; i < def.count; i++) {
132
+ for (let c = 0; c < itemCount; c++) {
133
+ let value = view[getter](start + i * stride + c * size, true);
134
+ if (def.normalized && def.componentType !== 5126) {
135
+ if (def.componentType === 5120) value = Math.max(value / 127, -1);
136
+ else if (def.componentType === 5121) value /= 255;
137
+ else if (def.componentType === 5122) value = Math.max(value / 32767, -1);
138
+ else if (def.componentType === 5123) value /= 65535;
139
+ else fail('Normalized unsigned-int accessors are not supported.');
140
+ }
141
+ values[i * itemCount + c] = value;
142
+ }
143
+ }
144
+ return { values, count: def.count, components: itemCount, componentType: def.componentType };
145
+ };
146
+ }
147
+ function identity() { return [1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1]; }
148
+ function multiply(a, b) {
149
+ const out = new Array(16).fill(0);
150
+ for (let c = 0; c < 4; c++) for (let r = 0; r < 4; r++)
151
+ for (let k = 0; k < 4; k++) out[c * 4 + r] += a[k * 4 + r] * b[c * 4 + k];
152
+ return out;
153
+ }
154
+ function nodeMatrix(node) {
155
+ if (node.matrix) return node.matrix;
156
+ const [x,y,z,w] = node.rotation || [0,0,0,1];
157
+ const [sx,sy,sz] = node.scale || [1,1,1];
158
+ const [tx,ty,tz] = node.translation || [0,0,0];
159
+ return [
160
+ (1-2*y*y-2*z*z)*sx, (2*x*y+2*w*z)*sx, (2*x*z-2*w*y)*sx, 0,
161
+ (2*x*y-2*w*z)*sy, (1-2*x*x-2*z*z)*sy, (2*y*z+2*w*x)*sy, 0,
162
+ (2*x*z+2*w*y)*sz, (2*y*z-2*w*x)*sz, (1-2*x*x-2*y*y)*sz, 0,
163
+ tx,ty,tz,1
164
+ ];
165
+ }
166
+ function transformPosition(m, x, y, z) {
167
+ const w = m[3]*x + m[7]*y + m[11]*z + m[15] || 1;
168
+ return [(m[0]*x + m[4]*y + m[8]*z + m[12])/w, (m[1]*x + m[5]*y + m[9]*z + m[13])/w, (m[2]*x + m[6]*y + m[10]*z + m[14])/w];
169
+ }
170
+ function normalMatrix(m) {
171
+ const a=m[0], b=m[4], c=m[8], d=m[1], e=m[5], f=m[9], g=m[2], h=m[6], i=m[10];
172
+ const A=e*i-f*h, B=f*g-d*i, C=d*h-e*g;
173
+ const det=a*A+b*B+c*C;
174
+ if (Math.abs(det) < 1e-12) fail('Cannot convert a mesh with a singular node transform.');
175
+ const inv=1/det;
176
+ return [A*inv,(c*h-b*i)*inv,(b*f-c*e)*inv, (B*inv),(a*i-c*g)*inv,(c*d-a*f)*inv, (C*inv),(b*g-a*h)*inv,(a*e-b*d)*inv];
177
+ }
178
+ function transformNormal(nm, x, y, z) {
179
+ const nx=nm[0]*x+nm[1]*y+nm[2]*z, ny=nm[3]*x+nm[4]*y+nm[5]*z, nz=nm[6]*x+nm[7]*y+nm[8]*z;
180
+ const length=Math.hypot(nx,ny,nz) || 1;
181
+ return [nx/length,ny/length,nz/length];
182
+ }
183
+ function generateNormals(positions, indices) {
184
+ const normals = new Array(positions.length).fill(0);
185
+ for (let i=0;i<indices.length;i+=3) {
186
+ const a=indices[i]*3,b=indices[i+1]*3,c=indices[i+2]*3;
187
+ const ab=[positions[b]-positions[a],positions[b+1]-positions[a+1],positions[b+2]-positions[a+2]];
188
+ const ac=[positions[c]-positions[a],positions[c+1]-positions[a+1],positions[c+2]-positions[a+2]];
189
+ const n=[ab[1]*ac[2]-ab[2]*ac[1],ab[2]*ac[0]-ab[0]*ac[2],ab[0]*ac[1]-ab[1]*ac[0]];
190
+ for (const v of [a,b,c]) { normals[v]+=n[0]; normals[v+1]+=n[1]; normals[v+2]+=n[2]; }
191
+ }
192
+ for (let i=0;i<normals.length;i+=3) { const l=Math.hypot(normals[i],normals[i+1],normals[i+2])||1; normals[i]/=l; normals[i+1]/=l; normals[i+2]/=l; }
193
+ return normals;
194
+ }
195
+ function primitiveIndices(primitive, accessor, vertexCount) {
196
+ let indices = primitive.indices === undefined ? Array.from({length: vertexCount}, (_,i)=>i) : accessor(primitive.indices).values;
197
+ const mode = primitive.mode === undefined ? 4 : primitive.mode;
198
+ if (mode === 4) {
199
+ if (indices.length % 3) fail('Triangle-list index count must be divisible by 3.');
200
+ return indices;
201
+ }
202
+ const triangles=[];
203
+ if (mode === 5) {
204
+ for (let i=2;i<indices.length;i++) {
205
+ if (i%2===0) triangles.push(indices[i-2],indices[i-1],indices[i]);
206
+ else triangles.push(indices[i-1],indices[i-2],indices[i]);
207
+ }
208
+ } else if (mode === 6) {
209
+ for (let i=2;i<indices.length;i++) triangles.push(indices[0],indices[i-1],indices[i]);
210
+ } else fail('Only glTF triangle, triangle-strip, and triangle-fan primitives are supported.');
211
+ return triangles;
212
+ }
213
+
214
+ function convert(json, buffers) {
215
+ if (json.asset?.version?.split('.')[0] !== '2') fail('Only glTF 2.x assets are supported.');
216
+ if (json.extensionsRequired?.length) fail(`Required glTF extensions are not supported: ${json.extensionsRequired.join(', ')}.`);
217
+ const accessor = makeAccessorReader(json, buffers), meshes=[];
218
+ const materials = json.materials || [];
219
+ const addMesh = (meshDef, world, nodeName) => {
220
+ for (let p=0;p<(meshDef.primitives || []).length;p++) {
221
+ const primitive=meshDef.primitives[p];
222
+ if (primitive.attributes?.POSITION === undefined) fail('Every supported primitive must have POSITION data.');
223
+ const pos=accessor(primitive.attributes.POSITION);
224
+ if (pos.components !== 3) fail('POSITION accessor must be VEC3.');
225
+ let indices=primitiveIndices(primitive, accessor, pos.count);
226
+ if (!indices.length || indices.length%3) continue;
227
+ if (indices.some(index => !Number.isInteger(index) || index<0 || index>=pos.count)) fail('Primitive contains an out-of-range vertex index.');
228
+ let positions=pos.values;
229
+ let normals=primitive.attributes.NORMAL === undefined ? generateNormals(positions,indices) : accessor(primitive.attributes.NORMAL).values;
230
+ if (normals.length !== positions.length) fail('NORMAL accessor must match POSITION vertex count.');
231
+ const tex=primitive.attributes.TEXCOORD_0 === undefined ? {values:new Array(pos.count*2).fill(0),count:pos.count,components:2} : accessor(primitive.attributes.TEXCOORD_0);
232
+ if (tex.components!==2 || tex.count!==pos.count) fail('TEXCOORD_0 must be a VEC2 accessor matching POSITION.');
233
+ const nm=normalMatrix(world), transformedPositions=[], transformedNormals=[];
234
+ for(let i=0;i<positions.length;i+=3) transformedPositions.push(...transformPosition(world,positions[i],positions[i+1],positions[i+2]));
235
+ for(let i=0;i<normals.length;i+=3) transformedNormals.push(...transformNormal(nm,normals[i],normals[i+1],normals[i+2]));
236
+ const material=materials[primitive.material] || {};
237
+ const color=material.pbrMetallicRoughness?.baseColorFactor || [1,1,1,1];
238
+ const name=meshDef.name || nodeName || `mesh_${meshes.length}`;
239
+ meshes.push({name: meshDef.primitives.length>1 ? `${name}_${p}` : name, positions:transformedPositions, normals:transformedNormals, uv:tex.values, indices, color});
240
+ }
241
+ };
242
+ const nodes=json.nodes || [], visited=new Set();
243
+ function visit(nodeIndex,parent) {
244
+ if(visited.has(nodeIndex)) fail('glTF node graph contains a cycle or repeated node instance.');
245
+ const node=nodes[nodeIndex]; if(!node) fail(`Missing glTF node ${nodeIndex}.`);
246
+ visited.add(nodeIndex);
247
+ const world=multiply(parent,nodeMatrix(node));
248
+ if(node.mesh!==undefined) { const mesh=json.meshes?.[node.mesh]; if(!mesh) fail(`Node references missing mesh ${node.mesh}.`); addMesh(mesh,world,node.name); }
249
+ for(const child of node.children || []) visit(child,world);
250
+ visited.delete(nodeIndex);
251
+ }
252
+ if (nodes.length) {
253
+ const scenes=json.scenes || [];
254
+ const scene=scenes[json.scene === undefined ? 0 : json.scene];
255
+ if (scene) for (const root of scene.nodes || []) visit(root,identity());
256
+ else if (!scenes.length) for (let i=0;i<nodes.length;i++) if (!nodes.some(n => (n.children || []).includes(i))) visit(i,identity());
257
+ } else {
258
+ for(const mesh of json.meshes || []) addMesh(mesh,identity(),mesh.name);
259
+ }
260
+ if (!meshes.length) fail('No supported triangle meshes found in this glTF asset.');
261
+ return meshes;
262
+ }
263
+ function encodeGML(meshes) {
264
+ const encoder=new TextEncoder();
265
+ const encoded=meshes.map(mesh => {
266
+ const name=encoder.encode(mesh.name);
267
+ if(name.length>65535) fail(`Mesh name is too long: ${mesh.name}`);
268
+ const vertexCount=mesh.positions.length/3, indexCount=mesh.indices.length;
269
+ if(!Number.isInteger(vertexCount) || vertexCount<1 || vertexCount>0xffffffff || indexCount>0xffffffff) fail('Mesh is too large for GML1.');
270
+ return {mesh,name,vertexCount,indexCount};
271
+ });
272
+ if(encoded.length>65535) fail('GML1 supports at most 65535 meshes.');
273
+ let total=8;
274
+ for(const {name,vertexCount,indexCount} of encoded) total+=2+name.length+8+16+(vertexCount*8+indexCount)*4;
275
+ if(!Number.isSafeInteger(total)) fail('Output model is too large.');
276
+ const buffer=new ArrayBuffer(total), view=new DataView(buffer), bytes=new Uint8Array(buffer);
277
+ bytes.set(encoder.encode(MAGIC),0); view.setUint16(4,1,true); view.setUint16(6,encoded.length,true);
278
+ let offset=8;
279
+ const putU16=v=>{view.setUint16(offset,v,true);offset+=2;};
280
+ const putU32=v=>{view.setUint32(offset,v,true);offset+=4;};
281
+ const putFloats=values=>{for(const value of values){if(!Number.isFinite(value))fail('Model contains a non-finite numeric value.');view.setFloat32(offset,value,true);offset+=4;}};
282
+ for(const {mesh,name,vertexCount,indexCount} of encoded){
283
+ putU16(name.length); bytes.set(name,offset); offset+=name.length;
284
+ putU32(vertexCount); putU32(indexCount);
285
+ putFloats(mesh.color.length===4?mesh.color:[1,1,1,1]);
286
+ putFloats(mesh.positions); putFloats(mesh.normals); putFloats(mesh.uv);
287
+ for(const index of mesh.indices) putU32(index);
288
+ }
289
+ return buffer;
290
+ }
291
+ button.addEventListener('click', async () => {
292
+ status.textContent='';
293
+ try {
294
+ const files=Array.from(fileInput.files || []);
295
+ const modelFile=files.find(file => /\.(gltf|glb)$/i.test(file.name));
296
+ if(!modelFile) fail('Choose one .gltf or .glb model file.');
297
+ const {json,buffers}=await getGltf(modelFile,files);
298
+ const meshes=convert(json,buffers), output=encodeGML(meshes);
299
+ const url=URL.createObjectURL(new Blob([output],{type:'application/octet-stream'}));
300
+ const link=document.createElement('a');
301
+ link.href=url; link.download=modelFile.name.replace(/\.(gltf|glb)$/i,'')+'.gml';
302
+ document.body.append(link); link.click(); link.remove();
303
+ setTimeout(()=>URL.revokeObjectURL(url),1000);
304
+ status.textContent=`Converted ${meshes.length} mesh${meshes.length===1?'':'es'} (${(output.byteLength/1024).toFixed(1)} KiB).`;
305
+ } catch(error) {
306
+ status.textContent=error instanceof Error ? error.message : String(error);
307
+ }
308
+ });
309
+ })();
310
+ </script>
311
+ </body>
312
+ </html>
package/src/2D/Box.js ADDED
@@ -0,0 +1,42 @@
1
+ class Box {
2
+ constructor(coord, size) {
3
+ this.coord = coord;
4
+ this.size = size;
5
+ }
6
+ move(dx, dy) {
7
+ this.coord.move(dx, dy);
8
+ }
9
+ moveTo(x, y) {
10
+ this.coord.moveTo(x, y);
11
+ }
12
+ resize(dw, dh) {
13
+ this.size.mod(dw, dh);
14
+ }
15
+ resizeTo(w, h) {
16
+ this.size.modTo(w, h);
17
+ }
18
+ get x() {
19
+ return this.coord.x;
20
+ }
21
+ get y() {
22
+ return this.coord.y;
23
+ }
24
+ get width() {
25
+ return this.size.width;
26
+ }
27
+ get height() {
28
+ return this.size.height;
29
+ }
30
+ checkCollision(otherBox) {
31
+ var checkCollisionInternal = (function (x1, y1, w1, h1, x2, y2, w2, h2) {
32
+ var overlapX = x1 < (x2 + w2) && (x1 + w1) > x2;
33
+ var overlapY = y1 < (y2 + h2) && (y1 + h1) > y2;
34
+ var collision = overlapX && overlapY;
35
+ return collision;
36
+ });
37
+ return checkCollisionInternal(this.coord.x, this.coord.y, this.size.width, this.size.height, otherBox.x, otherBox.y, otherBox.width, otherBox.height);
38
+ }
39
+ }
40
+ export {
41
+ Box
42
+ }
@@ -0,0 +1,17 @@
1
+ class Coord {
2
+ constructor(x, y) {
3
+ this.x = x;
4
+ this.y = y;
5
+ }
6
+ move(dx, dy) {
7
+ this.x += dx;
8
+ this.y += dy;
9
+ }
10
+ moveTo(x, y) {
11
+ this.x = x;
12
+ this.y = y;
13
+ }
14
+ }
15
+ export {
16
+ Coord
17
+ }