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,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ActionPhysicsBox3D - 3D Box Physics Object with Single Color System
|
|
3
|
+
*
|
|
4
|
+
* BREAKING CHANGE: Previously used rainbow faces by default.
|
|
5
|
+
* Now uses single green color system for consistency with other shapes.
|
|
6
|
+
*
|
|
7
|
+
* @param {ActionPhysicsWorld3D} physicsWorld - The physics world
|
|
8
|
+
* @param {number} width - Box width (default: 10)
|
|
9
|
+
* @param {number} height - Box height (default: 10)
|
|
10
|
+
* @param {number} depth - Box depth (default: 10)
|
|
11
|
+
* @param {number} mass - Physics mass (default: 1)
|
|
12
|
+
* @param {Vector3} initialPosition - Starting position (default: 0,500,0)
|
|
13
|
+
* @param {string|Array} color - Single hex color "#228B22" or array of 6 colors for faces (default: "#228B22" green)
|
|
14
|
+
*/
|
|
15
|
+
class ActionPhysicsBox3D extends ActionPhysicsObject3D {
|
|
16
|
+
constructor(physicsWorld, width = 10, height = 10, depth = 10, mass = 1,
|
|
17
|
+
initialPosition = new Vector3(0, 500, 0), color = "#228B22", options = {}) {
|
|
18
|
+
// Create visual mesh with triangles
|
|
19
|
+
const triangles = [];
|
|
20
|
+
|
|
21
|
+
// Determine what colors to use
|
|
22
|
+
let faceColors;
|
|
23
|
+
|
|
24
|
+
if (typeof color === 'string') {
|
|
25
|
+
// Single color for all faces (including default green)
|
|
26
|
+
faceColors = Array(6).fill(color);
|
|
27
|
+
} else if (Array.isArray(color)) {
|
|
28
|
+
// Array of colors provided - use them for corresponding faces
|
|
29
|
+
faceColors = color.slice(0, 6);
|
|
30
|
+
// Fill missing colors with first provided color or default green
|
|
31
|
+
const fillColor = color[0] || "#228B22";
|
|
32
|
+
while (faceColors.length < 6) {
|
|
33
|
+
faceColors.push(fillColor);
|
|
34
|
+
}
|
|
35
|
+
} else {
|
|
36
|
+
// Fallback to green for any other case
|
|
37
|
+
faceColors = Array(6).fill("#228B22");
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Helper to create face vertices
|
|
41
|
+
const createFace = (v1, v2, v3, v4, faceIndex) => {
|
|
42
|
+
triangles.push(new Triangle(v1, v2, v3, faceColors[faceIndex]));
|
|
43
|
+
triangles.push(new Triangle(v1, v3, v4, faceColors[faceIndex]));
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
// Half dimensions for vertex creation
|
|
47
|
+
const hw = width / 2;
|
|
48
|
+
const hh = height / 2;
|
|
49
|
+
const hd = depth / 2;
|
|
50
|
+
|
|
51
|
+
// Create vertices for each face
|
|
52
|
+
const vertices = {
|
|
53
|
+
frontTopLeft: new Vector3(-hw, hh, hd),
|
|
54
|
+
frontTopRight: new Vector3( hw, hh, hd),
|
|
55
|
+
frontBottomRight: new Vector3( hw, -hh, hd),
|
|
56
|
+
frontBottomLeft: new Vector3(-hw, -hh, hd),
|
|
57
|
+
backTopLeft: new Vector3(-hw, hh, -hd),
|
|
58
|
+
backTopRight: new Vector3( hw, hh, -hd),
|
|
59
|
+
backBottomRight: new Vector3( hw, -hh, -hd),
|
|
60
|
+
backBottomLeft: new Vector3(-hw, -hh, -hd)
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// Create all six faces with their respective colors
|
|
64
|
+
// Front face
|
|
65
|
+
createFace(
|
|
66
|
+
vertices.frontBottomLeft,
|
|
67
|
+
vertices.frontBottomRight,
|
|
68
|
+
vertices.frontTopRight,
|
|
69
|
+
vertices.frontTopLeft,
|
|
70
|
+
0 // Index for front face color
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
// Back face
|
|
74
|
+
createFace(
|
|
75
|
+
vertices.backBottomRight,
|
|
76
|
+
vertices.backBottomLeft,
|
|
77
|
+
vertices.backTopLeft,
|
|
78
|
+
vertices.backTopRight,
|
|
79
|
+
1 // Index for back face color
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
// Top face
|
|
83
|
+
createFace(
|
|
84
|
+
vertices.frontTopLeft,
|
|
85
|
+
vertices.frontTopRight,
|
|
86
|
+
vertices.backTopRight,
|
|
87
|
+
vertices.backTopLeft,
|
|
88
|
+
2 // Index for top face color
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
// Bottom face
|
|
92
|
+
createFace(
|
|
93
|
+
vertices.frontBottomRight,
|
|
94
|
+
vertices.frontBottomLeft,
|
|
95
|
+
vertices.backBottomLeft,
|
|
96
|
+
vertices.backBottomRight,
|
|
97
|
+
3 // Index for bottom face color
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
// Right face
|
|
101
|
+
createFace(
|
|
102
|
+
vertices.frontBottomRight,
|
|
103
|
+
vertices.backBottomRight,
|
|
104
|
+
vertices.backTopRight,
|
|
105
|
+
vertices.frontTopRight,
|
|
106
|
+
4 // Index for right face color
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
// Left face
|
|
110
|
+
createFace(
|
|
111
|
+
vertices.backBottomLeft,
|
|
112
|
+
vertices.frontBottomLeft,
|
|
113
|
+
vertices.frontTopLeft,
|
|
114
|
+
vertices.backTopLeft,
|
|
115
|
+
5 // Index for left face color
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
super(physicsWorld, triangles, options);
|
|
119
|
+
|
|
120
|
+
// Create physics shape and body
|
|
121
|
+
const shape = new Goblin.BoxShape(width/2, height/2, depth/2);
|
|
122
|
+
this.body = new Goblin.RigidBody(shape, mass);
|
|
123
|
+
this.body.position.set(
|
|
124
|
+
initialPosition.x,
|
|
125
|
+
initialPosition.y,
|
|
126
|
+
initialPosition.z
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
this.body.linear_damping = 0.01;
|
|
130
|
+
this.body.angular_damping = 0.01;
|
|
131
|
+
|
|
132
|
+
this.storeOriginalData();
|
|
133
|
+
|
|
134
|
+
// Set visibility flag
|
|
135
|
+
this.isVisible = options.isVisible !== undefined ? options.isVisible : true;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
storeOriginalData() {
|
|
139
|
+
this.originalNormals = [];
|
|
140
|
+
this.originalVerts = [];
|
|
141
|
+
|
|
142
|
+
this.triangles.forEach((triangle) => {
|
|
143
|
+
this.originalNormals.push(new Vector3(
|
|
144
|
+
triangle.normal.x,
|
|
145
|
+
triangle.normal.y,
|
|
146
|
+
triangle.normal.z
|
|
147
|
+
));
|
|
148
|
+
|
|
149
|
+
triangle.vertices.forEach((vertex) => {
|
|
150
|
+
this.originalVerts.push(new Vector3(
|
|
151
|
+
vertex.x,
|
|
152
|
+
vertex.y,
|
|
153
|
+
vertex.z
|
|
154
|
+
));
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
// actionengine/math/physics/actionphysicscapsule3D.js
|
|
2
|
+
/**
|
|
3
|
+
* ActionPhysicsCapsule3D - 3D Capsule Physics Object with Single Color System
|
|
4
|
+
*
|
|
5
|
+
* BREAKING CHANGE: Previously used two-color checkerboard pattern (color1, color2).
|
|
6
|
+
* Now uses single color system for consistent developer experience.
|
|
7
|
+
*
|
|
8
|
+
* IMPORTANT GEOMETRY CONSTRAINT:
|
|
9
|
+
* Total height MUST be greater than 2 × radius!
|
|
10
|
+
*
|
|
11
|
+
* Why? A capsule = cylinder + 2 hemisphere caps
|
|
12
|
+
* - Cylinder height = total height - (2 × radius)
|
|
13
|
+
* - If total height < 2 × radius, cylinder height becomes negative = impossible!
|
|
14
|
+
* - This constraint is enforced by the Goblin physics library
|
|
15
|
+
*
|
|
16
|
+
* @param {ActionPhysicsWorld3D} physicsWorld - The physics world
|
|
17
|
+
* @param {number} radius - Capsule radius (default: 2)
|
|
18
|
+
* @param {number} height - Total height including caps - MUST be > 2×radius (default: 10)
|
|
19
|
+
* @param {number} mass - Physics mass (default: 1)
|
|
20
|
+
* @param {Vector3} initialPosition - Starting position (default: 0,10,0)
|
|
21
|
+
* @param {string} color - Hex color string like "#FF0000" (default: "#E94B3C" red)
|
|
22
|
+
*/
|
|
23
|
+
class ActionPhysicsCapsule3D extends ActionPhysicsObject3D {
|
|
24
|
+
constructor(
|
|
25
|
+
physicsWorld,
|
|
26
|
+
radius = 2,
|
|
27
|
+
height = 10,
|
|
28
|
+
mass = 1,
|
|
29
|
+
initialPosition = new Vector3(0, 10, 0),
|
|
30
|
+
color = "#E94B3C"
|
|
31
|
+
) {
|
|
32
|
+
// Create visual mesh with triangles using single color system
|
|
33
|
+
const triangles = [];
|
|
34
|
+
|
|
35
|
+
// Calculate the cylinder height (total height minus the two hemisphere caps)
|
|
36
|
+
// This will be negative if height < 2*radius, causing physics library to throw error
|
|
37
|
+
const cylinderHeight = height - 2 * radius;
|
|
38
|
+
const halfCylinderHeight = cylinderHeight * 0.5;
|
|
39
|
+
|
|
40
|
+
// Segments for mesh detail
|
|
41
|
+
const radialSegments = 12;
|
|
42
|
+
const heightSegments = 4;
|
|
43
|
+
const capSegments = 6;
|
|
44
|
+
|
|
45
|
+
// Use single color for all triangles (no more checkerboard)
|
|
46
|
+
const capsuleColor = color;
|
|
47
|
+
|
|
48
|
+
// Helper function to create cylinder and cap vertices
|
|
49
|
+
const createVertex = (theta, y, radius) => {
|
|
50
|
+
return new Vector3(
|
|
51
|
+
radius * Math.cos(theta),
|
|
52
|
+
y,
|
|
53
|
+
radius * Math.sin(theta)
|
|
54
|
+
);
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
// 1. Create Cylinder Body - FIXED WINDING ORDER
|
|
58
|
+
for (let y = 0; y < heightSegments; y++) {
|
|
59
|
+
const yBottom = -halfCylinderHeight + (y / heightSegments) * cylinderHeight;
|
|
60
|
+
const yTop = -halfCylinderHeight + ((y + 1) / heightSegments) * cylinderHeight;
|
|
61
|
+
|
|
62
|
+
for (let x = 0; x < radialSegments; x++) {
|
|
63
|
+
const theta = (x / radialSegments) * Math.PI * 2;
|
|
64
|
+
const thetaNext = ((x + 1) % radialSegments) / radialSegments * Math.PI * 2;
|
|
65
|
+
|
|
66
|
+
// Create four vertices for this quad segment
|
|
67
|
+
const v1 = createVertex(theta, yBottom, radius);
|
|
68
|
+
const v2 = createVertex(thetaNext, yBottom, radius);
|
|
69
|
+
const v3 = createVertex(thetaNext, yTop, radius);
|
|
70
|
+
const v4 = createVertex(theta, yTop, radius);
|
|
71
|
+
|
|
72
|
+
// FIXED: Reversed winding order for outward-facing normals
|
|
73
|
+
triangles.push(new Triangle(v1, v3, v2, capsuleColor));
|
|
74
|
+
triangles.push(new Triangle(v1, v4, v3, capsuleColor));
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// 2. Create Top Hemisphere Cap - FIXED WINDING ORDER
|
|
79
|
+
const topCenter = new Vector3(0, halfCylinderHeight + radius, 0);
|
|
80
|
+
for (let y = 0; y < capSegments; y++) {
|
|
81
|
+
const phi = (y / capSegments) * (Math.PI / 2);
|
|
82
|
+
const phiNext = ((y + 1) / capSegments) * (Math.PI / 2);
|
|
83
|
+
|
|
84
|
+
const radiusAtPhi = radius * Math.cos(phi);
|
|
85
|
+
const radiusAtPhiNext = radius * Math.cos(phiNext);
|
|
86
|
+
|
|
87
|
+
const yAtPhi = halfCylinderHeight + radius * Math.sin(phi);
|
|
88
|
+
const yAtPhiNext = halfCylinderHeight + radius * Math.sin(phiNext);
|
|
89
|
+
|
|
90
|
+
if (y === capSegments - 1) {
|
|
91
|
+
// Special case for the top pole
|
|
92
|
+
for (let x = 0; x < radialSegments; x++) {
|
|
93
|
+
const theta = (x / radialSegments) * Math.PI * 2;
|
|
94
|
+
const thetaNext = ((x + 1) % radialSegments) / radialSegments * Math.PI * 2;
|
|
95
|
+
|
|
96
|
+
const v1 = createVertex(theta, yAtPhi, radiusAtPhi);
|
|
97
|
+
const v2 = createVertex(thetaNext, yAtPhi, radiusAtPhi);
|
|
98
|
+
|
|
99
|
+
// FIXED: Reversed winding order for top pole triangles
|
|
100
|
+
triangles.push(new Triangle(v1, topCenter, v2, capsuleColor));
|
|
101
|
+
}
|
|
102
|
+
} else {
|
|
103
|
+
for (let x = 0; x < radialSegments; x++) {
|
|
104
|
+
const theta = (x / radialSegments) * Math.PI * 2;
|
|
105
|
+
const thetaNext = ((x + 1) % radialSegments) / radialSegments * Math.PI * 2;
|
|
106
|
+
|
|
107
|
+
// Create four vertices for this quad segment
|
|
108
|
+
const v1 = createVertex(theta, yAtPhi, radiusAtPhi);
|
|
109
|
+
const v2 = createVertex(thetaNext, yAtPhi, radiusAtPhi);
|
|
110
|
+
const v3 = createVertex(thetaNext, yAtPhiNext, radiusAtPhiNext);
|
|
111
|
+
const v4 = createVertex(theta, yAtPhiNext, radiusAtPhiNext);
|
|
112
|
+
|
|
113
|
+
// FIXED: Reversed winding order for top hemisphere
|
|
114
|
+
triangles.push(new Triangle(v1, v3, v2, capsuleColor));
|
|
115
|
+
triangles.push(new Triangle(v1, v4, v3, capsuleColor));
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// 3. Create Bottom Hemisphere Cap - FIXED WINDING ORDER
|
|
121
|
+
const bottomCenter = new Vector3(0, -halfCylinderHeight - radius, 0);
|
|
122
|
+
for (let y = 0; y < capSegments; y++) {
|
|
123
|
+
const phi = (y / capSegments) * (Math.PI / 2);
|
|
124
|
+
const phiNext = ((y + 1) / capSegments) * (Math.PI / 2);
|
|
125
|
+
|
|
126
|
+
const radiusAtPhi = radius * Math.cos(phi);
|
|
127
|
+
const radiusAtPhiNext = radius * Math.cos(phiNext);
|
|
128
|
+
|
|
129
|
+
const yAtPhi = -halfCylinderHeight - radius * Math.sin(phi);
|
|
130
|
+
const yAtPhiNext = -halfCylinderHeight - radius * Math.sin(phiNext);
|
|
131
|
+
|
|
132
|
+
if (y === capSegments - 1) {
|
|
133
|
+
// Special case for the bottom pole
|
|
134
|
+
for (let x = 0; x < radialSegments; x++) {
|
|
135
|
+
const theta = (x / radialSegments) * Math.PI * 2;
|
|
136
|
+
const thetaNext = ((x + 1) % radialSegments) / radialSegments * Math.PI * 2;
|
|
137
|
+
|
|
138
|
+
const v1 = createVertex(theta, yAtPhi, radiusAtPhi);
|
|
139
|
+
const v2 = createVertex(thetaNext, yAtPhi, radiusAtPhi);
|
|
140
|
+
|
|
141
|
+
// FIXED: Correct winding order for bottom pole
|
|
142
|
+
triangles.push(new Triangle(v1, v2, bottomCenter, capsuleColor));
|
|
143
|
+
}
|
|
144
|
+
} else {
|
|
145
|
+
for (let x = 0; x < radialSegments; x++) {
|
|
146
|
+
const theta = (x / radialSegments) * Math.PI * 2;
|
|
147
|
+
const thetaNext = ((x + 1) % radialSegments) / radialSegments * Math.PI * 2;
|
|
148
|
+
|
|
149
|
+
// Create four vertices for this quad segment
|
|
150
|
+
const v1 = createVertex(theta, yAtPhi, radiusAtPhi);
|
|
151
|
+
const v2 = createVertex(thetaNext, yAtPhi, radiusAtPhi);
|
|
152
|
+
const v3 = createVertex(thetaNext, yAtPhiNext, radiusAtPhiNext);
|
|
153
|
+
const v4 = createVertex(theta, yAtPhiNext, radiusAtPhiNext);
|
|
154
|
+
|
|
155
|
+
// FIXED: Correct winding order for bottom hemisphere
|
|
156
|
+
triangles.push(new Triangle(v1, v2, v3, capsuleColor));
|
|
157
|
+
triangles.push(new Triangle(v1, v3, v4, capsuleColor));
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Rest of constructor remains the same
|
|
163
|
+
super(physicsWorld, triangles);
|
|
164
|
+
|
|
165
|
+
const shape = new Goblin.CapsuleShape(radius, height);
|
|
166
|
+
this.body = new Goblin.RigidBody(shape, mass);
|
|
167
|
+
this.body.position.set(
|
|
168
|
+
initialPosition.x,
|
|
169
|
+
initialPosition.y,
|
|
170
|
+
initialPosition.z
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
this.body.linear_damping = 0.01;
|
|
174
|
+
this.body.angular_damping = 0.01;
|
|
175
|
+
|
|
176
|
+
this.storeOriginalData();
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
storeOriginalData() {
|
|
181
|
+
this.originalNormals = [];
|
|
182
|
+
this.originalVerts = [];
|
|
183
|
+
|
|
184
|
+
this.triangles.forEach((triangle) => {
|
|
185
|
+
this.originalNormals.push(new Vector3(
|
|
186
|
+
triangle.normal.x,
|
|
187
|
+
triangle.normal.y,
|
|
188
|
+
triangle.normal.z
|
|
189
|
+
));
|
|
190
|
+
|
|
191
|
+
triangle.vertices.forEach((vertex) => {
|
|
192
|
+
this.originalVerts.push(new Vector3(
|
|
193
|
+
vertex.x,
|
|
194
|
+
vertex.y,
|
|
195
|
+
vertex.z
|
|
196
|
+
));
|
|
197
|
+
});
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
class ActionPhysicsCompoundShape3D extends ActionPhysicsObject3D {
|
|
2
|
+
constructor(physicsWorld, initialPosition = new Vector3(0, 500, 0), mass = 1) {
|
|
3
|
+
// Start with an empty triangle list - we'll add them as child shapes are added
|
|
4
|
+
super(physicsWorld, []);
|
|
5
|
+
|
|
6
|
+
// Create the Goblin compound shape
|
|
7
|
+
this.compoundShape = new Goblin.CompoundShape();
|
|
8
|
+
this.body = new Goblin.RigidBody(this.compoundShape, mass);
|
|
9
|
+
this.body.position.set(
|
|
10
|
+
initialPosition.x,
|
|
11
|
+
initialPosition.y,
|
|
12
|
+
initialPosition.z
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
this.body.linear_damping = 0.01;
|
|
16
|
+
this.body.angular_damping = 0.01;
|
|
17
|
+
|
|
18
|
+
// Keep track of the child objects
|
|
19
|
+
this.childObjects = [];
|
|
20
|
+
|
|
21
|
+
// Store triangles from all children
|
|
22
|
+
this.allTriangles = [];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Add a child shape to the compound shape
|
|
26
|
+
addChildShape(physicsObject, position, rotation = new Goblin.Quaternion()) {
|
|
27
|
+
if (!physicsObject || !physicsObject.body || !physicsObject.body.shape) {
|
|
28
|
+
console.error("[ActionPhysicsCompoundShape3D] Cannot add invalid physics object");
|
|
29
|
+
return this;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Store the shape with its position and rotation
|
|
33
|
+
const childShape = physicsObject.body.shape;
|
|
34
|
+
const childPosition = new Goblin.Vector3(position.x, position.y, position.z);
|
|
35
|
+
|
|
36
|
+
// Add the shape to the compound shape
|
|
37
|
+
this.compoundShape.addChildShape(childShape, childPosition, rotation);
|
|
38
|
+
|
|
39
|
+
// Store the object and its transform information for visual updates
|
|
40
|
+
this.childObjects.push({
|
|
41
|
+
object: physicsObject,
|
|
42
|
+
position: new Vector3(position.x, position.y, position.z),
|
|
43
|
+
rotation: rotation
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// Get the triangles from the child object and transform them
|
|
47
|
+
const transformedTriangles = this.transformTriangles(
|
|
48
|
+
physicsObject.triangles,
|
|
49
|
+
position,
|
|
50
|
+
rotation
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
// Add the transformed triangles to our collection
|
|
54
|
+
this.allTriangles.push(...transformedTriangles);
|
|
55
|
+
|
|
56
|
+
// Update our triangle list
|
|
57
|
+
this.triangles = this.allTriangles.slice();
|
|
58
|
+
|
|
59
|
+
// Update original data
|
|
60
|
+
this.storeOriginalData();
|
|
61
|
+
|
|
62
|
+
return this;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Transform triangles from a child shape to the compound shape's space
|
|
66
|
+
transformTriangles(triangles, position, rotation) {
|
|
67
|
+
return triangles.map(triangle => {
|
|
68
|
+
// Create new transformed vertices
|
|
69
|
+
const transformedVertices = triangle.vertices.map(vertex => {
|
|
70
|
+
// First create a Goblin vector for the vertex
|
|
71
|
+
const goblinVec = new Goblin.Vector3(vertex.x, vertex.y, vertex.z);
|
|
72
|
+
|
|
73
|
+
// Apply rotation
|
|
74
|
+
rotation.transformVector3(goblinVec);
|
|
75
|
+
|
|
76
|
+
// Apply translation
|
|
77
|
+
goblinVec.x += position.x;
|
|
78
|
+
goblinVec.y += position.y;
|
|
79
|
+
goblinVec.z += position.z;
|
|
80
|
+
|
|
81
|
+
// Convert back to Vector3
|
|
82
|
+
return new Vector3(goblinVec.x, goblinVec.y, goblinVec.z);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// Create a new triangle with the transformed vertices
|
|
86
|
+
return new Triangle(
|
|
87
|
+
transformedVertices[0],
|
|
88
|
+
transformedVertices[1],
|
|
89
|
+
transformedVertices[2],
|
|
90
|
+
triangle.color
|
|
91
|
+
);
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
storeOriginalData() {
|
|
96
|
+
this.originalNormals = [];
|
|
97
|
+
this.originalVerts = [];
|
|
98
|
+
|
|
99
|
+
this.triangles.forEach((triangle) => {
|
|
100
|
+
this.originalNormals.push(new Vector3(
|
|
101
|
+
triangle.normal.x,
|
|
102
|
+
triangle.normal.y,
|
|
103
|
+
triangle.normal.z
|
|
104
|
+
));
|
|
105
|
+
|
|
106
|
+
triangle.vertices.forEach((vertex) => {
|
|
107
|
+
this.originalVerts.push(new Vector3(
|
|
108
|
+
vertex.x,
|
|
109
|
+
vertex.y,
|
|
110
|
+
vertex.z
|
|
111
|
+
));
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/* Usage example:
|
|
118
|
+
const physicsWorld = new ActionPhysicsWorld3D();
|
|
119
|
+
|
|
120
|
+
// Create a compound shape at position (0, 20, 0) with mass 5
|
|
121
|
+
const compoundShape = new ActionPhysicsCompoundShape3D(
|
|
122
|
+
physicsWorld,
|
|
123
|
+
new Vector3(0, 20, 0),
|
|
124
|
+
5
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
// Create a sphere (without adding it to the world)
|
|
128
|
+
const sphere = new ActionPhysicsSphere3D(
|
|
129
|
+
physicsWorld,
|
|
130
|
+
2, // radius
|
|
131
|
+
1 // mass (this won't matter for the compound)
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
// Create a box (without adding it to the world)
|
|
135
|
+
const box = new ActionPhysicsBox3D(
|
|
136
|
+
physicsWorld,
|
|
137
|
+
3, 3, 3, // dimensions
|
|
138
|
+
1 // mass (this won't matter for the compound)
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
// Add the sphere and box to the compound shape
|
|
142
|
+
compoundShape.addChildShape(sphere, new Vector3(0, 0, 0));
|
|
143
|
+
compoundShape.addChildShape(box, new Vector3(0, 5, 0));
|
|
144
|
+
|
|
145
|
+
// Add the compound shape to the physics world
|
|
146
|
+
physicsWorld.addObject(compoundShape);
|
|
147
|
+
*/
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
// actionengine/math/physics/actionphysicscone3D.js
|
|
2
|
+
/**
|
|
3
|
+
* ActionPhysicsCone3D - 3D Cone Physics Object with Single Color System
|
|
4
|
+
*
|
|
5
|
+
* BREAKING CHANGE: Previously used two-color checkerboard pattern (color1, color2).
|
|
6
|
+
* Now uses single color system for consistency with other shapes.
|
|
7
|
+
*
|
|
8
|
+
* @param {ActionPhysicsWorld3D} physicsWorld - The physics world
|
|
9
|
+
* @param {number} radius - Cone base radius (default: 2)
|
|
10
|
+
* @param {number} height - Cone height (default: 10)
|
|
11
|
+
* @param {number} mass - Physics mass (default: 1)
|
|
12
|
+
* @param {Vector3} initialPosition - Starting position (default: 0,10,0)
|
|
13
|
+
* @param {string} color - Hex color string like "#FF0000" (default: "#FFA500" orange)
|
|
14
|
+
*/
|
|
15
|
+
class ActionPhysicsCone3D extends ActionPhysicsObject3D {
|
|
16
|
+
constructor(
|
|
17
|
+
physicsWorld,
|
|
18
|
+
radius = 2,
|
|
19
|
+
height = 10,
|
|
20
|
+
mass = 1,
|
|
21
|
+
initialPosition = new Vector3(0, 10, 0),
|
|
22
|
+
color = "#FFA500"
|
|
23
|
+
) {
|
|
24
|
+
// Create visual mesh with triangles using single color system
|
|
25
|
+
const triangles = [];
|
|
26
|
+
|
|
27
|
+
// Segments for mesh detail
|
|
28
|
+
const radialSegments = 12;
|
|
29
|
+
const heightSegments = 6;
|
|
30
|
+
|
|
31
|
+
// Use single color for all triangles (changed from checkerboard pattern)
|
|
32
|
+
const coneColor = color;
|
|
33
|
+
|
|
34
|
+
// Helper function to create vertices
|
|
35
|
+
const createVertex = (theta, heightPercent, radiusPercent) => {
|
|
36
|
+
const currentRadius = radius * (1 - heightPercent) * radiusPercent;
|
|
37
|
+
return new Vector3(
|
|
38
|
+
currentRadius * Math.cos(theta),
|
|
39
|
+
height * (heightPercent - 0.5), // Ranges from -half_height to half_height
|
|
40
|
+
currentRadius * Math.sin(theta)
|
|
41
|
+
);
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
// 1. Create Cone Body
|
|
45
|
+
const tip = new Vector3(0, height/2, 0);
|
|
46
|
+
const base = new Vector3(0, -height/2, 0);
|
|
47
|
+
|
|
48
|
+
// Create cone sides
|
|
49
|
+
for (let y = 0; y < heightSegments; y++) {
|
|
50
|
+
const yBottom = y / heightSegments;
|
|
51
|
+
const yTop = (y + 1) / heightSegments;
|
|
52
|
+
|
|
53
|
+
for (let x = 0; x < radialSegments; x++) {
|
|
54
|
+
const theta = (x / radialSegments) * Math.PI * 2;
|
|
55
|
+
const thetaNext = ((x + 1) % radialSegments) / radialSegments * Math.PI * 2;
|
|
56
|
+
|
|
57
|
+
if (y === heightSegments - 1) {
|
|
58
|
+
// Top segment connects to tip
|
|
59
|
+
const v1 = createVertex(theta, yBottom, 1);
|
|
60
|
+
const v2 = createVertex(thetaNext, yBottom, 1);
|
|
61
|
+
|
|
62
|
+
// Correct winding order for outward-facing normal
|
|
63
|
+
triangles.push(new Triangle(v1, tip, v2, coneColor));
|
|
64
|
+
} else {
|
|
65
|
+
// Regular segment
|
|
66
|
+
const v1 = createVertex(theta, yBottom, 1);
|
|
67
|
+
const v2 = createVertex(thetaNext, yBottom, 1);
|
|
68
|
+
const v3 = createVertex(thetaNext, yTop, 1);
|
|
69
|
+
const v4 = createVertex(theta, yTop, 1);
|
|
70
|
+
|
|
71
|
+
// Correct winding order for outward-facing normals
|
|
72
|
+
triangles.push(new Triangle(v1, v3, v2, coneColor));
|
|
73
|
+
triangles.push(new Triangle(v1, v4, v3, coneColor));
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// 2. Create Base with correct winding order for downward-facing normal
|
|
79
|
+
for (let x = 0; x < radialSegments; x++) {
|
|
80
|
+
const theta = (x / radialSegments) * Math.PI * 2;
|
|
81
|
+
const thetaNext = ((x + 1) % radialSegments) / radialSegments * Math.PI * 2;
|
|
82
|
+
|
|
83
|
+
const v1 = createVertex(theta, 0, 1);
|
|
84
|
+
const v2 = createVertex(thetaNext, 0, 1);
|
|
85
|
+
|
|
86
|
+
triangles.push(new Triangle(v1, v2, base, coneColor));
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
super(physicsWorld, triangles);
|
|
90
|
+
|
|
91
|
+
// Create physics shape and body - Goblin expects half-height
|
|
92
|
+
const shape = new Goblin.ConeShape(radius, height/2);
|
|
93
|
+
this.body = new Goblin.RigidBody(shape, mass);
|
|
94
|
+
this.body.position.set(
|
|
95
|
+
initialPosition.x,
|
|
96
|
+
initialPosition.y,
|
|
97
|
+
initialPosition.z
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
this.body.linear_damping = 0.01;
|
|
101
|
+
this.body.angular_damping = 0.01;
|
|
102
|
+
|
|
103
|
+
this.storeOriginalData();
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
storeOriginalData() {
|
|
107
|
+
this.originalNormals = [];
|
|
108
|
+
this.originalVerts = [];
|
|
109
|
+
|
|
110
|
+
this.triangles.forEach((triangle) => {
|
|
111
|
+
this.originalNormals.push(new Vector3(
|
|
112
|
+
triangle.normal.x,
|
|
113
|
+
triangle.normal.y,
|
|
114
|
+
triangle.normal.z
|
|
115
|
+
));
|
|
116
|
+
|
|
117
|
+
triangle.vertices.forEach((vertex) => {
|
|
118
|
+
this.originalVerts.push(new Vector3(
|
|
119
|
+
vertex.x,
|
|
120
|
+
vertex.y,
|
|
121
|
+
vertex.z
|
|
122
|
+
));
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
}
|