action-engine-js 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.
Files changed (93) hide show
  1. package/LICENSE +45 -0
  2. package/README.md +348 -0
  3. package/actionengine/3rdparty/goblin/goblin.js +9609 -0
  4. package/actionengine/3rdparty/goblin/goblin.min.js +5 -0
  5. package/actionengine/camera/actioncamera.js +90 -0
  6. package/actionengine/camera/cameracollisionhandler.js +69 -0
  7. package/actionengine/character/actioncharacter.js +360 -0
  8. package/actionengine/character/actioncharacter3D.js +61 -0
  9. package/actionengine/core/app.js +430 -0
  10. package/actionengine/debug/basedebugpanel.js +858 -0
  11. package/actionengine/display/canvasmanager.js +75 -0
  12. package/actionengine/display/gl/programmanager.js +570 -0
  13. package/actionengine/display/gl/shaders/lineshader.js +118 -0
  14. package/actionengine/display/gl/shaders/objectshader.js +1756 -0
  15. package/actionengine/display/gl/shaders/particleshader.js +43 -0
  16. package/actionengine/display/gl/shaders/shadowshader.js +319 -0
  17. package/actionengine/display/gl/shaders/spriteshader.js +100 -0
  18. package/actionengine/display/gl/shaders/watershader.js +67 -0
  19. package/actionengine/display/graphics/actionmodel3D.js +191 -0
  20. package/actionengine/display/graphics/actionsprite3D.js +230 -0
  21. package/actionengine/display/graphics/lighting/actiondirectionalshadowlight.js +864 -0
  22. package/actionengine/display/graphics/lighting/actionlight.js +211 -0
  23. package/actionengine/display/graphics/lighting/actionomnidirectionalshadowlight.js +862 -0
  24. package/actionengine/display/graphics/lighting/lightingconstants.js +263 -0
  25. package/actionengine/display/graphics/lighting/lightmanager.js +789 -0
  26. package/actionengine/display/graphics/renderableobject.js +44 -0
  27. package/actionengine/display/graphics/renderers/actionrenderer2D.js +341 -0
  28. package/actionengine/display/graphics/renderers/actionrenderer3D/actionrenderer3D.js +655 -0
  29. package/actionengine/display/graphics/renderers/actionrenderer3D/canvasmanager3D.js +82 -0
  30. package/actionengine/display/graphics/renderers/actionrenderer3D/debugrenderer3D.js +493 -0
  31. package/actionengine/display/graphics/renderers/actionrenderer3D/objectrenderer3D.js +790 -0
  32. package/actionengine/display/graphics/renderers/actionrenderer3D/spriteRenderer3D.js +266 -0
  33. package/actionengine/display/graphics/renderers/actionrenderer3D/sunrenderer3D.js +140 -0
  34. package/actionengine/display/graphics/renderers/actionrenderer3D/waterrenderer3D.js +173 -0
  35. package/actionengine/display/graphics/renderers/actionrenderer3D/weatherrenderer3D.js +87 -0
  36. package/actionengine/display/graphics/texture/proceduraltexture.js +192 -0
  37. package/actionengine/display/graphics/texture/texturemanager.js +242 -0
  38. package/actionengine/display/graphics/texture/textureregistry.js +177 -0
  39. package/actionengine/input/actionscrollablearea.js +1405 -0
  40. package/actionengine/input/inputhandler.js +1647 -0
  41. package/actionengine/math/geometry/geometrybuilder.js +161 -0
  42. package/actionengine/math/geometry/glbexporter.js +364 -0
  43. package/actionengine/math/geometry/glbloader.js +722 -0
  44. package/actionengine/math/geometry/modelcodegenerator.js +97 -0
  45. package/actionengine/math/geometry/triangle.js +33 -0
  46. package/actionengine/math/geometry/triangleutils.js +34 -0
  47. package/actionengine/math/mathutils.js +25 -0
  48. package/actionengine/math/matrix4.js +785 -0
  49. package/actionengine/math/physics/actionphysics.js +108 -0
  50. package/actionengine/math/physics/actionphysicsobject3D.js +164 -0
  51. package/actionengine/math/physics/actionphysicsworld3D.js +238 -0
  52. package/actionengine/math/physics/actionraycast.js +129 -0
  53. package/actionengine/math/physics/shapes/actionphysicsbox3D.js +158 -0
  54. package/actionengine/math/physics/shapes/actionphysicscapsule3D.js +200 -0
  55. package/actionengine/math/physics/shapes/actionphysicscompoundshape3D.js +147 -0
  56. package/actionengine/math/physics/shapes/actionphysicscone3D.js +126 -0
  57. package/actionengine/math/physics/shapes/actionphysicsconvexshape3D.js +72 -0
  58. package/actionengine/math/physics/shapes/actionphysicscylinder3D.js +117 -0
  59. package/actionengine/math/physics/shapes/actionphysicsmesh3D.js +74 -0
  60. package/actionengine/math/physics/shapes/actionphysicsplane3D.js +100 -0
  61. package/actionengine/math/physics/shapes/actionphysicssphere3D.js +95 -0
  62. package/actionengine/math/quaternion.js +61 -0
  63. package/actionengine/math/vector2.js +277 -0
  64. package/actionengine/math/vector3.js +318 -0
  65. package/actionengine/math/viewfrustum.js +136 -0
  66. package/actionengine/network/ACTIONNETREADME.md +810 -0
  67. package/actionengine/network/client/ActionNetManager.js +802 -0
  68. package/actionengine/network/client/ActionNetManagerGUI.js +1709 -0
  69. package/actionengine/network/client/ActionNetManagerP2P.js +1537 -0
  70. package/actionengine/network/client/SyncSystem.js +422 -0
  71. package/actionengine/network/p2p/ActionNetPeer.js +142 -0
  72. package/actionengine/network/p2p/ActionNetTrackerClient.js +623 -0
  73. package/actionengine/network/p2p/DataConnection.js +282 -0
  74. package/actionengine/network/p2p/README.md +510 -0
  75. package/actionengine/network/p2p/example.html +502 -0
  76. package/actionengine/network/server/ActionNetServer.js +577 -0
  77. package/actionengine/network/server/ActionNetServerSSL.js +579 -0
  78. package/actionengine/network/server/ActionNetServerUtils.js +458 -0
  79. package/actionengine/network/server/SERVERREADME.md +314 -0
  80. package/actionengine/network/server/package-lock.json +35 -0
  81. package/actionengine/network/server/package.json +13 -0
  82. package/actionengine/network/server/start.bat +27 -0
  83. package/actionengine/network/server/start.sh +25 -0
  84. package/actionengine/network/server/startwss.bat +27 -0
  85. package/actionengine/sound/audiomanager.js +1589 -0
  86. package/actionengine/sound/soundfont/ACTIONSOUNDFONT_README.md +205 -0
  87. package/actionengine/sound/soundfont/actionparser.js +718 -0
  88. package/actionengine/sound/soundfont/actionreverb.js +252 -0
  89. package/actionengine/sound/soundfont/actionsoundfont.js +543 -0
  90. package/actionengine/sound/soundfont/sf2playerlicence.txt +29 -0
  91. package/actionengine/sound/soundfont/soundfont.js +2 -0
  92. package/dist/action-engine.min.js +328 -0
  93. package/package.json +35 -0
@@ -0,0 +1,192 @@
1
+ // actionengine/display/graphics/texture/proceduraltexture.js
2
+ class ProceduralTexture {
3
+ constructor(width, height) {
4
+ this.width = width;
5
+ this.height = height;
6
+ this.data = new Uint8ClampedArray(width * height * 4);
7
+ this.widthMask = width - 1; // For power-of-2 textures, faster modulo
8
+ this.heightMask = height - 1;
9
+ }
10
+
11
+ getPixel(x, y) {
12
+ // Fast modulo for power-of-2 textures using bitwise AND
13
+ const tx = x & this.widthMask;
14
+ const ty = y & this.heightMask;
15
+ const i = (ty * this.width + tx) * 4;
16
+ return {
17
+ r: this.data[i],
18
+ g: this.data[i + 1],
19
+ b: this.data[i + 2]
20
+ };
21
+ }
22
+
23
+ generateCheckerboard() {
24
+ const size = 4; // Make checkers a bit bigger
25
+ for (let y = 0; y < this.height; y++) {
26
+ for (let x = 0; x < this.width; x++) {
27
+ const isEven = ((x >> 2) + (y >> 2)) & 1; // Simpler calculation using bitwise
28
+ const i = (y * this.width + x) * 4;
29
+ if (isEven) {
30
+ this.data[i] = 0; // Blue square
31
+ this.data[i + 1] = 0;
32
+ this.data[i + 2] = 255;
33
+ } else {
34
+ this.data[i] = 255; // Purple square
35
+ this.data[i + 1] = 0;
36
+ this.data[i + 2] = 255;
37
+ }
38
+ this.data[i + 3] = 255;
39
+ }
40
+ }
41
+ }
42
+
43
+ generateGrass() {
44
+ const baseColor = { r: 34, g: 139, b: 34 };
45
+ // Pre-calculate a noise table for faster lookup
46
+ const noiseTable = new Uint8Array(256);
47
+ for (let i = 0; i < 256; i++) {
48
+ noiseTable[i] = Math.floor(Math.random() * 30);
49
+ }
50
+ for (let y = 0; y < this.height; y++) {
51
+ for (let x = 0; x < this.width; x++) {
52
+ const variation = noiseTable[(x * 7 + y * 13) & 255];
53
+ const i = (y * this.width + x) * 4;
54
+ this.data[i] = Math.min(255, baseColor.r + variation);
55
+ this.data[i + 1] = Math.min(255, baseColor.g + variation);
56
+ this.data[i + 2] = Math.min(255, baseColor.b + variation);
57
+ this.data[i + 3] = 255;
58
+ }
59
+ }
60
+ }
61
+
62
+ generateWater() {
63
+ const baseColor = { r: 0, g: 100, b: 255 }; // Keeping your bright blue base
64
+ for (let y = 0; y < this.height; y++) {
65
+ for (let x = 0; x < this.width; x++) {
66
+ // Just gentle noise for natural water look
67
+ const noise = Math.random() * 15 - 7;
68
+
69
+ const i = (y * this.width + x) * 4;
70
+ this.data[i] = Math.min(255, baseColor.r + noise);
71
+ this.data[i + 1] = Math.min(255, baseColor.g + noise);
72
+ this.data[i + 2] = Math.min(255, baseColor.b + noise);
73
+ this.data[i + 3] = 255;
74
+ }
75
+ }
76
+ }
77
+
78
+ generateDeepWater() {
79
+ const baseColor = { r: 0, g: 21, b: 37 }; // Dark blue base
80
+ for (let y = 0; y < this.height; y++) {
81
+ for (let x = 0; x < this.width; x++) {
82
+ // Just subtle random variation
83
+ const variation = Math.random() * 15 - 7;
84
+
85
+ const i = (y * this.width + x) * 4;
86
+ this.data[i] = Math.min(255, baseColor.r + variation);
87
+ this.data[i + 1] = Math.min(255, baseColor.g + variation);
88
+ this.data[i + 2] = Math.min(255, baseColor.b + variation);
89
+ this.data[i + 3] = 255;
90
+ }
91
+ }
92
+ }
93
+
94
+ generateSand() {
95
+ const baseColor = { r: 210, g: 185, b: 139 }; // Sandy beige
96
+ for (let y = 0; y < this.height; y++) {
97
+ for (let x = 0; x < this.width; x++) {
98
+ // Create grainy noise pattern
99
+ const noise = Math.random() * 20 - 10;
100
+
101
+ const i = (y * this.width + x) * 4;
102
+ this.data[i] = Math.min(255, baseColor.r + noise);
103
+ this.data[i + 1] = Math.min(255, baseColor.g + noise);
104
+ this.data[i + 2] = Math.min(255, baseColor.b + noise);
105
+ this.data[i + 3] = 255;
106
+ }
107
+ }
108
+ }
109
+
110
+ generateRock() {
111
+ const baseColor = { r: 115, g: 109, b: 105 }; // Gray
112
+ for (let y = 0; y < this.height; y++) {
113
+ for (let x = 0; x < this.width; x++) {
114
+ // Just natural noise variation
115
+ const noise = Math.random() * 35 - 17;
116
+
117
+ const i = (y * this.width + x) * 4;
118
+ this.data[i] = Math.min(255, baseColor.r + noise);
119
+ this.data[i + 1] = Math.min(255, baseColor.g + noise);
120
+ this.data[i + 2] = Math.min(255, baseColor.b + noise);
121
+ this.data[i + 3] = 255;
122
+ }
123
+ }
124
+ }
125
+
126
+ generateHighlandGrass() {
127
+ const baseColor = { r: 45, g: 89, b: 41 }; // Darker green
128
+ for (let y = 0; y < this.height; y++) {
129
+ for (let x = 0; x < this.width; x++) {
130
+ // Simple noise variation for natural look
131
+ const noise = Math.random() * 35 - 17;
132
+
133
+ const i = (y * this.width + x) * 4;
134
+ this.data[i] = Math.min(255, baseColor.r + noise);
135
+ this.data[i + 1] = Math.min(255, baseColor.g + noise);
136
+ this.data[i + 2] = Math.min(255, baseColor.b + noise);
137
+ this.data[i + 3] = 255;
138
+ }
139
+ }
140
+ }
141
+
142
+ generateTreeline() {
143
+ const baseColor = { r: 116, g: 71, b: 0 }; // Brown base
144
+ const grayColor = { r: 115, g: 109, b: 105 }; // Gray instead of green
145
+
146
+ for (let y = 0; y < this.height; y++) {
147
+ for (let x = 0; x < this.width; x++) {
148
+ // Smooth blend between brown and gray
149
+ const blendFactor = Math.random();
150
+ const noise = Math.random() * 20 - 10;
151
+
152
+ const i = (y * this.width + x) * 4;
153
+ this.data[i] = Math.min(255, baseColor.r * blendFactor + grayColor.r * (1 - blendFactor) + noise);
154
+ this.data[i + 1] = Math.min(255, baseColor.g * blendFactor + grayColor.g * (1 - blendFactor) + noise);
155
+ this.data[i + 2] = Math.min(255, baseColor.b * blendFactor + grayColor.b * (1 - blendFactor) + noise);
156
+ this.data[i + 3] = 255;
157
+ }
158
+ }
159
+ }
160
+
161
+ generateDunes() {
162
+ const baseColor = { r: 230, g: 185, b: 115 }; // More orange/reddish sand color
163
+ for (let y = 0; y < this.height; y++) {
164
+ for (let x = 0; x < this.width; x++) {
165
+ // Natural noise variation
166
+ const noise = Math.random() * 25 - 12;
167
+
168
+ const i = (y * this.width + x) * 4;
169
+ this.data[i] = Math.min(255, baseColor.r + noise);
170
+ this.data[i + 1] = Math.min(255, baseColor.g + noise);
171
+ this.data[i + 2] = Math.min(255, baseColor.b + noise);
172
+ this.data[i + 3] = 255;
173
+ }
174
+ }
175
+ }
176
+
177
+ generateSnow() {
178
+ const baseColor = { r: 232, g: 232, b: 232 }; // Very light gray/white
179
+ for (let y = 0; y < this.height; y++) {
180
+ for (let x = 0; x < this.width; x++) {
181
+ // Just subtle noise variation
182
+ const noise = Math.random() * 12 - 6;
183
+
184
+ const i = (y * this.width + x) * 4;
185
+ this.data[i] = Math.min(255, baseColor.r + noise);
186
+ this.data[i + 1] = Math.min(255, baseColor.g + noise);
187
+ this.data[i + 2] = Math.min(255, baseColor.b + noise);
188
+ this.data[i + 3] = 255;
189
+ }
190
+ }
191
+ }
192
+ }
@@ -0,0 +1,242 @@
1
+ // actionengine/display/graphics/texture/texturemanager.js
2
+ class TextureManager {
3
+ constructor(gl) {
4
+ this.gl = gl;
5
+ this.isWebGL2 = gl.getParameter(gl.VERSION).includes("WebGL 2.0");
6
+ this.textureArray = this.createTextureArray();
7
+
8
+ // Create material properties texture
9
+ this.materialPropertiesTexture = this.createMaterialPropertiesTexture();
10
+
11
+ // Flag to control per-texture material usage
12
+ this.usePerTextureMaterials = true;
13
+
14
+ // Add a flag to track if material properties need updating
15
+ this.materialPropertiesDirty = true;
16
+
17
+ // Store a hash of the last material properties to detect changes
18
+ this._lastMaterialPropertiesHash = 0;
19
+ }
20
+
21
+ createTextureArray() {
22
+ if (this.isWebGL2) {
23
+ return this.createWebGL2TextureArray();
24
+ } else {
25
+ return this.createWebGL1Texture();
26
+ }
27
+ }
28
+
29
+ createWebGL2TextureArray() {
30
+ console.log('[TextureManager] Creating WebGL2 texture array');
31
+ const array = this.gl.createTexture();
32
+ this.gl.bindTexture(this.gl.TEXTURE_2D_ARRAY, array);
33
+
34
+ // All our procedural textures are 256x256
35
+ this.gl.texImage3D(
36
+ this.gl.TEXTURE_2D_ARRAY,
37
+ 0, // mip level
38
+ this.gl.RGBA, // internal format
39
+ 256, // width
40
+ 256, // height
41
+ textureRegistry.getTextureCount(), // number of layers
42
+ 0, // border
43
+ this.gl.RGBA, // format
44
+ this.gl.UNSIGNED_BYTE, // type
45
+ null // data
46
+ );
47
+
48
+ // Load each texture as a layer
49
+ textureRegistry.textureList.forEach((textureName, i) => {
50
+ const proceduralTexture = textureRegistry.get(textureName);
51
+
52
+ // Convert to RGBA format
53
+ const rgbaData = new Uint8Array(proceduralTexture.width * proceduralTexture.height * 4);
54
+ for (let j = 0; j < proceduralTexture.data.length; j += 4) {
55
+ rgbaData[j] = proceduralTexture.data[j]; // R
56
+ rgbaData[j + 1] = proceduralTexture.data[j + 1]; // G
57
+ rgbaData[j + 2] = proceduralTexture.data[j + 2]; // B
58
+ rgbaData[j + 3] = 255; // A
59
+ }
60
+
61
+ this.gl.texSubImage3D(
62
+ this.gl.TEXTURE_2D_ARRAY,
63
+ 0, // mip level
64
+ 0, // x offset
65
+ 0, // y offset
66
+ i, // z offset (layer)
67
+ 256, // width
68
+ 256, // height
69
+ 1, // depth
70
+ this.gl.RGBA,
71
+ this.gl.UNSIGNED_BYTE,
72
+ rgbaData
73
+ );
74
+ });
75
+
76
+ // Set texture parameters
77
+ this.gl.texParameteri(this.gl.TEXTURE_2D_ARRAY, this.gl.TEXTURE_MIN_FILTER, this.gl.NEAREST);
78
+ this.gl.texParameteri(this.gl.TEXTURE_2D_ARRAY, this.gl.TEXTURE_MAG_FILTER, this.gl.NEAREST);
79
+ this.gl.texParameteri(this.gl.TEXTURE_2D_ARRAY, this.gl.TEXTURE_WRAP_S, this.gl.REPEAT);
80
+ this.gl.texParameteri(this.gl.TEXTURE_2D_ARRAY, this.gl.TEXTURE_WRAP_T, this.gl.REPEAT);
81
+
82
+ return array;
83
+ }
84
+
85
+ createWebGL1Texture() {
86
+ console.log('[TextureManager] Creating WebGL1 basic texture (no array support)');
87
+ // For WebGL1, just use the first texture in the registry
88
+ const array = this.gl.createTexture();
89
+ this.gl.bindTexture(this.gl.TEXTURE_2D, array);
90
+
91
+ // Get the first texture (grass)
92
+ const proceduralTexture = textureRegistry.get(textureRegistry.textureList[0]);
93
+
94
+ // Convert to RGBA format
95
+ const rgbaData = new Uint8Array(proceduralTexture.width * proceduralTexture.height * 4);
96
+ for (let j = 0; j < proceduralTexture.data.length; j += 4) {
97
+ rgbaData[j] = proceduralTexture.data[j]; // R
98
+ rgbaData[j + 1] = proceduralTexture.data[j + 1]; // G
99
+ rgbaData[j + 2] = proceduralTexture.data[j + 2]; // B
100
+ rgbaData[j + 3] = 255; // A
101
+ }
102
+
103
+ this.gl.texImage2D(
104
+ this.gl.TEXTURE_2D,
105
+ 0, // mip level
106
+ this.gl.RGBA, // internal format
107
+ 256, // width
108
+ 256, // height
109
+ 0, // border
110
+ this.gl.RGBA, // format
111
+ this.gl.UNSIGNED_BYTE, // type
112
+ rgbaData // data
113
+ );
114
+
115
+ // Set texture parameters
116
+ this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MIN_FILTER, this.gl.NEAREST);
117
+ this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MAG_FILTER, this.gl.NEAREST);
118
+ this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_S, this.gl.REPEAT);
119
+ this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_T, this.gl.REPEAT);
120
+
121
+ return array;
122
+ }
123
+
124
+ // Create a texture to store material properties for each texture
125
+ createMaterialPropertiesTexture() {
126
+ const gl = this.gl;
127
+ console.log('[TextureManager] Creating material properties texture');
128
+
129
+ // Create a texture for material properties
130
+ // Each texel contains [roughness, metallic, baseReflectivity, reserved]
131
+ const texture = gl.createTexture();
132
+ gl.bindTexture(gl.TEXTURE_2D, texture);
133
+
134
+ // Get material properties data from texture registry
135
+ const textureCount = textureRegistry.getTextureCount();
136
+ const data = textureRegistry.getMaterialPropertiesArray();
137
+
138
+ // Create and set texture data based on WebGL version
139
+ if (this.isWebGL2) {
140
+ gl.texImage2D(
141
+ gl.TEXTURE_2D,
142
+ 0, // mip level
143
+ gl.RGBA32F, // internal format - use float format for WebGL2
144
+ textureCount, // width (one texel per texture)
145
+ 1, // height
146
+ 0, // border
147
+ gl.RGBA, // format
148
+ gl.FLOAT, // type
149
+ data // data
150
+ );
151
+ } else {
152
+ // WebGL 1.0 fallback - try to use OES_texture_float extension
153
+ const ext = gl.getExtension('OES_texture_float');
154
+ if (ext) {
155
+ gl.texImage2D(
156
+ gl.TEXTURE_2D,
157
+ 0, // mip level
158
+ gl.RGBA, // internal format
159
+ textureCount, // width (one texel per texture)
160
+ 1, // height
161
+ 0, // border
162
+ gl.RGBA, // format
163
+ gl.FLOAT, // type
164
+ data // data
165
+ );
166
+ } else {
167
+ console.warn('[TextureManager] Float textures not supported by this device. Falling back to global material properties.');
168
+ this.usePerTextureMaterials = false;
169
+ return null;
170
+ }
171
+ }
172
+
173
+ // Set texture parameters - we need NEAREST filter for exact sampling
174
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
175
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
176
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
177
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
178
+
179
+ return texture;
180
+ }
181
+
182
+ // Update the material properties texture with current values from the registry
183
+ updateMaterialPropertiesTexture() {
184
+ if (!this.materialPropertiesTexture || !this.usePerTextureMaterials) {
185
+ return;
186
+ }
187
+
188
+ const gl = this.gl;
189
+
190
+ // Get the current material properties data
191
+ const textureCount = textureRegistry.getTextureCount();
192
+ const data = textureRegistry.getMaterialPropertiesArray();
193
+
194
+ // IMPORTANT: Save WebGL state before modifying
195
+ const previousTexture = gl.getParameter(gl.TEXTURE_BINDING_2D);
196
+ const previousActiveTexture = gl.getParameter(gl.ACTIVE_TEXTURE);
197
+
198
+ // Bind and update the texture
199
+ gl.bindTexture(gl.TEXTURE_2D, this.materialPropertiesTexture);
200
+
201
+ if (this.isWebGL2) {
202
+ gl.texSubImage2D(
203
+ gl.TEXTURE_2D,
204
+ 0, // mip level
205
+ 0, // x offset
206
+ 0, // y offset
207
+ textureCount, // width
208
+ 1, // height
209
+ gl.RGBA, // format
210
+ gl.FLOAT, // type
211
+ data // data
212
+ );
213
+ } else {
214
+ // For WebGL1, we need to re-specify the entire texture
215
+ gl.texImage2D(
216
+ gl.TEXTURE_2D,
217
+ 0, // mip level
218
+ gl.RGBA, // internal format
219
+ textureCount, // width
220
+ 1, // height
221
+ 0, // border
222
+ gl.RGBA, // format
223
+ gl.FLOAT, // type
224
+ data // data
225
+ );
226
+ }
227
+
228
+ // IMPORTANT: Restore WebGL state when done
229
+ gl.activeTexture(previousActiveTexture);
230
+ gl.bindTexture(gl.TEXTURE_2D, previousTexture);
231
+ }
232
+
233
+ // Toggle per-texture material usage
234
+ togglePerTextureMaterials(enabled) {
235
+ this.usePerTextureMaterials = enabled;
236
+
237
+ // If enabling and we don't have a material texture, create one
238
+ if (enabled && !this.materialPropertiesTexture) {
239
+ this.materialPropertiesTexture = this.createMaterialPropertiesTexture();
240
+ }
241
+ }
242
+ }
@@ -0,0 +1,177 @@
1
+ class TextureRegistry {
2
+
3
+ constructor() {
4
+ this.textures = new Map();
5
+ this.textureList = []; // Array to maintain texture order
6
+
7
+ // Add material properties storage for each texture
8
+ this.materialProperties = new Map();
9
+
10
+ // Default material properties (used when not specified for a texture)
11
+ // Note: Roughness is from 0.0 (mirror-like) to 1.0 (completely rough/diffuse)
12
+ // The shader inverts roughness internally so 0 = reflective, 1 = diffuse
13
+ this.defaultMaterialProperties = {
14
+ roughness: 0.0, // Lower values = more reflective
15
+ metallic: 0.0, // Higher values = more metallic
16
+ baseReflectivity: 0.0 // Higher values = more reflective at glancing angles
17
+ };
18
+
19
+ this.generateTextures();
20
+ }
21
+
22
+ generateTextures() {
23
+ // Create textures in a specific order for indexing
24
+ const grass = new ProceduralTexture(256, 256);
25
+ grass.generateGrass();
26
+ this.addTexture("grass", grass);
27
+
28
+ const water = new ProceduralTexture(256, 256);
29
+ water.generateWater();
30
+ this.addTexture("water", water);
31
+
32
+ const deepWater = new ProceduralTexture(256, 256);
33
+ deepWater.generateDeepWater();
34
+ this.addTexture("deepwater", deepWater);
35
+
36
+ const sand = new ProceduralTexture(256, 256);
37
+ sand.generateSand();
38
+ this.addTexture("sand", sand);
39
+
40
+ const dunes = new ProceduralTexture(256, 256);
41
+ dunes.generateDunes();
42
+ this.addTexture("dunes", dunes);
43
+
44
+ const rock = new ProceduralTexture(256, 256);
45
+ rock.generateRock();
46
+ this.addTexture("rock", rock);
47
+
48
+ const highland = new ProceduralTexture(256, 256);
49
+ highland.generateHighlandGrass();
50
+ this.addTexture("highland", highland);
51
+
52
+ const treeline = new ProceduralTexture(256, 256);
53
+ treeline.generateTreeline();
54
+ this.addTexture("treeline", treeline);
55
+
56
+ const snow = new ProceduralTexture(256, 256);
57
+ snow.generateSnow();
58
+ this.addTexture("snow", snow);
59
+
60
+ this.setMaterialProperties("grass", { roughness: 1.0, metallic: 0.0, baseReflectivity: 0.0 });
61
+ this.setMaterialProperties("water", { roughness: 0.08, metallic: 0.0, baseReflectivity: 0.72 });
62
+ this.setMaterialProperties("deepwater", { roughness: 0.05, metallic: 0.0, baseReflectivity: 0.8 });
63
+ this.setMaterialProperties("sand", { roughness: 0.54, metallic: 0.0, baseReflectivity: 0.21 });
64
+ this.setMaterialProperties("dunes", { roughness: 0.75, metallic: 0.2, baseReflectivity: 0.42 });
65
+ this.setMaterialProperties("rock", { roughness: 0.9, metallic: 0.05, baseReflectivity: 0.3 });
66
+ this.setMaterialProperties("highland", { roughness: 0.0, metallic: 0.0, baseReflectivity: 0.0 });
67
+ this.setMaterialProperties("treeline", { roughness: 0.7, metallic: 0.0, baseReflectivity: 0.2 });
68
+ this.setMaterialProperties("snow", { roughness: 0.65, metallic: 0.1, baseReflectivity: 0.09 });
69
+ }
70
+
71
+ addTexture(name, texture) {
72
+ this.textures.set(name, texture);
73
+ this.textureList.push(name);
74
+
75
+ // Initialize material properties with defaults
76
+ if (!this.materialProperties.has(name)) {
77
+ this.materialProperties.set(name, { ...this.defaultMaterialProperties });
78
+ }
79
+ }
80
+
81
+ get(type) {
82
+ return this.textures.get(type);
83
+ }
84
+
85
+ getTextureIndex(textureName) {
86
+ return this.textureList.indexOf(textureName);
87
+ }
88
+
89
+ getTextureByIndex(index) {
90
+ return this.textures.get(this.textureList[index]);
91
+ }
92
+
93
+ getTextureCount() {
94
+ return this.textureList.length;
95
+ }
96
+
97
+ // Get material properties for a texture
98
+ getMaterialProperties(textureName) {
99
+ if (this.materialProperties.has(textureName)) {
100
+ return this.materialProperties.get(textureName);
101
+ }
102
+ return { ...this.defaultMaterialProperties };
103
+ }
104
+
105
+ // Get material properties by texture index
106
+ getMaterialPropertiesByIndex(index) {
107
+ if (index >= 0 && index < this.textureList.length) {
108
+ const textureName = this.textureList[index];
109
+ return this.getMaterialProperties(textureName);
110
+ }
111
+ return { ...this.defaultMaterialProperties };
112
+ }
113
+
114
+ // Set material properties for a texture
115
+ setMaterialProperties(textureName, properties) {
116
+ if (!this.textures.has(textureName)) {
117
+ console.warn(`Texture '${textureName}' does not exist.`);
118
+ return;
119
+ }
120
+
121
+ // Get existing properties or default ones
122
+ const existing = this.materialProperties.get(textureName) || { ...this.defaultMaterialProperties };
123
+
124
+
125
+
126
+ // Update with new properties
127
+ this.materialProperties.set(textureName, {
128
+ ...existing,
129
+ ...properties
130
+ });
131
+
132
+ // Material properties have been updated
133
+ }
134
+
135
+ // Update default material properties (global settings)
136
+ setDefaultMaterialProperties(properties) {
137
+ // Check if any property is actually changing
138
+ let hasChanges = false;
139
+ Object.keys(properties).forEach(key => {
140
+ if (properties[key] !== this.defaultMaterialProperties[key]) {
141
+ hasChanges = true;
142
+ }
143
+ });
144
+
145
+ // If nothing is changing, don't update
146
+ if (!hasChanges) {
147
+ return;
148
+ }
149
+
150
+ this.defaultMaterialProperties = {
151
+ ...this.defaultMaterialProperties,
152
+ ...properties
153
+ };
154
+
155
+ // Material properties have been updated
156
+ }
157
+
158
+ // Get all material properties as a flat array for use in a texture
159
+ getMaterialPropertiesArray() {
160
+ const textureCount = this.textureList.length;
161
+ const data = new Float32Array(textureCount * 4); // 4 components per texture (roughness, metallic, baseReflectivity, reserved)
162
+
163
+ for (let i = 0; i < textureCount; i++) {
164
+ const textureName = this.textureList[i];
165
+ const props = this.getMaterialProperties(textureName);
166
+
167
+ // Set values in the array
168
+ data[i * 4] = props.roughness;
169
+ data[i * 4 + 1] = props.metallic;
170
+ data[i * 4 + 2] = props.baseReflectivity;
171
+ data[i * 4 + 3] = 0; // Reserved for future use
172
+ }
173
+
174
+ return data;
175
+ }
176
+ }
177
+ const textureRegistry = new TextureRegistry(); // global