diff --git a/extensions/community/Sprite3D.json b/extensions/community/Sprite3D.json index 5d94d3196..615bb998e 100644 --- a/extensions/community/Sprite3D.json +++ b/extensions/community/Sprite3D.json @@ -19,7 +19,8 @@ "3d" ], "authorIds": [ - "IWykYNRvhCZBN3vEgKEbBPOR3Oc2" + "IWykYNRvhCZBN3vEgKEbBPOR3Oc2", + "sXdoMxxHF7hAXkEPRO8oZcfnBgC2" ], "dependencies": [], "globalVariables": [], @@ -39,7 +40,6 @@ "if (gdjs.__sprite3DExtension) {", " return;", "}", - "", "const vertexColors = [];", "", "class Sprite3DRenderer {", @@ -47,22 +47,70 @@ " 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;", + " 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;", + " }", + " ", + " 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).", " 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).", " 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;", + " }", + " ", + " /**", + " * 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": "",