aether-engine 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -0
- package/biome.json +51 -0
- package/bun.lock +192 -0
- package/index.ts +1 -0
- package/package.json +25 -0
- package/serve.ts +125 -0
- package/src/audio/AudioEngine.ts +61 -0
- package/src/components/Animator3D.ts +65 -0
- package/src/components/AudioSource.ts +26 -0
- package/src/components/BitmapText.ts +25 -0
- package/src/components/Camera.ts +33 -0
- package/src/components/CameraFollow.ts +5 -0
- package/src/components/Collider.ts +16 -0
- package/src/components/Components.test.ts +68 -0
- package/src/components/Light.ts +15 -0
- package/src/components/MeshRenderer.ts +58 -0
- package/src/components/ParticleEmitter.ts +59 -0
- package/src/components/RigidBody.ts +9 -0
- package/src/components/ShadowCaster.ts +3 -0
- package/src/components/SkinnedMeshRenderer.ts +25 -0
- package/src/components/SpriteAnimator.ts +42 -0
- package/src/components/SpriteRenderer.ts +26 -0
- package/src/components/Transform.test.ts +39 -0
- package/src/components/Transform.ts +54 -0
- package/src/core/AssetManager.ts +123 -0
- package/src/core/Input.test.ts +67 -0
- package/src/core/Input.ts +94 -0
- package/src/core/Scene.ts +24 -0
- package/src/core/SceneManager.ts +57 -0
- package/src/core/Storage.ts +161 -0
- package/src/desktop/SteamClient.ts +52 -0
- package/src/ecs/System.ts +11 -0
- package/src/ecs/World.test.ts +29 -0
- package/src/ecs/World.ts +149 -0
- package/src/index.ts +115 -0
- package/src/math/Color.ts +100 -0
- package/src/math/Vector2.ts +96 -0
- package/src/math/Vector3.ts +103 -0
- package/src/math/math.test.ts +168 -0
- package/src/renderer/GlowMaterial.ts +66 -0
- package/src/renderer/LitMaterial.ts +337 -0
- package/src/renderer/Material.test.ts +23 -0
- package/src/renderer/Material.ts +80 -0
- package/src/renderer/OcclusionMaterial.ts +43 -0
- package/src/renderer/ParticleMaterial.ts +66 -0
- package/src/renderer/Shader.ts +44 -0
- package/src/renderer/SkinnedLitMaterial.ts +55 -0
- package/src/renderer/WaterMaterial.ts +298 -0
- package/src/renderer/WebGLRenderer.ts +917 -0
- package/src/systems/Animation3DSystem.ts +148 -0
- package/src/systems/AnimationSystem.ts +58 -0
- package/src/systems/AudioSystem.ts +62 -0
- package/src/systems/LightingSystem.ts +114 -0
- package/src/systems/ParticleSystem.ts +278 -0
- package/src/systems/PhysicsSystem.ts +211 -0
- package/src/systems/Systems.test.ts +165 -0
- package/src/systems/TextSystem.ts +153 -0
- package/src/ui/AnimationEditor.tsx +639 -0
- package/src/ui/BottomPanel.tsx +443 -0
- package/src/ui/EntityExplorer.tsx +420 -0
- package/src/ui/GameState.ts +286 -0
- package/src/ui/Icons.tsx +239 -0
- package/src/ui/InventoryPanel.tsx +335 -0
- package/src/ui/PlayerHUD.tsx +250 -0
- package/src/ui/SpriteEditor.tsx +3241 -0
- package/src/ui/SpriteSheetManager.tsx +198 -0
- package/src/utils/GLTFLoader.ts +257 -0
- package/src/utils/ObjLoader.ts +81 -0
- package/src/utils/idb.ts +137 -0
- package/src/utils/packer.ts +85 -0
- package/test_obj.ts +12 -0
- package/tsconfig.json +21 -0
- package/tsconfig.tsbuildinfo +1 -0
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
import { Material } from "./Material";
|
|
2
|
+
|
|
3
|
+
export const WATER_SPRITE_VS = `
|
|
4
|
+
attribute vec4 aVertexPosition;
|
|
5
|
+
attribute vec2 aTextureCoord;
|
|
6
|
+
attribute vec3 aVertexNormal;
|
|
7
|
+
|
|
8
|
+
uniform mat4 uModelMatrix;
|
|
9
|
+
uniform mat4 uModelViewMatrix;
|
|
10
|
+
uniform mat4 uProjectionMatrix;
|
|
11
|
+
uniform vec2 uTexOffset;
|
|
12
|
+
uniform vec2 uTexScale;
|
|
13
|
+
uniform highp float uTime; // Passed automatically by WebGLRenderer
|
|
14
|
+
|
|
15
|
+
varying highp vec2 vTextureCoord;
|
|
16
|
+
varying highp vec3 vWorldPos;
|
|
17
|
+
varying highp vec3 vNormal;
|
|
18
|
+
|
|
19
|
+
varying highp vec2 vMinUV;
|
|
20
|
+
varying highp vec2 vMaxUV;
|
|
21
|
+
|
|
22
|
+
void main(void) {
|
|
23
|
+
// We strictly avoid vertex displacement because boundary tiles contain structural
|
|
24
|
+
// ground graphics (grass) which should never physically undulate.
|
|
25
|
+
vec4 displacedPos = aVertexPosition;
|
|
26
|
+
|
|
27
|
+
gl_Position = uProjectionMatrix * uModelViewMatrix * displacedPos;
|
|
28
|
+
vTextureCoord = (aTextureCoord * uTexScale) + uTexOffset;
|
|
29
|
+
|
|
30
|
+
vMinUV = uTexOffset;
|
|
31
|
+
vMaxUV = uTexOffset + uTexScale;
|
|
32
|
+
|
|
33
|
+
vWorldPos = (uModelMatrix * displacedPos).xyz;
|
|
34
|
+
vNormal = normalize(mat3(uModelMatrix) * aVertexNormal);
|
|
35
|
+
}
|
|
36
|
+
`;
|
|
37
|
+
|
|
38
|
+
export const WATER_SPRITE_FS = `
|
|
39
|
+
precision mediump float;
|
|
40
|
+
|
|
41
|
+
varying highp vec2 vTextureCoord;
|
|
42
|
+
varying highp vec3 vWorldPos;
|
|
43
|
+
varying highp vec3 vNormal;
|
|
44
|
+
|
|
45
|
+
uniform sampler2D uSampler;
|
|
46
|
+
uniform lowp vec4 uColor;
|
|
47
|
+
|
|
48
|
+
// Lighting Uniforms
|
|
49
|
+
uniform lowp vec3 uAmbientColor;
|
|
50
|
+
uniform int uActivePointLights;
|
|
51
|
+
uniform highp vec3 uLightPositions[16];
|
|
52
|
+
uniform lowp vec3 uLightColors[16];
|
|
53
|
+
uniform highp vec4 uLightParams[16];
|
|
54
|
+
|
|
55
|
+
uniform sampler2D uOcclusionSampler;
|
|
56
|
+
uniform highp vec2 uResolution;
|
|
57
|
+
uniform highp mat4 uViewMatrix;
|
|
58
|
+
uniform highp mat4 uProjectionMatrix;
|
|
59
|
+
|
|
60
|
+
uniform int uActiveParticleLights;
|
|
61
|
+
uniform highp vec4 uParticleLightPositions[64];
|
|
62
|
+
uniform lowp vec3 uParticleLightColors[64];
|
|
63
|
+
|
|
64
|
+
uniform highp float uTime;
|
|
65
|
+
|
|
66
|
+
varying highp vec2 vMinUV;
|
|
67
|
+
varying highp vec2 vMaxUV;
|
|
68
|
+
|
|
69
|
+
float IGN(highp vec2 fragCoord) {
|
|
70
|
+
vec3 magic = vec3(0.06711056, 0.00583715, 52.9829189);
|
|
71
|
+
return fract(magic.z * fract(dot(fragCoord, magic.xy)));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
float getFlicker(float time, vec3 pos) {
|
|
75
|
+
float localTimeOffset = pos.x * 137.0 + pos.y * 311.0;
|
|
76
|
+
float absoluteTime = time + localTimeOffset;
|
|
77
|
+
float decasecond = floor(absoluteTime * 0.1);
|
|
78
|
+
float dice = fract(sin(decasecond * 12.9898 + pos.x * 78.233) * 43758.5453);
|
|
79
|
+
if (dice < 0.015) {
|
|
80
|
+
float localProgress = fract(absoluteTime * 0.1) * 10.0;
|
|
81
|
+
if (localProgress < 1.0) {
|
|
82
|
+
float noise = fract(sin(time * 50.0 + pos.y) * 43758.5453);
|
|
83
|
+
float envelope = 1.0 - localProgress;
|
|
84
|
+
return 1.0 - (noise * 0.7 * envelope);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return 1.0;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
void main(void) {
|
|
91
|
+
vec4 texColor = texture2D(uSampler, vTextureCoord);
|
|
92
|
+
if (texColor.a < 0.05) discard;
|
|
93
|
+
|
|
94
|
+
// Smooth boundary mask extracting pure water geometry from mixed grass-edge sprites
|
|
95
|
+
float isWaterMask = clamp((texColor.b - max(texColor.r, texColor.g)) * 10.0, 0.0, 1.0);
|
|
96
|
+
|
|
97
|
+
// Small, Soft Water Ripple Effect
|
|
98
|
+
// Reconstruct the physical texture distortion with higher frequencies
|
|
99
|
+
// to simulate gentle little ripples rather than large undulating swells.
|
|
100
|
+
float timeX = uTime * 2.0;
|
|
101
|
+
float timeY = uTime * 1.5;
|
|
102
|
+
|
|
103
|
+
float wave1 = sin(vWorldPos.x * 12.0 + vWorldPos.y * 8.0 + timeX);
|
|
104
|
+
float wave2 = cos(vWorldPos.x * -9.0 + vWorldPos.y * 14.0 - timeY);
|
|
105
|
+
float wave3 = sin(vWorldPos.x * 15.0 + vWorldPos.y * -6.0 + timeX * 1.2);
|
|
106
|
+
|
|
107
|
+
float uvWaveX = (wave1 + wave2 * 0.5) * 0.015;
|
|
108
|
+
float uvWaveY = (wave2 + wave3 * 0.5) * 0.015;
|
|
109
|
+
|
|
110
|
+
// Safely constrain UV distortion to stay within the pure pixel boundaries of this specific sprite instance
|
|
111
|
+
// A 16x16 tile means 1 texel is 1/16th of the UV scale. 0.5 of that is a perfectly safe half-texel inset!
|
|
112
|
+
vec2 safeInset = (vMaxUV - vMinUV) / 16.0 * 0.5;
|
|
113
|
+
vec2 distortedUV = clamp(vTextureCoord + vec2(uvWaveX, uvWaveY), vMinUV + safeInset, vMaxUV - safeInset);
|
|
114
|
+
vec4 displacedColor = texture2D(uSampler, distortedUV);
|
|
115
|
+
|
|
116
|
+
// A brilliant, dynamic solution:
|
|
117
|
+
// Blend smoothly from the rigidly perfect static edges (grass borders) into the fully undulating
|
|
118
|
+
// water interior. This mathematically destroys edge-tearing gaps while preserving liquid physics.
|
|
119
|
+
vec4 baseColor = mix(texColor, displacedColor, isWaterMask) * uColor;
|
|
120
|
+
|
|
121
|
+
vec3 lightAdded = vec3(0.0);
|
|
122
|
+
vec3 specularAdded = vec3(0.0);
|
|
123
|
+
vec2 fragUV = gl_FragCoord.xy / uResolution;
|
|
124
|
+
|
|
125
|
+
float is2D = uProjectionMatrix[3][3] > 0.5 ? 1.0 : 0.0;
|
|
126
|
+
|
|
127
|
+
vec3 norm = normalize(vNormal);
|
|
128
|
+
if (is2D > 0.5) {
|
|
129
|
+
norm = vec3(0.0, 0.0, 1.0);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
vec3 viewDir = normalize(vec3(0.0, 0.707, 0.707));
|
|
133
|
+
|
|
134
|
+
// A. CORE POINT LIGHTS
|
|
135
|
+
for (int i = 0; i < 16; i++) {
|
|
136
|
+
if (i >= uActivePointLights) break;
|
|
137
|
+
|
|
138
|
+
vec3 lightPos = uLightPositions[i];
|
|
139
|
+
vec3 lightColor = uLightColors[i];
|
|
140
|
+
float intensity = uLightParams[i].x;
|
|
141
|
+
float radius = uLightParams[i].y;
|
|
142
|
+
float height = uLightParams[i].z;
|
|
143
|
+
float flickerIntensity = uLightParams[i].w;
|
|
144
|
+
|
|
145
|
+
vec3 physPos = vWorldPos;
|
|
146
|
+
|
|
147
|
+
if (is2D > 0.5) {
|
|
148
|
+
if (vWorldPos.z > -4.0) {
|
|
149
|
+
float trueBaseY = -vWorldPos.z * 100.0;
|
|
150
|
+
float pixelElevation = max(0.0, vWorldPos.y - trueBaseY);
|
|
151
|
+
physPos = vec3(vWorldPos.x, trueBaseY, pixelElevation);
|
|
152
|
+
} else {
|
|
153
|
+
physPos = vec3(vWorldPos.x, vWorldPos.y, 0.0);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
vec3 diff3D = lightPos - physPos;
|
|
158
|
+
float distSq = dot(diff3D, diff3D);
|
|
159
|
+
float radiusSq = radius * radius;
|
|
160
|
+
|
|
161
|
+
if (distSq < radiusSq) {
|
|
162
|
+
float dist = max(0.001, sqrt(distSq));
|
|
163
|
+
vec3 lightDir = diff3D / dist;
|
|
164
|
+
float normDist = dist / radius;
|
|
165
|
+
|
|
166
|
+
float diffuse = dot(norm, lightDir) * 0.5 + 0.5;
|
|
167
|
+
vec3 specular = vec3(0.0);
|
|
168
|
+
|
|
169
|
+
if (is2D > 0.5) {
|
|
170
|
+
if (vWorldPos.z < -4.0) {
|
|
171
|
+
// Extra specular glints for water surface interacting with geometry offsets!
|
|
172
|
+
float bump = fract(sin(dot(floor(vWorldPos.xy * 16.0), vec2(12.9898, 78.233))) * 43758.5453);
|
|
173
|
+
diffuse = mix(0.9, 0.7 + bump * 0.4, clamp(1.0 - normDist, 0.0, 1.0));
|
|
174
|
+
|
|
175
|
+
// WATER SPECIFIC HIGHLIGHTS
|
|
176
|
+
vec3 pHalfVector = normalize(lightDir + viewDir);
|
|
177
|
+
float pSpec = pow(max(dot(norm, pHalfVector), 0.0), 12.0);
|
|
178
|
+
specularAdded += pSpec * lightColor * intensity * 0.5 * (1.0 - normDist) * isWaterMask;
|
|
179
|
+
} else {
|
|
180
|
+
diffuse = 0.6 + (dot(vec3(0,0,1), lightDir) * 0.4);
|
|
181
|
+
}
|
|
182
|
+
} else {
|
|
183
|
+
vec3 halfVector = normalize(lightDir + viewDir);
|
|
184
|
+
float spec = pow(max(dot(norm, halfVector), 0.0), 16.0);
|
|
185
|
+
specularAdded += spec * lightColor * intensity * 0.8;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
float attenuation = max(0.0, (exp(-normDist * normDist * 4.0) - 0.0183) / 0.9817);
|
|
189
|
+
|
|
190
|
+
if (is2D > 0.5) {
|
|
191
|
+
float spotlightCone = smoothstep(0.55, 0.85, lightDir.z);
|
|
192
|
+
attenuation *= spotlightCone;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
float flickerAmount = getFlicker(uTime, lightPos);
|
|
196
|
+
attenuation *= mix(1.0, flickerAmount, flickerIntensity);
|
|
197
|
+
|
|
198
|
+
float shadowMultiplier = 1.0;
|
|
199
|
+
|
|
200
|
+
if (is2D < 0.5) {
|
|
201
|
+
vec4 lClip = uProjectionMatrix * uViewMatrix * vec4(lightPos, 1.0);
|
|
202
|
+
vec2 lightScreenUV = (lClip.xy / lClip.w) * 0.5 + 0.5;
|
|
203
|
+
|
|
204
|
+
vec2 rayVec = lightScreenUV - fragUV;
|
|
205
|
+
float stepsNum = 12.0;
|
|
206
|
+
vec2 stepUV = rayVec / stepsNum;
|
|
207
|
+
|
|
208
|
+
vec2 sampleUV = fragUV;
|
|
209
|
+
sampleUV += stepUV * (IGN(gl_FragCoord.xy) - 0.5);
|
|
210
|
+
|
|
211
|
+
for (int k = 0; k < 12; k++) {
|
|
212
|
+
sampleUV += stepUV;
|
|
213
|
+
if (sampleUV.x < 0.0 || sampleUV.x > 1.0 || sampleUV.y < 0.0 || sampleUV.y > 1.0) {
|
|
214
|
+
break;
|
|
215
|
+
}
|
|
216
|
+
vec4 occData = texture2D(uOcclusionSampler, sampleUV);
|
|
217
|
+
float isOccluder = 1.0 - occData.b;
|
|
218
|
+
|
|
219
|
+
if (isOccluder > 0.5) {
|
|
220
|
+
float normZ = occData.r + occData.g * (1.0/255.0);
|
|
221
|
+
float occluderZ = normZ * 20.0 - 10.0;
|
|
222
|
+
if (occluderZ > vWorldPos.z + 0.005) {
|
|
223
|
+
shadowMultiplier = 0.0;
|
|
224
|
+
break;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
lightAdded += lightColor * intensity * attenuation * diffuse * shadowMultiplier;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// B. SECONDARY PARTICLE LIGHTS
|
|
235
|
+
vec3 particleAdded = vec3(0.0);
|
|
236
|
+
|
|
237
|
+
for (int j = 0; j < 64; j++) {
|
|
238
|
+
if (j >= uActiveParticleLights) break;
|
|
239
|
+
|
|
240
|
+
vec3 pLightPos = uParticleLightPositions[j].xyz;
|
|
241
|
+
float pRadius = uParticleLightPositions[j].w;
|
|
242
|
+
vec3 pLightColor = uParticleLightColors[j];
|
|
243
|
+
|
|
244
|
+
vec3 posToLight = vWorldPos;
|
|
245
|
+
if (is2D > 0.5) {
|
|
246
|
+
posToLight = floor(vWorldPos * 16.0) / 16.0;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
vec3 pDiff3D = pLightPos - posToLight;
|
|
250
|
+
if (is2D > 0.5) pDiff3D.z = 0.5;
|
|
251
|
+
|
|
252
|
+
float pDistSq = dot(pDiff3D, pDiff3D);
|
|
253
|
+
float pRadiusSq = pRadius * pRadius;
|
|
254
|
+
|
|
255
|
+
if (pDistSq < pRadiusSq) {
|
|
256
|
+
float pDist = sqrt(pDistSq);
|
|
257
|
+
vec3 pLightDir = pDiff3D / pDist;
|
|
258
|
+
|
|
259
|
+
float pDiffuse = dot(norm, pLightDir) * 0.5 + 0.5;
|
|
260
|
+
if (is2D > 0.5) pDiffuse = 1.0;
|
|
261
|
+
|
|
262
|
+
float pNormDist = pDist / pRadius;
|
|
263
|
+
float pAtten = max(0.0, (exp(-pNormDist * pNormDist * 4.0) - 0.0183) / 0.9817);
|
|
264
|
+
|
|
265
|
+
if (is2D > 0.5) pAtten = floor(pAtten * 6.0) / 6.0;
|
|
266
|
+
|
|
267
|
+
particleAdded += pLightColor * pAtten * pDiffuse;
|
|
268
|
+
|
|
269
|
+
vec3 pHalfVector = normalize(pLightDir + viewDir);
|
|
270
|
+
float pSpec = pow(max(dot(norm, pHalfVector), 0.0), 32.0);
|
|
271
|
+
specularAdded += pSpec * pLightColor * pAtten * 0.5;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
vec3 shadedBase = baseColor.rgb * uAmbientColor;
|
|
276
|
+
vec3 mappedLight = baseColor.rgb * lightAdded;
|
|
277
|
+
|
|
278
|
+
vec3 ambientGlow = vec3(0.0);
|
|
279
|
+
if (is2D > 0.5 && vWorldPos.z < -4.0) {
|
|
280
|
+
vec3 safeGlowEnergy = min(lightAdded, vec3(1.1));
|
|
281
|
+
ambientGlow = safeGlowEnergy * (vec3(1.0) - baseColor.rgb) * 0.9;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
vec3 litColor = shadedBase + mappedLight + (baseColor.rgb * particleAdded) + specularAdded + ambientGlow + (particleAdded * 0.1);
|
|
285
|
+
litColor = clamp(litColor, 0.0, 1.0);
|
|
286
|
+
|
|
287
|
+
float dither = IGN(gl_FragCoord.xy) * (1.0 / 255.0);
|
|
288
|
+
litColor += dither - (0.5 / 255.0);
|
|
289
|
+
|
|
290
|
+
gl_FragColor = vec4(litColor, baseColor.a);
|
|
291
|
+
}
|
|
292
|
+
`;
|
|
293
|
+
|
|
294
|
+
export class WaterMaterial extends Material {
|
|
295
|
+
constructor() {
|
|
296
|
+
super(WATER_SPRITE_VS, WATER_SPRITE_FS);
|
|
297
|
+
}
|
|
298
|
+
}
|