action-engine-js 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. package/LICENSE +45 -0
  2. package/README.md +348 -0
  3. package/actionengine/3rdparty/goblin/goblin.js +9609 -0
  4. package/actionengine/3rdparty/goblin/goblin.min.js +5 -0
  5. package/actionengine/camera/actioncamera.js +90 -0
  6. package/actionengine/camera/cameracollisionhandler.js +69 -0
  7. package/actionengine/character/actioncharacter.js +360 -0
  8. package/actionengine/character/actioncharacter3D.js +61 -0
  9. package/actionengine/core/app.js +430 -0
  10. package/actionengine/debug/basedebugpanel.js +858 -0
  11. package/actionengine/display/canvasmanager.js +75 -0
  12. package/actionengine/display/gl/programmanager.js +570 -0
  13. package/actionengine/display/gl/shaders/lineshader.js +118 -0
  14. package/actionengine/display/gl/shaders/objectshader.js +1756 -0
  15. package/actionengine/display/gl/shaders/particleshader.js +43 -0
  16. package/actionengine/display/gl/shaders/shadowshader.js +319 -0
  17. package/actionengine/display/gl/shaders/spriteshader.js +100 -0
  18. package/actionengine/display/gl/shaders/watershader.js +67 -0
  19. package/actionengine/display/graphics/actionmodel3D.js +191 -0
  20. package/actionengine/display/graphics/actionsprite3D.js +230 -0
  21. package/actionengine/display/graphics/lighting/actiondirectionalshadowlight.js +864 -0
  22. package/actionengine/display/graphics/lighting/actionlight.js +211 -0
  23. package/actionengine/display/graphics/lighting/actionomnidirectionalshadowlight.js +862 -0
  24. package/actionengine/display/graphics/lighting/lightingconstants.js +263 -0
  25. package/actionengine/display/graphics/lighting/lightmanager.js +789 -0
  26. package/actionengine/display/graphics/renderableobject.js +44 -0
  27. package/actionengine/display/graphics/renderers/actionrenderer2D.js +341 -0
  28. package/actionengine/display/graphics/renderers/actionrenderer3D/actionrenderer3D.js +655 -0
  29. package/actionengine/display/graphics/renderers/actionrenderer3D/canvasmanager3D.js +82 -0
  30. package/actionengine/display/graphics/renderers/actionrenderer3D/debugrenderer3D.js +493 -0
  31. package/actionengine/display/graphics/renderers/actionrenderer3D/objectrenderer3D.js +790 -0
  32. package/actionengine/display/graphics/renderers/actionrenderer3D/spriteRenderer3D.js +266 -0
  33. package/actionengine/display/graphics/renderers/actionrenderer3D/sunrenderer3D.js +140 -0
  34. package/actionengine/display/graphics/renderers/actionrenderer3D/waterrenderer3D.js +173 -0
  35. package/actionengine/display/graphics/renderers/actionrenderer3D/weatherrenderer3D.js +87 -0
  36. package/actionengine/display/graphics/texture/proceduraltexture.js +192 -0
  37. package/actionengine/display/graphics/texture/texturemanager.js +242 -0
  38. package/actionengine/display/graphics/texture/textureregistry.js +177 -0
  39. package/actionengine/input/actionscrollablearea.js +1405 -0
  40. package/actionengine/input/inputhandler.js +1647 -0
  41. package/actionengine/math/geometry/geometrybuilder.js +161 -0
  42. package/actionengine/math/geometry/glbexporter.js +364 -0
  43. package/actionengine/math/geometry/glbloader.js +722 -0
  44. package/actionengine/math/geometry/modelcodegenerator.js +97 -0
  45. package/actionengine/math/geometry/triangle.js +33 -0
  46. package/actionengine/math/geometry/triangleutils.js +34 -0
  47. package/actionengine/math/mathutils.js +25 -0
  48. package/actionengine/math/matrix4.js +785 -0
  49. package/actionengine/math/physics/actionphysics.js +108 -0
  50. package/actionengine/math/physics/actionphysicsobject3D.js +164 -0
  51. package/actionengine/math/physics/actionphysicsworld3D.js +238 -0
  52. package/actionengine/math/physics/actionraycast.js +129 -0
  53. package/actionengine/math/physics/shapes/actionphysicsbox3D.js +158 -0
  54. package/actionengine/math/physics/shapes/actionphysicscapsule3D.js +200 -0
  55. package/actionengine/math/physics/shapes/actionphysicscompoundshape3D.js +147 -0
  56. package/actionengine/math/physics/shapes/actionphysicscone3D.js +126 -0
  57. package/actionengine/math/physics/shapes/actionphysicsconvexshape3D.js +72 -0
  58. package/actionengine/math/physics/shapes/actionphysicscylinder3D.js +117 -0
  59. package/actionengine/math/physics/shapes/actionphysicsmesh3D.js +74 -0
  60. package/actionengine/math/physics/shapes/actionphysicsplane3D.js +100 -0
  61. package/actionengine/math/physics/shapes/actionphysicssphere3D.js +95 -0
  62. package/actionengine/math/quaternion.js +61 -0
  63. package/actionengine/math/vector2.js +277 -0
  64. package/actionengine/math/vector3.js +318 -0
  65. package/actionengine/math/viewfrustum.js +136 -0
  66. package/actionengine/network/ACTIONNETREADME.md +810 -0
  67. package/actionengine/network/client/ActionNetManager.js +802 -0
  68. package/actionengine/network/client/ActionNetManagerGUI.js +1709 -0
  69. package/actionengine/network/client/ActionNetManagerP2P.js +1537 -0
  70. package/actionengine/network/client/SyncSystem.js +422 -0
  71. package/actionengine/network/p2p/ActionNetPeer.js +142 -0
  72. package/actionengine/network/p2p/ActionNetTrackerClient.js +623 -0
  73. package/actionengine/network/p2p/DataConnection.js +282 -0
  74. package/actionengine/network/p2p/README.md +510 -0
  75. package/actionengine/network/p2p/example.html +502 -0
  76. package/actionengine/network/server/ActionNetServer.js +577 -0
  77. package/actionengine/network/server/ActionNetServerSSL.js +579 -0
  78. package/actionengine/network/server/ActionNetServerUtils.js +458 -0
  79. package/actionengine/network/server/SERVERREADME.md +314 -0
  80. package/actionengine/network/server/package-lock.json +35 -0
  81. package/actionengine/network/server/package.json +13 -0
  82. package/actionengine/network/server/start.bat +27 -0
  83. package/actionengine/network/server/start.sh +25 -0
  84. package/actionengine/network/server/startwss.bat +27 -0
  85. package/actionengine/sound/audiomanager.js +1589 -0
  86. package/actionengine/sound/soundfont/ACTIONSOUNDFONT_README.md +205 -0
  87. package/actionengine/sound/soundfont/actionparser.js +718 -0
  88. package/actionengine/sound/soundfont/actionreverb.js +252 -0
  89. package/actionengine/sound/soundfont/actionsoundfont.js +543 -0
  90. package/actionengine/sound/soundfont/sf2playerlicence.txt +29 -0
  91. package/actionengine/sound/soundfont/soundfont.js +2 -0
  92. package/dist/action-engine.min.js +328 -0
  93. package/package.json +35 -0
@@ -0,0 +1,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
+ }