-
Notifications
You must be signed in to change notification settings - Fork 68
Extension update: <3D Sprite> #1957
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -19,7 +19,8 @@ | |
| "3d" | ||
| ], | ||
| "authorIds": [ | ||
| "IWykYNRvhCZBN3vEgKEbBPOR3Oc2" | ||
| "IWykYNRvhCZBN3vEgKEbBPOR3Oc2", | ||
| "sXdoMxxHF7hAXkEPRO8oZcfnBgC2" | ||
| ], | ||
| "dependencies": [], | ||
| "globalVariables": [], | ||
|
|
@@ -39,30 +40,77 @@ | |
| "if (gdjs.__sprite3DExtension) {", | ||
| " return;", | ||
| "}", | ||
| "", | ||
| "const vertexColors = [];", | ||
| "", | ||
| "class Sprite3DRenderer {", | ||
| " /** @type {gdjs.CustomRuntimeObject} */", | ||
| " object;", | ||
| " /** @type {THREE.Mesh} */", | ||
| " mesh;", | ||
| "", | ||
| " /** @type {number} */", | ||
| " depthOffset;", | ||
| " /** @type {boolean} */", | ||
| " autoRotate;", | ||
| " ", | ||
| " /**", | ||
| " * @param object {gdjs.CustomRuntimeObject}", | ||
| " */", | ||
| " constructor(object) {", | ||
| " this.object = object;", | ||
| "", | ||
| " this.depthOffset = 0;", | ||
| " this.autoRotate = true;", | ||
| " ", | ||
| " const geometry = new THREE.PlaneGeometry(1, -1);", | ||
| " const animationFrame = object.getAnimator().getCurrentFrame();", | ||
| " if (animationFrame) {", | ||
| " const material = animationFrame.texture;", | ||
| " ", | ||
| " // Enhanced transparency and depth settings", | ||
| " material.alphaTest = 0.5;", | ||
| " material.depthWrite = true;", | ||
| " material.depthTest = true;", | ||
| " material.transparent = true;", | ||
| " ", | ||
| " // Advanced professional improvements", | ||
| " material.side = THREE.DoubleSide;", | ||
| " material.shadowSide = THREE.FrontSide;", | ||
| " material.toneMapped = true;", | ||
| " material.fog = true;", | ||
| " material.premultipliedAlpha = true;", | ||
| " ", | ||
| " // Advanced depth settings to prevent z-fighting", | ||
| " material.depthFunc = THREE.LessEqualDepth;", | ||
| " material.polygonOffset = true;", | ||
| " material.polygonOffsetFactor = -1;", | ||
| " material.polygonOffsetUnits = -1;", | ||
| " ", | ||
| " // Professional visual effects", | ||
| " if (material.emissive) {", | ||
| " material.emissive = new THREE.Color(0x111111);", | ||
| " material.emissiveIntensity = 0.2;", | ||
| " }", | ||
| " ", | ||
| " material.needsUpdate = true;", | ||
| " ", | ||
| " this.mesh = new THREE.Mesh(geometry, material);", | ||
| " this.mesh.rotation.order = 'ZYX';", | ||
| " ", | ||
| " // Optimized rotation order for realism (YXZ is better for standing objects)", | ||
| " this.mesh.rotation.order = 'YXZ';", | ||
| " ", | ||
| " // Enable enhanced shadows", | ||
| " this.mesh.castShadow = true;", | ||
| " this.mesh.receiveShadow = true;", | ||
| " ", | ||
| " // Performance and rendering improvements", | ||
| " this.mesh.frustumCulled = true;", | ||
| " this.mesh.matrixAutoUpdate = true;", | ||
| " ", | ||
| " // Dynamic renderOrder based on Z position", | ||
| " this.updateRenderOrder();", | ||
| " ", | ||
| " object.get3DRendererObject().add(this.mesh);", | ||
| " // Ensure a forward compatibility when vertexColors will be set to true", | ||
| " // in the engine to allow to tint 3D sprites.", | ||
| " ", | ||
| " // Add vertex colors for advanced coloring", | ||
| " vertexColors.length = geometry.attributes.position.count * 3;", | ||
| " vertexColors.fill(1);", | ||
| " geometry.setAttribute(", | ||
|
|
@@ -73,41 +121,162 @@ | |
| " this.updateFrame();", | ||
| " object.getAnimator().setOnFrameChangeCallback(() => this.updateFrame());", | ||
| " }", | ||
| "", | ||
| " ", | ||
| " /**", | ||
| " * Update render order based on depth", | ||
| " */", | ||
| " updateRenderOrder() {", | ||
| " if (!this.mesh) return;", | ||
| " ", | ||
| " // Calculate renderOrder based on Z and Y position to ensure correct rendering", | ||
| " const zPos = this.object.getZ ? this.object.getZ() : 0;", | ||
| " const yPos = this.object.getY ? this.object.getY() : 0;", | ||
| " ", | ||
| " // Use complex formula to get precise ordering", | ||
| " this.mesh.renderOrder = Math.floor(zPos * 1000 + yPos);", | ||
| " ", | ||
| " // Apply additional depth offset", | ||
| " this.mesh.position.z = this.depthOffset;", | ||
| " }", | ||
| " ", | ||
| " /**", | ||
| " * Apply automatic rotation angles based on position and velocity", | ||
| " */", | ||
| " applyAutoRotation() {", | ||
| " if (!this.autoRotate || !this.mesh) return;", | ||
| " ", | ||
| " const obj = this.object;", | ||
| " ", | ||
| " // Get velocity and direction", | ||
| " const velocityX = (obj._customState && obj._customState.velocityX) || 0;", | ||
| " const velocityY = (obj._customState && obj._customState.velocityY) || 0;", | ||
| " const angle = obj.getAngle ? obj.getAngle() : 0;", | ||
| " ", | ||
| " // Calculate automatic tilt on X axis (pitch) based on vertical velocity", | ||
| " const maxPitchAngle = 15; // degrees", | ||
| " const pitchFactor = Math.min(Math.abs(velocityY) / 500, 1);", | ||
| " const targetPitch = (velocityY < 0 ? -1 : 1) * pitchFactor * maxPitchAngle;", | ||
| " ", | ||
| " // Calculate tilt on Y axis (yaw) based on direction", | ||
| " const maxYawAngle = 10; // degrees", | ||
| " const yawFactor = Math.min(Math.abs(velocityX) / 500, 1);", | ||
| " const targetYaw = (velocityX < 0 ? -1 : 1) * yawFactor * maxYawAngle;", | ||
| " ", | ||
| " // Apply rotation smoothly (lerp)", | ||
| " const lerpFactor = 0.1;", | ||
| " const currentRotation = this.mesh.rotation;", | ||
| " ", | ||
| " // Convert base angle to Z rotation", | ||
| " const targetZ = THREE.MathUtils.degToRad(-angle);", | ||
| " ", | ||
| " // Apply smooth rotation", | ||
| " currentRotation.x = THREE.MathUtils.lerp(", | ||
| " currentRotation.x,", | ||
| " THREE.MathUtils.degToRad(targetPitch),", | ||
| " lerpFactor", | ||
| " );", | ||
| " ", | ||
| " currentRotation.y = THREE.MathUtils.lerp(", | ||
| " currentRotation.y,", | ||
| " THREE.MathUtils.degToRad(targetYaw),", | ||
| " lerpFactor", | ||
| " );", | ||
| " ", | ||
| " currentRotation.z = THREE.MathUtils.lerp(", | ||
| " currentRotation.z,", | ||
| " targetZ,", | ||
| " lerpFactor", | ||
| " );", | ||
| " }", | ||
| " ", | ||
| " /**", | ||
| " * Apply deep perspective effect", | ||
| " */", | ||
| " applyDepthPerspective() {", | ||
| " if (!this.mesh) return;", | ||
| " ", | ||
| " const obj = this.object;", | ||
| " const yPos = obj.getY ? obj.getY() : 0;", | ||
| " const zPos = obj.getZ ? obj.getZ() : 0;", | ||
| " ", | ||
| " // Calculate scale factor based on depth (perspective)", | ||
| " const perspectiveFactor = 1 - (zPos * 0.0001); // Reduce size of distant objects", | ||
| " const scaleFactor = Math.max(0.5, Math.min(1.5, perspectiveFactor));", | ||
| " ", | ||
| " // Apply additional factor based on Y (apparent height)", | ||
| " const heightFactor = 1 + (yPos * 0.00005);", | ||
| " ", | ||
| " // Update scale while maintaining original proportions", | ||
| " const frame = this.object.getAnimator().getCurrentFrame();", | ||
| " if (frame) {", | ||
| " const image = frame.texture.map.image;", | ||
| " const width = image.width;", | ||
| " const height = image.height;", | ||
| " ", | ||
| " this.mesh.scale.set(", | ||
| " width * scaleFactor * heightFactor,", | ||
| " height * scaleFactor * heightFactor,", | ||
| " 1", | ||
| " );", | ||
| " }", | ||
| " }", | ||
| " ", | ||
| " updateFrame() {", | ||
| " const frame = this.object.getAnimator().getCurrentFrame();", | ||
| " if (!frame) {", | ||
| " return;", | ||
| " }", | ||
| " const material = frame.texture;", | ||
| "", | ||
| " ", | ||
| " // Update basic settings", | ||
| " material.alphaTest = 0.5;", | ||
| " material.depthWrite = true;", | ||
| " material.depthTest = true;", | ||
| " material.transparent = true;", | ||
| " material.side = THREE.DoubleSide;", | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This wasn’t the default behavior, so all previous projects using the extension will now have their sprites visible on both faces. From creator point of view if this wasn’t intended, it introduces a regression in the extension and breaks compatibility with existing projects. |
||
| " material.shadowSide = THREE.FrontSide;", | ||
| " material.toneMapped = true;", | ||
| " material.fog = true;", | ||
| " material.premultipliedAlpha = true;", | ||
| " material.depthFunc = THREE.LessEqualDepth;", | ||
| " material.polygonOffset = true;", | ||
| " material.polygonOffsetFactor = -1;", | ||
| " material.polygonOffsetUnits = -1;", | ||
| " ", | ||
| " // Professional visual effects", | ||
| " if (material.emissive) {", | ||
| " material.emissive = new THREE.Color(0x111111);", | ||
| " material.emissiveIntensity = 0.2;", | ||
| " }", | ||
|
Comment on lines
+246
to
+250
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This will override the existing material if any. |
||
| " ", | ||
| " material.needsUpdate = true;", | ||
| " ", | ||
| " const image = material.map.image;", | ||
| " const width = image.width;", | ||
| " const height = image.height;", | ||
| " const origin = frame.origin;", | ||
| " this.mesh.position.set(-origin.x + width / 2, -origin.y + height / 2, 0);", | ||
| " this.mesh.scale.set(width, height, 1);", | ||
| "", | ||
| " const center = frame.center;", | ||
| " this.object.setRotationCenter(center.x - origin.x, center.y - origin.y);", | ||
| "", | ||
| " this.mesh.material = material;", | ||
| "", | ||
| " ", | ||
| " // Apply automatic improvements", | ||
| " this.updateRenderOrder();", | ||
| " this.applyAutoRotation();", | ||
| " this.applyDepthPerspective();", | ||
| " ", | ||
| " const hitBoxes = this.object._untransformedHitBoxes;", | ||
| " if (frame.hasCustomCollisionMask) {", | ||
| " let i = 0;", | ||
| " for (let len = frame.customCollisionMask.length; i < len; ++i) {", | ||
| " const polygonData = frame.customCollisionMask[i];", | ||
| "", | ||
| " // Add a polygon, if necessary (Avoid recreating a polygon if it already exists).", | ||
|
Comment on lines
-101
to
-102
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Put this comment back. |
||
| " if (i >= hitBoxes.length) {", | ||
| " hitBoxes.push(new gdjs.Polygon());", | ||
| " }", | ||
| " let j = 0;", | ||
| " for (const len2 = polygonData.length; j < len2; ++j) {", | ||
| " const pointData = polygonData[j];", | ||
| "", | ||
| " // Add a point, if necessary (Avoid recreating a point if it already exists).", | ||
|
Comment on lines
-109
to
-110
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Put this comment back. |
||
| " if (j >= hitBoxes[i].vertices.length) {", | ||
| " hitBoxes[i].vertices.push([0, 0]);", | ||
| " }", | ||
|
|
@@ -127,21 +296,57 @@ | |
| " vertices.push([-origin.x + width, -origin.y + height]);", | ||
| " vertices.push([-origin.x, -origin.y + height]);", | ||
| " }", | ||
| "", | ||
| " const aabb = this.object._unrotatedAABB;", | ||
| " aabb.min[0] = -origin.x;", | ||
| " aabb.min[1] = -origin.y;", | ||
| " aabb.max[0] = -origin.x + width;", | ||
| " aabb.max[1] = -origin.y + height;", | ||
| "", | ||
| " this.object._isUntransformedHitBoxesDirty = false;", | ||
| " }", | ||
| " ", | ||
| " /**", | ||
| " * Manually set depth offset", | ||
| " * @param {number} offset", | ||
| " */", | ||
| " setDepthOffset(offset) {", | ||
| " this.depthOffset = offset;", | ||
| " this.updateRenderOrder();", | ||
| " }", | ||
| " ", | ||
| " /**", | ||
| " * Enable/disable automatic rotation", | ||
| " * @param {boolean} enabled", | ||
| " */", | ||
| " setAutoRotation(enabled) {", | ||
| " this.autoRotate = enabled;", | ||
| " }", | ||
|
Comment on lines
+320
to
+322
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is never used? |
||
| " ", | ||
| " /**", | ||
| " * Set a specific angle on a specific axis", | ||
| " * @param {string} axis - 'x', 'y', or 'z'", | ||
| " * @param {number} angle - Angle in degrees", | ||
| " */", | ||
| " setRotationAngle(axis, angle) {", | ||
| " if (!this.mesh) return;", | ||
| " ", | ||
| " const radians = THREE.MathUtils.degToRad(angle);", | ||
| " switch(axis.toLowerCase()) {", | ||
| " case 'x':", | ||
| " this.mesh.rotation.x = radians;", | ||
| " break;", | ||
| " case 'y':", | ||
| " this.mesh.rotation.y = radians;", | ||
| " break;", | ||
| " case 'z':", | ||
| " this.mesh.rotation.z = radians;", | ||
| " break;", | ||
| " }", | ||
| " }", | ||
| "}", | ||
| "", | ||
| "gdjs.__sprite3DExtension = {", | ||
| " Sprite3DRenderer", | ||
| "};", | ||
| "" | ||
| "};" | ||
| ], | ||
| "parameterObjects": "", | ||
| "useStrict": true, | ||
|
|
@@ -178,6 +383,7 @@ | |
| "ambientLightColorB": 200, | ||
| "ambientLightColorG": 200, | ||
| "ambientLightColorR": 200, | ||
| "camera2DPlaneMaxDrawingDistance": 5000, | ||
| "camera3DFarPlaneDistance": 10000, | ||
| "camera3DFieldOfView": 45, | ||
| "camera3DNearPlaneDistance": 3, | ||
|
|
@@ -204,6 +410,7 @@ | |
| } | ||
| ], | ||
| "instances": [], | ||
| "editionSettings": {}, | ||
| "eventsFunctions": [ | ||
| { | ||
| "fullName": "", | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Get back the original comments.