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.
- package/LICENSE +45 -0
- package/README.md +348 -0
- package/actionengine/3rdparty/goblin/goblin.js +9609 -0
- package/actionengine/3rdparty/goblin/goblin.min.js +5 -0
- package/actionengine/camera/actioncamera.js +90 -0
- package/actionengine/camera/cameracollisionhandler.js +69 -0
- package/actionengine/character/actioncharacter.js +360 -0
- package/actionengine/character/actioncharacter3D.js +61 -0
- package/actionengine/core/app.js +430 -0
- package/actionengine/debug/basedebugpanel.js +858 -0
- package/actionengine/display/canvasmanager.js +75 -0
- package/actionengine/display/gl/programmanager.js +570 -0
- package/actionengine/display/gl/shaders/lineshader.js +118 -0
- package/actionengine/display/gl/shaders/objectshader.js +1756 -0
- package/actionengine/display/gl/shaders/particleshader.js +43 -0
- package/actionengine/display/gl/shaders/shadowshader.js +319 -0
- package/actionengine/display/gl/shaders/spriteshader.js +100 -0
- package/actionengine/display/gl/shaders/watershader.js +67 -0
- package/actionengine/display/graphics/actionmodel3D.js +191 -0
- package/actionengine/display/graphics/actionsprite3D.js +230 -0
- package/actionengine/display/graphics/lighting/actiondirectionalshadowlight.js +864 -0
- package/actionengine/display/graphics/lighting/actionlight.js +211 -0
- package/actionengine/display/graphics/lighting/actionomnidirectionalshadowlight.js +862 -0
- package/actionengine/display/graphics/lighting/lightingconstants.js +263 -0
- package/actionengine/display/graphics/lighting/lightmanager.js +789 -0
- package/actionengine/display/graphics/renderableobject.js +44 -0
- package/actionengine/display/graphics/renderers/actionrenderer2D.js +341 -0
- package/actionengine/display/graphics/renderers/actionrenderer3D/actionrenderer3D.js +655 -0
- package/actionengine/display/graphics/renderers/actionrenderer3D/canvasmanager3D.js +82 -0
- package/actionengine/display/graphics/renderers/actionrenderer3D/debugrenderer3D.js +493 -0
- package/actionengine/display/graphics/renderers/actionrenderer3D/objectrenderer3D.js +790 -0
- package/actionengine/display/graphics/renderers/actionrenderer3D/spriteRenderer3D.js +266 -0
- package/actionengine/display/graphics/renderers/actionrenderer3D/sunrenderer3D.js +140 -0
- package/actionengine/display/graphics/renderers/actionrenderer3D/waterrenderer3D.js +173 -0
- package/actionengine/display/graphics/renderers/actionrenderer3D/weatherrenderer3D.js +87 -0
- package/actionengine/display/graphics/texture/proceduraltexture.js +192 -0
- package/actionengine/display/graphics/texture/texturemanager.js +242 -0
- package/actionengine/display/graphics/texture/textureregistry.js +177 -0
- package/actionengine/input/actionscrollablearea.js +1405 -0
- package/actionengine/input/inputhandler.js +1647 -0
- package/actionengine/math/geometry/geometrybuilder.js +161 -0
- package/actionengine/math/geometry/glbexporter.js +364 -0
- package/actionengine/math/geometry/glbloader.js +722 -0
- package/actionengine/math/geometry/modelcodegenerator.js +97 -0
- package/actionengine/math/geometry/triangle.js +33 -0
- package/actionengine/math/geometry/triangleutils.js +34 -0
- package/actionengine/math/mathutils.js +25 -0
- package/actionengine/math/matrix4.js +785 -0
- package/actionengine/math/physics/actionphysics.js +108 -0
- package/actionengine/math/physics/actionphysicsobject3D.js +164 -0
- package/actionengine/math/physics/actionphysicsworld3D.js +238 -0
- package/actionengine/math/physics/actionraycast.js +129 -0
- package/actionengine/math/physics/shapes/actionphysicsbox3D.js +158 -0
- package/actionengine/math/physics/shapes/actionphysicscapsule3D.js +200 -0
- package/actionengine/math/physics/shapes/actionphysicscompoundshape3D.js +147 -0
- package/actionengine/math/physics/shapes/actionphysicscone3D.js +126 -0
- package/actionengine/math/physics/shapes/actionphysicsconvexshape3D.js +72 -0
- package/actionengine/math/physics/shapes/actionphysicscylinder3D.js +117 -0
- package/actionengine/math/physics/shapes/actionphysicsmesh3D.js +74 -0
- package/actionengine/math/physics/shapes/actionphysicsplane3D.js +100 -0
- package/actionengine/math/physics/shapes/actionphysicssphere3D.js +95 -0
- package/actionengine/math/quaternion.js +61 -0
- package/actionengine/math/vector2.js +277 -0
- package/actionengine/math/vector3.js +318 -0
- package/actionengine/math/viewfrustum.js +136 -0
- package/actionengine/network/ACTIONNETREADME.md +810 -0
- package/actionengine/network/client/ActionNetManager.js +802 -0
- package/actionengine/network/client/ActionNetManagerGUI.js +1709 -0
- package/actionengine/network/client/ActionNetManagerP2P.js +1537 -0
- package/actionengine/network/client/SyncSystem.js +422 -0
- package/actionengine/network/p2p/ActionNetPeer.js +142 -0
- package/actionengine/network/p2p/ActionNetTrackerClient.js +623 -0
- package/actionengine/network/p2p/DataConnection.js +282 -0
- package/actionengine/network/p2p/README.md +510 -0
- package/actionengine/network/p2p/example.html +502 -0
- package/actionengine/network/server/ActionNetServer.js +577 -0
- package/actionengine/network/server/ActionNetServerSSL.js +579 -0
- package/actionengine/network/server/ActionNetServerUtils.js +458 -0
- package/actionengine/network/server/SERVERREADME.md +314 -0
- package/actionengine/network/server/package-lock.json +35 -0
- package/actionengine/network/server/package.json +13 -0
- package/actionengine/network/server/start.bat +27 -0
- package/actionengine/network/server/start.sh +25 -0
- package/actionengine/network/server/startwss.bat +27 -0
- package/actionengine/sound/audiomanager.js +1589 -0
- package/actionengine/sound/soundfont/ACTIONSOUNDFONT_README.md +205 -0
- package/actionengine/sound/soundfont/actionparser.js +718 -0
- package/actionengine/sound/soundfont/actionreverb.js +252 -0
- package/actionengine/sound/soundfont/actionsoundfont.js +543 -0
- package/actionengine/sound/soundfont/sf2playerlicence.txt +29 -0
- package/actionengine/sound/soundfont/soundfont.js +2 -0
- package/dist/action-engine.min.js +328 -0
- 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
|