pixospritz-core 0.10.1 → 1.0.1

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 (157) hide show
  1. package/README.md +36 -286
  2. package/dist/bundle.js +13 -3
  3. package/dist/bundle.js.map +1 -1
  4. package/dist/style.css +1 -0
  5. package/package.json +43 -44
  6. package/src/components/WebGLView.jsx +318 -0
  7. package/src/css/pixos.css +372 -0
  8. package/src/engine/actions/animate.js +41 -0
  9. package/src/engine/actions/changezone.js +135 -0
  10. package/src/engine/actions/chat.js +109 -0
  11. package/src/engine/actions/dialogue.js +90 -0
  12. package/src/engine/actions/face.js +22 -0
  13. package/src/engine/actions/greeting.js +28 -0
  14. package/src/engine/actions/interact.js +86 -0
  15. package/src/engine/actions/move.js +67 -0
  16. package/src/engine/actions/patrol.js +109 -0
  17. package/src/engine/actions/prompt.js +185 -0
  18. package/src/engine/actions/script.js +42 -0
  19. package/src/engine/core/audio/AudioSystem.js +543 -0
  20. package/src/engine/core/cutscene/PxcPlayer.js +956 -0
  21. package/src/engine/core/cutscene/manager.js +243 -0
  22. package/src/engine/core/database/index.js +75 -0
  23. package/src/engine/core/debug/index.js +371 -0
  24. package/src/engine/core/hud/index.js +765 -0
  25. package/src/engine/core/index.js +540 -0
  26. package/src/engine/core/input/gamepad/Controller.js +71 -0
  27. package/src/engine/core/input/gamepad/ControllerButtons.js +231 -0
  28. package/src/engine/core/input/gamepad/ControllerStick.js +173 -0
  29. package/src/engine/core/input/gamepad/index.js +592 -0
  30. package/src/engine/core/input/keyboard.js +196 -0
  31. package/src/engine/core/input/manager.js +485 -0
  32. package/src/engine/core/input/mouse.js +203 -0
  33. package/src/engine/core/input/touch.js +175 -0
  34. package/src/engine/core/mode/manager.js +199 -0
  35. package/src/engine/core/net/manager.js +535 -0
  36. package/src/engine/core/queue/action.js +83 -0
  37. package/src/engine/core/queue/event.js +82 -0
  38. package/src/engine/core/queue/index.js +44 -0
  39. package/src/engine/core/queue/loadable.js +33 -0
  40. package/src/engine/core/render/CameraEffects.js +494 -0
  41. package/src/engine/core/render/FrustumCuller.js +417 -0
  42. package/src/engine/core/render/LODManager.js +285 -0
  43. package/src/engine/core/render/ParticleManager.js +529 -0
  44. package/src/engine/core/render/TextureAtlas.js +465 -0
  45. package/src/engine/core/render/camera.js +338 -0
  46. package/src/engine/core/render/light.js +197 -0
  47. package/src/engine/core/render/manager.js +1079 -0
  48. package/src/engine/core/render/shaders.js +110 -0
  49. package/src/engine/core/render/skybox.js +342 -0
  50. package/src/engine/core/resource/manager.js +133 -0
  51. package/src/engine/core/resource/object.js +611 -0
  52. package/src/engine/core/resource/texture.js +103 -0
  53. package/src/engine/core/resource/tileset.js +177 -0
  54. package/src/engine/core/scene/avatar.js +215 -0
  55. package/src/engine/core/scene/speech.js +138 -0
  56. package/src/engine/core/scene/sprite.js +702 -0
  57. package/src/engine/core/scene/spritz.js +189 -0
  58. package/src/engine/core/scene/world.js +681 -0
  59. package/src/engine/core/scene/zone.js +1167 -0
  60. package/src/engine/core/store/index.js +110 -0
  61. package/src/engine/dynamic/animatedSprite.js +64 -0
  62. package/src/engine/dynamic/animatedTile.js +98 -0
  63. package/src/engine/dynamic/avatar.js +110 -0
  64. package/src/engine/dynamic/map.js +174 -0
  65. package/src/engine/dynamic/sprite.js +255 -0
  66. package/src/engine/dynamic/spritz.js +119 -0
  67. package/src/engine/events/EventSystem.js +609 -0
  68. package/src/engine/events/camera.js +142 -0
  69. package/src/engine/events/chat.js +75 -0
  70. package/src/engine/events/menu.js +186 -0
  71. package/src/engine/scripting/CallbackManager.js +514 -0
  72. package/src/engine/scripting/PixoScriptInterpreter.js +81 -0
  73. package/src/engine/scripting/PixoScriptLibrary.js +704 -0
  74. package/src/engine/shaders/effects/index.js +450 -0
  75. package/src/engine/shaders/fs.js +222 -0
  76. package/src/engine/shaders/particles/fs.js +41 -0
  77. package/src/engine/shaders/particles/vs.js +61 -0
  78. package/src/engine/shaders/picker/fs.js +34 -0
  79. package/src/engine/shaders/picker/init.js +62 -0
  80. package/src/engine/shaders/picker/vs.js +42 -0
  81. package/src/engine/shaders/pxsl/README.md +250 -0
  82. package/src/engine/shaders/pxsl/index.js +25 -0
  83. package/src/engine/shaders/pxsl/library.js +608 -0
  84. package/src/engine/shaders/pxsl/manager.js +338 -0
  85. package/src/engine/shaders/pxsl/specification.js +363 -0
  86. package/src/engine/shaders/pxsl/transpiler.js +753 -0
  87. package/src/engine/shaders/skybox/cosmic/fs.js +147 -0
  88. package/src/engine/shaders/skybox/cosmic/vs.js +23 -0
  89. package/src/engine/shaders/skybox/matrix/fs.js +127 -0
  90. package/src/engine/shaders/skybox/matrix/vs.js +23 -0
  91. package/src/engine/shaders/skybox/morning/fs.js +109 -0
  92. package/src/engine/shaders/skybox/morning/vs.js +23 -0
  93. package/src/engine/shaders/skybox/neon/fs.js +119 -0
  94. package/src/engine/shaders/skybox/neon/vs.js +23 -0
  95. package/src/engine/shaders/skybox/sky/fs.js +114 -0
  96. package/src/engine/shaders/skybox/sky/vs.js +23 -0
  97. package/src/engine/shaders/skybox/sunset/fs.js +101 -0
  98. package/src/engine/shaders/skybox/sunset/vs.js +23 -0
  99. package/src/engine/shaders/transition/blur/fs.js +42 -0
  100. package/src/engine/shaders/transition/blur/vs.js +26 -0
  101. package/src/engine/shaders/transition/cross/fs.js +36 -0
  102. package/src/engine/shaders/transition/cross/vs.js +26 -0
  103. package/src/engine/shaders/transition/crossBlur/fs.js +41 -0
  104. package/src/engine/shaders/transition/crossBlur/vs.js +25 -0
  105. package/src/engine/shaders/transition/dissolve/fs.js +78 -0
  106. package/src/engine/shaders/transition/dissolve/vs.js +24 -0
  107. package/src/engine/shaders/transition/fade/fs.js +31 -0
  108. package/src/engine/shaders/transition/fade/vs.js +27 -0
  109. package/src/engine/shaders/transition/iris/fs.js +52 -0
  110. package/src/engine/shaders/transition/iris/vs.js +24 -0
  111. package/src/engine/shaders/transition/pixelate/fs.js +44 -0
  112. package/src/engine/shaders/transition/pixelate/vs.js +24 -0
  113. package/src/engine/shaders/transition/slide/fs.js +53 -0
  114. package/src/engine/shaders/transition/slide/vs.js +24 -0
  115. package/src/engine/shaders/transition/swirl/fs.js +39 -0
  116. package/src/engine/shaders/transition/swirl/vs.js +26 -0
  117. package/src/engine/shaders/transition/wipe/fs.js +50 -0
  118. package/src/engine/shaders/transition/wipe/vs.js +24 -0
  119. package/src/engine/shaders/vs.js +60 -0
  120. package/src/engine/utils/CameraController.js +506 -0
  121. package/src/engine/utils/ObjHelper.js +551 -0
  122. package/src/engine/utils/debug-logger.js +110 -0
  123. package/src/engine/utils/enums.js +305 -0
  124. package/src/engine/utils/generator.js +156 -0
  125. package/src/engine/utils/index.js +21 -0
  126. package/src/engine/utils/loaders/ActionLoader.js +77 -0
  127. package/src/engine/utils/loaders/AudioLoader.js +157 -0
  128. package/src/engine/utils/loaders/EventLoader.js +66 -0
  129. package/src/engine/utils/loaders/ObjectLoader.js +67 -0
  130. package/src/engine/utils/loaders/SpriteLoader.js +77 -0
  131. package/src/engine/utils/loaders/TilesetLoader.js +103 -0
  132. package/src/engine/utils/loaders/index.js +21 -0
  133. package/src/engine/utils/math/matrix4.js +367 -0
  134. package/src/engine/utils/math/vector.js +458 -0
  135. package/src/engine/utils/obj/_old_js/index.js +46 -0
  136. package/src/engine/utils/obj/_old_js/layout.js +308 -0
  137. package/src/engine/utils/obj/_old_js/material.js +711 -0
  138. package/src/engine/utils/obj/_old_js/mesh.js +761 -0
  139. package/src/engine/utils/obj/_old_js/utils.js +647 -0
  140. package/src/engine/utils/obj/index.js +24 -0
  141. package/src/engine/utils/obj/js/index.js +277 -0
  142. package/src/engine/utils/obj/js/loader.js +232 -0
  143. package/src/engine/utils/obj/layout.js +246 -0
  144. package/src/engine/utils/obj/material.js +665 -0
  145. package/src/engine/utils/obj/mesh.js +657 -0
  146. package/src/engine/utils/obj/ts/index.ts +72 -0
  147. package/src/engine/utils/obj/ts/layout.ts +265 -0
  148. package/src/engine/utils/obj/ts/material.ts +760 -0
  149. package/src/engine/utils/obj/ts/mesh.ts +785 -0
  150. package/src/engine/utils/obj/ts/utils.ts +501 -0
  151. package/src/engine/utils/obj/utils.js +428 -0
  152. package/src/engine/utils/resources.js +18 -0
  153. package/src/index.jsx +55 -0
  154. package/src/spritz/player.js +18 -0
  155. package/src/spritz/readme.md +18 -0
  156. package/LICENSE +0 -437
  157. package/dist/bundle.js.LICENSE.txt +0 -31
package/dist/style.css ADDED
@@ -0,0 +1 @@
1
+ :root{--color-bg: #0a0a0f;--color-primary: #00ff88;--color-secondary: #00ccff;--color-accent: #ff0055;--color-border: #2a2a3e;--glow-primary: rgba(0, 255, 136, .5);--glow-secondary: rgba(0, 204, 255, .5)}.pixos{font-family:"Press Start 2P",Courier New,monospace;font-size:16px;width:100%;text-shadow:#000 2px 2px 0px;position:relative;align-items:center;align-content:center}.renderSurface{height:100%;left:0;position:absolute;top:0;width:100%}.materialSelector{background:#0a0a0ff2;margin:auto;position:relative;border:2px solid var(--color-border);border-radius:4px;box-shadow:0 0 20px #000c}.materialSelector tr{height:70px}.materialSelector td{background-position:0px 0px;cursor:pointer;margin:0;opacity:.5;padding:0;width:70px;transition:opacity .2s}.materialSelector td:hover{opacity:1;box-shadow:0 0 10px var(--glow-primary)}.nickname{color:var(--color-primary);cursor:default;left:42%;position:absolute;top:40%;width:300px;text-shadow:0 0 10px var(--glow-primary)}.nickname input{background:#0a0a0fcc;border-bottom:2px solid var(--color-primary);border:1px solid var(--color-border);border-radius:4px;color:var(--color-primary);font-family:"Press Start 2P",Courier New,monospace;font-size:16px;outline:none;width:100%;padding:8px;box-shadow:0 0 10px #00000080}.nickname input:focus{border-color:var(--color-primary);box-shadow:0 0 15px var(--glow-primary)}.joininfo{color:var(--color-primary);cursor:default;font-size:16px;position:absolute;text-align:center;top:42%;width:99%;text-shadow:0 0 10px var(--glow-primary)}.chatbox{background:#0a0a0ff2;border:2px solid var(--color-border);border-radius:8px;bottom:55px;color:var(--color-primary);cursor:default;height:195px;left:20px;overflow:hidden;padding:10px;position:absolute;width:600px;box-shadow:0 0 20px #000c,inset 0 0 10px #00000080}.chatbox_text{bottom:8px;position:absolute;text-shadow:none;font-size:12px;line-height:1.6}.chatbox_entry{background:#0a0a0ff2;border:2px solid var(--color-border);border-radius:4px;bottom:18px;color:var(--color-primary);font-family:"Press Start 2P",Courier New,monospace;font-size:12px;height:30px;left:20px;outline:none;padding:8px 10px;position:absolute;width:610px;box-shadow:0 0 10px #00000080}.chatbox_entry:focus{border-color:var(--color-primary);box-shadow:0 0 15px var(--glow-primary)}html{height:100%}.pixos-body{height:100%;margin:0;overflow:hidden;background:linear-gradient(to bottom,#202020,#111119)}.rain,.snow{position:absolute;left:0;width:100%;height:100%;z-index:2}.rain.back-row{z-index:1;bottom:60px;opacity:.5}.snow.back-row{z-index:1;bottom:60px;opacity:.8}body.back-row-toggle .rain.back-row .snow.back-row{display:block}.drop{position:absolute;bottom:100%;width:15px;height:120px;pointer-events:none;animation:drop .5s linear infinite}.flake{position:absolute;bottom:100%;width:24px;height:24px;pointer-events:none;animation:snow 2.5s linear infinite}@keyframes drop{0%{transform:translateY(0)}75%{transform:translateY(90vh)}to{transform:translateY(90vh)}}@keyframes snow{0%{transform:translateY(0)}70%{transform:translateY(50vh)}85%{transform:translateY(85vh)}75%{transform:translateY(75vh)}to{transform:translateY(95vh);transform:translate(30vw)}}.stem{width:1px;height:60%;margin-left:7px;background:linear-gradient(to bottom,#fff0,#ffffff40);animation:stem .5s linear infinite}@keyframes stem{0%{opacity:1}65%{opacity:1}75%{opacity:0}to{opacity:0}}.splat{width:15px;height:10px;border-top:2px dotted rgba(255,255,255,.5);border-radius:50%;opacity:1;transform:scale(0);animation:splat .5s linear infinite}body.splat-toggle .splat{display:block}@keyframes splat{0%{opacity:1;transform:scale(0)}80%{opacity:1;transform:scale(0)}90%{opacity:.5;transform:scale(1)}to{opacity:0;transform:scale(1.5)}}.toggles{position:absolute;top:0;left:0;z-index:3}.toggle{position:absolute;left:20px;width:50px;height:50px;line-height:51px;box-sizing:border-box;text-align:center;font-family:sans-serif;font-size:10px;font-weight:700;background-color:#fff3;color:#00000080;border-radius:50%;cursor:pointer;transition:background-color .3s}.toggle:hover{background-color:#ffffff40}.toggle:active{background-color:#ffffff4d}.toggle.active{background-color:#fff6}.splat-toggle{top:20px}.back-row-toggle{top:90px;line-height:12px;padding-top:14px}.single-toggle{top:160px}body.single-toggle .drop{display:none}body.single-toggle .drop:nth-child(10){display:block}
package/package.json CHANGED
@@ -1,33 +1,36 @@
1
1
  {
2
2
  "name": "pixospritz-core",
3
- "version": "0.10.1",
4
- "description": "Pixos is a mini interactive spritz avatar plugin for calliope static site generator for generating pixospritz'.",
3
+ "version": "1.0.1",
4
+ "description": "PixoSpritz Core Engine - WebGL-based game engine",
5
+ "type": "module",
5
6
  "main": "dist/bundle.js",
7
+ "module": "dist/bundle.js",
8
+ "exports": {
9
+ ".": "./dist/bundle.js",
10
+ "./engine/*": "./src/engine/*",
11
+ "./components/*": "./src/components/*",
12
+ "./spritz/*": "./src/spritz/*"
13
+ },
6
14
  "scripts": {
7
- "test": "echo \"Error: no test specified\" && exit 1",
8
- "clean": "rimraf build",
9
- "compile": "yarn clean && cross-env NODE_ENV=production babel pixospritz/src --out-dir build --copy-files --ignore __tests__,spec.js,test.js,stories.js,__snapshots__",
10
- "webpack": "npx webpack",
11
- "lint": "cd pixoscript && yarn typecheck",
12
- "lint:editor": "cd editor && yarn lint",
13
- "lint:player": "cd player && yarn lint",
14
- "start:player": "cd player && yarn start",
15
- "start:editor": "cd editor && yarn start",
16
- "start:server": "node server/v2/main.js",
17
- "start:server:legacy": "node server/v1/main.js"
15
+ "clean": "rimraf dist build",
16
+ "build": "vite build",
17
+ "dev": "vite",
18
+ "preview": "vite preview",
19
+ "test": "vitest run",
20
+ "test:watch": "vitest",
21
+ "test:coverage": "vitest run --coverage"
18
22
  },
19
23
  "files": [
20
24
  "dist",
25
+ "src",
21
26
  "README.md"
22
27
  ],
23
28
  "keywords": [
24
- "calliope",
25
- "pixos",
26
- "html5",
27
- "game",
28
- "react",
29
+ "pixospritz",
30
+ "game-engine",
29
31
  "webgl",
30
- "canvas"
32
+ "canvas",
33
+ "react"
31
34
  ],
32
35
  "homepage": "https://pixospritz.com",
33
36
  "repository": {
@@ -35,42 +38,38 @@
35
38
  "type": "git"
36
39
  },
37
40
  "author": "Kyle Derby MacInnis",
38
- "license": "Attribution-NonCommercial-ShareAlike 4.0 International",
39
- "devDependencies": {
40
- "@babel/cli": "^7.17.10",
41
- "@babel/core": "^7.18.0",
42
- "@babel/node": "^7.17.10",
43
- "@babel/preset-env": "^7.18.0",
44
- "@babel/preset-react": "^7.17.12",
45
- "@babel/register": "^7.17.7",
46
- "babel-plugin-macros": "^3.1.0",
47
- "babel-register": "^6.26.0",
48
- "buffer": "^6.0.3",
49
- "cross-env": "^7.0.3",
50
- "react-recollect": "^5.2.3",
51
- "webpack": "^5.72.1",
52
- "webpack-cli": "^4.9.2",
53
- "webpack-dev-server": "^4.9.0"
54
- },
41
+ "license": "CC-BY-NC-SA-4.0",
55
42
  "peerDependencies": {
56
43
  "react": "^17.0.0 || ^18.0.0",
57
- "react-dom": "^17.0.0 || ^18.0.0",
58
- "react-recollect": "^5.2.3"
44
+ "react-dom": "^17.0.0 || ^18.0.0"
59
45
  },
60
46
  "dependencies": {
61
- "babel-loader": "^8.2.5",
62
- "css-loader": "^6.7.1",
63
47
  "dexie": "^3.2.0",
64
48
  "file-saver": "^2.0.5",
65
49
  "gl-transition": "^1.13.0",
66
50
  "jszip": "^3.7.1",
67
- "luaparse": "^0.3.1",
68
- "pixoscript": "^1.0.1",
51
+ "pixoscript": "*",
52
+ "pixospritz-math": "*",
69
53
  "printj": "^1.3.1",
70
54
  "prop-types": "^15.7.2",
71
55
  "realtime-bpm-analyzer": "^2.1.6",
56
+ "uuid": "^13.0.0"
57
+ },
58
+ "devDependencies": {
59
+ "@babel/cli": "^7.17.10",
60
+ "@babel/core": "^7.18.0",
61
+ "@babel/preset-env": "^7.18.0",
62
+ "@babel/preset-react": "^7.17.12",
63
+ "@testing-library/jest-dom": "^6.9.1",
64
+ "@testing-library/react": "^16.3.0",
65
+ "@testing-library/user-event": "^14.6.1",
66
+ "babel-loader": "^8.2.5",
67
+ "cross-env": "^7.0.3",
68
+ "css-loader": "^6.7.1",
69
+ "identity-obj-proxy": "^3.0.0",
70
+ "rimraf": "^5.0.0",
72
71
  "style-loader": "^3.3.1",
73
- "uuid": "^13.0.0",
74
- "ws": "^8.18.3"
72
+ "webpack": "^5.72.1",
73
+ "webpack-cli": "^4.9.2"
75
74
  }
76
75
  }
@@ -0,0 +1,318 @@
1
+ /* *\
2
+ ** ----------------------------------------------- **
3
+ ** Calliope - Pixos Game Engine **
4
+ ** ----------------------------------------------- **
5
+ ** Copyright (c) 2020-2025 - Kyle Derby MacInnis **
6
+ ** **
7
+ ** Any unauthorized distribution or transfer **
8
+ ** of this work is strictly prohibited. **
9
+ ** **
10
+ ** All Rights Reserved. **
11
+ ** ----------------------------------------------- **
12
+ \* */
13
+
14
+ import React, { useEffect, useRef, useState } from 'react';
15
+ import PropTypes from 'prop-types';
16
+
17
+ import glEngine from '@Engine/core/index.js';
18
+ import { minecraftia } from '@Engine/core/hud/index.js';
19
+ //
20
+ const WebGLView = ({ width, height, SpritzProvider, class: string, zipData }) => {
21
+ // Canvas
22
+ const ref = useRef();
23
+ const hudRef = useRef();
24
+ const gamepadRef = useRef();
25
+ const fileRef = useRef();
26
+ const mmRef = useRef();
27
+
28
+ // keyboard & touch - use wrapper functions to guard against uninitialized engine
29
+ const onKeyEvent = (e) => {
30
+ try {
31
+ if (SpritzProvider && SpritzProvider.onKeyEvent) SpritzProvider.onKeyEvent(e);
32
+ } catch (err) {
33
+ // swallow until engine initialized
34
+ }
35
+ };
36
+
37
+ /**
38
+ * Handles touch/mouse events with proper coordinate transformation.
39
+ * The canvas element may be scaled via CSS to fit the viewport, but its
40
+ * internal resolution (width/height attributes) can differ from the display
41
+ * size (getBoundingClientRect). This function computes the correct canvas
42
+ * coordinates by accounting for the scale difference and any offset.
43
+ */
44
+ const onTouchEvent = (e) => {
45
+ try {
46
+ const canvas = hudRef.current;
47
+ if (!canvas) {
48
+ if (SpritzProvider && SpritzProvider.onTouchEvent) SpritzProvider.onTouchEvent(e);
49
+ return;
50
+ }
51
+
52
+ const rect = canvas.getBoundingClientRect();
53
+
54
+ // Handle both mouse and touch events
55
+ let clientX, clientY;
56
+ if (e.touches && e.touches.length > 0) {
57
+ clientX = e.touches[0].clientX;
58
+ clientY = e.touches[0].clientY;
59
+ } else if (e.changedTouches && e.changedTouches.length > 0) {
60
+ clientX = e.changedTouches[0].clientX;
61
+ clientY = e.changedTouches[0].clientY;
62
+ } else {
63
+ clientX = e.clientX;
64
+ clientY = e.clientY;
65
+ }
66
+
67
+ // Calculate scale factors between internal canvas size and displayed size
68
+ const scaleX = canvas.width / rect.width;
69
+ const scaleY = canvas.height / rect.height;
70
+
71
+ // Calculate position relative to canvas with proper scaling
72
+ const canvasX = (clientX - rect.left) * scaleX;
73
+ const canvasY = (clientY - rect.top) * scaleY;
74
+
75
+ // Create adjusted event with canvas-relative coordinates
76
+ const adjustedEvent = {
77
+ ...e,
78
+ type: e.type,
79
+ clientX: clientX,
80
+ clientY: clientY,
81
+ // Pre-computed canvas coordinates for the engine
82
+ canvasX: canvasX,
83
+ canvasY: canvasY,
84
+ pageX: canvasX + rect.left,
85
+ pageY: canvasY + rect.top,
86
+ _canvasRect: rect,
87
+ _scaleX: scaleX,
88
+ _scaleY: scaleY
89
+ };
90
+
91
+ if (SpritzProvider && SpritzProvider.onTouchEvent) {
92
+ SpritzProvider.onTouchEvent(adjustedEvent);
93
+ }
94
+ } catch (err) {
95
+ console.warn('onTouchEvent error:', err);
96
+ }
97
+ };
98
+
99
+ let engine = null;
100
+
101
+ // Resize
102
+ const [screenSize, getDimension] = useState({
103
+ dynamicWidth: window.innerWidth,
104
+ dynamicHeight: window.innerHeight,
105
+ });
106
+
107
+ // window dimensions
108
+ const setDimension = () => {
109
+ getDimension({
110
+ dynamicWidth: window.innerWidth,
111
+ dynamicHeight: window.innerHeight,
112
+ });
113
+ };
114
+
115
+ // load fonts
116
+ async function loadFonts() {
117
+ await minecraftia.load();
118
+ document.fonts.add(minecraftia);
119
+ }
120
+
121
+ function stopTouchScrolling(canvas) {
122
+ // Prevent scrolling when touching the canvas
123
+ document.body.addEventListener(
124
+ 'touchstart',
125
+ function (e) {
126
+ if (e.target == canvas) {
127
+ e.preventDefault();
128
+ }
129
+ },
130
+ { passive: false }
131
+ );
132
+ document.body.addEventListener(
133
+ 'touchend',
134
+ function (e) {
135
+ if (e.target == canvas) {
136
+ e.preventDefault();
137
+ }
138
+ },
139
+ { passive: false }
140
+ );
141
+ document.body.addEventListener(
142
+ 'touchmove',
143
+ function (e) {
144
+ if (e.target == canvas) {
145
+ e.preventDefault();
146
+ }
147
+ },
148
+ { passive: false }
149
+ );
150
+ }
151
+
152
+ useEffect(async () => {
153
+ // handle resize
154
+ window.addEventListener('resize', setDimension);
155
+
156
+ // setup canvases
157
+ const canvas = ref.current;
158
+ const hud = hudRef.current;
159
+ const mipmap = mmRef.current;
160
+ const gamepad = gamepadRef.current;
161
+ const fileUpload = fileRef.current;
162
+
163
+ // Webgl Engine
164
+ engine = new glEngine(canvas, hud, mipmap, gamepad, fileUpload, width, height);
165
+
166
+ // load fonts
167
+ await loadFonts();
168
+
169
+ // Initialize Spritz
170
+ await engine.init(SpritzProvider);
171
+
172
+ // Create ResizeObserver for proper canvas resize handling
173
+ let resizeObserver = null;
174
+ if (typeof ResizeObserver !== 'undefined') {
175
+ resizeObserver = new ResizeObserver((entries) => {
176
+ for (const entry of entries) {
177
+ if (entry.target === canvas || entry.target === hud) {
178
+ // Notify engine of resize
179
+ if (engine && engine.handleResize) {
180
+ engine.handleResize();
181
+ }
182
+ }
183
+ }
184
+ });
185
+ resizeObserver.observe(canvas);
186
+ resizeObserver.observe(hud);
187
+ }
188
+
189
+ // render loop
190
+ engine.render();
191
+
192
+ // cleanup
193
+ return () => {
194
+ stopTouchScrolling(canvas);
195
+ stopTouchScrolling(gamepad);
196
+ stopTouchScrolling(hud);
197
+ window.removeEventListener('resize', setDimension);
198
+ if (resizeObserver) {
199
+ resizeObserver.disconnect();
200
+ }
201
+ engine.close();
202
+ };
203
+ }, [SpritzProvider]);
204
+
205
+ let wrapperHeight = (screenSize.dynamicWidth * 3) / 4 > 1080 ? 1080 : screenSize.dynamicHeight;
206
+ let canvasHeight = (screenSize.dynamicWidth * 3) / 4 > 1080 ? wrapperHeight : wrapperHeight - 200;
207
+ let canvasWidth = screenSize.dynamicWidth > 1920 ? 1920 : screenSize.dynamicWidth;
208
+ let showGamepad = screenSize.dynamicWidth <= 900;
209
+ let gamepadHeight = 200;
210
+
211
+ return (
212
+ <div style={{ marginLeft: 'auto', marginRight: 'auto' }}>
213
+ <div
214
+ style={{
215
+ position: 'relative',
216
+ padding: 'none',
217
+ background: 'var(--color-bg, #0a0a0f)',
218
+ height: canvasHeight + 'px',
219
+ width: canvasWidth + 'px',
220
+ }}
221
+ onKeyDownCapture={(e) => onKeyEvent(e.nativeEvent)}
222
+ onKeyUpCapture={(e) => onKeyEvent(e.nativeEvent)}
223
+ tabIndex={0}
224
+ >
225
+ {/* Game */}
226
+ <div>
227
+ {/* // WEBGL - For 3D Rendering */}
228
+ <canvas
229
+ style={{
230
+ position: 'absolute',
231
+ zIndex: 1,
232
+ top: 0,
233
+ left: 0,
234
+ width: '100%',
235
+ height: '100%',
236
+ }}
237
+ ref={ref}
238
+ width={canvasWidth}
239
+ height={canvasHeight}
240
+ className={string}
241
+ />
242
+ {/* HUD - For Dialogue / Menus / Overlays */}
243
+ <canvas
244
+ style={{
245
+ position: 'absolute',
246
+ zIndex: 2,
247
+ top: 0,
248
+ left: 0,
249
+ background: 'none',
250
+ width: '100%',
251
+ height: '100%',
252
+ touchAction: 'none', // Prevent browser gestures
253
+ cursor: 'pointer',
254
+ }}
255
+ ref={hudRef}
256
+ width={canvasWidth}
257
+ height={canvasHeight}
258
+ className={string}
259
+ onMouseUp={(e) => { e.stopPropagation(); onTouchEvent(e.nativeEvent); }}
260
+ onMouseDown={(e) => { e.stopPropagation(); onTouchEvent(e.nativeEvent); }}
261
+ onMouseMove={(e) => { e.stopPropagation(); onTouchEvent(e.nativeEvent); }}
262
+ onClick={(e) => { e.stopPropagation(); onTouchEvent(e.nativeEvent); }}
263
+ onTouchStart={(e) => { e.stopPropagation(); onTouchEvent(e.nativeEvent); }}
264
+ onTouchEnd={(e) => { e.stopPropagation(); onTouchEvent(e.nativeEvent); }}
265
+ onTouchMove={(e) => { e.stopPropagation(); onTouchEvent(e.nativeEvent); }}
266
+ onTouchCancel={(e) => { e.stopPropagation(); onTouchEvent(e.nativeEvent); }}
267
+ />
268
+ {/* MIPMAP - For Sprite Text / Speech / Titles */}
269
+ <canvas style={{ display: 'none' }} ref={mmRef} width={256} height={256} />
270
+ </div>
271
+ </div>
272
+ {/* Gamepad - For controls on Mobile Only - Positioned BELOW game canvas */}
273
+ <div style={{
274
+ width: canvasWidth + 'px',
275
+ height: showGamepad ? gamepadHeight + 'px' : '1px',
276
+ marginTop: showGamepad ? '10px' : '0px',
277
+ position: 'relative',
278
+ overflow: 'hidden',
279
+ }}>
280
+ <canvas
281
+ style={{
282
+ position: 'absolute',
283
+ zIndex: 5,
284
+ top: 0,
285
+ left: 0,
286
+ background: 'none',
287
+ width: '100%',
288
+ height: '100%',
289
+ display: showGamepad ? 'block' : 'none',
290
+ }}
291
+ ref={gamepadRef}
292
+ width={canvasWidth}
293
+ height={gamepadHeight}
294
+ className={string}
295
+ onMouseUp={(e) => onTouchEvent(e.nativeEvent)}
296
+ onMouseDown={(e) => onTouchEvent(e.nativeEvent)}
297
+ onMouseMove={(e) => onTouchEvent(e.nativeEvent)}
298
+ onTouchMoveCapture={(e) => onTouchEvent(e.nativeEvent)}
299
+ onTouchCancelCapture={(e) => onTouchEvent(e.nativeEvent)}
300
+ onTouchStartCapture={(e) => onTouchEvent(e.nativeEvent)}
301
+ onTouchEndCapture={(e) => onTouchEvent(e.nativeEvent)}
302
+ />
303
+ </div>
304
+ <div>
305
+ <input type="file" ref={fileRef} src={zipData ?? null} hidden />
306
+ </div>
307
+ </div>
308
+ );
309
+ };
310
+
311
+ WebGLView.propTypes = {
312
+ width: PropTypes.number.isRequired,
313
+ height: PropTypes.number.isRequired,
314
+ SpritzProvider: PropTypes.object.isRequired,
315
+ class: PropTypes.string.isRequired,
316
+ };
317
+
318
+ export default WebGLView;