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,100 @@
|
|
|
1
|
+
import { vec4 } from "gl-matrix";
|
|
2
|
+
|
|
3
|
+
export class Color {
|
|
4
|
+
public raw: vec4;
|
|
5
|
+
|
|
6
|
+
constructor(
|
|
7
|
+
r: number = 255,
|
|
8
|
+
g: number = 255,
|
|
9
|
+
b: number = 255,
|
|
10
|
+
a: number = 255,
|
|
11
|
+
) {
|
|
12
|
+
this.raw = vec4.fromValues(r / 255, g / 255, b / 255, a / 255);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
get r() {
|
|
16
|
+
return this.raw[0] * 255;
|
|
17
|
+
}
|
|
18
|
+
set r(v) {
|
|
19
|
+
this.raw[0] = v / 255;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
get g() {
|
|
23
|
+
return this.raw[1] * 255;
|
|
24
|
+
}
|
|
25
|
+
set g(v) {
|
|
26
|
+
this.raw[1] = v / 255;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
get b() {
|
|
30
|
+
return this.raw[2] * 255;
|
|
31
|
+
}
|
|
32
|
+
set b(v) {
|
|
33
|
+
this.raw[2] = v / 255;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
get a() {
|
|
37
|
+
return this.raw[3] * 255;
|
|
38
|
+
}
|
|
39
|
+
set a(v) {
|
|
40
|
+
this.raw[3] = v / 255;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
get rNorm() {
|
|
44
|
+
return this.raw[0];
|
|
45
|
+
}
|
|
46
|
+
get gNorm() {
|
|
47
|
+
return this.raw[1];
|
|
48
|
+
}
|
|
49
|
+
get bNorm() {
|
|
50
|
+
return this.raw[2];
|
|
51
|
+
}
|
|
52
|
+
get aNorm() {
|
|
53
|
+
return this.raw[3];
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
set(r: number, g: number, b: number, a: number = 255): this {
|
|
57
|
+
vec4.set(this.raw, r / 255, g / 255, b / 255, a / 255);
|
|
58
|
+
return this;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
clone(): Color {
|
|
62
|
+
return new Color(this.r, this.g, this.b, this.a);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
static Red() {
|
|
66
|
+
return new Color(255, 0, 0, 255);
|
|
67
|
+
}
|
|
68
|
+
static Green() {
|
|
69
|
+
return new Color(0, 255, 0, 255);
|
|
70
|
+
}
|
|
71
|
+
static Blue() {
|
|
72
|
+
return new Color(0, 0, 255, 255);
|
|
73
|
+
}
|
|
74
|
+
static White() {
|
|
75
|
+
return new Color(255, 255, 255, 255);
|
|
76
|
+
}
|
|
77
|
+
static Black() {
|
|
78
|
+
return new Color(0, 0, 0, 255);
|
|
79
|
+
}
|
|
80
|
+
static Transparent() {
|
|
81
|
+
return new Color(0, 0, 0, 0);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
static fromHex(hex: string): Color {
|
|
85
|
+
let h = hex.replace("#", "");
|
|
86
|
+
if (h.length === 3)
|
|
87
|
+
h = h
|
|
88
|
+
.split("")
|
|
89
|
+
.map((c) => c + c)
|
|
90
|
+
.join("");
|
|
91
|
+
if (h.length === 6) h += "FF";
|
|
92
|
+
const val = parseInt(h, 16);
|
|
93
|
+
return new Color(
|
|
94
|
+
(val >> 24) & 255,
|
|
95
|
+
(val >> 16) & 255,
|
|
96
|
+
(val >> 8) & 255,
|
|
97
|
+
val & 255,
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { vec2 } from "gl-matrix";
|
|
2
|
+
|
|
3
|
+
export class Vector2 {
|
|
4
|
+
public raw: vec2;
|
|
5
|
+
|
|
6
|
+
private static _pool: Vector2[] = [];
|
|
7
|
+
|
|
8
|
+
public static obtain(x: number = 0, y: number = 0): Vector2 {
|
|
9
|
+
if (Vector2._pool.length > 0) {
|
|
10
|
+
return Vector2._pool.pop()?.set(x, y);
|
|
11
|
+
}
|
|
12
|
+
return new Vector2(x, y);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
public free() {
|
|
16
|
+
Vector2._pool.push(this);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
constructor(x: number = 0, y: number = 0) {
|
|
20
|
+
this.raw = vec2.fromValues(x, y);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
get x() {
|
|
24
|
+
return this.raw[0];
|
|
25
|
+
}
|
|
26
|
+
set x(v) {
|
|
27
|
+
this.raw[0] = v;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
get y() {
|
|
31
|
+
return this.raw[1];
|
|
32
|
+
}
|
|
33
|
+
set y(v) {
|
|
34
|
+
this.raw[1] = v;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
set(x: number, y: number): this {
|
|
38
|
+
vec2.set(this.raw, x, y);
|
|
39
|
+
return this;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
copy(other: Vector2): this {
|
|
43
|
+
vec2.copy(this.raw, other.raw);
|
|
44
|
+
return this;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
add(other: Vector2): this {
|
|
48
|
+
vec2.add(this.raw, this.raw, other.raw);
|
|
49
|
+
return this;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
sub(other: Vector2): this {
|
|
53
|
+
vec2.sub(this.raw, this.raw, other.raw);
|
|
54
|
+
return this;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
multiply(other: Vector2): this {
|
|
58
|
+
vec2.multiply(this.raw, this.raw, other.raw);
|
|
59
|
+
return this;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
divide(other: Vector2): this {
|
|
63
|
+
vec2.divide(this.raw, this.raw, other.raw);
|
|
64
|
+
return this;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
scale(scalar: number): this {
|
|
68
|
+
vec2.scale(this.raw, this.raw, scalar);
|
|
69
|
+
return this;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
distance(other: Vector2): number {
|
|
73
|
+
return vec2.dist(this.raw, other.raw);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
magnitude(): number {
|
|
77
|
+
return vec2.length(this.raw);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
length(): number {
|
|
81
|
+
return vec2.length(this.raw);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
normalize(): this {
|
|
85
|
+
vec2.normalize(this.raw, this.raw);
|
|
86
|
+
return this;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
clone(): Vector2 {
|
|
90
|
+
return new Vector2(this.x, this.y);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
static Zero(): Vector2 {
|
|
94
|
+
return new Vector2(0, 0);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { vec3 } from "gl-matrix";
|
|
2
|
+
|
|
3
|
+
export class Vector3 {
|
|
4
|
+
public raw: vec3;
|
|
5
|
+
|
|
6
|
+
private static _pool: Vector3[] = [];
|
|
7
|
+
|
|
8
|
+
public static obtain(x: number = 0, y: number = 0, z: number = 0): Vector3 {
|
|
9
|
+
if (Vector3._pool.length > 0) {
|
|
10
|
+
return Vector3._pool.pop()?.set(x, y, z);
|
|
11
|
+
}
|
|
12
|
+
return new Vector3(x, y, z);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
public free() {
|
|
16
|
+
Vector3._pool.push(this);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
constructor(x: number = 0, y: number = 0, z: number = 0) {
|
|
20
|
+
this.raw = vec3.fromValues(x, y, z);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
get x() {
|
|
24
|
+
return this.raw[0];
|
|
25
|
+
}
|
|
26
|
+
set x(v) {
|
|
27
|
+
this.raw[0] = v;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
get y() {
|
|
31
|
+
return this.raw[1];
|
|
32
|
+
}
|
|
33
|
+
set y(v) {
|
|
34
|
+
this.raw[1] = v;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
get z() {
|
|
38
|
+
return this.raw[2];
|
|
39
|
+
}
|
|
40
|
+
set z(v) {
|
|
41
|
+
this.raw[2] = v;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
set(x: number, y: number, z: number): this {
|
|
45
|
+
vec3.set(this.raw, x, y, z);
|
|
46
|
+
return this;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
copy(other: Vector3): this {
|
|
50
|
+
vec3.copy(this.raw, other.raw);
|
|
51
|
+
return this;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
add(other: Vector3): this {
|
|
55
|
+
vec3.add(this.raw, this.raw, other.raw);
|
|
56
|
+
return this;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
sub(other: Vector3): this {
|
|
60
|
+
vec3.sub(this.raw, this.raw, other.raw);
|
|
61
|
+
return this;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
multiply(other: Vector3): this {
|
|
65
|
+
vec3.multiply(this.raw, this.raw, other.raw);
|
|
66
|
+
return this;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
divide(other: Vector3): this {
|
|
70
|
+
vec3.divide(this.raw, this.raw, other.raw);
|
|
71
|
+
return this;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
scale(scalar: number): this {
|
|
75
|
+
vec3.scale(this.raw, this.raw, scalar);
|
|
76
|
+
return this;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
distance(other: Vector3): number {
|
|
80
|
+
return vec3.dist(this.raw, other.raw);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
magnitude(): number {
|
|
84
|
+
return vec3.length(this.raw);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
length(): number {
|
|
88
|
+
return vec3.length(this.raw);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
normalize(): this {
|
|
92
|
+
vec3.normalize(this.raw, this.raw);
|
|
93
|
+
return this;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
clone(): Vector3 {
|
|
97
|
+
return new Vector3(this.x, this.y, this.z);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
static Zero(): Vector3 {
|
|
101
|
+
return new Vector3(0, 0, 0);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import { Color } from "./Color";
|
|
3
|
+
import { Vector2 } from "./Vector2";
|
|
4
|
+
import { Vector3 } from "./Vector3";
|
|
5
|
+
|
|
6
|
+
describe("Math API", () => {
|
|
7
|
+
test("Vector2 object pooling", () => {
|
|
8
|
+
const v1 = Vector2.obtain(10, 20);
|
|
9
|
+
expect(v1.x).toBe(10);
|
|
10
|
+
expect(v1.y).toBe(20);
|
|
11
|
+
|
|
12
|
+
v1.free();
|
|
13
|
+
|
|
14
|
+
const v2 = Vector2.obtain(5, 5);
|
|
15
|
+
expect(v2).toBe(v1); // Should be the same object reference
|
|
16
|
+
expect(v2.x).toBe(5);
|
|
17
|
+
expect(v2.y).toBe(5);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
test("Vector3 chaining and operations", () => {
|
|
21
|
+
const v = new Vector3(1, 2, 3);
|
|
22
|
+
const result = v.add(new Vector3(1, 1, 1)).scale(2);
|
|
23
|
+
|
|
24
|
+
expect(result).toBe(v); // Chain should return itself
|
|
25
|
+
expect(v.x).toBe(4);
|
|
26
|
+
expect(v.y).toBe(6);
|
|
27
|
+
expect(v.z).toBe(8);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test("Color normalization", () => {
|
|
31
|
+
const c = new Color(255, 127.5, 0, 255);
|
|
32
|
+
expect(c.rNorm).toBe(1);
|
|
33
|
+
expect(c.gNorm).toBeCloseTo(0.5);
|
|
34
|
+
expect(c.bNorm).toBe(0);
|
|
35
|
+
expect(c.aNorm).toBe(1);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test("Color statics and hex parsing", () => {
|
|
39
|
+
const c1 = Color.White();
|
|
40
|
+
expect(c1.r).toBe(255);
|
|
41
|
+
const c2 = Color.Black();
|
|
42
|
+
expect(c2.r).toBe(0);
|
|
43
|
+
|
|
44
|
+
const c3 = Color.fromHex("#FF00FF");
|
|
45
|
+
expect(c3.r).toBe(255);
|
|
46
|
+
expect(c3.g).toBe(0);
|
|
47
|
+
expect(c3.b).toBe(255);
|
|
48
|
+
|
|
49
|
+
const c4 = Color.fromHex("00ff00ff");
|
|
50
|
+
expect(c4.g).toBe(255);
|
|
51
|
+
|
|
52
|
+
c4.r = 100;
|
|
53
|
+
c4.g = 100;
|
|
54
|
+
c4.b = 100;
|
|
55
|
+
c4.a = 100;
|
|
56
|
+
expect(c4.r).toBeCloseTo(100);
|
|
57
|
+
expect(c4.g).toBeCloseTo(100);
|
|
58
|
+
expect(c4.b).toBeCloseTo(100);
|
|
59
|
+
expect(c4.a).toBeCloseTo(100);
|
|
60
|
+
|
|
61
|
+
c4.set(50, 50, 50, 50);
|
|
62
|
+
expect(c4.r).toBeCloseTo(50);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test("Vector2 math operations", () => {
|
|
66
|
+
const v1 = new Vector2(10, 10);
|
|
67
|
+
const v2 = new Vector2(2, 2);
|
|
68
|
+
|
|
69
|
+
v1.add(v2);
|
|
70
|
+
expect(v1.x).toBe(12);
|
|
71
|
+
|
|
72
|
+
v1.sub(v2);
|
|
73
|
+
expect(v1.x).toBe(10);
|
|
74
|
+
|
|
75
|
+
v1.multiply(v2);
|
|
76
|
+
expect(v1.x).toBe(20);
|
|
77
|
+
|
|
78
|
+
v1.divide(v2);
|
|
79
|
+
expect(v1.x).toBe(10);
|
|
80
|
+
|
|
81
|
+
v1.scale(0.5);
|
|
82
|
+
expect(v1.x).toBe(5);
|
|
83
|
+
|
|
84
|
+
const len = v1.length();
|
|
85
|
+
expect(len).toBeCloseTo(Math.sqrt(50));
|
|
86
|
+
|
|
87
|
+
const mag = v1.magnitude();
|
|
88
|
+
expect(mag).toBe(len);
|
|
89
|
+
|
|
90
|
+
v1.normalize();
|
|
91
|
+
expect(v1.x).toBeCloseTo(Math.SQRT1_2);
|
|
92
|
+
|
|
93
|
+
const dist = v1.distance(new Vector2(0, 0));
|
|
94
|
+
expect(dist).toBeCloseTo(1);
|
|
95
|
+
|
|
96
|
+
const clone = v1.clone();
|
|
97
|
+
expect(clone.x).toBe(v1.x);
|
|
98
|
+
|
|
99
|
+
v1.set(100, 100);
|
|
100
|
+
expect(v1.x).toBe(100);
|
|
101
|
+
expect(v1.y).toBe(100);
|
|
102
|
+
|
|
103
|
+
const v3 = new Vector2();
|
|
104
|
+
v3.x = 7;
|
|
105
|
+
v3.y = 8;
|
|
106
|
+
expect(v3.x).toBe(7);
|
|
107
|
+
expect(v3.y).toBe(8);
|
|
108
|
+
|
|
109
|
+
const v4 = new Vector2().copy(v3);
|
|
110
|
+
expect(v4.x).toBe(7);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
test("Vector3 full math operations", () => {
|
|
114
|
+
const v1 = new Vector3(10, 10, 10);
|
|
115
|
+
const v2 = new Vector3(2, 2, 2);
|
|
116
|
+
|
|
117
|
+
v1.add(v2);
|
|
118
|
+
expect(v1.x).toBe(12);
|
|
119
|
+
|
|
120
|
+
v1.sub(v2);
|
|
121
|
+
expect(v1.x).toBe(10);
|
|
122
|
+
|
|
123
|
+
v1.multiply(v2);
|
|
124
|
+
expect(v1.x).toBe(20);
|
|
125
|
+
|
|
126
|
+
v1.divide(v2);
|
|
127
|
+
expect(v1.x).toBe(10);
|
|
128
|
+
|
|
129
|
+
v1.scale(2);
|
|
130
|
+
expect(v1.x).toBe(20);
|
|
131
|
+
|
|
132
|
+
const len = v1.length();
|
|
133
|
+
expect(len).toBeCloseTo(Math.sqrt(1200));
|
|
134
|
+
|
|
135
|
+
const mag = v1.magnitude();
|
|
136
|
+
expect(mag).toBe(len);
|
|
137
|
+
|
|
138
|
+
v1.normalize();
|
|
139
|
+
expect(v1.x).toBeCloseTo(0.577);
|
|
140
|
+
|
|
141
|
+
expect(v1.distance(new Vector3(0, 0, 0))).toBeCloseTo(1);
|
|
142
|
+
|
|
143
|
+
v1.set(5, 5, 5);
|
|
144
|
+
expect(v1.x).toBe(5);
|
|
145
|
+
|
|
146
|
+
const clone = v1.clone();
|
|
147
|
+
expect(clone.y).toBe(v1.y);
|
|
148
|
+
|
|
149
|
+
const pooled = Vector3.obtain(1, 2, 3);
|
|
150
|
+
expect(pooled.x).toBe(1);
|
|
151
|
+
expect(pooled.y).toBe(2);
|
|
152
|
+
expect(pooled.z).toBe(3);
|
|
153
|
+
|
|
154
|
+
pooled.x = 9;
|
|
155
|
+
pooled.y = 8;
|
|
156
|
+
pooled.z = 7;
|
|
157
|
+
expect(pooled.x).toBe(9);
|
|
158
|
+
expect(pooled.y).toBe(8);
|
|
159
|
+
expect(pooled.z).toBe(7);
|
|
160
|
+
|
|
161
|
+
const copyCat = new Vector3().copy(pooled);
|
|
162
|
+
expect(copyCat.z).toBe(7);
|
|
163
|
+
|
|
164
|
+
pooled.free();
|
|
165
|
+
const p2 = Vector3.obtain(2, 2, 2);
|
|
166
|
+
expect(p2).toBe(pooled);
|
|
167
|
+
});
|
|
168
|
+
});
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { Material } from "./Material";
|
|
2
|
+
|
|
3
|
+
export const GLOW_VS = `
|
|
4
|
+
attribute vec4 aVertexPosition;
|
|
5
|
+
attribute vec2 aTextureCoord;
|
|
6
|
+
|
|
7
|
+
uniform mat4 uModelMatrix;
|
|
8
|
+
uniform mat4 uModelViewMatrix;
|
|
9
|
+
uniform mat4 uProjectionMatrix;
|
|
10
|
+
|
|
11
|
+
varying highp vec2 vTextureCoord;
|
|
12
|
+
|
|
13
|
+
void main(void) {
|
|
14
|
+
gl_Position = uProjectionMatrix * uModelViewMatrix * aVertexPosition;
|
|
15
|
+
vTextureCoord = aTextureCoord;
|
|
16
|
+
}
|
|
17
|
+
`;
|
|
18
|
+
|
|
19
|
+
export const GLOW_FS = `
|
|
20
|
+
precision mediump float;
|
|
21
|
+
|
|
22
|
+
varying highp vec2 vTextureCoord;
|
|
23
|
+
uniform lowp vec4 uColor;
|
|
24
|
+
|
|
25
|
+
void main(void) {
|
|
26
|
+
// Distance from the center of the quad
|
|
27
|
+
vec2 center = vec2(0.5, 0.5);
|
|
28
|
+
float dist = distance(vTextureCoord, center);
|
|
29
|
+
|
|
30
|
+
// Zelda-like sharp, warm core with a very soft ambient scatter edge
|
|
31
|
+
// CRITICAL: smoothstep(0.5, 0.0) is mathematically UNDEFINED in basic GLSL.
|
|
32
|
+
// Always use edge0 < edge1, and invert algebraically!
|
|
33
|
+
float glow = 1.0 - smoothstep(0.0, 0.5, dist);
|
|
34
|
+
|
|
35
|
+
// Core hot spot for realism (toned down to prevent blinding white center)
|
|
36
|
+
// Avoid pow() completely to bypass catastrophic macOS fractional exp2 driver traps!
|
|
37
|
+
float glow2 = glow * glow;
|
|
38
|
+
float glow4 = glow2 * glow2;
|
|
39
|
+
float hotspot = glow4 * 0.7;
|
|
40
|
+
|
|
41
|
+
// Outer atmospheric scattering (approx glow^2.5)
|
|
42
|
+
float scatter = glow2 * sqrt(glow) * 0.3;
|
|
43
|
+
|
|
44
|
+
float finalAlpha = clamp((hotspot + scatter) * uColor.a, 0.0, 1.0);
|
|
45
|
+
|
|
46
|
+
// Discard completely transparent pixels to absolutely maximize fillrate hardware performance
|
|
47
|
+
if (finalAlpha < 0.01) {
|
|
48
|
+
discard;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Pre-multiply color natively because Screen blending uses (ONE, ONE_MINUS_SRC_COLOR) directly!
|
|
52
|
+
// The GL Fixed Function ALU no longer applies an implicit SRC_ALPHA weight for us.
|
|
53
|
+
gl_FragColor = vec4(uColor.rgb * finalAlpha, finalAlpha);
|
|
54
|
+
}
|
|
55
|
+
`;
|
|
56
|
+
|
|
57
|
+
export class GlowMaterial extends Material {
|
|
58
|
+
constructor() {
|
|
59
|
+
super(GLOW_VS, GLOW_FS);
|
|
60
|
+
// We dynamically use Screen blending to output additive luminosity while inherently securely capping
|
|
61
|
+
// maximum bounds at 1.0, neutralizing aggressive macOS GPU Additive integer wrap artifacts!
|
|
62
|
+
this.blendMode = "Screen";
|
|
63
|
+
this.depthTest = true; // Eclipsed by taller solid things
|
|
64
|
+
this.depthWrite = false; // Soft particles shouldn't interact natively into depth buffer
|
|
65
|
+
}
|
|
66
|
+
}
|