From 78fa8aa57aa2df1705d74d81d6cefd7d7bbcf7e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Davy=20H=C3=A9lard?= Date: Fri, 20 Mar 2026 18:36:36 +0100 Subject: [PATCH 01/22] [Reviewed] [3D spot light] Light up a cone like a flashlight --- extensions/reviewed/SpotLight3D.json | 2112 ++++++++++++++++++++++++++ 1 file changed, 2112 insertions(+) create mode 100644 extensions/reviewed/SpotLight3D.json diff --git a/extensions/reviewed/SpotLight3D.json b/extensions/reviewed/SpotLight3D.json new file mode 100644 index 000000000..14e787492 --- /dev/null +++ b/extensions/reviewed/SpotLight3D.json @@ -0,0 +1,2112 @@ +{ + "author": "", + "category": "Visual effect", + "dimension": "3D", + "extensionNamespace": "", + "fullName": "3D spot light", + "gdevelopVersion": ">=5.5.222", + "helpPath": "", + "iconUrl": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0ibWRpLWZsYXNobGlnaHQiIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0Ij48cGF0aCBkPSJNOSwxMEw2LDVIMThMMTUsMTBIOU0xOCw0SDZWMkgxOFY0TTksMjJWMTFIMTVWMjJIOU0xMiwxM0ExLDEgMCAwLDAgMTEsMTRBMSwxIDAgMCwwIDEyLDE1QTEsMSAwIDAsMCAxMywxNEExLDEgMCAwLDAgMTIsMTNaIiAvPjwvc3ZnPg==", + "name": "SpotLight3D", + "previewIconUrl": "https://asset-resources.gdevelop.io/public-resources/Icons/b627f8e676b3aac26945b2c38db0cfa787d34d2eff23755d4eb1289452f6cd28_flashlight.svg", + "shortDescription": "Light up a cone like a flashlight.", + "version": "1.0.0", + "description": "Light up a cone like a flashlight.", + "tags": [ + "3d", + "light" + ], + "authorIds": [ + "IWykYNRvhCZBN3vEgKEbBPOR3Oc2" + ], + "dependencies": [], + "globalVariables": [], + "sceneVariables": [], + "eventsFunctions": [ + { + "description": "Define helper classes JavaScript code.", + "fullName": "Define helper classes", + "functionType": "Action", + "name": "DefineHelperClasses", + "private": true, + "sentence": "Define helper classes JavaScript code", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "//@ts-ignore", + "if (gdjs.__spotLight3DExtension) {", + " //@ts-ignore", + " return;", + "}", + "", + "const game = runtimeScene.getGame();", + "", + "const visibleLightCountMax = 10;", + "const editorVisibleLightCountMax = 4;", + "let visibleLightCount = 0;", + "gdjs.registerRuntimeScenePreEventsCallback(", + " runtimeScene => {", + " visibleLightCount = 0;", + " });", + "if (gdjs.registerInGameEditorPostStepCallback) {", + " gdjs.registerInGameEditorPostStepCallback(", + " runtimeScene => {", + " visibleLightCount = 0;", + " });", + "}", + "", + "class SpotLight3DRenderer extends gdjs.CustomRuntimeObject3DRenderer {", + " constructor(", + " object,", + " instanceContainer,", + " parent", + " ) {", + " super(object, instanceContainer, parent);", + " }", + "", + " _updateThreeGroup() {", + " const object = this._object;", + " /** @type {THREE.SpotLight} */", + " //@ts-ignore", + " const threeObject3D = this.get3DRendererObject();", + "", + " threeObject3D.rotation.set(", + " gdjs.toRad(object.getRotationX()),", + " gdjs.toRad(object.getRotationY()),", + " gdjs.toRad(object.angle)", + " );", + "", + " threeObject3D.position.set(", + " object.getX(),", + " object.getY(),", + " object.getZ()", + " );", + "", + " // Force the scale to 1 because the light doesn't really has a size.", + " threeObject3D.scale.set(", + " object.isFlippedX() ? -1 : 1,", + " object.isFlippedY() ? -1 : 1,", + " object.isFlippedZ() ? -1 : 1", + " );", + "", + " threeObject3D.visible = !this._object.hidden;", + "", + " const editor = game.getInGameEditor ? game.getInGameEditor() : null;", + "", + " if (editor) {", + " const selectedObject = editor._selection.getLastSelectedObject();", + " let parentObject = object;", + " let isSelected = parentObject === selectedObject;", + " const isDirectlySelected = isSelected;", + " while (!isSelected && parentObject.getInstanceContainer()", + " //@ts-ignore", + " .getOwner) {", + " parentObject = parentObject.getInstanceContainer()", + " //@ts-ignore", + " .getOwner();", + " isSelected = parentObject === selectedObject;", + " }", + " /** @type {THREE.SpotLight} */", + " //@ts-ignore", + " const spotLight = object.__spotLight;", + " spotLight.visible = isDirectlySelected || (isSelected && visibleLightCount < editorVisibleLightCountMax);", + " if (spotLight.visible) {", + " visibleLightCount++;", + " }", + " }", + " else if (object.isHidden()) {", + " threeObject3D.visible = false;", + " }", + " else if (visibleLightCount >= visibleLightCountMax) {", + " threeObject3D.visible = false;", + " } else {", + " let rootObject = object;", + " while (rootObject.getInstanceContainer()", + " //@ts-ignore", + " .getOwner) {", + " rootObject = rootObject.getInstanceContainer()", + " //@ts-ignore", + " .getOwner();", + " }", + " const layerName = rootObject.getLayer();", + "", + " const runtimeScene = object.getRuntimeScene();", + " const cameraX = gdjs.evtTools.camera.getCameraX(runtimeScene, layerName, 0);", + " const cameraY = gdjs.evtTools.camera.getCameraY(runtimeScene, layerName, 0);", + " const cameraZ = gdjs.scene3d.camera.getCameraZ(runtimeScene, layerName, 0);", + " const deltaX = rootObject.getX() - cameraX;", + " const deltaY = rootObject.getY() - cameraY;", + " const deltaZ = rootObject.getZ() - cameraZ;", + " //@ts-ignore", + " const distanceMax = object._getVisibilityDistanceMax();", + " const isWithInRange = deltaX * deltaX + deltaY * deltaY + deltaZ * deltaZ <= distanceMax * distanceMax;", + " threeObject3D.visible = isWithInRange;", + " if (isWithInRange) {", + " visibleLightCount++;", + " }", + " }", + "", + " // Force the visibility to be checked every frame", + " this._isContainerDirty = true;", + " }", + "}", + "", + "const coneLength = 64;", + "", + "class SpotLightHelper extends THREE.Object3D {", + " /** @type {gdjs.CustomRuntimeObject3D} */", + " object;", + " /** @type {THREE.LineSegments} */", + " cone;", + " /** @type {THREE.LineSegments} */", + " centerLine;", + " /** @type {THREE.Mesh} */", + " originBox;", + "", + "\t/**", + " * @param {gdjs.CustomRuntimeObject3D} object", + "\t */", + " constructor(object) {", + " super();", + " this.object = object;", + " this.type = 'SpotLightHelper';", + "", + " const coneGeometry = new THREE.BufferGeometry();", + " const conePositions = [", + " 0, 0, 0, 1, 0, 0,", + " 0, 0, 0, 1, 1, 0,", + " 0, 0, 0, 1, - 1, 0,", + " 0, 0, 0, 1, 0, 1,", + " 0, 0, 0, 1, 0, - 1", + " ];", + " for (let i = 0, j = 1, l = 32; i < l; i++ , j++) {", + " const p1 = (i / l) * Math.PI * 2;", + " const p2 = (j / l) * Math.PI * 2;", + " conePositions.push(", + " 1, Math.cos(p1), Math.sin(p1),", + " 1, Math.cos(p2), Math.sin(p2)", + " );", + " }", + " coneGeometry.setAttribute('position', new THREE.Float32BufferAttribute(conePositions, 3));", + " this.cone = new THREE.LineSegments(", + " coneGeometry,", + " new THREE.LineBasicMaterial({ fog: false, toneMapped: false })", + " );", + " this.add(this.cone);", + " const centerLineGeometry = new THREE.BufferGeometry();", + " centerLineGeometry.setAttribute('position', new THREE.Float32BufferAttribute([", + " 0, 0, 0, coneLength, 0, 0", + " ], 3));", + " this.centerLine = new THREE.LineSegments(", + " centerLineGeometry,", + " new THREE.LineBasicMaterial({ fog: false, toneMapped: false })", + " );", + " this.add(this.centerLine);", + "", + " this.originBox = new THREE.Mesh(", + " new THREE.BoxGeometry(16, 16, 16),", + " new THREE.MeshBasicMaterial({ fog: false, toneMapped: false })", + " );", + " this.add(this.originBox);", + " this.update();", + "", + " //@ts-ignore", + " this.originBox.gdjsRuntimeObject = object;", + " }", + "", + " dispose() {", + " this.cone.geometry.dispose();", + " //@ts-ignore", + " this.cone.material.dispose();", + " this.centerLine.geometry.dispose();", + " //@ts-ignore", + " this.centerLine.material.dispose();", + " this.originBox.geometry.dispose();", + " //@ts-ignore", + " this.originBox.material.dispose();", + " }", + "", + " update() {", + " /** @type {number} */", + " //@ts-ignore", + " const coneAngle = gdjs.toRad(this.object._getConeAngle());", + " const coneWidth = coneLength * Math.sin(coneAngle);", + " const coneHeight = coneLength * Math.cos(coneAngle);", + " this.cone.scale.set(coneHeight, coneWidth, coneWidth);", + " /** @type {number} */", + " //@ts-ignore", + " const color = gdjs.rgbOrHexStringToNumber(this.object._getColor())", + " /** @type {THREE.LineBasicMaterial} */", + " //@ts-ignore", + " const coneMaterial = this.cone.material;", + " coneMaterial.color.set(color);", + " /** @type {THREE.LineBasicMaterial} */", + " //@ts-ignore", + " const centerLineMaterial = this.centerLine.material;", + " centerLineMaterial.color.set(color);", + " /** @type {THREE.MeshBasicMaterial} */", + " //@ts-ignore", + " const originBoxMaterial = this.originBox.material;", + " originBoxMaterial.color.set(color);", + " }", + "}", + "", + "/**", + " * @param {string} colorString", + " * @param {THREE.Color} threeColor", + " */", + "const setThreeColor = (colorString, threeColor) => {", + " const integerColor = gdjs.rgbOrHexToRGBColor(colorString);", + " threeColor.r = integerColor[0] / 255;", + " threeColor.g = integerColor[1] / 255;", + " threeColor.b = integerColor[2] / 255;", + "};", + "", + "class SpotLightAdapter {", + " /**", + " * @param object {gdjs.CustomRuntimeObject3D}", + " * @param spotLight {THREE.SpotLight}", + " */", + " constructor(object, spotLight) {", + " this.object = object;", + " this.spotLight = spotLight;", + " }", + "", + " /**", + " * @param targetX {number}", + " * @param targetY {number}", + " * @param targetZ {number}", + " */", + " lookAtPosition(targetX, targetY, targetZ) {", + " // Remove from the parent to avoid the scene scale of -1 on Y to mess with the formula.", + " const parent = this.spotLight.parent;", + " this.spotLight.parent = null;", + " this.spotLight.lookAt(targetX, targetY, targetZ);", + " this.spotLight.parent = parent;", + "", + " // Angle setters update Three.js angles, so we save them first.", + " const rotationX = gdjs.toDegrees(this.spotLight.rotation.x);", + " const rotationY = gdjs.toDegrees(this.spotLight.rotation.y);", + " const rotationZ = gdjs.toDegrees(this.spotLight.rotation.z);", + " this.object.setRotationX(rotationX);", + " this.object.setRotationY(rotationY);", + " this.object.setAngle(rotationZ + 90);", + " }", + "", + " /**", + " * @param color {string}", + " */", + " setColor(color) {", + " setThreeColor(color, this.spotLight.color);", + " }", + "", + " /**", + " * @param intensity {number}", + " */", + " setIntensity(intensity) {", + " this.spotLight.intensity = intensity;", + " }", + "", + " /**", + " * @param decay {number}", + " */", + " setDecay(decay) {", + " this.spotLight.decay = decay;", + " }", + "", + " /**", + " * @param coneAngle {number}", + " */", + " setConeAngle(coneAngle) {", + " this.spotLight.angle = coneAngle * Math.PI / 180;", + " }", + "", + " /**", + " * @param smoothing {number}", + " */", + " setSmoothing(smoothing) {", + " this.spotLight.penumbra = smoothing;", + " }", + "", + " /**", + " * @param isCastingShadow {boolean}", + " */", + " setCastingShadow(isCastingShadow) {", + " this.spotLight.castShadow = isCastingShadow;", + " }", + "", + " /**", + " * @param shadowQuality {\"Low\" | \"Medium\" | \"High\"}", + " */", + " setShadowQuality(shadowQuality) {", + " let size = 512;", + " switch (shadowQuality) {", + " case \"Low\":", + " size = 256;", + " break;", + " case \"Medium\":", + " size = 512;", + " break;", + " case \"High\":", + " size = 1024;", + " break;", + " }", + " this.spotLight.shadow.mapSize.width = size;", + " this.spotLight.shadow.mapSize.height = size;", + " }", + "", + " /**", + " * @param value {number}", + " */", + " setShadowCameraNearPlane(value) {", + " this.spotLight.shadow.camera.near = value;", + " }", + "", + " /**", + " * @param value {number}", + " */", + " setShadowCameraFarPlane(value) {", + " this.spotLight.shadow.camera.far = value;", + " }", + "", + " /**", + " * @param resourceName {string}", + " */", + " setImage(resourceName) {", + " if (!resourceName) {", + " this.spotLight.map = null;", + " return;", + " }", + " const texture = game.getImageManager().getThreeTexture(resourceName);", + " this.spotLight.map = texture;", + " }", + "}", + "", + "", + "", + "//@ts-ignore", + "gdjs.__spotLight3DExtension = {", + " SpotLight3DRenderer,", + " SpotLightAdapter,", + " SpotLightHelper,", + "}" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "parameters": [], + "objectGroups": [] + } + ], + "eventsFunctionsFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "functionName": "DefineHelperClasses" + } + ] + }, + "eventsBasedBehaviors": [], + "eventsBasedObjects": [ + { + "areaMaxX": 40, + "areaMaxY": 40, + "areaMaxZ": 40, + "areaMinX": -40, + "areaMinY": -40, + "areaMinZ": -40, + "defaultName": "SpotLight", + "description": "Light up a cone like a flashlight.", + "fullName": "3D spot light", + "is3D": true, + "isUsingLegacyInstancesRenderer": false, + "name": "SpotLight3D", + "objects": [ + { + "assetStoreId": "", + "name": "Placeholder", + "persistentUuid": "6786eefd-3c18-4fcb-b3ae-95b07f953045", + "type": "Scene3D::Cube3DObject", + "variables": [], + "effects": [], + "behaviors": [], + "content": { + "width": 80, + "height": 80, + "depth": 80, + "enableTextureTransparency": false, + "facesOrientation": "Z", + "frontFaceResourceName": "", + "backFaceResourceName": "", + "backFaceUpThroughWhichAxisRotation": "X", + "leftFaceResourceName": "", + "rightFaceResourceName": "", + "topFaceResourceName": "", + "bottomFaceResourceName": "", + "frontFaceVisible": true, + "backFaceVisible": true, + "leftFaceVisible": true, + "rightFaceVisible": true, + "topFaceVisible": true, + "bottomFaceVisible": true, + "frontFaceResourceRepeat": false, + "backFaceResourceRepeat": false, + "leftFaceResourceRepeat": false, + "rightFaceResourceRepeat": false, + "topFaceResourceRepeat": false, + "bottomFaceResourceRepeat": false, + "materialType": "Basic", + "tint": "255;255;255", + "isCastingShadow": false, + "isReceivingShadow": false + } + } + ], + "objectsFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "objectName": "Placeholder" + } + ] + }, + "objectsGroups": [], + "layers": [ + { + "ambientLightColorB": 200, + "ambientLightColorG": 200, + "ambientLightColorR": 200, + "camera2DPlaneMaxDrawingDistance": 5000, + "camera3DFarPlaneDistance": 10000, + "camera3DFieldOfView": 45, + "camera3DNearPlaneDistance": 3, + "cameraType": "", + "followBaseLayerCamera": false, + "isLightingLayer": false, + "isLocked": false, + "name": "", + "renderingType": "", + "visibility": true, + "cameras": [ + { + "defaultSize": true, + "defaultViewport": true, + "height": 0, + "viewportBottom": 1, + "viewportLeft": 0, + "viewportRight": 1, + "viewportTop": 0, + "width": 0 + } + ], + "effects": [] + } + ], + "instances": [ + { + "angle": 0, + "customSize": true, + "depth": 80, + "height": 80, + "layer": "", + "name": "Placeholder", + "persistentUuid": "1310c768-04d4-4133-98d9-37d836f8a990", + "width": 80, + "x": -40, + "y": -40, + "z": -40, + "zOrder": 1, + "numberProperties": [], + "stringProperties": [], + "initialVariables": [] + } + ], + "editionSettings": { + "grid": false, + "gridType": "rectangular", + "gridWidth": 32, + "gridHeight": 32, + "gridDepth": 32, + "gridOffsetX": 0, + "gridOffsetY": 0, + "gridOffsetZ": 0, + "gridColor": 10401023, + "gridAlpha": 0.8, + "snap": false, + "zoomFactor": 4.204424638835088, + "windowMask": false, + "selectedLayer": "", + "gameEditorMode": "instances-editor" + }, + "eventsFunctions": [ + { + "fullName": "", + "functionType": "Action", + "name": "onCreated", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SpotLight3D::DefineHelperClasses" + }, + "parameters": [ + "", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const {", + " SpotLightAdapter,", + " SpotLight3DRenderer,", + " SpotLightHelper,", + " //@ts-ignore", + "} = gdjs.__spotLight3DExtension;", + "", + "/** @type {gdjs.CustomRuntimeObject3D} */", + "//@ts-ignore", + "const object = objects[0];", + "", + "// Here runtimeScene is the gdjs.CustomRuntimeObjectInstanceContainer inside the custom object.", + "const gameScene = object.getRuntimeScene();", + "const game = runtimeScene.getGame();", + "", + "// This is a hack that may break in future releases.", + "// Replace the group that would hold children objects by the emmiter.", + "const layer = object.getInstanceContainer().getLayer(object.getLayer());", + "const group = object.getRenderer()._threeGroup;", + "layer.getRenderer().remove3DRendererObject(group);", + "", + "const spotLight = new THREE.SpotLight(0xffffff);", + "spotLight.position.copy(group.position);", + "spotLight.rotation.copy(group.rotation);", + "spotLight.shadow.camera.fov = 30;", + "spotLight.add(spotLight.target);", + "spotLight.target.position.x = 100;", + "spotLight.up.set(0, 0, 1);", + "spotLight.shadow.camera.up.set(0, 0, 1);", + "//@ts-ignore", + "object.__spotLight = spotLight;", + "//@ts-ignore", + "object.__spotLightAdapter = new SpotLightAdapter(object, spotLight);", + "", + "const spotLight3DRenderer = new SpotLight3DRenderer(object, object._instanceContainer, object.getInstanceContainer());", + "object._renderer = spotLight3DRenderer;", + "if (game.isInGameEdition && game.isInGameEdition()) {", + " const spotLightHelper = new SpotLightHelper(object);", + " spotLightHelper.add(spotLight);", + " spotLight3DRenderer._threeGroup = spotLightHelper;", + " layer.getRenderer().add3DRendererObject(spotLightHelper);", + "}", + "else {", + " spotLight3DRenderer._threeGroup = spotLight;", + " // The light is added in Three.js scene from doStepPostEvents", + " spotLight3DRenderer._threeGroup = spotLight;", + " layer.getRenderer().add3DRendererObject(spotLight);", + "}", + "", + "spotLight.updateMatrixWorld(true);", + "", + "// Allow to tween the light color.", + "//@ts-ignore", + "if (!object.setColor && !object.getColor) {", + " const prototype = Object.getPrototypeOf(object);", + " prototype.setColor = function(tint) {", + " this.SetColor(tint, eventsFunctionContext);", + " }", + " prototype.getColor = function() {", + " return this.Color(eventsFunctionContext);", + " }", + "}", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": true + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SpotLight3D::SpotLight3D::UpdateFromProperties" + }, + "parameters": [ + "Object", + "" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "SpotLight3D::SpotLight3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "Action", + "name": "onDestroy", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "/** @type {gdjs.CustomRuntimeObject} */", + "const object = objects[0];", + "// Here runtimeScene is the gdjs.CustomRuntimeObjectInstanceContainer inside the custom object.", + "const gameScene = object.getRuntimeScene();", + "", + "object.__spotLight.dispose();", + "object.get3DRendererObject().dispose();", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "SpotLight3D::SpotLight3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "Action", + "name": "onHotReloading", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SpotLight3D::SpotLight3D::UpdateFromProperties" + }, + "parameters": [ + "Object", + "" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "SpotLight3D::SpotLight3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "Update from properties.", + "fullName": "Update from properties", + "functionType": "Action", + "name": "UpdateFromProperties", + "private": true, + "sentence": "Update from properties of _PARAM0_", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SpotLight3D::SpotLight3D::SetColor" + }, + "parameters": [ + "Object", + "=", + "Color", + "" + ] + }, + { + "type": { + "value": "SpotLight3D::SpotLight3D::SetIntensity" + }, + "parameters": [ + "Object", + "=", + "Intensity", + "" + ] + }, + { + "type": { + "value": "SpotLight3D::SpotLight3D::SetDecay" + }, + "parameters": [ + "Object", + "=", + "Decay", + "" + ] + }, + { + "type": { + "value": "SpotLight3D::SpotLight3D::SetConeAngle" + }, + "parameters": [ + "Object", + "=", + "ConeAngle", + "" + ] + }, + { + "type": { + "value": "SpotLight3D::SpotLight3D::SetSmoothing" + }, + "parameters": [ + "Object", + "=", + "Smoothing", + "" + ] + }, + { + "type": { + "value": "SpotLight3D::SpotLight3D::SetShadowQuality" + }, + "parameters": [ + "Object", + "=", + "ShadowQuality", + "" + ] + }, + { + "type": { + "value": "SpotLight3D::SpotLight3D::SetShadowCameraNearPlane" + }, + "parameters": [ + "Object", + "=", + "ShadowCameraNearPlane", + "" + ] + }, + { + "type": { + "value": "SpotLight3D::SpotLight3D::SetShadowCameraFarPlane" + }, + "parameters": [ + "Object", + "=", + "ShadowCameraFarPlane", + "" + ] + }, + { + "type": { + "value": "SpotLight3D::SpotLight3D::UpdateImage" + }, + "parameters": [ + "Object", + "" + ] + }, + { + "type": { + "value": "SpotLight3D::SpotLight3D::UpdateHelper" + }, + "parameters": [ + "Object", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "IsCastingShadow", + "=", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SpotLight3D::SpotLight3D::SetCastingShadow" + }, + "parameters": [ + "Object", + "no", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "IsCastingShadow", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SpotLight3D::SpotLight3D::SetCastingShadow" + }, + "parameters": [ + "Object", + "yes", + "" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "SpotLight3D::SpotLight3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "Update helper", + "functionType": "Action", + "name": "UpdateHelper", + "private": true, + "sentence": "Update graphical helper of _PARAM0_", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const game = runtimeScene.getGame();", + "if (game.isInGameEdition && game.isInGameEdition()) {", + " const spotLightHelper = objects[0].get3DRendererObject();", + " spotLightHelper.update();", + "}", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "SpotLight3D::SpotLight3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "Update image", + "functionType": "Action", + "name": "UpdateImage", + "private": true, + "sentence": "Update the image of _PARAM0_", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "/** @type {gdjs.CustomRuntimeObject3D} */\r", + "const object = objects[0];\r", + "const resourceName = object._getImage();\r", + "object.__spotLightAdapter.setImage(resourceName);" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "SpotLight3D::SpotLight3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the cone angle of the light.", + "fullName": "Cone angle", + "functionType": "ExpressionAndCondition", + "name": "ConeAngle", + "sentence": "the cone angle", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "ConeAngle" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "SpotLight3D::SpotLight3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "ConeAngle", + "name": "SetConeAngle", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "ConeAngle", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const coneAngle = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__spotLightAdapter.setConeAngle(coneAngle);\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "SpotLight3D::SpotLight3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the color of the light.", + "fullName": "Color", + "functionType": "ExpressionAndCondition", + "name": "Color", + "sentence": "the color", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnString" + }, + "parameters": [ + "Color" + ] + } + ] + } + ], + "expressionType": { + "type": "color" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "SpotLight3D::SpotLight3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "Color", + "name": "SetColor", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "Color", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const color = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__spotLightAdapter.setColor(color);" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "SpotLight3D::SpotLight3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the smoothing of the light . Percent of the spotlight cone that is attenuated due to penumbra (between 0 and 1).", + "fullName": "Smoothing", + "functionType": "ExpressionAndCondition", + "name": "Smoothing", + "sentence": "the smoothing", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "Smoothing" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "SpotLight3D::SpotLight3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "Smoothing", + "name": "SetSmoothing", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Smoothing", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const value = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__spotLightAdapter.setSmoothing(value);" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "SpotLight3D::SpotLight3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the intensity of the light.", + "fullName": "Intensity", + "functionType": "ExpressionAndCondition", + "name": "Intensity", + "sentence": "the intensity", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "Intensity" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "SpotLight3D::SpotLight3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "Intensity", + "name": "SetIntensity", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Intensity", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const value = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__spotLightAdapter.setIntensity(value);\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "SpotLight3D::SpotLight3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the decay of the light. The amount the light dims along the distance of the light.", + "fullName": "Decay", + "functionType": "ExpressionAndCondition", + "name": "Decay", + "sentence": "the decay", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "Decay" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "SpotLight3D::SpotLight3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "Decay", + "name": "SetDecay", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Decay", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const value = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__spotLightAdapter.setSmoothing(value);" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "SpotLight3D::SpotLight3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "Check if the light is casting shadows.", + "fullName": "Shadow casting", + "functionType": "Condition", + "group": "3D spot light shadow configuration", + "name": "IsCastingShadow", + "sentence": "_PARAM0_ casting shadows", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "IsCastingShadow", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetReturnBoolean" + }, + "parameters": [ + "True" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "SpotLight3D::SpotLight3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "Change if the light is casting shadows.", + "fullName": "Shadow casting", + "functionType": "Action", + "group": "3D spot light shadow configuration", + "name": "SetCastingShadow", + "sentence": "_PARAM0_ casting shadows: _PARAM1_", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "Value", + "False", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetBooleanVariable" + }, + "parameters": [ + "IsCastingShadow", + "False", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "Value", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetBooleanVariable" + }, + "parameters": [ + "IsCastingShadow", + "True", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const value = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__spotLightAdapter.setCastingShadow(value);" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "SpotLight3D::SpotLight3D", + "type": "object" + }, + { + "defaultValue": "yes", + "description": "Shadow casting", + "name": "Value", + "optional": true, + "type": "yesorno" + } + ], + "objectGroups": [] + }, + { + "description": "Rotate the light to light up a position.", + "fullName": "Look at position", + "functionType": "Action", + "group": "Angle", + "name": "LookAtPosition", + "sentence": "_PARAM0_ look at _PARAM1_ ; _PARAM2_ ; _PARAM3_", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const targetX = eventsFunctionContext.getArgument(\"TargetX\");\r", + "const targetY = eventsFunctionContext.getArgument(\"TargetY\");\r", + "const targetZ = eventsFunctionContext.getArgument(\"TargetZ\");\r", + "\r", + "object.__spotLightAdapter.lookAtPosition(targetX, targetY, targetZ);" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "SpotLight3D::SpotLight3D", + "type": "object" + }, + { + "description": "Target X", + "name": "TargetX", + "type": "expression" + }, + { + "description": "Target Y", + "name": "TargetY", + "type": "expression" + }, + { + "description": "Target Z", + "name": "TargetZ", + "type": "expression" + } + ], + "objectGroups": [] + }, + { + "description": "Rotate the light to light up an object.", + "fullName": "Look at object", + "functionType": "Action", + "group": "Angle", + "name": "LookAtObject", + "sentence": "_PARAM0_ look at _PARAM1_", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SpotLight3D::SpotLight3D::LookAtPosition" + }, + "parameters": [ + "Object", + "Target.CenterX()", + "Target.CenterY()", + "Target.Object3D::CenterZ()", + "" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "SpotLight3D::SpotLight3D", + "type": "object" + }, + { + "description": "Object", + "name": "Target", + "type": "objectList" + }, + { + "description": "3D capability", + "name": "Object3D", + "supplementaryInformation": "Scene3D::Base3DBehavior", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the shadow quality of the light.", + "fullName": "Shadow quality", + "functionType": "ExpressionAndCondition", + "group": "3D spot light shadow configuration", + "name": "ShadowQuality", + "sentence": "the shadow quality", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnString" + }, + "parameters": [ + "ShadowQuality" + ] + } + ] + } + ], + "expressionType": { + "supplementaryInformation": "[\"Low\",\"Medium\",\"High\"]", + "type": "stringWithSelector" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "SpotLight3D::SpotLight3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "ShadowQuality", + "group": "3D spot light shadow configuration", + "name": "SetShadowQuality", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "ShadowQuality", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const value = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__spotLightAdapter.setShadowQuality(value);" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "SpotLight3D::SpotLight3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the shadow camera near plane of the light.", + "fullName": "Shadow camera near plane", + "functionType": "ExpressionAndCondition", + "group": "3D spot light shadow configuration", + "name": "ShadowCameraNearPlane", + "sentence": "the shadow camera near plane", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "ShadowCameraNearPlane" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "SpotLight3D::SpotLight3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "ShadowCameraNearPlane", + "group": "3D spot light shadow configuration", + "name": "SetShadowCameraNearPlane", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "ShadowCameraNearPlane", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const value = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__spotLightAdapter.setShadowCameraNearPlane(value);\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "SpotLight3D::SpotLight3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the shadow camera far plane of the light.", + "fullName": "Shadow camera far plane", + "functionType": "ExpressionAndCondition", + "group": "3D spot light shadow configuration", + "name": "ShadowCameraFarPlane", + "sentence": "the shadow camera far plane", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "ShadowCameraFarPlane" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "SpotLight3D::SpotLight3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "ShadowCameraFarPlane", + "group": "3D spot light shadow configuration", + "name": "SetShadowCameraFarPlane", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "ShadowCameraFarPlane", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const value = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__spotLightAdapter.setShadowCameraFarPlane(value);\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "SpotLight3D::SpotLight3D", + "type": "object" + } + ], + "objectGroups": [] + } + ], + "eventsFunctionsFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "functionName": "onCreated" + }, + { + "functionName": "onDestroy" + }, + { + "functionName": "onHotReloading" + }, + { + "functionName": "UpdateFromProperties" + }, + { + "functionName": "UpdateHelper" + }, + { + "functionName": "UpdateImage" + }, + { + "folderName": "Angle", + "children": [ + { + "functionName": "LookAtPosition" + }, + { + "functionName": "LookAtObject" + } + ] + }, + { + "functionName": "Color" + }, + { + "functionName": "SetColor" + }, + { + "functionName": "Intensity" + }, + { + "functionName": "SetIntensity" + }, + { + "functionName": "Decay" + }, + { + "functionName": "SetDecay" + }, + { + "functionName": "ConeAngle" + }, + { + "functionName": "SetConeAngle" + }, + { + "functionName": "Smoothing" + }, + { + "functionName": "SetSmoothing" + }, + { + "folderName": "3D spot light shadow configuration", + "children": [ + { + "functionName": "IsCastingShadow" + }, + { + "functionName": "SetCastingShadow" + }, + { + "functionName": "ShadowQuality" + }, + { + "functionName": "SetShadowQuality" + }, + { + "functionName": "ShadowCameraNearPlane" + }, + { + "functionName": "SetShadowCameraNearPlane" + }, + { + "functionName": "ShadowCameraFarPlane" + }, + { + "functionName": "SetShadowCameraFarPlane" + } + ] + } + ] + }, + "propertyDescriptors": [ + { + "value": "255;255;255", + "type": "Color", + "label": "Color", + "name": "Color" + }, + { + "value": "30", + "type": "Number", + "unit": "DegreeAngle", + "label": "Cone angle", + "group": "Cone", + "name": "ConeAngle" + }, + { + "value": "1", + "type": "Number", + "label": "Intensity", + "name": "Intensity" + }, + { + "value": "1", + "type": "Number", + "label": "Smoothing", + "description": "Percent of the spotlight cone that is attenuated due to penumbra (between 0 and 1).", + "group": "Cone", + "name": "Smoothing" + }, + { + "value": "2", + "type": "Number", + "label": "Decay", + "description": "The amount the light dims along the distance of the light.", + "name": "Decay" + }, + { + "value": "", + "type": "Resource", + "label": "Image", + "description": "Shadow casting must be enable for the image to have any effect.", + "group": "Shadow", + "extraInformation": [ + "image" + ], + "choices": [], + "advanced": true, + "name": "Image" + }, + { + "value": "true", + "type": "Boolean", + "label": "Shadow casting", + "group": "Shadow", + "name": "IsCastingShadow" + }, + { + "value": "1000", + "type": "Number", + "label": "Visibilty distance", + "name": "VisibilityDistanceMax" + }, + { + "value": "Medium", + "type": "Choice", + "label": "Shadow quality", + "group": "Shadow", + "choices": [ + { + "label": "", + "value": "Low" + }, + { + "label": "", + "value": "Medium" + }, + { + "label": "", + "value": "High" + } + ], + "name": "ShadowQuality" + }, + { + "value": "100", + "type": "Number", + "unit": "Pixel", + "label": "Shadow camera near plane", + "group": "Shadow", + "advanced": true, + "name": "ShadowCameraNearPlane" + }, + { + "value": "2000", + "type": "Number", + "unit": "Pixel", + "label": "Shadow camera far plane", + "group": "Shadow", + "advanced": true, + "name": "ShadowCameraFarPlane" + } + ], + "propertiesFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "propertyName": "Color" + }, + { + "propertyName": "Intensity" + }, + { + "propertyName": "Decay" + }, + { + "propertyName": "VisibilityDistanceMax" + }, + { + "folderName": "Cone", + "children": [ + { + "propertyName": "ConeAngle" + }, + { + "propertyName": "Smoothing" + } + ] + }, + { + "folderName": "Shadow", + "children": [ + { + "propertyName": "IsCastingShadow" + }, + { + "propertyName": "ShadowQuality" + }, + { + "propertyName": "Image" + }, + { + "propertyName": "ShadowCameraNearPlane" + }, + { + "propertyName": "ShadowCameraFarPlane" + } + ] + } + ] + } + } + ] +} \ No newline at end of file From 08b66d758657df463cef83d15e71597d9b115c24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Davy=20H=C3=A9lard?= Date: Tue, 31 Mar 2026 10:51:12 +0200 Subject: [PATCH 02/22] Remove useless line --- extensions/reviewed/SpotLight3D.json | 1 - 1 file changed, 1 deletion(-) diff --git a/extensions/reviewed/SpotLight3D.json b/extensions/reviewed/SpotLight3D.json index 14e787492..b2b0bc7af 100644 --- a/extensions/reviewed/SpotLight3D.json +++ b/extensions/reviewed/SpotLight3D.json @@ -590,7 +590,6 @@ "const spotLight = new THREE.SpotLight(0xffffff);", "spotLight.position.copy(group.position);", "spotLight.rotation.copy(group.rotation);", - "spotLight.shadow.camera.fov = 30;", "spotLight.add(spotLight.target);", "spotLight.target.position.x = 100;", "spotLight.up.set(0, 0, 1);", From abc08a5475dd5eefd81003af9e2c8db6b63adbb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Davy=20H=C3=A9lard?= Date: Tue, 31 Mar 2026 13:05:20 +0200 Subject: [PATCH 03/22] Declare JS usage --- scripts/lib/ExtensionsValidatorExceptions.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/scripts/lib/ExtensionsValidatorExceptions.js b/scripts/lib/ExtensionsValidatorExceptions.js index fcf93cbdb..989417016 100644 --- a/scripts/lib/ExtensionsValidatorExceptions.js +++ b/scripts/lib/ExtensionsValidatorExceptions.js @@ -511,6 +511,21 @@ const extensionsAllowedProperties = { runtimeSceneAllowedProperties: [], javaScriptObjectAllowedProperties: [], }, + SpotLight3D: { + gdjsAllowedProperties: [ + '__spotLight3DExtension', + 'CustomRuntimeObject', + 'CustomRuntimeObjectInstanceContainer', + 'CustomRuntimeObject3DRenderer', + 'CustomRuntimeObject3D', + 'registerRuntimeScenePreEventsCallback', + 'registerInGameEditorPostStepCallback', + 'scene3d', + ], + gdjsEvtToolsAllowedProperties: ['camera'], + runtimeSceneAllowedProperties: ['__spotLight3DExtension'], + javaScriptObjectAllowedProperties: ['getPrototypeOf'], + }, SpriteMultitouchJoystick: { gdjsAllowedProperties: [], gdjsEvtToolsAllowedProperties: ['input'], From d8043ebfde6ab5e7d5f736cae1ce7bfb190e101c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Davy=20H=C3=A9lard?= Date: Tue, 31 Mar 2026 19:31:21 +0200 Subject: [PATCH 04/22] Better culling --- extensions/reviewed/SpotLight3D.json | 238 +++++++++++++++++++-------- 1 file changed, 172 insertions(+), 66 deletions(-) diff --git a/extensions/reviewed/SpotLight3D.json b/extensions/reviewed/SpotLight3D.json index b2b0bc7af..0901d44a9 100644 --- a/extensions/reviewed/SpotLight3D.json +++ b/extensions/reviewed/SpotLight3D.json @@ -40,23 +40,16 @@ " return;", "}", "", + "/**", + " * @typedef {gdjs.RuntimeObject & {__cameraDistance: number, __spotLight: THREE.SpotLight, _getIsCastingShadow: () => boolean}} SpotLightRuntimeObject", + " */", + "", "const game = runtimeScene.getGame();", + "const isInGameEdition = game.isInGameEdition && game.isInGameEdition();", "", - "const visibleLightCountMax = 10;", - "const editorVisibleLightCountMax = 4;", - "let visibleLightCount = 0;", - "gdjs.registerRuntimeScenePreEventsCallback(", - " runtimeScene => {", - " visibleLightCount = 0;", - " });", - "if (gdjs.registerInGameEditorPostStepCallback) {", - " gdjs.registerInGameEditorPostStepCallback(", - " runtimeScene => {", - " visibleLightCount = 0;", - " });", - "}", "", "class SpotLight3DRenderer extends gdjs.CustomRuntimeObject3DRenderer {", + "", " constructor(", " object,", " instanceContainer,", @@ -92,35 +85,30 @@ "", " threeObject3D.visible = !this._object.hidden;", "", + " /** @type {THREE.SpotLight} */", + " //@ts-ignore", + " const spotLight = object.__spotLight;", + "", " const editor = game.getInGameEditor ? game.getInGameEditor() : null;", "", - " if (editor) {", - " const selectedObject = editor._selection.getLastSelectedObject();", - " let parentObject = object;", - " let isSelected = parentObject === selectedObject;", - " const isDirectlySelected = isSelected;", - " while (!isSelected && parentObject.getInstanceContainer()", - " //@ts-ignore", - " .getOwner) {", - " parentObject = parentObject.getInstanceContainer()", - " //@ts-ignore", - " .getOwner();", + " spotLight.castShadow = false;", + " spotLight.visible = false;", + "", + " if (!object.isHidden()) {", + " let isSelected = false;", + " if (editor) {", + " const selectedObject = editor._selection.getLastSelectedObject();", + " let parentObject = object;", " isSelected = parentObject === selectedObject;", + " while (!isSelected && parentObject.getInstanceContainer()", + " //@ts-ignore", + " .getOwner) {", + " parentObject = parentObject.getInstanceContainer()", + " //@ts-ignore", + " .getOwner();", + " isSelected = parentObject === selectedObject;", + " }", " }", - " /** @type {THREE.SpotLight} */", - " //@ts-ignore", - " const spotLight = object.__spotLight;", - " spotLight.visible = isDirectlySelected || (isSelected && visibleLightCount < editorVisibleLightCountMax);", - " if (spotLight.visible) {", - " visibleLightCount++;", - " }", - " }", - " else if (object.isHidden()) {", - " threeObject3D.visible = false;", - " }", - " else if (visibleLightCount >= visibleLightCountMax) {", - " threeObject3D.visible = false;", - " } else {", " let rootObject = object;", " while (rootObject.getInstanceContainer()", " //@ts-ignore", @@ -129,22 +117,28 @@ " //@ts-ignore", " .getOwner();", " }", - " const layerName = rootObject.getLayer();", - "", - " const runtimeScene = object.getRuntimeScene();", - " const cameraX = gdjs.evtTools.camera.getCameraX(runtimeScene, layerName, 0);", - " const cameraY = gdjs.evtTools.camera.getCameraY(runtimeScene, layerName, 0);", - " const cameraZ = gdjs.scene3d.camera.getCameraZ(runtimeScene, layerName, 0);", - " const deltaX = rootObject.getX() - cameraX;", - " const deltaY = rootObject.getY() - cameraY;", - " const deltaZ = rootObject.getZ() - cameraZ;", + " const runtimeScene = rootObject.getRuntimeScene();", + " let distanceSq = 0;", + " if (!isSelected) {", + " const layerName = rootObject.getLayer();", + " const cameraX = gdjs.evtTools.camera.getCameraX(runtimeScene, layerName, 0);", + " const cameraY = gdjs.evtTools.camera.getCameraY(runtimeScene, layerName, 0);", + " const cameraZ = gdjs.scene3d.camera.getCameraZ(runtimeScene, layerName, 0);", + " const deltaX = rootObject.getX() - cameraX;", + " const deltaY = rootObject.getY() - cameraY;", + " const deltaZ = rootObject.getZ() - cameraZ;", + " distanceSq = deltaX * deltaX + deltaY * deltaY + deltaZ * deltaZ;", + " }", " //@ts-ignore", " const distanceMax = object._getVisibilityDistanceMax();", - " const isWithInRange = deltaX * deltaX + deltaY * deltaY + deltaZ * deltaZ <= distanceMax * distanceMax;", - " threeObject3D.visible = isWithInRange;", + " const isWithInRange = distanceSq <= distanceMax * distanceMax;", " if (isWithInRange) {", - " visibleLightCount++;", + " getSpotLightManager(", + " //@ts-ignore", + " runtimeScene", + " ).applyVisibilityAndShadow(object, distanceSq);", " }", + "", " }", "", " // Force the visibility to be checked every frame", @@ -152,6 +146,119 @@ " }", "}", "", + "gdjs.registerRuntimeScenePreEventsCallback(", + " runtimeScene => {", + " getSpotLightManager(", + " //@ts-ignore", + " runtimeScene.getScene()", + " ).clear();", + " });", + "if (gdjs.registerInGameEditorPostStepCallback) {", + " gdjs.registerInGameEditorPostStepCallback(", + " inGameEditor => {", + " getSpotLightManager(", + " //@ts-ignore", + " inGameEditor.getEditedInstanceContainer().getScene()", + " ).clear();", + " });", + "}", + "", + "/**", + " * Get the platforms manager of an instance container.", + " * @param {gdjs.RuntimeScene & {__spotLightManager: SpotLightManager}} runtimeScene", + " */", + "function getSpotLightManager(runtimeScene) {", + " if (!runtimeScene.__spotLightManager) {", + " // Create the shared manager if necessary.", + " runtimeScene.__spotLightManager = isInGameEdition ?", + " new SpotLightManager(4, 1) :", + " new SpotLightManager(20, 4);", + " }", + " return runtimeScene.__spotLightManager;", + "}", + "", + "/** @type {{isInserted: boolean, removedObject: SpotLightRuntimeObject | null}} */", + "const sortResult = { isInserted: false, removedObject: null };", + "", + "/**", + " * @param objects {Array}", + " * @param maxCount {number}", + " * @param object {SpotLightRuntimeObject}", + " * @param distance {number}", + " */", + "function insertByDistance(objects, maxCount, object, distance) {", + " sortResult.isInserted = false;", + " sortResult.removedObject = null;", + " let index = objects.length - 1;", + " for (; index >= 0; index--) {", + " const other = objects[index];", + " const otherDistance = other.__cameraDistance;", + " if (distance >= otherDistance) {", + " break;", + " }", + " }", + " if (index + 1 >= maxCount) {", + " return sortResult;", + " }", + " object.__cameraDistance = distance", + " objects.splice(index + 1, 0, object);", + " sortResult.isInserted = true;", + " if (objects.length > maxCount) {", + " sortResult.removedObject = objects.pop();", + " }", + " return sortResult;", + "}", + "", + "class SpotLightManager {", + " /** @type {Array} */", + " visibleObjects = new Array();", + " /** @type {Array} */", + " shadowObjects = new Array();", + " /** @type {number} */", + " maxCount;", + " /** @type {number} */", + " shadowCount;", + "", + " /**", + " * @param maxCount {number}", + " * @param shadowCount {number}", + " */", + " constructor(maxCount, shadowCount) {", + " this.maxCount = maxCount;", + " this.shadowCount = shadowCount;", + " }", + "", + " clear() {", + " this.visibleObjects.length = 0;", + " this.shadowObjects.length = 0;", + " }", + "", + " /**", + " * @param object {SpotLightRuntimeObject}", + " * @param distance {number}", + " */", + " applyVisibilityAndShadow(object, distance) {", + " if (object._getIsCastingShadow()) {", + " const { isInserted, removedObject } = insertByDistance(", + " this.shadowObjects, this.shadowCount, object, distance);", + " if (isInserted) {", + " object.__spotLight.castShadow = true;", + " }", + " if (removedObject) {", + " removedObject.__spotLight.castShadow = false;", + " }", + " }", + " const { isInserted, removedObject } = insertByDistance(", + " this.visibleObjects, this.maxCount, object, distance);", + " if (isInserted) {", + " object.__spotLight.visible = true;", + " }", + " if (removedObject) {", + " removedObject.__spotLight.visible = false;", + " }", + " }", + "}", + "", "const coneLength = 64;", "", "class SpotLightHelper extends THREE.Object3D {", @@ -333,7 +440,7 @@ " * @param isCastingShadow {boolean}", " */", " setCastingShadow(isCastingShadow) {", - " this.spotLight.castShadow = isCastingShadow;", + " // This is applied by applyVisibilityAndShadow", " }", "", " /**", @@ -343,14 +450,14 @@ " let size = 512;", " switch (shadowQuality) {", " case \"Low\":", - " size = 256;", - " break;", + " size = 256;", + " break;", " case \"Medium\":", - " size = 512;", - " break;", + " size = 512;", + " break;", " case \"High\":", - " size = 1024;", - " break;", + " size = 1024;", + " break;", " }", " this.spotLight.shadow.mapSize.width = size;", " this.spotLight.shadow.mapSize.height = size;", @@ -582,7 +689,7 @@ "const game = runtimeScene.getGame();", "", "// This is a hack that may break in future releases.", - "// Replace the group that would hold children objects by the emmiter.", + "// Replace the group that would hold children objects by the light.", "const layer = object.getInstanceContainer().getLayer(object.getLayer());", "const group = object.getRenderer()._threeGroup;", "layer.getRenderer().remove3DRendererObject(group);", @@ -603,14 +710,13 @@ "object._renderer = spotLight3DRenderer;", "if (game.isInGameEdition && game.isInGameEdition()) {", " const spotLightHelper = new SpotLightHelper(object);", + " spotLightHelper.rotation.order = 'ZYX';", " spotLightHelper.add(spotLight);", " spotLight3DRenderer._threeGroup = spotLightHelper;", " layer.getRenderer().add3DRendererObject(spotLightHelper);", "}", "else {", " spotLight3DRenderer._threeGroup = spotLight;", - " // The light is added in Three.js scene from doStepPostEvents", - " spotLight3DRenderer._threeGroup = spotLight;", " layer.getRenderer().add3DRendererObject(spotLight);", "}", "", @@ -1135,7 +1241,7 @@ "objectGroups": [] }, { - "description": "the smoothing of the light . Percent of the spotlight cone that is attenuated due to penumbra (between 0 and 1).", + "description": "the smoothing of the light. Percent of the spotlight cone that is attenuated due to penumbra (between 0 and 1).", "fullName": "Smoothing", "functionType": "ExpressionAndCondition", "name": "Smoothing", @@ -1361,7 +1467,7 @@ "const object = objects[0];\r", "const value = eventsFunctionContext.getArgument(\"Value\");\r", "\r", - "object.__spotLightAdapter.setSmoothing(value);" + "object.__spotLightAdapter.setDecay(value);" ], "parameterObjects": "Object", "useStrict": true, @@ -1996,7 +2102,7 @@ "value": "", "type": "Resource", "label": "Image", - "description": "Shadow casting must be enable for the image to have any effect.", + "description": "Shadow casting must be enabled for the image to have any effect.", "group": "Shadow", "extraInformation": [ "image" @@ -2013,9 +2119,9 @@ "name": "IsCastingShadow" }, { - "value": "1000", + "value": "2000", "type": "Number", - "label": "Visibilty distance", + "label": "Visibility distance", "name": "VisibilityDistanceMax" }, { @@ -2040,7 +2146,7 @@ "name": "ShadowQuality" }, { - "value": "100", + "value": "20", "type": "Number", "unit": "Pixel", "label": "Shadow camera near plane", From 6101d0a768db825d31f6d603485b1650ffcb846b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Davy=20H=C3=A9lard?= Date: Tue, 31 Mar 2026 21:58:40 +0200 Subject: [PATCH 05/22] Clearer sorting --- extensions/reviewed/SpotLight3D.json | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/extensions/reviewed/SpotLight3D.json b/extensions/reviewed/SpotLight3D.json index 0901d44a9..e13ed3f90 100644 --- a/extensions/reviewed/SpotLight3D.json +++ b/extensions/reviewed/SpotLight3D.json @@ -189,19 +189,20 @@ "function insertByDistance(objects, maxCount, object, distance) {", " sortResult.isInserted = false;", " sortResult.removedObject = null;", - " let index = objects.length - 1;", - " for (; index >= 0; index--) {", + " let insertionIndex = 0;", + " for (let index = objects.length - 1; index >= 0; index--) {", " const other = objects[index];", " const otherDistance = other.__cameraDistance;", " if (distance >= otherDistance) {", + " insertionIndex = index + 1;", " break;", " }", " }", - " if (index + 1 >= maxCount) {", + " if (insertionIndex >= maxCount) {", " return sortResult;", " }", " object.__cameraDistance = distance", - " objects.splice(index + 1, 0, object);", + " objects.splice(insertionIndex, 0, object);", " sortResult.isInserted = true;", " if (objects.length > maxCount) {", " sortResult.removedObject = objects.pop();", From 7ba14ad29f122ad35cea5c72d093ab43dadefb3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Davy=20H=C3=A9lard?= Date: Wed, 1 Apr 2026 11:38:09 +0200 Subject: [PATCH 06/22] Remove the distance property --- extensions/reviewed/SpotLight3D.json | 64 +++++++--------------------- 1 file changed, 16 insertions(+), 48 deletions(-) diff --git a/extensions/reviewed/SpotLight3D.json b/extensions/reviewed/SpotLight3D.json index e13ed3f90..a56307231 100644 --- a/extensions/reviewed/SpotLight3D.json +++ b/extensions/reviewed/SpotLight3D.json @@ -41,7 +41,7 @@ "}", "", "/**", - " * @typedef {gdjs.RuntimeObject & {__cameraDistance: number, __spotLight: THREE.SpotLight, _getIsCastingShadow: () => boolean}} SpotLightRuntimeObject", + " * @typedef {gdjs.CustomRuntimeObject3D & {__cameraDistance: number, __spotLight: THREE.SpotLight, _getIsCastingShadow: () => boolean, _getConeAngle: () => number, _getColor: () => string}} SpotLightRuntimeObject", " */", "", "const game = runtimeScene.getGame();", @@ -59,9 +59,9 @@ " }", "", " _updateThreeGroup() {", - " const object = this._object;", - " /** @type {THREE.SpotLight} */", + " /** @type {SpotLightRuntimeObject} */", " //@ts-ignore", + " const object = this._object;", " const threeObject3D = this.get3DRendererObject();", "", " threeObject3D.rotation.set(", @@ -85,8 +85,6 @@ "", " threeObject3D.visible = !this._object.hidden;", "", - " /** @type {THREE.SpotLight} */", - " //@ts-ignore", " const spotLight = object.__spotLight;", "", " const editor = game.getInGameEditor ? game.getInGameEditor() : null;", @@ -129,16 +127,10 @@ " const deltaZ = rootObject.getZ() - cameraZ;", " distanceSq = deltaX * deltaX + deltaY * deltaY + deltaZ * deltaZ;", " }", - " //@ts-ignore", - " const distanceMax = object._getVisibilityDistanceMax();", - " const isWithInRange = distanceSq <= distanceMax * distanceMax;", - " if (isWithInRange) {", - " getSpotLightManager(", - " //@ts-ignore", - " runtimeScene", - " ).applyVisibilityAndShadow(object, distanceSq);", - " }", - "", + " getSpotLightManager(", + " //@ts-ignore", + " runtimeScene", + " ).applyVisibilityAndShadow(object, distanceSq);", " }", "", " // Force the visibility to be checked every frame", @@ -263,17 +255,17 @@ "const coneLength = 64;", "", "class SpotLightHelper extends THREE.Object3D {", - " /** @type {gdjs.CustomRuntimeObject3D} */", + " /** @type {SpotLightRuntimeObject} */", " object;", - " /** @type {THREE.LineSegments} */", + " /** @type {THREE.LineSegments} */", " cone;", - " /** @type {THREE.LineSegments} */", + " /** @type {THREE.LineSegments} */", " centerLine;", - " /** @type {THREE.Mesh} */", + " /** @type {THREE.Mesh} */", " originBox;", "", "\t/**", - " * @param {gdjs.CustomRuntimeObject3D} object", + " * @param {SpotLightRuntimeObject} object", "\t */", " constructor(object) {", " super();", @@ -325,38 +317,23 @@ "", " dispose() {", " this.cone.geometry.dispose();", - " //@ts-ignore", " this.cone.material.dispose();", " this.centerLine.geometry.dispose();", - " //@ts-ignore", " this.centerLine.material.dispose();", " this.originBox.geometry.dispose();", - " //@ts-ignore", " this.originBox.material.dispose();", " }", "", " update() {", " /** @type {number} */", - " //@ts-ignore", " const coneAngle = gdjs.toRad(this.object._getConeAngle());", " const coneWidth = coneLength * Math.sin(coneAngle);", " const coneHeight = coneLength * Math.cos(coneAngle);", " this.cone.scale.set(coneHeight, coneWidth, coneWidth);", - " /** @type {number} */", - " //@ts-ignore", - " const color = gdjs.rgbOrHexStringToNumber(this.object._getColor())", - " /** @type {THREE.LineBasicMaterial} */", - " //@ts-ignore", - " const coneMaterial = this.cone.material;", - " coneMaterial.color.set(color);", - " /** @type {THREE.LineBasicMaterial} */", - " //@ts-ignore", - " const centerLineMaterial = this.centerLine.material;", - " centerLineMaterial.color.set(color);", - " /** @type {THREE.MeshBasicMaterial} */", - " //@ts-ignore", - " const originBoxMaterial = this.originBox.material;", - " originBoxMaterial.color.set(color);", + " const color = gdjs.rgbOrHexStringToNumber(this.object._getColor());", + " this.cone.material.color.set(color);", + " this.centerLine.material.color.set(color);", + " this.originBox.material.color.set(color);", " }", "}", "", @@ -2119,12 +2096,6 @@ "group": "Shadow", "name": "IsCastingShadow" }, - { - "value": "2000", - "type": "Number", - "label": "Visibility distance", - "name": "VisibilityDistanceMax" - }, { "value": "Medium", "type": "Choice", @@ -2177,9 +2148,6 @@ { "propertyName": "Decay" }, - { - "propertyName": "VisibilityDistanceMax" - }, { "folderName": "Cone", "children": [ From b6e76af04a37ce11b37161926cf5fa7909326286 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Davy=20H=C3=A9lard?= Date: Wed, 1 Apr 2026 12:02:10 +0200 Subject: [PATCH 07/22] Smaller default size --- extensions/reviewed/SpotLight3D.json | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/extensions/reviewed/SpotLight3D.json b/extensions/reviewed/SpotLight3D.json index a56307231..df6f16b6d 100644 --- a/extensions/reviewed/SpotLight3D.json +++ b/extensions/reviewed/SpotLight3D.json @@ -497,12 +497,12 @@ "eventsBasedBehaviors": [], "eventsBasedObjects": [ { - "areaMaxX": 40, - "areaMaxY": 40, - "areaMaxZ": 40, - "areaMinX": -40, - "areaMinY": -40, - "areaMinZ": -40, + "areaMaxX": 20, + "areaMaxY": 20, + "areaMaxZ": 20, + "areaMinX": -20, + "areaMinY": -20, + "areaMinZ": -20, "defaultName": "SpotLight", "description": "Light up a cone like a flashlight.", "fullName": "3D spot light", @@ -594,15 +594,15 @@ { "angle": 0, "customSize": true, - "depth": 80, - "height": 80, + "depth": 40, + "height": 40, "layer": "", "name": "Placeholder", "persistentUuid": "1310c768-04d4-4133-98d9-37d836f8a990", - "width": 80, - "x": -40, - "y": -40, - "z": -40, + "width": 40, + "x": -20, + "y": -20, + "z": -20, "zOrder": 1, "numberProperties": [], "stringProperties": [], @@ -621,7 +621,7 @@ "gridColor": 10401023, "gridAlpha": 0.8, "snap": false, - "zoomFactor": 4.204424638835088, + "zoomFactor": 9.06473532531222, "windowMask": false, "selectedLayer": "", "gameEditorMode": "instances-editor" From a9443cb14b3335eaaa17e3e820e9ab331f999d99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Davy=20H=C3=A9lard?= Date: Wed, 1 Apr 2026 12:08:41 +0200 Subject: [PATCH 08/22] Update JS usage --- scripts/lib/ExtensionsValidatorExceptions.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/scripts/lib/ExtensionsValidatorExceptions.js b/scripts/lib/ExtensionsValidatorExceptions.js index 989417016..27a361d4d 100644 --- a/scripts/lib/ExtensionsValidatorExceptions.js +++ b/scripts/lib/ExtensionsValidatorExceptions.js @@ -521,9 +521,14 @@ const extensionsAllowedProperties = { 'registerRuntimeScenePreEventsCallback', 'registerInGameEditorPostStepCallback', 'scene3d', + 'RuntimeScene', ], gdjsEvtToolsAllowedProperties: ['camera'], - runtimeSceneAllowedProperties: ['__spotLight3DExtension'], + runtimeSceneAllowedProperties: [ + '__spotLight3DExtension', + 'getScene', + '__spotLightManager', + ], javaScriptObjectAllowedProperties: ['getPrototypeOf'], }, SpriteMultitouchJoystick: { From 382f00bbb7cdf50d679abb2d954fa3f48dd5298e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Davy=20H=C3=A9lard?= Date: Wed, 1 Apr 2026 14:22:04 +0200 Subject: [PATCH 09/22] Add a property for the cone length --- extensions/reviewed/SpotLight3D.json | 119 +++++++++++++++++++++++++++ 1 file changed, 119 insertions(+) diff --git a/extensions/reviewed/SpotLight3D.json b/extensions/reviewed/SpotLight3D.json index df6f16b6d..a94d427c6 100644 --- a/extensions/reviewed/SpotLight3D.json +++ b/extensions/reviewed/SpotLight3D.json @@ -401,6 +401,13 @@ " }", "", " /**", + " * @param value {number}", + " */", + " setConeLength(value) {", + " this.spotLight.distance = value;", + " }", + "", + " /**", " * @param coneAngle {number}", " */", " setConeAngle(coneAngle) {", @@ -853,6 +860,17 @@ "" ] }, + { + "type": { + "value": "SpotLight3D::SpotLight3D::SetConeLength" + }, + "parameters": [ + "Object", + "=", + "ConeLength", + "" + ] + }, { "type": { "value": "SpotLight3D::SpotLight3D::SetConeAngle" @@ -1945,6 +1963,88 @@ } ], "objectGroups": [] + }, + { + "description": "the cone length of the light. 0 means no limit.", + "fullName": "Cone length", + "functionType": "ExpressionAndCondition", + "name": "ConeLength", + "sentence": "the cone length", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "ConeLength" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "SpotLight3D::SpotLight3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "ConeLength", + "name": "SetConeLength", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "ConeLength", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const value = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__spotLightAdapter.setConeLength(value);\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "SpotLight3D::SpotLight3D", + "type": "object" + } + ], + "objectGroups": [] } ], "eventsFunctionsFolderStructure": { @@ -1997,6 +2097,12 @@ { "functionName": "SetDecay" }, + { + "functionName": "ConeLength" + }, + { + "functionName": "SetConeLength" + }, { "functionName": "ConeAngle" }, @@ -2134,6 +2240,16 @@ "group": "Shadow", "advanced": true, "name": "ShadowCameraFarPlane" + }, + { + "value": "0", + "type": "Number", + "unit": "Pixel", + "label": "Cone length", + "description": "0 means no limit.", + "group": "Cone", + "advanced": true, + "name": "ConeLength" } ], "propertiesFolderStructure": { @@ -2154,6 +2270,9 @@ { "propertyName": "ConeAngle" }, + { + "propertyName": "ConeLength" + }, { "propertyName": "Smoothing" } From 403868797d9b5aae5b671ea891ab93e7401f1f08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Davy=20H=C3=A9lard?= Date: Wed, 1 Apr 2026 15:16:41 +0200 Subject: [PATCH 10/22] Fix shadow quality change --- extensions/reviewed/SpotLight3D.json | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/extensions/reviewed/SpotLight3D.json b/extensions/reviewed/SpotLight3D.json index a94d427c6..d27fdf38a 100644 --- a/extensions/reviewed/SpotLight3D.json +++ b/extensions/reviewed/SpotLight3D.json @@ -444,8 +444,18 @@ " size = 1024;", " break;", " }", - " this.spotLight.shadow.mapSize.width = size;", - " this.spotLight.shadow.mapSize.height = size;", + " const shadow = this.spotLight.shadow;", + " const mapSize = shadow.mapSize;", + " if (mapSize.width !== size) {", + " mapSize.set(size, size);", + "", + " // Force the recreation of the shadow map texture:", + " if (shadow.map) {", + " shadow.map.dispose();", + " shadow.map = null;", + " }", + " shadow.needsUpdate = true;", + " }", " }", "", " /**", @@ -669,8 +679,6 @@ "//@ts-ignore", "const object = objects[0];", "", - "// Here runtimeScene is the gdjs.CustomRuntimeObjectInstanceContainer inside the custom object.", - "const gameScene = object.getRuntimeScene();", "const game = runtimeScene.getGame();", "", "// This is a hack that may break in future releases.", From 520ce33f192d22787792e885920ff023a55cc5f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Davy=20H=C3=A9lard?= Date: Thu, 2 Apr 2026 14:05:51 +0200 Subject: [PATCH 11/22] Add a point light object --- .../{SpotLight3D.json => Light3D.json} | 2856 ++++++++++++++--- scripts/lib/ExtensionsValidatorExceptions.js | 6 +- 2 files changed, 2340 insertions(+), 522 deletions(-) rename extensions/reviewed/{SpotLight3D.json => Light3D.json} (53%) diff --git a/extensions/reviewed/SpotLight3D.json b/extensions/reviewed/Light3D.json similarity index 53% rename from extensions/reviewed/SpotLight3D.json rename to extensions/reviewed/Light3D.json index d27fdf38a..8204b6f89 100644 --- a/extensions/reviewed/SpotLight3D.json +++ b/extensions/reviewed/Light3D.json @@ -7,7 +7,7 @@ "gdevelopVersion": ">=5.5.222", "helpPath": "", "iconUrl": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0ibWRpLWZsYXNobGlnaHQiIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0Ij48cGF0aCBkPSJNOSwxMEw2LDVIMThMMTUsMTBIOU0xOCw0SDZWMkgxOFY0TTksMjJWMTFIMTVWMjJIOU0xMiwxM0ExLDEgMCAwLDAgMTEsMTRBMSwxIDAgMCwwIDEyLDE1QTEsMSAwIDAsMCAxMywxNEExLDEgMCAwLDAgMTIsMTNaIiAvPjwvc3ZnPg==", - "name": "SpotLight3D", + "name": "Light3D", "previewIconUrl": "https://asset-resources.gdevelop.io/public-resources/Icons/b627f8e676b3aac26945b2c38db0cfa787d34d2eff23755d4eb1289452f6cd28_flashlight.svg", "shortDescription": "Light up a cone like a flashlight.", "version": "1.0.0", @@ -24,18 +24,18 @@ "sceneVariables": [], "eventsFunctions": [ { - "description": "Define helper classes JavaScript code.", - "fullName": "Define helper classes", + "description": "Define spot light helper classes JavaScript code.", + "fullName": "Define spot light helper classes", "functionType": "Action", - "name": "DefineHelperClasses", + "name": "DefineSpotLightHelperClasses", "private": true, - "sentence": "Define helper classes JavaScript code", + "sentence": "Define spot light helper classes JavaScript code", "events": [ { "type": "BuiltinCommonInstructions::JsCode", "inlineCode": [ "//@ts-ignore", - "if (gdjs.__spotLight3DExtension) {", + "if (gdjs.__light3DExtension && gdjs.__light3DExtension.spot) {", " //@ts-ignore", " return;", "}", @@ -488,7 +488,8 @@ "", "", "//@ts-ignore", - "gdjs.__spotLight3DExtension = {", + "gdjs.__light3DExtension = gdjs.__light3DExtension || {};", + "gdjs.__light3DExtension.spot = {", " SpotLight3DRenderer,", " SpotLightAdapter,", " SpotLightHelper,", @@ -501,18 +502,2223 @@ ], "parameters": [], "objectGroups": [] + }, + { + "description": "Define point light helper classes JavaScript code.", + "fullName": "Define point light helper classes", + "functionType": "Action", + "name": "DefinePointLightHelperClasses", + "private": true, + "sentence": "Define point light helper classes JavaScript code", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "//@ts-ignore", + "if (gdjs.__light3DExtension && gdjs.__light3DExtension.point) {", + " //@ts-ignore", + " return;", + "}", + "", + "/**", + " * @typedef {gdjs.CustomRuntimeObject3D & {__cameraDistance: number, __pointLight: THREE.PointLight, _getIsCastingShadow: () => boolean, _getConeAngle: () => number, _getColor: () => string}} PointLightRuntimeObject", + " */", + "", + "const game = runtimeScene.getGame();", + "const isInGameEdition = game.isInGameEdition && game.isInGameEdition();", + "", + "", + "class PointLight3DRenderer extends gdjs.CustomRuntimeObject3DRenderer {", + "", + " constructor(", + " object,", + " instanceContainer,", + " parent", + " ) {", + " super(object, instanceContainer, parent);", + " }", + "", + " _updateThreeGroup() {", + " /** @type {PointLightRuntimeObject} */", + " //@ts-ignore", + " const object = this._object;", + " const threeObject3D = this.get3DRendererObject();", + "", + " threeObject3D.rotation.set(", + " gdjs.toRad(object.getRotationX()),", + " gdjs.toRad(object.getRotationY()),", + " gdjs.toRad(object.angle)", + " );", + "", + " threeObject3D.position.set(", + " object.getX(),", + " object.getY(),", + " object.getZ()", + " );", + "", + " // Force the scale to 1 because the light doesn't really has a size.", + " threeObject3D.scale.set(", + " object.isFlippedX() ? -1 : 1,", + " object.isFlippedY() ? -1 : 1,", + " object.isFlippedZ() ? -1 : 1", + " );", + "", + " threeObject3D.visible = !this._object.hidden;", + "", + " const pointLight = object.__pointLight;", + "", + " const editor = game.getInGameEditor ? game.getInGameEditor() : null;", + "", + " pointLight.castShadow = false;", + " pointLight.visible = false;", + "", + " if (!object.isHidden()) {", + " let isSelected = false;", + " if (editor) {", + " const selectedObject = editor._selection.getLastSelectedObject();", + " let parentObject = object;", + " isSelected = parentObject === selectedObject;", + " while (!isSelected && parentObject.getInstanceContainer()", + " //@ts-ignore", + " .getOwner) {", + " parentObject = parentObject.getInstanceContainer()", + " //@ts-ignore", + " .getOwner();", + " isSelected = parentObject === selectedObject;", + " }", + " }", + " let rootObject = object;", + " while (rootObject.getInstanceContainer()", + " //@ts-ignore", + " .getOwner) {", + " rootObject = rootObject.getInstanceContainer()", + " //@ts-ignore", + " .getOwner();", + " }", + " const runtimeScene = rootObject.getRuntimeScene();", + " let distanceSq = 0;", + " if (!isSelected) {", + " const layerName = rootObject.getLayer();", + " const cameraX = gdjs.evtTools.camera.getCameraX(runtimeScene, layerName, 0);", + " const cameraY = gdjs.evtTools.camera.getCameraY(runtimeScene, layerName, 0);", + " const cameraZ = gdjs.scene3d.camera.getCameraZ(runtimeScene, layerName, 0);", + " const deltaX = rootObject.getX() - cameraX;", + " const deltaY = rootObject.getY() - cameraY;", + " const deltaZ = rootObject.getZ() - cameraZ;", + " distanceSq = deltaX * deltaX + deltaY * deltaY + deltaZ * deltaZ;", + " }", + " getPointLightManager(", + " //@ts-ignore", + " runtimeScene", + " ).applyVisibilityAndShadow(object, distanceSq);", + " }", + "", + " // Force the visibility to be checked every frame", + " this._isContainerDirty = true;", + " }", + "}", + "", + "gdjs.registerRuntimeScenePreEventsCallback(", + " runtimeScene => {", + " getPointLightManager(", + " //@ts-ignore", + " runtimeScene.getScene()", + " ).clear();", + " });", + "if (gdjs.registerInGameEditorPostStepCallback) {", + " gdjs.registerInGameEditorPostStepCallback(", + " inGameEditor => {", + " getPointLightManager(", + " //@ts-ignore", + " inGameEditor.getEditedInstanceContainer().getScene()", + " ).clear();", + " });", + "}", + "", + "/**", + " * Get the platforms manager of an instance container.", + " * @param {gdjs.RuntimeScene & {__pointLightManager: PointLightManager}} runtimeScene", + " */", + "function getPointLightManager(runtimeScene) {", + " if (!runtimeScene.__pointLightManager) {", + " // Create the shared manager if necessary.", + " runtimeScene.__pointLightManager = isInGameEdition ?", + " new PointLightManager(4, 1) :", + " new PointLightManager(20, 4);", + " }", + " return runtimeScene.__pointLightManager;", + "}", + "", + "/** @type {{isInserted: boolean, removedObject: PointLightRuntimeObject | null}} */", + "const sortResult = { isInserted: false, removedObject: null };", + "", + "/**", + " * @param objects {Array}", + " * @param maxCount {number}", + " * @param object {PointLightRuntimeObject}", + " * @param distance {number}", + " */", + "function insertByDistance(objects, maxCount, object, distance) {", + " sortResult.isInserted = false;", + " sortResult.removedObject = null;", + " let insertionIndex = 0;", + " for (let index = objects.length - 1; index >= 0; index--) {", + " const other = objects[index];", + " const otherDistance = other.__cameraDistance;", + " if (distance >= otherDistance) {", + " insertionIndex = index + 1;", + " break;", + " }", + " }", + " if (insertionIndex >= maxCount) {", + " return sortResult;", + " }", + " object.__cameraDistance = distance", + " objects.splice(insertionIndex, 0, object);", + " sortResult.isInserted = true;", + " if (objects.length > maxCount) {", + " sortResult.removedObject = objects.pop();", + " }", + " return sortResult;", + "}", + "", + "class PointLightManager {", + " /** @type {Array} */", + " visibleObjects = new Array();", + " /** @type {Array} */", + " shadowObjects = new Array();", + " /** @type {number} */", + " maxCount;", + " /** @type {number} */", + " shadowCount;", + "", + " /**", + " * @param maxCount {number}", + " * @param shadowCount {number}", + " */", + " constructor(maxCount, shadowCount) {", + " this.maxCount = maxCount;", + " this.shadowCount = shadowCount;", + " }", + "", + " clear() {", + " this.visibleObjects.length = 0;", + " this.shadowObjects.length = 0;", + " }", + "", + " /**", + " * @param object {PointLightRuntimeObject}", + " * @param distance {number}", + " */", + " applyVisibilityAndShadow(object, distance) {", + " if (object._getIsCastingShadow()) {", + " const { isInserted, removedObject } = insertByDistance(", + " this.shadowObjects, this.shadowCount, object, distance);", + " if (isInserted) {", + " object.__pointLight.castShadow = true;", + " }", + " if (removedObject) {", + " removedObject.__pointLight.castShadow = false;", + " }", + " }", + " const { isInserted, removedObject } = insertByDistance(", + " this.visibleObjects, this.maxCount, object, distance);", + " if (isInserted) {", + " object.__pointLight.visible = true;", + " }", + " if (removedObject) {", + " removedObject.__pointLight.visible = false;", + " }", + " }", + "}", + "", + "const coneLength = 64;", + "", + "/**", + " * @extends {THREE.Mesh}", + " */", + "class PointLightHelper extends THREE.Mesh {", + " /** @type {PointLightRuntimeObject} */", + " object;", + "", + "\t/**", + " * @param {PointLightRuntimeObject} object", + "\t */", + " constructor(object) {", + "\t\tconst geometry = new THREE.SphereGeometry( 16, 4, 2 );", + "\t\tconst material = new THREE.MeshBasicMaterial( { wireframe: true, fog: false, toneMapped: false } );", + " super(geometry, material);", + " this.object = object;", + " this.update();", + "", + " //@ts-ignore", + " this.gdjsRuntimeObject = object;", + " }", + "", + " dispose() {", + "\t\tthis.geometry.dispose();", + "\t\tthis.material.dispose();", + " }", + "", + " update() {", + " const color = gdjs.rgbOrHexStringToNumber(this.object._getColor());", + " this.material.color.set(color);", + " }", + "}", + "", + "/**", + " * @param {string} colorString", + " * @param {THREE.Color} threeColor", + " */", + "const setThreeColor = (colorString, threeColor) => {", + " const integerColor = gdjs.rgbOrHexToRGBColor(colorString);", + " threeColor.r = integerColor[0] / 255;", + " threeColor.g = integerColor[1] / 255;", + " threeColor.b = integerColor[2] / 255;", + "};", + "", + "class PointLightAdapter {", + " /**", + " * @param object {gdjs.CustomRuntimeObject3D}", + " * @param pointLight {THREE.PointLight}", + " */", + " constructor(object, pointLight) {", + " this.object = object;", + " this.pointLight = pointLight;", + " }", + "", + " /**", + " * @param targetX {number}", + " * @param targetY {number}", + " * @param targetZ {number}", + " */", + " lookAtPosition(targetX, targetY, targetZ) {", + " // Remove from the parent to avoid the scene scale of -1 on Y to mess with the formula.", + " const parent = this.pointLight.parent;", + " this.pointLight.parent = null;", + " this.pointLight.lookAt(targetX, targetY, targetZ);", + " this.pointLight.parent = parent;", + "", + " // Angle setters update Three.js angles, so we save them first.", + " const rotationX = gdjs.toDegrees(this.pointLight.rotation.x);", + " const rotationY = gdjs.toDegrees(this.pointLight.rotation.y);", + " const rotationZ = gdjs.toDegrees(this.pointLight.rotation.z);", + " this.object.setRotationX(rotationX);", + " this.object.setRotationY(rotationY);", + " this.object.setAngle(rotationZ + 90);", + " }", + "", + " /**", + " * @param color {string}", + " */", + " setColor(color) {", + " setThreeColor(color, this.pointLight.color);", + " }", + "", + " /**", + " * @param intensity {number}", + " */", + " setIntensity(intensity) {", + " this.pointLight.intensity = intensity;", + " }", + "", + " /**", + " * @param decay {number}", + " */", + " setDecay(decay) {", + " this.pointLight.decay = decay;", + " }", + "", + " /**", + " * @param value {number}", + " */", + " setRange(value) {", + " this.pointLight.distance = value;", + " }", + "", + " /**", + " * @param isCastingShadow {boolean}", + " */", + " setCastingShadow(isCastingShadow) {", + " // This is applied by applyVisibilityAndShadow", + " }", + "", + " /**", + " * @param shadowQuality {\"Low\" | \"Medium\" | \"High\"}", + " */", + " setShadowQuality(shadowQuality) {", + " let size = 512;", + " switch (shadowQuality) {", + " case \"Low\":", + " size = 256;", + " break;", + " case \"Medium\":", + " size = 512;", + " break;", + " case \"High\":", + " size = 1024;", + " break;", + " }", + " const shadow = this.pointLight.shadow;", + " const mapSize = shadow.mapSize;", + " if (mapSize.width !== size) {", + " mapSize.set(size, size);", + "", + " // Force the recreation of the shadow map texture:", + " if (shadow.map) {", + " shadow.map.dispose();", + " shadow.map = null;", + " }", + " shadow.needsUpdate = true;", + " }", + " }", + "", + " /**", + " * @param value {number}", + " */", + " setShadowCameraNearPlane(value) {", + " this.pointLight.shadow.camera.near = value;", + " }", + "", + " /**", + " * @param value {number}", + " */", + " setShadowCameraFarPlane(value) {", + " this.pointLight.shadow.camera.far = value;", + " }", + "}", + "", + "", + "", + "//@ts-ignore", + "gdjs.__light3DExtension = gdjs.__light3DExtension || {};", + "gdjs.__light3DExtension.point = {", + " PointLight3DRenderer,", + " PointLightAdapter,", + " PointLightHelper,", + "}" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "parameters": [], + "objectGroups": [] } ], "eventsFunctionsFolderStructure": { "folderName": "__ROOT", "children": [ { - "functionName": "DefineHelperClasses" + "functionName": "DefineSpotLightHelperClasses" + }, + { + "functionName": "DefinePointLightHelperClasses" + } + ] + }, + "eventsBasedBehaviors": [], + "eventsBasedObjects": [ + { + "areaMaxX": 20, + "areaMaxY": 20, + "areaMaxZ": 20, + "areaMinX": -20, + "areaMinY": -20, + "areaMinZ": -20, + "defaultName": "SpotLight", + "description": "Light up a cone like a flashlight.", + "fullName": "3D spot light", + "helpPath": "", + "iconUrl": "", + "is3D": true, + "isUsingLegacyInstancesRenderer": false, + "name": "SpotLight3D", + "previewIconUrl": "", + "objects": [ + { + "assetStoreId": "", + "name": "Placeholder", + "persistentUuid": "6786eefd-3c18-4fcb-b3ae-95b07f953045", + "type": "Scene3D::Cube3DObject", + "variables": [], + "effects": [], + "behaviors": [], + "content": { + "width": 80, + "height": 80, + "depth": 80, + "enableTextureTransparency": false, + "facesOrientation": "Z", + "frontFaceResourceName": "", + "backFaceResourceName": "", + "backFaceUpThroughWhichAxisRotation": "X", + "leftFaceResourceName": "", + "rightFaceResourceName": "", + "topFaceResourceName": "", + "bottomFaceResourceName": "", + "frontFaceVisible": true, + "backFaceVisible": true, + "leftFaceVisible": true, + "rightFaceVisible": true, + "topFaceVisible": true, + "bottomFaceVisible": true, + "frontFaceResourceRepeat": false, + "backFaceResourceRepeat": false, + "leftFaceResourceRepeat": false, + "rightFaceResourceRepeat": false, + "topFaceResourceRepeat": false, + "bottomFaceResourceRepeat": false, + "materialType": "Basic", + "tint": "255;255;255", + "isCastingShadow": false, + "isReceivingShadow": false + } + } + ], + "objectsFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "objectName": "Placeholder" + } + ] + }, + "objectsGroups": [], + "layers": [ + { + "ambientLightColorB": 200, + "ambientLightColorG": 200, + "ambientLightColorR": 200, + "camera2DPlaneMaxDrawingDistance": 5000, + "camera3DFarPlaneDistance": 10000, + "camera3DFieldOfView": 45, + "camera3DNearPlaneDistance": 3, + "cameraType": "", + "followBaseLayerCamera": false, + "isLightingLayer": false, + "isLocked": false, + "name": "", + "renderingType": "", + "visibility": true, + "cameras": [ + { + "defaultSize": true, + "defaultViewport": true, + "height": 0, + "viewportBottom": 1, + "viewportLeft": 0, + "viewportRight": 1, + "viewportTop": 0, + "width": 0 + } + ], + "effects": [] + } + ], + "instances": [ + { + "angle": 0, + "customSize": true, + "depth": 40, + "height": 40, + "layer": "", + "name": "Placeholder", + "persistentUuid": "1310c768-04d4-4133-98d9-37d836f8a990", + "width": 40, + "x": -20, + "y": -20, + "z": -20, + "zOrder": 1, + "numberProperties": [], + "stringProperties": [], + "initialVariables": [] + } + ], + "editionSettings": { + "grid": false, + "gridType": "rectangular", + "gridWidth": 32, + "gridHeight": 32, + "gridDepth": 32, + "gridOffsetX": 0, + "gridOffsetY": 0, + "gridOffsetZ": 0, + "gridColor": 10401023, + "gridAlpha": 0.8, + "snap": false, + "zoomFactor": 9.06473532531222, + "windowMask": false, + "selectedLayer": "", + "gameEditorMode": "instances-editor" + }, + "eventsFunctions": [ + { + "fullName": "", + "functionType": "Action", + "name": "onCreated", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "Light3D::DefineSpotLightHelperClasses" + }, + "parameters": [ + "", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const {", + " SpotLightAdapter,", + " SpotLight3DRenderer,", + " SpotLightHelper,", + " //@ts-ignore", + "} = gdjs.__light3DExtension.spot;", + "", + "/** @type {gdjs.CustomRuntimeObject3D} */", + "//@ts-ignore", + "const object = objects[0];", + "", + "const game = runtimeScene.getGame();", + "", + "// This is a hack that may break in future releases.", + "// Replace the group that would hold children objects by the light.", + "const layer = object.getInstanceContainer().getLayer(object.getLayer());", + "const group = object.getRenderer()._threeGroup;", + "layer.getRenderer().remove3DRendererObject(group);", + "", + "const spotLight = new THREE.SpotLight(0xffffff);", + "spotLight.position.copy(group.position);", + "spotLight.rotation.copy(group.rotation);", + "spotLight.add(spotLight.target);", + "spotLight.target.position.x = 100;", + "spotLight.up.set(0, 0, 1);", + "spotLight.shadow.camera.up.set(0, 0, 1);", + "//@ts-ignore", + "object.__spotLight = spotLight;", + "//@ts-ignore", + "object.__spotLightAdapter = new SpotLightAdapter(object, spotLight);", + "", + "const spotLight3DRenderer = new SpotLight3DRenderer(object, object._instanceContainer, object.getInstanceContainer());", + "object._renderer = spotLight3DRenderer;", + "if (game.isInGameEdition && game.isInGameEdition()) {", + " const spotLightHelper = new SpotLightHelper(object);", + " spotLightHelper.rotation.order = 'ZYX';", + " spotLightHelper.add(spotLight);", + " spotLight3DRenderer._threeGroup = spotLightHelper;", + " layer.getRenderer().add3DRendererObject(spotLightHelper);", + "}", + "else {", + " spotLight3DRenderer._threeGroup = spotLight;", + " layer.getRenderer().add3DRendererObject(spotLight);", + "}", + "", + "spotLight.updateMatrixWorld(true);", + "", + "// Allow to tween the light color.", + "//@ts-ignore", + "if (!object.setColor && !object.getColor) {", + " const prototype = Object.getPrototypeOf(object);", + " prototype.setColor = function(tint) {", + " this.SetColor(tint, eventsFunctionContext);", + " }", + " prototype.getColor = function() {", + " return this.Color(eventsFunctionContext);", + " }", + "}", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": true + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "Light3D::SpotLight3D::UpdateFromProperties" + }, + "parameters": [ + "Object", + "" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Light3D::SpotLight3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "Action", + "name": "onDestroy", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "/** @type {gdjs.CustomRuntimeObject} */", + "const object = objects[0];", + "// Here runtimeScene is the gdjs.CustomRuntimeObjectInstanceContainer inside the custom object.", + "const gameScene = object.getRuntimeScene();", + "", + "object.__spotLight.dispose();", + "object.get3DRendererObject().dispose();", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Light3D::SpotLight3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "Action", + "name": "onHotReloading", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "Light3D::SpotLight3D::UpdateFromProperties" + }, + "parameters": [ + "Object", + "" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Light3D::SpotLight3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "Update from properties.", + "fullName": "Update from properties", + "functionType": "Action", + "name": "UpdateFromProperties", + "private": true, + "sentence": "Update from properties of _PARAM0_", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "Light3D::SpotLight3D::SetColor" + }, + "parameters": [ + "Object", + "=", + "Color", + "" + ] + }, + { + "type": { + "value": "Light3D::SpotLight3D::SetIntensity" + }, + "parameters": [ + "Object", + "=", + "Intensity", + "" + ] + }, + { + "type": { + "value": "Light3D::SpotLight3D::SetDecay" + }, + "parameters": [ + "Object", + "=", + "Decay", + "" + ] + }, + { + "type": { + "value": "Light3D::SpotLight3D::SetConeLength" + }, + "parameters": [ + "Object", + "=", + "ConeLength", + "" + ] + }, + { + "type": { + "value": "Light3D::SpotLight3D::SetConeAngle" + }, + "parameters": [ + "Object", + "=", + "ConeAngle", + "" + ] + }, + { + "type": { + "value": "Light3D::SpotLight3D::SetSmoothing" + }, + "parameters": [ + "Object", + "=", + "Smoothing", + "" + ] + }, + { + "type": { + "value": "Light3D::SpotLight3D::SetShadowQuality" + }, + "parameters": [ + "Object", + "=", + "ShadowQuality", + "" + ] + }, + { + "type": { + "value": "Light3D::SpotLight3D::SetShadowCameraNearPlane" + }, + "parameters": [ + "Object", + "=", + "ShadowCameraNearPlane", + "" + ] + }, + { + "type": { + "value": "Light3D::SpotLight3D::SetShadowCameraFarPlane" + }, + "parameters": [ + "Object", + "=", + "ShadowCameraFarPlane", + "" + ] + }, + { + "type": { + "value": "Light3D::SpotLight3D::UpdateImage" + }, + "parameters": [ + "Object", + "" + ] + }, + { + "type": { + "value": "Light3D::SpotLight3D::UpdateHelper" + }, + "parameters": [ + "Object", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "IsCastingShadow", + "=", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "Light3D::SpotLight3D::SetCastingShadow" + }, + "parameters": [ + "Object", + "no", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "IsCastingShadow", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "Light3D::SpotLight3D::SetCastingShadow" + }, + "parameters": [ + "Object", + "yes", + "" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Light3D::SpotLight3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "Update helper", + "functionType": "Action", + "name": "UpdateHelper", + "private": true, + "sentence": "Update graphical helper of _PARAM0_", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const game = runtimeScene.getGame();", + "if (game.isInGameEdition && game.isInGameEdition()) {", + " const spotLightHelper = objects[0].get3DRendererObject();", + " spotLightHelper.update();", + "}", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Light3D::SpotLight3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "Update image", + "functionType": "Action", + "name": "UpdateImage", + "private": true, + "sentence": "Update the image of _PARAM0_", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "/** @type {gdjs.CustomRuntimeObject3D} */\r", + "const object = objects[0];\r", + "const resourceName = object._getImage();\r", + "object.__spotLightAdapter.setImage(resourceName);" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Light3D::SpotLight3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the cone angle of the light.", + "fullName": "Cone angle", + "functionType": "ExpressionAndCondition", + "name": "ConeAngle", + "sentence": "the cone angle", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "ConeAngle" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Light3D::SpotLight3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "ConeAngle", + "name": "SetConeAngle", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "ConeAngle", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const coneAngle = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__spotLightAdapter.setConeAngle(coneAngle);\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Light3D::SpotLight3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the color of the light.", + "fullName": "Color", + "functionType": "ExpressionAndCondition", + "name": "Color", + "sentence": "the color", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnString" + }, + "parameters": [ + "Color" + ] + } + ] + } + ], + "expressionType": { + "type": "color" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Light3D::SpotLight3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "Color", + "name": "SetColor", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "Color", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const color = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__spotLightAdapter.setColor(color);" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Light3D::SpotLight3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the smoothing of the light. Percent of the spotlight cone that is attenuated due to penumbra (between 0 and 1).", + "fullName": "Smoothing", + "functionType": "ExpressionAndCondition", + "name": "Smoothing", + "sentence": "the smoothing", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "Smoothing" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Light3D::SpotLight3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "Smoothing", + "name": "SetSmoothing", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Smoothing", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const value = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__spotLightAdapter.setSmoothing(value);" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Light3D::SpotLight3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the intensity of the light.", + "fullName": "Intensity", + "functionType": "ExpressionAndCondition", + "name": "Intensity", + "sentence": "the intensity", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "Intensity" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Light3D::SpotLight3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "Intensity", + "name": "SetIntensity", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Intensity", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const value = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__spotLightAdapter.setIntensity(value);\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Light3D::SpotLight3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the decay of the light. The amount the light dims along the distance of the light.", + "fullName": "Decay", + "functionType": "ExpressionAndCondition", + "name": "Decay", + "sentence": "the decay", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "Decay" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Light3D::SpotLight3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "Decay", + "name": "SetDecay", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "Decay", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const value = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__spotLightAdapter.setDecay(value);" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Light3D::SpotLight3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "Check if the light is casting shadows.", + "fullName": "Shadow casting", + "functionType": "Condition", + "group": "3D spot light shadow configuration", + "name": "IsCastingShadow", + "sentence": "_PARAM0_ casting shadows", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "IsCastingShadow", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetReturnBoolean" + }, + "parameters": [ + "True" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Light3D::SpotLight3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "Change if the light is casting shadows.", + "fullName": "Shadow casting", + "functionType": "Action", + "group": "3D spot light shadow configuration", + "name": "SetCastingShadow", + "sentence": "_PARAM0_ casting shadows: _PARAM1_", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "Value", + "False", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetBooleanVariable" + }, + "parameters": [ + "IsCastingShadow", + "False", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "Value", + "True", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "SetBooleanVariable" + }, + "parameters": [ + "IsCastingShadow", + "True", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const value = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__spotLightAdapter.setCastingShadow(value);" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Light3D::SpotLight3D", + "type": "object" + }, + { + "defaultValue": "yes", + "description": "Shadow casting", + "name": "Value", + "optional": true, + "type": "yesorno" + } + ], + "objectGroups": [] + }, + { + "description": "Rotate the light to light up a position.", + "fullName": "Look at position", + "functionType": "Action", + "group": "Angle", + "name": "LookAtPosition", + "sentence": "_PARAM0_ look at _PARAM1_ ; _PARAM2_ ; _PARAM3_", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const targetX = eventsFunctionContext.getArgument(\"TargetX\");\r", + "const targetY = eventsFunctionContext.getArgument(\"TargetY\");\r", + "const targetZ = eventsFunctionContext.getArgument(\"TargetZ\");\r", + "\r", + "object.__spotLightAdapter.lookAtPosition(targetX, targetY, targetZ);" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Light3D::SpotLight3D", + "type": "object" + }, + { + "description": "Target X", + "name": "TargetX", + "type": "expression" + }, + { + "description": "Target Y", + "name": "TargetY", + "type": "expression" + }, + { + "description": "Target Z", + "name": "TargetZ", + "type": "expression" + } + ], + "objectGroups": [] + }, + { + "description": "Rotate the light to light up an object.", + "fullName": "Look at object", + "functionType": "Action", + "group": "Angle", + "name": "LookAtObject", + "sentence": "_PARAM0_ look at _PARAM1_", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "Light3D::SpotLight3D::LookAtPosition" + }, + "parameters": [ + "Object", + "Target.CenterX()", + "Target.CenterY()", + "Target.Object3D::CenterZ()", + "" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Light3D::SpotLight3D", + "type": "object" + }, + { + "description": "Object", + "name": "Target", + "type": "objectList" + }, + { + "description": "3D capability", + "name": "Object3D", + "supplementaryInformation": "Scene3D::Base3DBehavior", + "type": "behavior" + } + ], + "objectGroups": [] + }, + { + "description": "the shadow quality of the light.", + "fullName": "Shadow quality", + "functionType": "ExpressionAndCondition", + "group": "3D spot light shadow configuration", + "name": "ShadowQuality", + "sentence": "the shadow quality", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnString" + }, + "parameters": [ + "ShadowQuality" + ] + } + ] + } + ], + "expressionType": { + "supplementaryInformation": "[\"Low\",\"Medium\",\"High\"]", + "type": "stringWithSelector" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Light3D::SpotLight3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "ShadowQuality", + "group": "3D spot light shadow configuration", + "name": "SetShadowQuality", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetStringVariable" + }, + "parameters": [ + "ShadowQuality", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const value = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__spotLightAdapter.setShadowQuality(value);" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Light3D::SpotLight3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the shadow camera near plane of the light.", + "fullName": "Shadow camera near plane", + "functionType": "ExpressionAndCondition", + "group": "3D spot light shadow configuration", + "name": "ShadowCameraNearPlane", + "sentence": "the shadow camera near plane", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "ShadowCameraNearPlane" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Light3D::SpotLight3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "ShadowCameraNearPlane", + "group": "3D spot light shadow configuration", + "name": "SetShadowCameraNearPlane", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "ShadowCameraNearPlane", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const value = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__spotLightAdapter.setShadowCameraNearPlane(value);\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Light3D::SpotLight3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the shadow camera far plane of the light.", + "fullName": "Shadow camera far plane", + "functionType": "ExpressionAndCondition", + "group": "3D spot light shadow configuration", + "name": "ShadowCameraFarPlane", + "sentence": "the shadow camera far plane", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "ShadowCameraFarPlane" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Light3D::SpotLight3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "ShadowCameraFarPlane", + "group": "3D spot light shadow configuration", + "name": "SetShadowCameraFarPlane", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "ShadowCameraFarPlane", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const value = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__spotLightAdapter.setShadowCameraFarPlane(value);\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Light3D::SpotLight3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "description": "the cone length of the light. 0 means no limit.", + "fullName": "Cone length", + "functionType": "ExpressionAndCondition", + "name": "ConeLength", + "sentence": "the cone length", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "ConeLength" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Light3D::SpotLight3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "ConeLength", + "name": "SetConeLength", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "ConeLength", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const value = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__spotLightAdapter.setConeLength(value);\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Light3D::SpotLight3D", + "type": "object" + } + ], + "objectGroups": [] + } + ], + "eventsFunctionsFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "functionName": "onCreated" + }, + { + "functionName": "onDestroy" + }, + { + "functionName": "onHotReloading" + }, + { + "functionName": "UpdateFromProperties" + }, + { + "functionName": "UpdateHelper" + }, + { + "functionName": "UpdateImage" + }, + { + "folderName": "Angle", + "children": [ + { + "functionName": "LookAtPosition" + }, + { + "functionName": "LookAtObject" + } + ] + }, + { + "functionName": "Color" + }, + { + "functionName": "SetColor" + }, + { + "functionName": "Intensity" + }, + { + "functionName": "SetIntensity" + }, + { + "functionName": "Decay" + }, + { + "functionName": "SetDecay" + }, + { + "functionName": "ConeLength" + }, + { + "functionName": "SetConeLength" + }, + { + "functionName": "ConeAngle" + }, + { + "functionName": "SetConeAngle" + }, + { + "functionName": "Smoothing" + }, + { + "functionName": "SetSmoothing" + }, + { + "folderName": "3D spot light shadow configuration", + "children": [ + { + "functionName": "IsCastingShadow" + }, + { + "functionName": "SetCastingShadow" + }, + { + "functionName": "ShadowQuality" + }, + { + "functionName": "SetShadowQuality" + }, + { + "functionName": "ShadowCameraNearPlane" + }, + { + "functionName": "SetShadowCameraNearPlane" + }, + { + "functionName": "ShadowCameraFarPlane" + }, + { + "functionName": "SetShadowCameraFarPlane" + } + ] + } + ] + }, + "propertyDescriptors": [ + { + "value": "255;255;255", + "type": "Color", + "label": "Color", + "name": "Color" + }, + { + "value": "30", + "type": "Number", + "unit": "DegreeAngle", + "label": "Cone angle", + "group": "Cone", + "name": "ConeAngle" + }, + { + "value": "1", + "type": "Number", + "label": "Intensity", + "name": "Intensity" + }, + { + "value": "1", + "type": "Number", + "label": "Smoothing", + "description": "Percent of the spotlight cone that is attenuated due to penumbra (between 0 and 1).", + "group": "Cone", + "name": "Smoothing" + }, + { + "value": "2", + "type": "Number", + "label": "Decay", + "description": "The amount the light dims along the distance of the light.", + "name": "Decay" + }, + { + "value": "", + "type": "Resource", + "label": "Image", + "description": "Shadow casting must be enabled for the image to have any effect.", + "group": "Shadow", + "extraInformation": [ + "image" + ], + "choices": [], + "advanced": true, + "name": "Image" + }, + { + "value": "true", + "type": "Boolean", + "label": "Shadow casting", + "group": "Shadow", + "name": "IsCastingShadow" + }, + { + "value": "Medium", + "type": "Choice", + "label": "Shadow quality", + "group": "Shadow", + "choices": [ + { + "label": "", + "value": "Low" + }, + { + "label": "", + "value": "Medium" + }, + { + "label": "", + "value": "High" + } + ], + "name": "ShadowQuality" + }, + { + "value": "20", + "type": "Number", + "unit": "Pixel", + "label": "Shadow camera near plane", + "group": "Shadow", + "advanced": true, + "name": "ShadowCameraNearPlane" + }, + { + "value": "2000", + "type": "Number", + "unit": "Pixel", + "label": "Shadow camera far plane", + "group": "Shadow", + "advanced": true, + "name": "ShadowCameraFarPlane" + }, + { + "value": "0", + "type": "Number", + "unit": "Pixel", + "label": "Cone length", + "description": "0 means no limit.", + "group": "Cone", + "advanced": true, + "name": "ConeLength" + } + ], + "propertiesFolderStructure": { + "folderName": "__ROOT", + "children": [ + { + "propertyName": "Color" + }, + { + "propertyName": "Intensity" + }, + { + "propertyName": "Decay" + }, + { + "folderName": "Cone", + "children": [ + { + "propertyName": "ConeAngle" + }, + { + "propertyName": "ConeLength" + }, + { + "propertyName": "Smoothing" + } + ] + }, + { + "folderName": "Shadow", + "children": [ + { + "propertyName": "IsCastingShadow" + }, + { + "propertyName": "ShadowQuality" + }, + { + "propertyName": "Image" + }, + { + "propertyName": "ShadowCameraNearPlane" + }, + { + "propertyName": "ShadowCameraFarPlane" + } + ] + } + ] } - ] - }, - "eventsBasedBehaviors": [], - "eventsBasedObjects": [ + }, { "areaMaxX": 20, "areaMaxY": 20, @@ -520,12 +2726,15 @@ "areaMinX": -20, "areaMinY": -20, "areaMinZ": -20, - "defaultName": "SpotLight", - "description": "Light up a cone like a flashlight.", - "fullName": "3D spot light", + "defaultName": "PointLight", + "description": "Light up in all directions like a torch.", + "fullName": "3D point light", + "helpPath": "", + "iconUrl": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0ibWRpLXRvcmNoIiB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZD0iTTguNiA5LjZDOSAxMC4yIDkuNSAxMC43IDEwLjIgMTFIMTQuMkMxNC41IDEwLjkgMTQuNyAxMC43IDE0LjkgMTAuNUMxNS45IDkuNSAxNi4zIDggMTUuOCA2LjdMMTUuNyA2LjVDMTUuNiA2LjIgMTUuNCA2IDE1LjIgNS44QzE1LjEgNS42IDE0LjkgNS41IDE0LjggNS4zQzE0LjQgNSAxNCA0LjcgMTMuNiA0LjNDMTIuNyAzLjQgMTIuNiAyIDEzLjEgMUMxMi42IDEuMSAxMi4xIDEuNCAxMS43IDEuOEMxMC4yIDMgOS42IDUuMSAxMC4zIDdWNy4yQzEwLjMgNy4zIDEwLjIgNy40IDEwLjEgNy41QzEwIDcuNiA5LjggNy41IDkuNyA3LjRMOS42IDcuM0M5IDYuNSA4LjkgNS4zIDkuMyA0LjNDOC40IDUuMSA3LjkgNi40IDggNy43QzggOCA4LjEgOC4zIDguMiA4LjZDOC4yIDguOSA4LjQgOS4zIDguNiA5LjZNMTIuMyA4LjFDMTIuNCA3LjYgMTIuMiA3LjIgMTIuMSA2LjhDMTIgNi40IDEyIDYgMTIuMiA1LjZMMTIuNSA2LjJDMTIuOSA2LjggMTMuNiA3IDEzLjggNy44VjguMUMxMy44IDguNiAxMy42IDkuMSAxMy4zIDkuNEMxMy4xIDkuNSAxMi45IDkuNyAxMi43IDkuN0MxMi4xIDkuOSAxMS40IDkuNiAxMSA5LjJDMTEuOCA5LjIgMTIuMiA4LjYgMTIuMyA4LjFNMTUgMTJWMTRIMTRMMTMgMjJIMTFMMTAgMTRIOVYxMkgxNVoiIC8+PC9zdmc+", "is3D": true, "isUsingLegacyInstancesRenderer": false, - "name": "SpotLight3D", + "name": "PointLight3D", + "previewIconUrl": "https://asset-resources.gdevelop.io/public-resources/Icons/bf9158c96f4346dabd9e8ae0ddd737809652797812eb50376bae62463ac7fc42_torch.svg", "objects": [ { "assetStoreId": "", @@ -656,7 +2865,7 @@ "actions": [ { "type": { - "value": "SpotLight3D::DefineHelperClasses" + "value": "Light3D::DefinePointLightHelperClasses" }, "parameters": [ "", @@ -669,11 +2878,11 @@ "type": "BuiltinCommonInstructions::JsCode", "inlineCode": [ "const {", - " SpotLightAdapter,", - " SpotLight3DRenderer,", - " SpotLightHelper,", + " PointLightAdapter,", + " PointLight3DRenderer,", + " PointLightHelper,", " //@ts-ignore", - "} = gdjs.__spotLight3DExtension;", + "} = gdjs.__light3DExtension.point;", "", "/** @type {gdjs.CustomRuntimeObject3D} */", "//@ts-ignore", @@ -687,33 +2896,31 @@ "const group = object.getRenderer()._threeGroup;", "layer.getRenderer().remove3DRendererObject(group);", "", - "const spotLight = new THREE.SpotLight(0xffffff);", - "spotLight.position.copy(group.position);", - "spotLight.rotation.copy(group.rotation);", - "spotLight.add(spotLight.target);", - "spotLight.target.position.x = 100;", - "spotLight.up.set(0, 0, 1);", - "spotLight.shadow.camera.up.set(0, 0, 1);", + "const pointLight = new THREE.PointLight(0xffffff);", + "pointLight.position.copy(group.position);", + "pointLight.rotation.copy(group.rotation);", + "pointLight.up.set(0, 0, 1);", + "pointLight.shadow.camera.up.set(0, 0, 1);", "//@ts-ignore", - "object.__spotLight = spotLight;", + "object.__pointLight = pointLight;", "//@ts-ignore", - "object.__spotLightAdapter = new SpotLightAdapter(object, spotLight);", + "object.__pointLightAdapter = new PointLightAdapter(object, pointLight);", "", - "const spotLight3DRenderer = new SpotLight3DRenderer(object, object._instanceContainer, object.getInstanceContainer());", - "object._renderer = spotLight3DRenderer;", + "const pointLight3DRenderer = new PointLight3DRenderer(object, object._instanceContainer, object.getInstanceContainer());", + "object._renderer = pointLight3DRenderer;", "if (game.isInGameEdition && game.isInGameEdition()) {", - " const spotLightHelper = new SpotLightHelper(object);", - " spotLightHelper.rotation.order = 'ZYX';", - " spotLightHelper.add(spotLight);", - " spotLight3DRenderer._threeGroup = spotLightHelper;", - " layer.getRenderer().add3DRendererObject(spotLightHelper);", + " const pointLightHelper = new PointLightHelper(object);", + " pointLightHelper.rotation.order = 'ZYX';", + " pointLightHelper.add(pointLight);", + " pointLight3DRenderer._threeGroup = pointLightHelper;", + " layer.getRenderer().add3DRendererObject(pointLightHelper);", "}", "else {", - " spotLight3DRenderer._threeGroup = spotLight;", - " layer.getRenderer().add3DRendererObject(spotLight);", + " pointLight3DRenderer._threeGroup = pointLight;", + " layer.getRenderer().add3DRendererObject(pointLight);", "}", "", - "spotLight.updateMatrixWorld(true);", + "pointLight.updateMatrixWorld(true);", "", "// Allow to tween the light color.", "//@ts-ignore", @@ -738,7 +2945,7 @@ "actions": [ { "type": { - "value": "SpotLight3D::SpotLight3D::UpdateFromProperties" + "value": "Light3D::PointLight3D::UpdateFromProperties" }, "parameters": [ "Object", @@ -752,7 +2959,7 @@ { "description": "Object", "name": "Object", - "supplementaryInformation": "SpotLight3D::SpotLight3D", + "supplementaryInformation": "Light3D::PointLight3D", "type": "object" } ], @@ -772,7 +2979,7 @@ "// Here runtimeScene is the gdjs.CustomRuntimeObjectInstanceContainer inside the custom object.", "const gameScene = object.getRuntimeScene();", "", - "object.__spotLight.dispose();", + "object.__pointLight.dispose();", "object.get3DRendererObject().dispose();", "" ], @@ -785,7 +2992,7 @@ { "description": "Object", "name": "Object", - "supplementaryInformation": "SpotLight3D::SpotLight3D", + "supplementaryInformation": "Light3D::PointLight3D", "type": "object" } ], @@ -803,7 +3010,7 @@ "actions": [ { "type": { - "value": "SpotLight3D::SpotLight3D::UpdateFromProperties" + "value": "Light3D::PointLight3D::UpdateFromProperties" }, "parameters": [ "Object", @@ -817,7 +3024,7 @@ { "description": "Object", "name": "Object", - "supplementaryInformation": "SpotLight3D::SpotLight3D", + "supplementaryInformation": "Light3D::PointLight3D", "type": "object" } ], @@ -837,7 +3044,7 @@ "actions": [ { "type": { - "value": "SpotLight3D::SpotLight3D::SetColor" + "value": "Light3D::PointLight3D::SetColor" }, "parameters": [ "Object", @@ -848,7 +3055,7 @@ }, { "type": { - "value": "SpotLight3D::SpotLight3D::SetIntensity" + "value": "Light3D::PointLight3D::SetIntensity" }, "parameters": [ "Object", @@ -859,7 +3066,7 @@ }, { "type": { - "value": "SpotLight3D::SpotLight3D::SetDecay" + "value": "Light3D::PointLight3D::SetDecay" }, "parameters": [ "Object", @@ -870,40 +3077,18 @@ }, { "type": { - "value": "SpotLight3D::SpotLight3D::SetConeLength" - }, - "parameters": [ - "Object", - "=", - "ConeLength", - "" - ] - }, - { - "type": { - "value": "SpotLight3D::SpotLight3D::SetConeAngle" - }, - "parameters": [ - "Object", - "=", - "ConeAngle", - "" - ] - }, - { - "type": { - "value": "SpotLight3D::SpotLight3D::SetSmoothing" + "value": "Light3D::PointLight3D::SetRange" }, "parameters": [ "Object", "=", - "Smoothing", + "Range", "" ] }, { "type": { - "value": "SpotLight3D::SpotLight3D::SetShadowQuality" + "value": "Light3D::PointLight3D::SetShadowQuality" }, "parameters": [ "Object", @@ -914,7 +3099,7 @@ }, { "type": { - "value": "SpotLight3D::SpotLight3D::SetShadowCameraNearPlane" + "value": "Light3D::PointLight3D::SetShadowCameraNearPlane" }, "parameters": [ "Object", @@ -925,7 +3110,7 @@ }, { "type": { - "value": "SpotLight3D::SpotLight3D::SetShadowCameraFarPlane" + "value": "Light3D::PointLight3D::SetShadowCameraFarPlane" }, "parameters": [ "Object", @@ -936,16 +3121,7 @@ }, { "type": { - "value": "SpotLight3D::SpotLight3D::UpdateImage" - }, - "parameters": [ - "Object", - "" - ] - }, - { - "type": { - "value": "SpotLight3D::SpotLight3D::UpdateHelper" + "value": "Light3D::PointLight3D::UpdateHelper" }, "parameters": [ "Object", @@ -971,7 +3147,7 @@ "actions": [ { "type": { - "value": "SpotLight3D::SpotLight3D::SetCastingShadow" + "value": "Light3D::PointLight3D::SetCastingShadow" }, "parameters": [ "Object", @@ -998,236 +3174,43 @@ "actions": [ { "type": { - "value": "SpotLight3D::SpotLight3D::SetCastingShadow" + "value": "Light3D::PointLight3D::SetCastingShadow" }, "parameters": [ "Object", "yes", - "" - ] - } - ] - } - ], - "parameters": [ - { - "description": "Object", - "name": "Object", - "supplementaryInformation": "SpotLight3D::SpotLight3D", - "type": "object" - } - ], - "objectGroups": [] - }, - { - "fullName": "Update helper", - "functionType": "Action", - "name": "UpdateHelper", - "private": true, - "sentence": "Update graphical helper of _PARAM0_", - "events": [ - { - "type": "BuiltinCommonInstructions::JsCode", - "inlineCode": [ - "const game = runtimeScene.getGame();", - "if (game.isInGameEdition && game.isInGameEdition()) {", - " const spotLightHelper = objects[0].get3DRendererObject();", - " spotLightHelper.update();", - "}", - "" - ], - "parameterObjects": "Object", - "useStrict": true, - "eventsSheetExpanded": false - } - ], - "parameters": [ - { - "description": "Object", - "name": "Object", - "supplementaryInformation": "SpotLight3D::SpotLight3D", - "type": "object" - } - ], - "objectGroups": [] - }, - { - "fullName": "Update image", - "functionType": "Action", - "name": "UpdateImage", - "private": true, - "sentence": "Update the image of _PARAM0_", - "events": [ - { - "type": "BuiltinCommonInstructions::JsCode", - "inlineCode": [ - "/** @type {gdjs.CustomRuntimeObject3D} */\r", - "const object = objects[0];\r", - "const resourceName = object._getImage();\r", - "object.__spotLightAdapter.setImage(resourceName);" - ], - "parameterObjects": "Object", - "useStrict": true, - "eventsSheetExpanded": true - } - ], - "parameters": [ - { - "description": "Object", - "name": "Object", - "supplementaryInformation": "SpotLight3D::SpotLight3D", - "type": "object" - } - ], - "objectGroups": [] - }, - { - "description": "the cone angle of the light.", - "fullName": "Cone angle", - "functionType": "ExpressionAndCondition", - "name": "ConeAngle", - "sentence": "the cone angle", - "events": [ - { - "type": "BuiltinCommonInstructions::Standard", - "conditions": [], - "actions": [ - { - "type": { - "value": "SetReturnNumber" - }, - "parameters": [ - "ConeAngle" - ] - } - ] - } - ], - "expressionType": { - "type": "expression" - }, - "parameters": [ - { - "description": "Object", - "name": "Object", - "supplementaryInformation": "SpotLight3D::SpotLight3D", - "type": "object" - } - ], - "objectGroups": [] - }, - { - "fullName": "", - "functionType": "ActionWithOperator", - "getterName": "ConeAngle", - "name": "SetConeAngle", - "sentence": "", - "events": [ - { - "type": "BuiltinCommonInstructions::Standard", - "conditions": [], - "actions": [ - { - "type": { - "value": "SetNumberVariable" - }, - "parameters": [ - "ConeAngle", - "=", - "Value" - ] - } - ] - }, - { - "type": "BuiltinCommonInstructions::JsCode", - "inlineCode": [ - "const object = objects[0];\r", - "const coneAngle = eventsFunctionContext.getArgument(\"Value\");\r", - "\r", - "object.__spotLightAdapter.setConeAngle(coneAngle);\r", - "" - ], - "parameterObjects": "Object", - "useStrict": true, - "eventsSheetExpanded": false - } - ], - "parameters": [ - { - "description": "Object", - "name": "Object", - "supplementaryInformation": "SpotLight3D::SpotLight3D", - "type": "object" - } - ], - "objectGroups": [] - }, - { - "description": "the color of the light.", - "fullName": "Color", - "functionType": "ExpressionAndCondition", - "name": "Color", - "sentence": "the color", - "events": [ - { - "type": "BuiltinCommonInstructions::Standard", - "conditions": [], - "actions": [ - { - "type": { - "value": "SetReturnString" - }, - "parameters": [ - "Color" + "" ] } ] } ], - "expressionType": { - "type": "color" - }, "parameters": [ { "description": "Object", "name": "Object", - "supplementaryInformation": "SpotLight3D::SpotLight3D", + "supplementaryInformation": "Light3D::PointLight3D", "type": "object" } ], "objectGroups": [] }, { - "fullName": "", - "functionType": "ActionWithOperator", - "getterName": "Color", - "name": "SetColor", - "sentence": "", + "fullName": "Update helper", + "functionType": "Action", + "name": "UpdateHelper", + "private": true, + "sentence": "Update graphical helper of _PARAM0_", "events": [ - { - "type": "BuiltinCommonInstructions::Standard", - "conditions": [], - "actions": [ - { - "type": { - "value": "SetStringVariable" - }, - "parameters": [ - "Color", - "=", - "Value" - ] - } - ] - }, { "type": "BuiltinCommonInstructions::JsCode", "inlineCode": [ - "const object = objects[0];\r", - "const color = eventsFunctionContext.getArgument(\"Value\");\r", - "\r", - "object.__spotLightAdapter.setColor(color);" + "const game = runtimeScene.getGame();", + "if (game.isInGameEdition && game.isInGameEdition()) {", + " const pointLightHelper = objects[0].get3DRendererObject();", + " pointLightHelper.update();", + "}", + "" ], "parameterObjects": "Object", "useStrict": true, @@ -1238,18 +3221,18 @@ { "description": "Object", "name": "Object", - "supplementaryInformation": "SpotLight3D::SpotLight3D", + "supplementaryInformation": "Light3D::PointLight3D", "type": "object" } ], "objectGroups": [] }, { - "description": "the smoothing of the light. Percent of the spotlight cone that is attenuated due to penumbra (between 0 and 1).", - "fullName": "Smoothing", + "description": "the color of the light.", + "fullName": "Color", "functionType": "ExpressionAndCondition", - "name": "Smoothing", - "sentence": "the smoothing", + "name": "Color", + "sentence": "the color", "events": [ { "type": "BuiltinCommonInstructions::Standard", @@ -1257,23 +3240,23 @@ "actions": [ { "type": { - "value": "SetReturnNumber" + "value": "SetReturnString" }, "parameters": [ - "Smoothing" + "Color" ] } ] } ], "expressionType": { - "type": "expression" + "type": "color" }, "parameters": [ { "description": "Object", "name": "Object", - "supplementaryInformation": "SpotLight3D::SpotLight3D", + "supplementaryInformation": "Light3D::PointLight3D", "type": "object" } ], @@ -1282,8 +3265,8 @@ { "fullName": "", "functionType": "ActionWithOperator", - "getterName": "Smoothing", - "name": "SetSmoothing", + "getterName": "Color", + "name": "SetColor", "sentence": "", "events": [ { @@ -1292,10 +3275,10 @@ "actions": [ { "type": { - "value": "SetNumberVariable" + "value": "SetStringVariable" }, "parameters": [ - "Smoothing", + "Color", "=", "Value" ] @@ -1306,9 +3289,9 @@ "type": "BuiltinCommonInstructions::JsCode", "inlineCode": [ "const object = objects[0];\r", - "const value = eventsFunctionContext.getArgument(\"Value\");\r", + "const color = eventsFunctionContext.getArgument(\"Value\");\r", "\r", - "object.__spotLightAdapter.setSmoothing(value);" + "object.__pointLightAdapter.setColor(color);" ], "parameterObjects": "Object", "useStrict": true, @@ -1319,7 +3302,7 @@ { "description": "Object", "name": "Object", - "supplementaryInformation": "SpotLight3D::SpotLight3D", + "supplementaryInformation": "Light3D::PointLight3D", "type": "object" } ], @@ -1354,7 +3337,7 @@ { "description": "Object", "name": "Object", - "supplementaryInformation": "SpotLight3D::SpotLight3D", + "supplementaryInformation": "Light3D::PointLight3D", "type": "object" } ], @@ -1389,7 +3372,7 @@ "const object = objects[0];\r", "const value = eventsFunctionContext.getArgument(\"Value\");\r", "\r", - "object.__spotLightAdapter.setIntensity(value);\r", + "object.__pointLightAdapter.setIntensity(value);\r", "" ], "parameterObjects": "Object", @@ -1401,7 +3384,7 @@ { "description": "Object", "name": "Object", - "supplementaryInformation": "SpotLight3D::SpotLight3D", + "supplementaryInformation": "Light3D::PointLight3D", "type": "object" } ], @@ -1436,7 +3419,7 @@ { "description": "Object", "name": "Object", - "supplementaryInformation": "SpotLight3D::SpotLight3D", + "supplementaryInformation": "Light3D::PointLight3D", "type": "object" } ], @@ -1471,7 +3454,7 @@ "const object = objects[0];\r", "const value = eventsFunctionContext.getArgument(\"Value\");\r", "\r", - "object.__spotLightAdapter.setDecay(value);" + "object.__pointLightAdapter.setDecay(value);" ], "parameterObjects": "Object", "useStrict": true, @@ -1482,7 +3465,7 @@ { "description": "Object", "name": "Object", - "supplementaryInformation": "SpotLight3D::SpotLight3D", + "supplementaryInformation": "Light3D::PointLight3D", "type": "object" } ], @@ -1492,7 +3475,7 @@ "description": "Check if the light is casting shadows.", "fullName": "Shadow casting", "functionType": "Condition", - "group": "3D spot light shadow configuration", + "group": "3D point light shadow configuration", "name": "IsCastingShadow", "sentence": "_PARAM0_ casting shadows", "events": [ @@ -1526,7 +3509,7 @@ { "description": "Object", "name": "Object", - "supplementaryInformation": "SpotLight3D::SpotLight3D", + "supplementaryInformation": "Light3D::PointLight3D", "type": "object" } ], @@ -1536,7 +3519,7 @@ "description": "Change if the light is casting shadows.", "fullName": "Shadow casting", "functionType": "Action", - "group": "3D spot light shadow configuration", + "group": "3D point light shadow configuration", "name": "SetCastingShadow", "sentence": "_PARAM0_ casting shadows: _PARAM1_", "events": [ @@ -1600,7 +3583,7 @@ "const object = objects[0];\r", "const value = eventsFunctionContext.getArgument(\"Value\");\r", "\r", - "object.__spotLightAdapter.setCastingShadow(value);" + "object.__pointLightAdapter.setCastingShadow(value);" ], "parameterObjects": "Object", "useStrict": true, @@ -1611,7 +3594,7 @@ { "description": "Object", "name": "Object", - "supplementaryInformation": "SpotLight3D::SpotLight3D", + "supplementaryInformation": "Light3D::PointLight3D", "type": "object" }, { @@ -1624,107 +3607,11 @@ ], "objectGroups": [] }, - { - "description": "Rotate the light to light up a position.", - "fullName": "Look at position", - "functionType": "Action", - "group": "Angle", - "name": "LookAtPosition", - "sentence": "_PARAM0_ look at _PARAM1_ ; _PARAM2_ ; _PARAM3_", - "events": [ - { - "type": "BuiltinCommonInstructions::JsCode", - "inlineCode": [ - "const object = objects[0];\r", - "const targetX = eventsFunctionContext.getArgument(\"TargetX\");\r", - "const targetY = eventsFunctionContext.getArgument(\"TargetY\");\r", - "const targetZ = eventsFunctionContext.getArgument(\"TargetZ\");\r", - "\r", - "object.__spotLightAdapter.lookAtPosition(targetX, targetY, targetZ);" - ], - "parameterObjects": "Object", - "useStrict": true, - "eventsSheetExpanded": false - } - ], - "parameters": [ - { - "description": "Object", - "name": "Object", - "supplementaryInformation": "SpotLight3D::SpotLight3D", - "type": "object" - }, - { - "description": "Target X", - "name": "TargetX", - "type": "expression" - }, - { - "description": "Target Y", - "name": "TargetY", - "type": "expression" - }, - { - "description": "Target Z", - "name": "TargetZ", - "type": "expression" - } - ], - "objectGroups": [] - }, - { - "description": "Rotate the light to light up an object.", - "fullName": "Look at object", - "functionType": "Action", - "group": "Angle", - "name": "LookAtObject", - "sentence": "_PARAM0_ look at _PARAM1_", - "events": [ - { - "type": "BuiltinCommonInstructions::Standard", - "conditions": [], - "actions": [ - { - "type": { - "value": "SpotLight3D::SpotLight3D::LookAtPosition" - }, - "parameters": [ - "Object", - "Target.CenterX()", - "Target.CenterY()", - "Target.Object3D::CenterZ()", - "" - ] - } - ] - } - ], - "parameters": [ - { - "description": "Object", - "name": "Object", - "supplementaryInformation": "SpotLight3D::SpotLight3D", - "type": "object" - }, - { - "description": "Object", - "name": "Target", - "type": "objectList" - }, - { - "description": "3D capability", - "name": "Object3D", - "supplementaryInformation": "Scene3D::Base3DBehavior", - "type": "behavior" - } - ], - "objectGroups": [] - }, { "description": "the shadow quality of the light.", "fullName": "Shadow quality", "functionType": "ExpressionAndCondition", - "group": "3D spot light shadow configuration", + "group": "3D point light shadow configuration", "name": "ShadowQuality", "sentence": "the shadow quality", "events": [ @@ -1751,7 +3638,7 @@ { "description": "Object", "name": "Object", - "supplementaryInformation": "SpotLight3D::SpotLight3D", + "supplementaryInformation": "Light3D::PointLight3D", "type": "object" } ], @@ -1761,7 +3648,7 @@ "fullName": "", "functionType": "ActionWithOperator", "getterName": "ShadowQuality", - "group": "3D spot light shadow configuration", + "group": "3D point light shadow configuration", "name": "SetShadowQuality", "sentence": "", "events": [ @@ -1787,7 +3674,7 @@ "const object = objects[0];\r", "const value = eventsFunctionContext.getArgument(\"Value\");\r", "\r", - "object.__spotLightAdapter.setShadowQuality(value);" + "object.__pointLightAdapter.setShadowQuality(value);" ], "parameterObjects": "Object", "useStrict": true, @@ -1798,7 +3685,7 @@ { "description": "Object", "name": "Object", - "supplementaryInformation": "SpotLight3D::SpotLight3D", + "supplementaryInformation": "Light3D::PointLight3D", "type": "object" } ], @@ -1808,7 +3695,7 @@ "description": "the shadow camera near plane of the light.", "fullName": "Shadow camera near plane", "functionType": "ExpressionAndCondition", - "group": "3D spot light shadow configuration", + "group": "3D point light shadow configuration", "name": "ShadowCameraNearPlane", "sentence": "the shadow camera near plane", "events": [ @@ -1834,7 +3721,7 @@ { "description": "Object", "name": "Object", - "supplementaryInformation": "SpotLight3D::SpotLight3D", + "supplementaryInformation": "Light3D::PointLight3D", "type": "object" } ], @@ -1844,7 +3731,7 @@ "fullName": "", "functionType": "ActionWithOperator", "getterName": "ShadowCameraNearPlane", - "group": "3D spot light shadow configuration", + "group": "3D point light shadow configuration", "name": "SetShadowCameraNearPlane", "sentence": "", "events": [ @@ -1870,7 +3757,7 @@ "const object = objects[0];\r", "const value = eventsFunctionContext.getArgument(\"Value\");\r", "\r", - "object.__spotLightAdapter.setShadowCameraNearPlane(value);\r", + "object.__pointLightAdapter.setShadowCameraNearPlane(value);\r", "" ], "parameterObjects": "Object", @@ -1882,7 +3769,7 @@ { "description": "Object", "name": "Object", - "supplementaryInformation": "SpotLight3D::SpotLight3D", + "supplementaryInformation": "Light3D::PointLight3D", "type": "object" } ], @@ -1892,7 +3779,7 @@ "description": "the shadow camera far plane of the light.", "fullName": "Shadow camera far plane", "functionType": "ExpressionAndCondition", - "group": "3D spot light shadow configuration", + "group": "3D point light shadow configuration", "name": "ShadowCameraFarPlane", "sentence": "the shadow camera far plane", "events": [ @@ -1918,7 +3805,7 @@ { "description": "Object", "name": "Object", - "supplementaryInformation": "SpotLight3D::SpotLight3D", + "supplementaryInformation": "Light3D::PointLight3D", "type": "object" } ], @@ -1928,7 +3815,7 @@ "fullName": "", "functionType": "ActionWithOperator", "getterName": "ShadowCameraFarPlane", - "group": "3D spot light shadow configuration", + "group": "3D point light shadow configuration", "name": "SetShadowCameraFarPlane", "sentence": "", "events": [ @@ -1954,7 +3841,7 @@ "const object = objects[0];\r", "const value = eventsFunctionContext.getArgument(\"Value\");\r", "\r", - "object.__spotLightAdapter.setShadowCameraFarPlane(value);\r", + "object.__pointLightAdapter.setShadowCameraFarPlane(value);\r", "" ], "parameterObjects": "Object", @@ -1966,18 +3853,18 @@ { "description": "Object", "name": "Object", - "supplementaryInformation": "SpotLight3D::SpotLight3D", + "supplementaryInformation": "Light3D::PointLight3D", "type": "object" } ], "objectGroups": [] }, { - "description": "the cone length of the light. 0 means no limit.", - "fullName": "Cone length", + "description": "the range of the light. 0 means no limit.", + "fullName": "Range", "functionType": "ExpressionAndCondition", - "name": "ConeLength", - "sentence": "the cone length", + "name": "Range", + "sentence": "the range", "events": [ { "type": "BuiltinCommonInstructions::Standard", @@ -1988,7 +3875,7 @@ "value": "SetReturnNumber" }, "parameters": [ - "ConeLength" + "Range" ] } ] @@ -2001,7 +3888,7 @@ { "description": "Object", "name": "Object", - "supplementaryInformation": "SpotLight3D::SpotLight3D", + "supplementaryInformation": "Light3D::PointLight3D", "type": "object" } ], @@ -2010,8 +3897,8 @@ { "fullName": "", "functionType": "ActionWithOperator", - "getterName": "ConeLength", - "name": "SetConeLength", + "getterName": "Range", + "name": "SetRange", "sentence": "", "events": [ { @@ -2023,7 +3910,7 @@ "value": "SetNumberVariable" }, "parameters": [ - "ConeLength", + "Range", "=", "Value" ] @@ -2036,8 +3923,7 @@ "const object = objects[0];\r", "const value = eventsFunctionContext.getArgument(\"Value\");\r", "\r", - "object.__spotLightAdapter.setConeLength(value);\r", - "" + "object.__pointLightAdapter.setRange(value);" ], "parameterObjects": "Object", "useStrict": true, @@ -2048,7 +3934,7 @@ { "description": "Object", "name": "Object", - "supplementaryInformation": "SpotLight3D::SpotLight3D", + "supplementaryInformation": "Light3D::PointLight3D", "type": "object" } ], @@ -2073,20 +3959,6 @@ { "functionName": "UpdateHelper" }, - { - "functionName": "UpdateImage" - }, - { - "folderName": "Angle", - "children": [ - { - "functionName": "LookAtPosition" - }, - { - "functionName": "LookAtObject" - } - ] - }, { "functionName": "Color" }, @@ -2106,25 +3978,13 @@ "functionName": "SetDecay" }, { - "functionName": "ConeLength" - }, - { - "functionName": "SetConeLength" - }, - { - "functionName": "ConeAngle" - }, - { - "functionName": "SetConeAngle" + "functionName": "Range" }, { - "functionName": "Smoothing" - }, - { - "functionName": "SetSmoothing" + "functionName": "SetRange" }, { - "folderName": "3D spot light shadow configuration", + "folderName": "3D point light shadow configuration", "children": [ { "functionName": "IsCastingShadow" @@ -2161,28 +4021,12 @@ "label": "Color", "name": "Color" }, - { - "value": "30", - "type": "Number", - "unit": "DegreeAngle", - "label": "Cone angle", - "group": "Cone", - "name": "ConeAngle" - }, { "value": "1", "type": "Number", "label": "Intensity", "name": "Intensity" }, - { - "value": "1", - "type": "Number", - "label": "Smoothing", - "description": "Percent of the spotlight cone that is attenuated due to penumbra (between 0 and 1).", - "group": "Cone", - "name": "Smoothing" - }, { "value": "2", "type": "Number", @@ -2190,19 +4034,6 @@ "description": "The amount the light dims along the distance of the light.", "name": "Decay" }, - { - "value": "", - "type": "Resource", - "label": "Image", - "description": "Shadow casting must be enabled for the image to have any effect.", - "group": "Shadow", - "extraInformation": [ - "image" - ], - "choices": [], - "advanced": true, - "name": "Image" - }, { "value": "true", "type": "Boolean", @@ -2253,11 +4084,10 @@ "value": "0", "type": "Number", "unit": "Pixel", - "label": "Cone length", + "label": "Range", "description": "0 means no limit.", - "group": "Cone", "advanced": true, - "name": "ConeLength" + "name": "Range" } ], "propertiesFolderStructure": { @@ -2273,18 +4103,7 @@ "propertyName": "Decay" }, { - "folderName": "Cone", - "children": [ - { - "propertyName": "ConeAngle" - }, - { - "propertyName": "ConeLength" - }, - { - "propertyName": "Smoothing" - } - ] + "propertyName": "Range" }, { "folderName": "Shadow", @@ -2295,9 +4114,6 @@ { "propertyName": "ShadowQuality" }, - { - "propertyName": "Image" - }, { "propertyName": "ShadowCameraNearPlane" }, diff --git a/scripts/lib/ExtensionsValidatorExceptions.js b/scripts/lib/ExtensionsValidatorExceptions.js index 27a361d4d..b0266726a 100644 --- a/scripts/lib/ExtensionsValidatorExceptions.js +++ b/scripts/lib/ExtensionsValidatorExceptions.js @@ -511,9 +511,9 @@ const extensionsAllowedProperties = { runtimeSceneAllowedProperties: [], javaScriptObjectAllowedProperties: [], }, - SpotLight3D: { + Light3D: { gdjsAllowedProperties: [ - '__spotLight3DExtension', + '__light3DExtension', 'CustomRuntimeObject', 'CustomRuntimeObjectInstanceContainer', 'CustomRuntimeObject3DRenderer', @@ -526,8 +526,10 @@ const extensionsAllowedProperties = { gdjsEvtToolsAllowedProperties: ['camera'], runtimeSceneAllowedProperties: [ '__spotLight3DExtension', + '__pointLight3DExtension', 'getScene', '__spotLightManager', + '__pointLightManager', ], javaScriptObjectAllowedProperties: ['getPrototypeOf'], }, From dc40bc2c29278b1ebfb4db21bd2df82922e0b161 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Davy=20H=C3=A9lard?= Date: Thu, 2 Apr 2026 18:13:27 +0200 Subject: [PATCH 12/22] Use a common manager --- extensions/reviewed/Light3D.json | 757 +++++++++++++------------------ 1 file changed, 305 insertions(+), 452 deletions(-) diff --git a/extensions/reviewed/Light3D.json b/extensions/reviewed/Light3D.json index 8204b6f89..5edb97a76 100644 --- a/extensions/reviewed/Light3D.json +++ b/extensions/reviewed/Light3D.json @@ -35,223 +35,18 @@ "type": "BuiltinCommonInstructions::JsCode", "inlineCode": [ "//@ts-ignore", - "if (gdjs.__light3DExtension && gdjs.__light3DExtension.spot) {", + "if (gdjs.__light3DExtension.spot) {", " //@ts-ignore", " return;", "}", "", "/**", - " * @typedef {gdjs.CustomRuntimeObject3D & {__cameraDistance: number, __spotLight: THREE.SpotLight, _getIsCastingShadow: () => boolean, _getConeAngle: () => number, _getColor: () => string}} SpotLightRuntimeObject", + " * @typedef {gdjs.CustomRuntimeObject3D & {__cameraDistance: number, __light3D: THREE.SpotLight, _getIsCastingShadow: () => boolean, _getConeAngle: () => number, _getColor: () => string}} SpotLightRuntimeObject", " */", "", "const game = runtimeScene.getGame();", "const isInGameEdition = game.isInGameEdition && game.isInGameEdition();", "", - "", - "class SpotLight3DRenderer extends gdjs.CustomRuntimeObject3DRenderer {", - "", - " constructor(", - " object,", - " instanceContainer,", - " parent", - " ) {", - " super(object, instanceContainer, parent);", - " }", - "", - " _updateThreeGroup() {", - " /** @type {SpotLightRuntimeObject} */", - " //@ts-ignore", - " const object = this._object;", - " const threeObject3D = this.get3DRendererObject();", - "", - " threeObject3D.rotation.set(", - " gdjs.toRad(object.getRotationX()),", - " gdjs.toRad(object.getRotationY()),", - " gdjs.toRad(object.angle)", - " );", - "", - " threeObject3D.position.set(", - " object.getX(),", - " object.getY(),", - " object.getZ()", - " );", - "", - " // Force the scale to 1 because the light doesn't really has a size.", - " threeObject3D.scale.set(", - " object.isFlippedX() ? -1 : 1,", - " object.isFlippedY() ? -1 : 1,", - " object.isFlippedZ() ? -1 : 1", - " );", - "", - " threeObject3D.visible = !this._object.hidden;", - "", - " const spotLight = object.__spotLight;", - "", - " const editor = game.getInGameEditor ? game.getInGameEditor() : null;", - "", - " spotLight.castShadow = false;", - " spotLight.visible = false;", - "", - " if (!object.isHidden()) {", - " let isSelected = false;", - " if (editor) {", - " const selectedObject = editor._selection.getLastSelectedObject();", - " let parentObject = object;", - " isSelected = parentObject === selectedObject;", - " while (!isSelected && parentObject.getInstanceContainer()", - " //@ts-ignore", - " .getOwner) {", - " parentObject = parentObject.getInstanceContainer()", - " //@ts-ignore", - " .getOwner();", - " isSelected = parentObject === selectedObject;", - " }", - " }", - " let rootObject = object;", - " while (rootObject.getInstanceContainer()", - " //@ts-ignore", - " .getOwner) {", - " rootObject = rootObject.getInstanceContainer()", - " //@ts-ignore", - " .getOwner();", - " }", - " const runtimeScene = rootObject.getRuntimeScene();", - " let distanceSq = 0;", - " if (!isSelected) {", - " const layerName = rootObject.getLayer();", - " const cameraX = gdjs.evtTools.camera.getCameraX(runtimeScene, layerName, 0);", - " const cameraY = gdjs.evtTools.camera.getCameraY(runtimeScene, layerName, 0);", - " const cameraZ = gdjs.scene3d.camera.getCameraZ(runtimeScene, layerName, 0);", - " const deltaX = rootObject.getX() - cameraX;", - " const deltaY = rootObject.getY() - cameraY;", - " const deltaZ = rootObject.getZ() - cameraZ;", - " distanceSq = deltaX * deltaX + deltaY * deltaY + deltaZ * deltaZ;", - " }", - " getSpotLightManager(", - " //@ts-ignore", - " runtimeScene", - " ).applyVisibilityAndShadow(object, distanceSq);", - " }", - "", - " // Force the visibility to be checked every frame", - " this._isContainerDirty = true;", - " }", - "}", - "", - "gdjs.registerRuntimeScenePreEventsCallback(", - " runtimeScene => {", - " getSpotLightManager(", - " //@ts-ignore", - " runtimeScene.getScene()", - " ).clear();", - " });", - "if (gdjs.registerInGameEditorPostStepCallback) {", - " gdjs.registerInGameEditorPostStepCallback(", - " inGameEditor => {", - " getSpotLightManager(", - " //@ts-ignore", - " inGameEditor.getEditedInstanceContainer().getScene()", - " ).clear();", - " });", - "}", - "", - "/**", - " * Get the platforms manager of an instance container.", - " * @param {gdjs.RuntimeScene & {__spotLightManager: SpotLightManager}} runtimeScene", - " */", - "function getSpotLightManager(runtimeScene) {", - " if (!runtimeScene.__spotLightManager) {", - " // Create the shared manager if necessary.", - " runtimeScene.__spotLightManager = isInGameEdition ?", - " new SpotLightManager(4, 1) :", - " new SpotLightManager(20, 4);", - " }", - " return runtimeScene.__spotLightManager;", - "}", - "", - "/** @type {{isInserted: boolean, removedObject: SpotLightRuntimeObject | null}} */", - "const sortResult = { isInserted: false, removedObject: null };", - "", - "/**", - " * @param objects {Array}", - " * @param maxCount {number}", - " * @param object {SpotLightRuntimeObject}", - " * @param distance {number}", - " */", - "function insertByDistance(objects, maxCount, object, distance) {", - " sortResult.isInserted = false;", - " sortResult.removedObject = null;", - " let insertionIndex = 0;", - " for (let index = objects.length - 1; index >= 0; index--) {", - " const other = objects[index];", - " const otherDistance = other.__cameraDistance;", - " if (distance >= otherDistance) {", - " insertionIndex = index + 1;", - " break;", - " }", - " }", - " if (insertionIndex >= maxCount) {", - " return sortResult;", - " }", - " object.__cameraDistance = distance", - " objects.splice(insertionIndex, 0, object);", - " sortResult.isInserted = true;", - " if (objects.length > maxCount) {", - " sortResult.removedObject = objects.pop();", - " }", - " return sortResult;", - "}", - "", - "class SpotLightManager {", - " /** @type {Array} */", - " visibleObjects = new Array();", - " /** @type {Array} */", - " shadowObjects = new Array();", - " /** @type {number} */", - " maxCount;", - " /** @type {number} */", - " shadowCount;", - "", - " /**", - " * @param maxCount {number}", - " * @param shadowCount {number}", - " */", - " constructor(maxCount, shadowCount) {", - " this.maxCount = maxCount;", - " this.shadowCount = shadowCount;", - " }", - "", - " clear() {", - " this.visibleObjects.length = 0;", - " this.shadowObjects.length = 0;", - " }", - "", - " /**", - " * @param object {SpotLightRuntimeObject}", - " * @param distance {number}", - " */", - " applyVisibilityAndShadow(object, distance) {", - " if (object._getIsCastingShadow()) {", - " const { isInserted, removedObject } = insertByDistance(", - " this.shadowObjects, this.shadowCount, object, distance);", - " if (isInserted) {", - " object.__spotLight.castShadow = true;", - " }", - " if (removedObject) {", - " removedObject.__spotLight.castShadow = false;", - " }", - " }", - " const { isInserted, removedObject } = insertByDistance(", - " this.visibleObjects, this.maxCount, object, distance);", - " if (isInserted) {", - " object.__spotLight.visible = true;", - " }", - " if (removedObject) {", - " removedObject.__spotLight.visible = false;", - " }", - " }", - "}", - "", "const coneLength = 64;", "", "class SpotLightHelper extends THREE.Object3D {", @@ -371,9 +166,229 @@ " this.spotLight.parent = parent;", "", " // Angle setters update Three.js angles, so we save them first.", - " const rotationX = gdjs.toDegrees(this.spotLight.rotation.x);", - " const rotationY = gdjs.toDegrees(this.spotLight.rotation.y);", - " const rotationZ = gdjs.toDegrees(this.spotLight.rotation.z);", + " const rotationX = gdjs.toDegrees(this.spotLight.rotation.x);", + " const rotationY = gdjs.toDegrees(this.spotLight.rotation.y);", + " const rotationZ = gdjs.toDegrees(this.spotLight.rotation.z);", + " this.object.setRotationX(rotationX);", + " this.object.setRotationY(rotationY);", + " this.object.setAngle(rotationZ + 90);", + " }", + "", + " /**", + " * @param color {string}", + " */", + " setColor(color) {", + " setThreeColor(color, this.spotLight.color);", + " }", + "", + " /**", + " * @param intensity {number}", + " */", + " setIntensity(intensity) {", + " this.spotLight.intensity = intensity;", + " }", + "", + " /**", + " * @param decay {number}", + " */", + " setDecay(decay) {", + " this.spotLight.decay = decay;", + " }", + "", + " /**", + " * @param value {number}", + " */", + " setConeLength(value) {", + " this.spotLight.distance = value;", + " }", + "", + " /**", + " * @param coneAngle {number}", + " */", + " setConeAngle(coneAngle) {", + " this.spotLight.angle = coneAngle * Math.PI / 180;", + " }", + "", + " /**", + " * @param smoothing {number}", + " */", + " setSmoothing(smoothing) {", + " this.spotLight.penumbra = smoothing;", + " }", + "", + " /**", + " * @param isCastingShadow {boolean}", + " */", + " setCastingShadow(isCastingShadow) {", + " // This is applied by applyVisibilityAndShadow", + " }", + "", + " /**", + " * @param shadowQuality {\"Low\" | \"Medium\" | \"High\"}", + " */", + " setShadowQuality(shadowQuality) {", + " let size = 512;", + " switch (shadowQuality) {", + " case \"Low\":", + " size = 256;", + " break;", + " case \"Medium\":", + " size = 512;", + " break;", + " case \"High\":", + " size = 1024;", + " break;", + " }", + " const shadow = this.spotLight.shadow;", + " const mapSize = shadow.mapSize;", + " if (mapSize.width !== size) {", + " mapSize.set(size, size);", + "", + " // Force the recreation of the shadow map texture:", + " if (shadow.map) {", + " shadow.map.dispose();", + " shadow.map = null;", + " }", + " shadow.needsUpdate = true;", + " }", + " }", + "", + " /**", + " * @param value {number}", + " */", + " setShadowCameraNearPlane(value) {", + " this.spotLight.shadow.camera.near = value;", + " }", + "", + " /**", + " * @param value {number}", + " */", + " setShadowCameraFarPlane(value) {", + " this.spotLight.shadow.camera.far = value;", + " }", + "", + " /**", + " * @param resourceName {string}", + " */", + " setImage(resourceName) {", + " if (!resourceName) {", + " this.spotLight.map = null;", + " return;", + " }", + " const texture = game.getImageManager().getThreeTexture(resourceName);", + " this.spotLight.map = texture;", + " }", + "}", + "", + "", + "", + "//@ts-ignore", + "gdjs.__light3DExtension.spot = {", + " SpotLightAdapter,", + " SpotLightHelper,", + "}" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": true + } + ], + "parameters": [], + "objectGroups": [] + }, + { + "description": "Define point light helper classes JavaScript code.", + "fullName": "Define point light helper classes", + "functionType": "Action", + "name": "DefinePointLightHelperClasses", + "private": true, + "sentence": "Define point light helper classes JavaScript code", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "//@ts-ignore", + "if (gdjs.__light3DExtension.point) {", + " //@ts-ignore", + " return;", + "}", + "", + "/**", + " * @typedef {gdjs.CustomRuntimeObject3D & {__cameraDistance: number, __light3D: THREE.PointLight, _getIsCastingShadow: () => boolean, _getColor: () => string}} PointLightRuntimeObject", + " */", + "", + "const game = runtimeScene.getGame();", + "const isInGameEdition = game.isInGameEdition && game.isInGameEdition();", + "", + "/**", + " * @extends {THREE.Mesh}", + " */", + "class PointLightHelper extends THREE.Mesh {", + " /** @type {PointLightRuntimeObject} */", + " object;", + "", + "\t/**", + " * @param {PointLightRuntimeObject} object", + "\t */", + " constructor(object) {", + "\t\tconst geometry = new THREE.SphereGeometry( 16, 4, 2 );", + "\t\tconst material = new THREE.MeshBasicMaterial( { wireframe: true, fog: false, toneMapped: false } );", + " super(geometry, material);", + " this.object = object;", + " this.update();", + "", + " //@ts-ignore", + " this.gdjsRuntimeObject = object;", + " }", + "", + " dispose() {", + "\t\tthis.geometry.dispose();", + "\t\tthis.material.dispose();", + " }", + "", + " update() {", + " const color = gdjs.rgbOrHexStringToNumber(this.object._getColor());", + " this.material.color.set(color);", + " }", + "}", + "", + "/**", + " * @param {string} colorString", + " * @param {THREE.Color} threeColor", + " */", + "const setThreeColor = (colorString, threeColor) => {", + " const integerColor = gdjs.rgbOrHexToRGBColor(colorString);", + " threeColor.r = integerColor[0] / 255;", + " threeColor.g = integerColor[1] / 255;", + " threeColor.b = integerColor[2] / 255;", + "};", + "", + "class PointLightAdapter {", + " /**", + " * @param object {gdjs.CustomRuntimeObject3D}", + " * @param pointLight {THREE.PointLight}", + " */", + " constructor(object, pointLight) {", + " this.object = object;", + " this.pointLight = pointLight;", + " }", + "", + " /**", + " * @param targetX {number}", + " * @param targetY {number}", + " * @param targetZ {number}", + " */", + " lookAtPosition(targetX, targetY, targetZ) {", + " // Remove from the parent to avoid the scene scale of -1 on Y to mess with the formula.", + " const parent = this.pointLight.parent;", + " this.pointLight.parent = null;", + " this.pointLight.lookAt(targetX, targetY, targetZ);", + " this.pointLight.parent = parent;", + "", + " // Angle setters update Three.js angles, so we save them first.", + " const rotationX = gdjs.toDegrees(this.pointLight.rotation.x);", + " const rotationY = gdjs.toDegrees(this.pointLight.rotation.y);", + " const rotationZ = gdjs.toDegrees(this.pointLight.rotation.z);", " this.object.setRotationX(rotationX);", " this.object.setRotationY(rotationY);", " this.object.setAngle(rotationZ + 90);", @@ -383,42 +398,28 @@ " * @param color {string}", " */", " setColor(color) {", - " setThreeColor(color, this.spotLight.color);", + " setThreeColor(color, this.pointLight.color);", " }", "", " /**", " * @param intensity {number}", " */", " setIntensity(intensity) {", - " this.spotLight.intensity = intensity;", + " this.pointLight.intensity = intensity;", " }", "", " /**", " * @param decay {number}", " */", " setDecay(decay) {", - " this.spotLight.decay = decay;", + " this.pointLight.decay = decay;", " }", "", " /**", " * @param value {number}", " */", - " setConeLength(value) {", - " this.spotLight.distance = value;", - " }", - "", - " /**", - " * @param coneAngle {number}", - " */", - " setConeAngle(coneAngle) {", - " this.spotLight.angle = coneAngle * Math.PI / 180;", - " }", - "", - " /**", - " * @param smoothing {number}", - " */", - " setSmoothing(smoothing) {", - " this.spotLight.penumbra = smoothing;", + " setRange(value) {", + " this.pointLight.distance = value;", " }", "", " /**", @@ -444,7 +445,7 @@ " size = 1024;", " break;", " }", - " const shadow = this.spotLight.shadow;", + " const shadow = this.pointLight.shadow;", " const mapSize = shadow.mapSize;", " if (mapSize.width !== size) {", " mapSize.set(size, size);", @@ -462,37 +463,23 @@ " * @param value {number}", " */", " setShadowCameraNearPlane(value) {", - " this.spotLight.shadow.camera.near = value;", + " this.pointLight.shadow.camera.near = value;", " }", "", " /**", " * @param value {number}", " */", " setShadowCameraFarPlane(value) {", - " this.spotLight.shadow.camera.far = value;", - " }", - "", - " /**", - " * @param resourceName {string}", - " */", - " setImage(resourceName) {", - " if (!resourceName) {", - " this.spotLight.map = null;", - " return;", - " }", - " const texture = game.getImageManager().getThreeTexture(resourceName);", - " this.spotLight.map = texture;", + " this.pointLight.shadow.camera.far = value;", " }", "}", "", "", "", "//@ts-ignore", - "gdjs.__light3DExtension = gdjs.__light3DExtension || {};", - "gdjs.__light3DExtension.spot = {", - " SpotLight3DRenderer,", - " SpotLightAdapter,", - " SpotLightHelper,", + "gdjs.__light3DExtension.point = {", + " PointLightAdapter,", + " PointLightHelper,", "}" ], "parameterObjects": "", @@ -504,31 +491,31 @@ "objectGroups": [] }, { - "description": "Define point light helper classes JavaScript code.", - "fullName": "Define point light helper classes", + "description": "Define helper classes JavaScript code.", + "fullName": "Define helper classes", "functionType": "Action", - "name": "DefinePointLightHelperClasses", + "name": "DefineHelperClasses", "private": true, - "sentence": "Define point light helper classes JavaScript code", + "sentence": "Define helper classes JavaScript code", "events": [ { "type": "BuiltinCommonInstructions::JsCode", "inlineCode": [ "//@ts-ignore", - "if (gdjs.__light3DExtension && gdjs.__light3DExtension.point) {", + "if (gdjs.__light3DExtension) {", " //@ts-ignore", " return;", "}", "", "/**", - " * @typedef {gdjs.CustomRuntimeObject3D & {__cameraDistance: number, __pointLight: THREE.PointLight, _getIsCastingShadow: () => boolean, _getConeAngle: () => number, _getColor: () => string}} PointLightRuntimeObject", + " * @typedef {gdjs.CustomRuntimeObject3D & {__cameraDistance: number, __light3D: THREE.SpotLight | THREE.PointLight, _getIsCastingShadow: () => boolean}} LightRuntimeObject", " */", "", "const game = runtimeScene.getGame();", "const isInGameEdition = game.isInGameEdition && game.isInGameEdition();", "", "", - "class PointLight3DRenderer extends gdjs.CustomRuntimeObject3DRenderer {", + "class Light3DRenderer extends gdjs.CustomRuntimeObject3DRenderer {", "", " constructor(", " object,", @@ -539,7 +526,7 @@ " }", "", " _updateThreeGroup() {", - " /** @type {PointLightRuntimeObject} */", + " /** @type {LightRuntimeObject} */", " //@ts-ignore", " const object = this._object;", " const threeObject3D = this.get3DRendererObject();", @@ -565,12 +552,12 @@ "", " threeObject3D.visible = !this._object.hidden;", "", - " const pointLight = object.__pointLight;", + " const spotLight = object.__light3D;", "", " const editor = game.getInGameEditor ? game.getInGameEditor() : null;", "", - " pointLight.castShadow = false;", - " pointLight.visible = false;", + " spotLight.castShadow = false;", + " spotLight.visible = false;", "", " if (!object.isHidden()) {", " let isSelected = false;", @@ -607,7 +594,7 @@ " const deltaZ = rootObject.getZ() - cameraZ;", " distanceSq = deltaX * deltaX + deltaY * deltaY + deltaZ * deltaZ;", " }", - " getPointLightManager(", + " getSpotLightManager(", " //@ts-ignore", " runtimeScene", " ).applyVisibilityAndShadow(object, distanceSq);", @@ -620,7 +607,7 @@ "", "gdjs.registerRuntimeScenePreEventsCallback(", " runtimeScene => {", - " getPointLightManager(", + " getSpotLightManager(", " //@ts-ignore", " runtimeScene.getScene()", " ).clear();", @@ -628,7 +615,7 @@ "if (gdjs.registerInGameEditorPostStepCallback) {", " gdjs.registerInGameEditorPostStepCallback(", " inGameEditor => {", - " getPointLightManager(", + " getSpotLightManager(", " //@ts-ignore", " inGameEditor.getEditedInstanceContainer().getScene()", " ).clear();", @@ -637,25 +624,25 @@ "", "/**", " * Get the platforms manager of an instance container.", - " * @param {gdjs.RuntimeScene & {__pointLightManager: PointLightManager}} runtimeScene", + " * @param {gdjs.RuntimeScene & {__spotLightManager: SpotLightManager}} runtimeScene", " */", - "function getPointLightManager(runtimeScene) {", - " if (!runtimeScene.__pointLightManager) {", + "function getSpotLightManager(runtimeScene) {", + " if (!runtimeScene.__spotLightManager) {", " // Create the shared manager if necessary.", - " runtimeScene.__pointLightManager = isInGameEdition ?", - " new PointLightManager(4, 1) :", - " new PointLightManager(20, 4);", + " runtimeScene.__spotLightManager = isInGameEdition ?", + " new SpotLightManager(4, 1) :", + " new SpotLightManager(20, 4);", " }", - " return runtimeScene.__pointLightManager;", + " return runtimeScene.__spotLightManager;", "}", "", - "/** @type {{isInserted: boolean, removedObject: PointLightRuntimeObject | null}} */", + "/** @type {{isInserted: boolean, removedObject: LightRuntimeObject | null}} */", "const sortResult = { isInserted: false, removedObject: null };", "", "/**", - " * @param objects {Array}", + " * @param objects {Array}", " * @param maxCount {number}", - " * @param object {PointLightRuntimeObject}", + " * @param object {LightRuntimeObject}", " * @param distance {number}", " */", "function insertByDistance(objects, maxCount, object, distance) {", @@ -682,10 +669,10 @@ " return sortResult;", "}", "", - "class PointLightManager {", - " /** @type {Array} */", + "class SpotLightManager {", + " /** @type {Array} */", " visibleObjects = new Array();", - " /** @type {Array} */", + " /** @type {Array} */", " shadowObjects = new Array();", " /** @type {number} */", " maxCount;", @@ -707,7 +694,7 @@ " }", "", " /**", - " * @param object {PointLightRuntimeObject}", + " * @param object {LightRuntimeObject}", " * @param distance {number}", " */", " applyVisibilityAndShadow(object, distance) {", @@ -715,187 +702,26 @@ " const { isInserted, removedObject } = insertByDistance(", " this.shadowObjects, this.shadowCount, object, distance);", " if (isInserted) {", - " object.__pointLight.castShadow = true;", + " object.__light3D.castShadow = true;", " }", " if (removedObject) {", - " removedObject.__pointLight.castShadow = false;", + " removedObject.__light3D.castShadow = false;", " }", " }", " const { isInserted, removedObject } = insertByDistance(", " this.visibleObjects, this.maxCount, object, distance);", " if (isInserted) {", - " object.__pointLight.visible = true;", + " object.__light3D.visible = true;", " }", " if (removedObject) {", - " removedObject.__pointLight.visible = false;", - " }", - " }", - "}", - "", - "const coneLength = 64;", - "", - "/**", - " * @extends {THREE.Mesh}", - " */", - "class PointLightHelper extends THREE.Mesh {", - " /** @type {PointLightRuntimeObject} */", - " object;", - "", - "\t/**", - " * @param {PointLightRuntimeObject} object", - "\t */", - " constructor(object) {", - "\t\tconst geometry = new THREE.SphereGeometry( 16, 4, 2 );", - "\t\tconst material = new THREE.MeshBasicMaterial( { wireframe: true, fog: false, toneMapped: false } );", - " super(geometry, material);", - " this.object = object;", - " this.update();", - "", - " //@ts-ignore", - " this.gdjsRuntimeObject = object;", - " }", - "", - " dispose() {", - "\t\tthis.geometry.dispose();", - "\t\tthis.material.dispose();", - " }", - "", - " update() {", - " const color = gdjs.rgbOrHexStringToNumber(this.object._getColor());", - " this.material.color.set(color);", - " }", - "}", - "", - "/**", - " * @param {string} colorString", - " * @param {THREE.Color} threeColor", - " */", - "const setThreeColor = (colorString, threeColor) => {", - " const integerColor = gdjs.rgbOrHexToRGBColor(colorString);", - " threeColor.r = integerColor[0] / 255;", - " threeColor.g = integerColor[1] / 255;", - " threeColor.b = integerColor[2] / 255;", - "};", - "", - "class PointLightAdapter {", - " /**", - " * @param object {gdjs.CustomRuntimeObject3D}", - " * @param pointLight {THREE.PointLight}", - " */", - " constructor(object, pointLight) {", - " this.object = object;", - " this.pointLight = pointLight;", - " }", - "", - " /**", - " * @param targetX {number}", - " * @param targetY {number}", - " * @param targetZ {number}", - " */", - " lookAtPosition(targetX, targetY, targetZ) {", - " // Remove from the parent to avoid the scene scale of -1 on Y to mess with the formula.", - " const parent = this.pointLight.parent;", - " this.pointLight.parent = null;", - " this.pointLight.lookAt(targetX, targetY, targetZ);", - " this.pointLight.parent = parent;", - "", - " // Angle setters update Three.js angles, so we save them first.", - " const rotationX = gdjs.toDegrees(this.pointLight.rotation.x);", - " const rotationY = gdjs.toDegrees(this.pointLight.rotation.y);", - " const rotationZ = gdjs.toDegrees(this.pointLight.rotation.z);", - " this.object.setRotationX(rotationX);", - " this.object.setRotationY(rotationY);", - " this.object.setAngle(rotationZ + 90);", - " }", - "", - " /**", - " * @param color {string}", - " */", - " setColor(color) {", - " setThreeColor(color, this.pointLight.color);", - " }", - "", - " /**", - " * @param intensity {number}", - " */", - " setIntensity(intensity) {", - " this.pointLight.intensity = intensity;", - " }", - "", - " /**", - " * @param decay {number}", - " */", - " setDecay(decay) {", - " this.pointLight.decay = decay;", - " }", - "", - " /**", - " * @param value {number}", - " */", - " setRange(value) {", - " this.pointLight.distance = value;", - " }", - "", - " /**", - " * @param isCastingShadow {boolean}", - " */", - " setCastingShadow(isCastingShadow) {", - " // This is applied by applyVisibilityAndShadow", - " }", - "", - " /**", - " * @param shadowQuality {\"Low\" | \"Medium\" | \"High\"}", - " */", - " setShadowQuality(shadowQuality) {", - " let size = 512;", - " switch (shadowQuality) {", - " case \"Low\":", - " size = 256;", - " break;", - " case \"Medium\":", - " size = 512;", - " break;", - " case \"High\":", - " size = 1024;", - " break;", - " }", - " const shadow = this.pointLight.shadow;", - " const mapSize = shadow.mapSize;", - " if (mapSize.width !== size) {", - " mapSize.set(size, size);", - "", - " // Force the recreation of the shadow map texture:", - " if (shadow.map) {", - " shadow.map.dispose();", - " shadow.map = null;", - " }", - " shadow.needsUpdate = true;", + " removedObject.__light3D.visible = false;", " }", " }", - "", - " /**", - " * @param value {number}", - " */", - " setShadowCameraNearPlane(value) {", - " this.pointLight.shadow.camera.near = value;", - " }", - "", - " /**", - " * @param value {number}", - " */", - " setShadowCameraFarPlane(value) {", - " this.pointLight.shadow.camera.far = value;", - " }", "}", "", - "", - "", "//@ts-ignore", - "gdjs.__light3DExtension = gdjs.__light3DExtension || {};", - "gdjs.__light3DExtension.point = {", - " PointLight3DRenderer,", - " PointLightAdapter,", - " PointLightHelper,", + "gdjs.__light3DExtension = {", + " Light3DRenderer,", "}" ], "parameterObjects": "", @@ -910,6 +736,9 @@ "eventsFunctionsFolderStructure": { "folderName": "__ROOT", "children": [ + { + "functionName": "DefineHelperClasses" + }, { "functionName": "DefineSpotLightHelperClasses" }, @@ -1064,6 +893,15 @@ "type": "BuiltinCommonInstructions::Standard", "conditions": [], "actions": [ + { + "type": { + "value": "Light3D::DefineHelperClasses" + }, + "parameters": [ + "", + "" + ] + }, { "type": { "value": "Light3D::DefineSpotLightHelperClasses" @@ -1078,9 +916,12 @@ { "type": "BuiltinCommonInstructions::JsCode", "inlineCode": [ + "const {", + " Light3DRenderer,", + " //@ts-ignore", + "} = gdjs.__light3DExtension;", "const {", " SpotLightAdapter,", - " SpotLight3DRenderer,", " SpotLightHelper,", " //@ts-ignore", "} = gdjs.__light3DExtension.spot;", @@ -1105,11 +946,11 @@ "spotLight.up.set(0, 0, 1);", "spotLight.shadow.camera.up.set(0, 0, 1);", "//@ts-ignore", - "object.__spotLight = spotLight;", + "object.__light3D = spotLight;", "//@ts-ignore", "object.__spotLightAdapter = new SpotLightAdapter(object, spotLight);", "", - "const spotLight3DRenderer = new SpotLight3DRenderer(object, object._instanceContainer, object.getInstanceContainer());", + "const spotLight3DRenderer = new Light3DRenderer(object, object._instanceContainer, object.getInstanceContainer());", "object._renderer = spotLight3DRenderer;", "if (game.isInGameEdition && game.isInGameEdition()) {", " const spotLightHelper = new SpotLightHelper(object);", @@ -1182,7 +1023,7 @@ "// Here runtimeScene is the gdjs.CustomRuntimeObjectInstanceContainer inside the custom object.", "const gameScene = object.getRuntimeScene();", "", - "object.__spotLight.dispose();", + "object.__light3D.dispose();", "object.get3DRendererObject().dispose();", "" ], @@ -2863,6 +2704,15 @@ "type": "BuiltinCommonInstructions::Standard", "conditions": [], "actions": [ + { + "type": { + "value": "Light3D::DefineHelperClasses" + }, + "parameters": [ + "", + "" + ] + }, { "type": { "value": "Light3D::DefinePointLightHelperClasses" @@ -2877,9 +2727,12 @@ { "type": "BuiltinCommonInstructions::JsCode", "inlineCode": [ + "const {", + " Light3DRenderer,", + " //@ts-ignore", + "} = gdjs.__light3DExtension;", "const {", " PointLightAdapter,", - " PointLight3DRenderer,", " PointLightHelper,", " //@ts-ignore", "} = gdjs.__light3DExtension.point;", @@ -2902,11 +2755,11 @@ "pointLight.up.set(0, 0, 1);", "pointLight.shadow.camera.up.set(0, 0, 1);", "//@ts-ignore", - "object.__pointLight = pointLight;", + "object.__light3D = pointLight;", "//@ts-ignore", "object.__pointLightAdapter = new PointLightAdapter(object, pointLight);", "", - "const pointLight3DRenderer = new PointLight3DRenderer(object, object._instanceContainer, object.getInstanceContainer());", + "const pointLight3DRenderer = new Light3DRenderer(object, object._instanceContainer, object.getInstanceContainer());", "object._renderer = pointLight3DRenderer;", "if (game.isInGameEdition && game.isInGameEdition()) {", " const pointLightHelper = new PointLightHelper(object);", @@ -2979,7 +2832,7 @@ "// Here runtimeScene is the gdjs.CustomRuntimeObjectInstanceContainer inside the custom object.", "const gameScene = object.getRuntimeScene();", "", - "object.__pointLight.dispose();", + "object.__light3D.dispose();", "object.get3DRendererObject().dispose();", "" ], From ee949b54bb59f91d9dc8759e869ceb59e06d7811 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Davy=20H=C3=A9lard?= Date: Thu, 2 Apr 2026 18:14:43 +0200 Subject: [PATCH 13/22] Add an action to choose the max number of lights --- extensions/reviewed/Light3D.json | 259 +++++++++++++++++++++++-------- 1 file changed, 191 insertions(+), 68 deletions(-) diff --git a/extensions/reviewed/Light3D.json b/extensions/reviewed/Light3D.json index 5edb97a76..de61a9fe2 100644 --- a/extensions/reviewed/Light3D.json +++ b/extensions/reviewed/Light3D.json @@ -45,7 +45,6 @@ " */", "", "const game = runtimeScene.getGame();", - "const isInGameEdition = game.isInGameEdition && game.isInGameEdition();", "", "const coneLength = 64;", "", @@ -318,7 +317,6 @@ " */", "", "const game = runtimeScene.getGame();", - "const isInGameEdition = game.isInGameEdition && game.isInGameEdition();", "", "/**", " * @extends {THREE.Mesh}", @@ -594,7 +592,7 @@ " const deltaZ = rootObject.getZ() - cameraZ;", " distanceSq = deltaX * deltaX + deltaY * deltaY + deltaZ * deltaZ;", " }", - " getSpotLightManager(", + " getLightManager(", " //@ts-ignore", " runtimeScene", " ).applyVisibilityAndShadow(object, distanceSq);", @@ -607,7 +605,7 @@ "", "gdjs.registerRuntimeScenePreEventsCallback(", " runtimeScene => {", - " getSpotLightManager(", + " getLightManager(", " //@ts-ignore", " runtimeScene.getScene()", " ).clear();", @@ -615,82 +613,135 @@ "if (gdjs.registerInGameEditorPostStepCallback) {", " gdjs.registerInGameEditorPostStepCallback(", " inGameEditor => {", - " getSpotLightManager(", + " getLightManager(", " //@ts-ignore", " inGameEditor.getEditedInstanceContainer().getScene()", " ).clear();", " });", "}", "", + "const editorLightCountMax = {", + " value: 4", + "};", + "", + "const lightCountMax = {", + " value: 20", + "};", + "", "/**", " * Get the platforms manager of an instance container.", - " * @param {gdjs.RuntimeScene & {__spotLightManager: SpotLightManager}} runtimeScene", + " * @param {gdjs.RuntimeScene & {__lightManager: LightManager}} runtimeScene", " */", - "function getSpotLightManager(runtimeScene) {", - " if (!runtimeScene.__spotLightManager) {", + "function getLightManager(runtimeScene) {", + " if (!runtimeScene.__lightManager) {", " // Create the shared manager if necessary.", - " runtimeScene.__spotLightManager = isInGameEdition ?", - " new SpotLightManager(4, 1) :", - " new SpotLightManager(20, 4);", + " runtimeScene.__lightManager = isInGameEdition ?", + " new LightManager(editorLightCountMax, 1) :", + " new LightManager(lightCountMax, 4);", " }", - " return runtimeScene.__spotLightManager;", + " return runtimeScene.__lightManager;", "}", "", "/** @type {{isInserted: boolean, removedObject: LightRuntimeObject | null}} */", "const sortResult = { isInserted: false, removedObject: null };", "", - "/**", - " * @param objects {Array}", - " * @param maxCount {number}", - " * @param object {LightRuntimeObject}", - " * @param distance {number}", - " */", - "function insertByDistance(objects, maxCount, object, distance) {", - " sortResult.isInserted = false;", - " sortResult.removedObject = null;", - " let insertionIndex = 0;", - " for (let index = objects.length - 1; index >= 0; index--) {", - " const other = objects[index];", - " const otherDistance = other.__cameraDistance;", - " if (distance >= otherDistance) {", - " insertionIndex = index + 1;", - " break;", - " }", + "class CappedLightList {", + " /** @type {Array<{object: LightRuntimeObject, weight: number}>} */", + " objects = [];", + " /** @type {{value: number}} */", + " capacity;", + " weight = 0;", + " /** @type {(object: LightRuntimeObject) => void} */", + " onInsertion;", + " /** @type {(object: LightRuntimeObject) => void} */", + " onDeletion;", + "", + " /**", + " * @param {{value: number}} capacity", + " * @param {(object: LightRuntimeObject) => void} onInsertion", + " * @param {(object: LightRuntimeObject) => void} onDeletion", + " */", + " constructor(capacity, onInsertion, onDeletion) {", + " this.capacity = capacity;", + " this.onInsertion = onInsertion;", + " this.onDeletion = onDeletion;", " }", - " if (insertionIndex >= maxCount) {", - " return sortResult;", + "", + " clear() {", + " this.objects.length = 0;", + " this.weight = 0;", " }", - " object.__cameraDistance = distance", - " objects.splice(insertionIndex, 0, object);", - " sortResult.isInserted = true;", - " if (objects.length > maxCount) {", - " sortResult.removedObject = objects.pop();", + "", + " /**", + " * @param object {LightRuntimeObject}", + " * @param distance {number}", + " * @param weight {number}", + " */", + " insertByDistance(object, distance, weight) {", + " let insertionIndex = 0;", + " for (let index = this.objects.length - 1; index >= 0; index--) {", + " const { object: other } = this.objects[index];", + " const otherDistance = other.__cameraDistance;", + " if (distance >= otherDistance) {", + " insertionIndex = index + 1;", + " break;", + " }", + " }", + " if (insertionIndex === this.objects.length", + " && this.weight + weight > this.capacity.value) {", + " return;", + " }", + " this.weight += weight;", + "", + " let deletedPair = null;", + " while (this.objects.length > 0 && this.weight > this.capacity.value) {", + " deletedPair = this.objects.pop();", + " const { object: removedObject, weight: otherWeight } = deletedPair;", + " this.weight -= otherWeight;", + " this.onDeletion(removedObject);", + " }", + "", + " let insertedPair;", + " if (deletedPair) {", + " insertedPair = deletedPair;", + " insertedPair.object = object;", + " insertedPair.weight = weight;", + " }", + " else {", + " insertedPair = { object, weight };", + " }", + " this.onInsertion(object);", + " this.objects.splice(insertionIndex, 0, insertedPair);", + " object.__cameraDistance = distance;", " }", - " return sortResult;", "}", "", - "class SpotLightManager {", - " /** @type {Array} */", - " visibleObjects = new Array();", - " /** @type {Array} */", - " shadowObjects = new Array();", - " /** @type {number} */", - " maxCount;", - " /** @type {number} */", - " shadowCount;", + "class LightManager {", + " /** @type {CappedLightList} */", + " visibleObjects;", + " /** @type {CappedLightList} */", + " shadowObjects;", "", " /**", - " * @param maxCount {number}", + " * @param maxCount {{value: number}}", " * @param shadowCount {number}", " */", " constructor(maxCount, shadowCount) {", - " this.maxCount = maxCount;", - " this.shadowCount = shadowCount;", + " this.visibleObjects = new CappedLightList(", + " maxCount,", + " (object) => { object.__light3D.visible = true; },", + " (object) => { object.__light3D.visible = false; }", + " );", + " this.shadowObjects = new CappedLightList(", + " { value: shadowCount },", + " (object) => { object.__light3D.castShadow = true; },", + " (object) => { object.__light3D.castShadow = false; }", + " );", " }", "", " clear() {", - " this.visibleObjects.length = 0;", - " this.shadowObjects.length = 0;", + " this.visibleObjects.clear();", + " this.shadowObjects.clear();", " }", "", " /**", @@ -699,29 +750,18 @@ " */", " applyVisibilityAndShadow(object, distance) {", " if (object._getIsCastingShadow()) {", - " const { isInserted, removedObject } = insertByDistance(", - " this.shadowObjects, this.shadowCount, object, distance);", - " if (isInserted) {", - " object.__light3D.castShadow = true;", - " }", - " if (removedObject) {", - " removedObject.__light3D.castShadow = false;", - " }", - " }", - " const { isInserted, removedObject } = insertByDistance(", - " this.visibleObjects, this.maxCount, object, distance);", - " if (isInserted) {", - " object.__light3D.visible = true;", - " }", - " if (removedObject) {", - " removedObject.__light3D.visible = false;", + " this.shadowObjects.insertByDistance(object, distance,", + " //@ts-ignore", + " object.__light3D.map ? 2 : 1);", " }", + " this.visibleObjects.insertByDistance(object, distance, 1);", " }", "}", "", "//@ts-ignore", "gdjs.__light3DExtension = {", " Light3DRenderer,", + " lightCountMax,", "}" ], "parameterObjects": "", @@ -731,6 +771,83 @@ ], "parameters": [], "objectGroups": [] + }, + { + "description": "the maximum number of nearest lights displayed simultaneously.", + "fullName": "Max lights count", + "functionType": "ExpressionAndCondition", + "name": "LightCountMax", + "sentence": "max lights count", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "Light3D::DefineHelperClasses" + }, + "parameters": [ + "", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "", + "eventsFunctionContext.returnValue = gdjs.__light3DExtension.lightCountMax.value;" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "LightCountMax", + "name": "SetLightCountMax", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "Light3D::DefineHelperClasses" + }, + "parameters": [ + "", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "", + "const value = eventsFunctionContext.getArgument(\"Value\");", + "", + "gdjs.__light3DExtension.lightCountMax.value = value;" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [], + "objectGroups": [] } ], "eventsFunctionsFolderStructure": { @@ -744,6 +861,12 @@ }, { "functionName": "DefinePointLightHelperClasses" + }, + { + "functionName": "LightCountMax" + }, + { + "functionName": "SetLightCountMax" } ] }, @@ -2451,7 +2574,7 @@ "image" ], "choices": [], - "advanced": true, + "hidden": true, "name": "Image" }, { From f58758c8c1dbe95ed0d7c690288826356201bc98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Davy=20H=C3=A9lard?= Date: Thu, 2 Apr 2026 18:24:49 +0200 Subject: [PATCH 14/22] Add an action to choose the max number of lights with shadow --- extensions/reviewed/Light3D.json | 100 +++++++++++++++++++++++++++++-- 1 file changed, 96 insertions(+), 4 deletions(-) diff --git a/extensions/reviewed/Light3D.json b/extensions/reviewed/Light3D.json index de61a9fe2..76f9828cc 100644 --- a/extensions/reviewed/Light3D.json +++ b/extensions/reviewed/Light3D.json @@ -628,6 +628,14 @@ " value: 20", "};", "", + "const editorLightShadowCountMax = {", + " value: 1", + "};", + "", + "const lightShadowCountMax = {", + " value: 4", + "};", + "", "/**", " * Get the platforms manager of an instance container.", " * @param {gdjs.RuntimeScene & {__lightManager: LightManager}} runtimeScene", @@ -636,8 +644,8 @@ " if (!runtimeScene.__lightManager) {", " // Create the shared manager if necessary.", " runtimeScene.__lightManager = isInGameEdition ?", - " new LightManager(editorLightCountMax, 1) :", - " new LightManager(lightCountMax, 4);", + " new LightManager(editorLightCountMax, editorLightShadowCountMax) :", + " new LightManager(lightCountMax, lightShadowCountMax);", " }", " return runtimeScene.__lightManager;", "}", @@ -724,7 +732,7 @@ "", " /**", " * @param maxCount {{value: number}}", - " * @param shadowCount {number}", + " * @param shadowCount {{value: number}}", " */", " constructor(maxCount, shadowCount) {", " this.visibleObjects = new CappedLightList(", @@ -733,7 +741,7 @@ " (object) => { object.__light3D.visible = false; }", " );", " this.shadowObjects = new CappedLightList(", - " { value: shadowCount },", + " shadowCount,", " (object) => { object.__light3D.castShadow = true; },", " (object) => { object.__light3D.castShadow = false; }", " );", @@ -762,6 +770,7 @@ "gdjs.__light3DExtension = {", " Light3DRenderer,", " lightCountMax,", + " lightShadowCountMax,", "}" ], "parameterObjects": "", @@ -848,6 +857,83 @@ ], "parameters": [], "objectGroups": [] + }, + { + "description": "the maximum number of nearest lights displayed with shadow simultaneously.", + "fullName": "Max lights with shadow count", + "functionType": "ExpressionAndCondition", + "name": "LightShadowCountMax", + "sentence": "max lights with shadow count", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "Light3D::DefineHelperClasses" + }, + "parameters": [ + "", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "", + "eventsFunctionContext.returnValue = gdjs.__light3DExtension.lightShadowCountMax.value;" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "LightShadowCountMax", + "name": "SetLightShadowCountMax", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "Light3D::DefineHelperClasses" + }, + "parameters": [ + "", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "", + "const value = eventsFunctionContext.getArgument(\"Value\");", + "", + "gdjs.__light3DExtension.lightShadowCountMax.value = value;" + ], + "parameterObjects": "", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [], + "objectGroups": [] } ], "eventsFunctionsFolderStructure": { @@ -867,6 +953,12 @@ }, { "functionName": "SetLightCountMax" + }, + { + "functionName": "LightShadowCountMax" + }, + { + "functionName": "SetLightShadowCountMax" } ] }, From 2b1453dd3fbfd8e7e96d0dd0686c45c4b3be6a1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Davy=20H=C3=A9lard?= Date: Thu, 2 Apr 2026 18:32:28 +0200 Subject: [PATCH 15/22] Fix description --- extensions/reviewed/Light3D.json | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/extensions/reviewed/Light3D.json b/extensions/reviewed/Light3D.json index 76f9828cc..c7413c761 100644 --- a/extensions/reviewed/Light3D.json +++ b/extensions/reviewed/Light3D.json @@ -6,9 +6,9 @@ "fullName": "3D spot light", "gdevelopVersion": ">=5.5.222", "helpPath": "", - "iconUrl": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0ibWRpLWZsYXNobGlnaHQiIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0Ij48cGF0aCBkPSJNOSwxMEw2LDVIMThMMTUsMTBIOU0xOCw0SDZWMkgxOFY0TTksMjJWMTFIMTVWMjJIOU0xMiwxM0ExLDEgMCAwLDAgMTEsMTRBMSwxIDAgMCwwIDEyLDE1QTEsMSAwIDAsMCAxMywxNEExLDEgMCAwLDAgMTIsMTNaIiAvPjwvc3ZnPg==", + "iconUrl": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0ibWRpLWxpZ2h0YnVsYi1vbi1vdXRsaW5lIiB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZD0iTTIwLDExSDIzVjEzSDIwVjExTTEsMTFINFYxM0gxVjExTTEzLDFWNEgxMVYxSDEzTTQuOTIsMy41TDcuMDUsNS42NEw1LjYzLDcuMDVMMy41LDQuOTNMNC45MiwzLjVNMTYuOTUsNS42M0wxOS4wNywzLjVMMjAuNSw0LjkzTDE4LjM3LDcuMDVMMTYuOTUsNS42M00xMiw2QTYsNiAwIDAsMSAxOCwxMkMxOCwxNC4yMiAxNi43OSwxNi4xNiAxNSwxNy4yVjE5QTEsMSAwIDAsMSAxNCwyMEgxMEExLDEgMCAwLDEgOSwxOVYxNy4yQzcuMjEsMTYuMTYgNiwxNC4yMiA2LDEyQTYsNiAwIDAsMSAxMiw2TTE0LDIxVjIyQTEsMSAwIDAsMSAxMywyM0gxMUExLDEgMCAwLDEgMTAsMjJWMjFIMTRNMTEsMThIMTNWMTUuODdDMTQuNzMsMTUuNDMgMTYsMTMuODYgMTYsMTJBNCw0IDAgMCwwIDEyLDhBNCw0IDAgMCwwIDgsMTJDOCwxMy44NiA5LjI3LDE1LjQzIDExLDE1Ljg3VjE4WiIgLz48L3N2Zz4=", "name": "Light3D", - "previewIconUrl": "https://asset-resources.gdevelop.io/public-resources/Icons/b627f8e676b3aac26945b2c38db0cfa787d34d2eff23755d4eb1289452f6cd28_flashlight.svg", + "previewIconUrl": "https://asset-resources.gdevelop.io/public-resources/Icons/f237ae4e3b857c556846c7b2c0b132556fd4bcdeff217034b4d9c97dc1aab1d6_lightbulb-on-outline.svg", "shortDescription": "Light up a cone like a flashlight.", "version": "1.0.0", "description": "Light up a cone like a flashlight.", @@ -805,10 +805,7 @@ }, { "type": "BuiltinCommonInstructions::JsCode", - "inlineCode": [ - "", - "eventsFunctionContext.returnValue = gdjs.__light3DExtension.lightCountMax.value;" - ], + "inlineCode": "eventsFunctionContext.returnValue = gdjs.__light3DExtension.lightCountMax.value;", "parameterObjects": "", "useStrict": true, "eventsSheetExpanded": false @@ -845,7 +842,6 @@ { "type": "BuiltinCommonInstructions::JsCode", "inlineCode": [ - "", "const value = eventsFunctionContext.getArgument(\"Value\");", "", "gdjs.__light3DExtension.lightCountMax.value = value;" @@ -882,10 +878,7 @@ }, { "type": "BuiltinCommonInstructions::JsCode", - "inlineCode": [ - "", - "eventsFunctionContext.returnValue = gdjs.__light3DExtension.lightShadowCountMax.value;" - ], + "inlineCode": "eventsFunctionContext.returnValue = gdjs.__light3DExtension.lightShadowCountMax.value;", "parameterObjects": "", "useStrict": true, "eventsSheetExpanded": false @@ -922,7 +915,6 @@ { "type": "BuiltinCommonInstructions::JsCode", "inlineCode": [ - "", "const value = eventsFunctionContext.getArgument(\"Value\");", "", "gdjs.__light3DExtension.lightShadowCountMax.value = value;" @@ -975,11 +967,11 @@ "description": "Light up a cone like a flashlight.", "fullName": "3D spot light", "helpPath": "", - "iconUrl": "", + "iconUrl": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0ibWRpLWZsYXNobGlnaHQiIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0Ij48cGF0aCBkPSJNOSwxMEw2LDVIMThMMTUsMTBIOU0xOCw0SDZWMkgxOFY0TTksMjJWMTFIMTVWMjJIOU0xMiwxM0ExLDEgMCAwLDAgMTEsMTRBMSwxIDAgMCwwIDEyLDE1QTEsMSAwIDAsMCAxMywxNEExLDEgMCAwLDAgMTIsMTNaIiAvPjwvc3ZnPg==", "is3D": true, "isUsingLegacyInstancesRenderer": false, "name": "SpotLight3D", - "previewIconUrl": "", + "previewIconUrl": "https://asset-resources.gdevelop.io/public-resources/Icons/b627f8e676b3aac26945b2c38db0cfa787d34d2eff23755d4eb1289452f6cd28_flashlight.svg", "objects": [ { "assetStoreId": "", @@ -2783,8 +2775,8 @@ "areaMinY": -20, "areaMinZ": -20, "defaultName": "PointLight", - "description": "Light up in all directions like a torch.", - "fullName": "3D point light", + "description": "A collection of light object for 3D.", + "fullName": "3D spot light and point light", "helpPath": "", "iconUrl": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0ibWRpLXRvcmNoIiB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZD0iTTguNiA5LjZDOSAxMC4yIDkuNSAxMC43IDEwLjIgMTFIMTQuMkMxNC41IDEwLjkgMTQuNyAxMC43IDE0LjkgMTAuNUMxNS45IDkuNSAxNi4zIDggMTUuOCA2LjdMMTUuNyA2LjVDMTUuNiA2LjIgMTUuNCA2IDE1LjIgNS44QzE1LjEgNS42IDE0LjkgNS41IDE0LjggNS4zQzE0LjQgNSAxNCA0LjcgMTMuNiA0LjNDMTIuNyAzLjQgMTIuNiAyIDEzLjEgMUMxMi42IDEuMSAxMi4xIDEuNCAxMS43IDEuOEMxMC4yIDMgOS42IDUuMSAxMC4zIDdWNy4yQzEwLjMgNy4zIDEwLjIgNy40IDEwLjEgNy41QzEwIDcuNiA5LjggNy41IDkuNyA3LjRMOS42IDcuM0M5IDYuNSA4LjkgNS4zIDkuMyA0LjNDOC40IDUuMSA3LjkgNi40IDggNy43QzggOCA4LjEgOC4zIDguMiA4LjZDOC4yIDguOSA4LjQgOS4zIDguNiA5LjZNMTIuMyA4LjFDMTIuNCA3LjYgMTIuMiA3LjIgMTIuMSA2LjhDMTIgNi40IDEyIDYgMTIuMiA1LjZMMTIuNSA2LjJDMTIuOSA2LjggMTMuNiA3IDEzLjggNy44VjguMUMxMy44IDguNiAxMy42IDkuMSAxMy4zIDkuNEMxMy4xIDkuNSAxMi45IDkuNyAxMi43IDkuN0MxMi4xIDkuOSAxMS40IDkuNiAxMSA5LjJDMTEuOCA5LjIgMTIuMiA4LjYgMTIuMyA4LjFNMTUgMTJWMTRIMTRMMTMgMjJIMTFMMTAgMTRIOVYxMkgxNVoiIC8+PC9zdmc+", "is3D": true, From 69a45125c48505dcd93d86642e585b0f05c8b621 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Davy=20H=C3=A9lard?= Date: Fri, 3 Apr 2026 17:10:14 +0200 Subject: [PATCH 16/22] Fix far plane when changing range --- extensions/reviewed/Light3D.json | 453 ++++++++++++++++--- scripts/lib/ExtensionsValidatorExceptions.js | 3 +- 2 files changed, 384 insertions(+), 72 deletions(-) diff --git a/extensions/reviewed/Light3D.json b/extensions/reviewed/Light3D.json index c7413c761..02a91b20b 100644 --- a/extensions/reviewed/Light3D.json +++ b/extensions/reviewed/Light3D.json @@ -279,8 +279,6 @@ " }", "}", "", - "", - "", "//@ts-ignore", "gdjs.__light3DExtension.spot = {", " SpotLightAdapter,", @@ -417,6 +415,7 @@ " * @param value {number}", " */", " setRange(value) {", + " const old = this.pointLight.shadow.camera.far;", " this.pointLight.distance = value;", " }", "", @@ -468,12 +467,11 @@ " * @param value {number}", " */", " setShadowCameraFarPlane(value) {", + " const old = this.pointLight.shadow.camera.far;", " this.pointLight.shadow.camera.far = value;", " }", "}", "", - "", - "", "//@ts-ignore", "gdjs.__light3DExtension.point = {", " PointLightAdapter,", @@ -572,30 +570,32 @@ " isSelected = parentObject === selectedObject;", " }", " }", - " let rootObject = object;", - " while (rootObject.getInstanceContainer()", - " //@ts-ignore", - " .getOwner) {", - " rootObject = rootObject.getInstanceContainer()", + " if (!editor || !editor.areEffectsHidden() || isSelected) {", + " let rootObject = object;", + " while (rootObject.getInstanceContainer()", " //@ts-ignore", - " .getOwner();", - " }", - " const runtimeScene = rootObject.getRuntimeScene();", - " let distanceSq = 0;", - " if (!isSelected) {", - " const layerName = rootObject.getLayer();", - " const cameraX = gdjs.evtTools.camera.getCameraX(runtimeScene, layerName, 0);", - " const cameraY = gdjs.evtTools.camera.getCameraY(runtimeScene, layerName, 0);", - " const cameraZ = gdjs.scene3d.camera.getCameraZ(runtimeScene, layerName, 0);", - " const deltaX = rootObject.getX() - cameraX;", - " const deltaY = rootObject.getY() - cameraY;", - " const deltaZ = rootObject.getZ() - cameraZ;", - " distanceSq = deltaX * deltaX + deltaY * deltaY + deltaZ * deltaZ;", + " .getOwner) {", + " rootObject = rootObject.getInstanceContainer()", + " //@ts-ignore", + " .getOwner();", + " }", + " const runtimeScene = rootObject.getRuntimeScene();", + " let distanceSq = 0;", + " if (!isSelected) {", + " const layerName = rootObject.getLayer();", + " const cameraX = gdjs.evtTools.camera.getCameraX(runtimeScene, layerName, 0);", + " const cameraY = gdjs.evtTools.camera.getCameraY(runtimeScene, layerName, 0);", + " const cameraZ = gdjs.scene3d.camera.getCameraZ(runtimeScene, layerName, 0);", + " const deltaX = rootObject.getX() - cameraX;", + " const deltaY = rootObject.getY() - cameraY;", + " const deltaZ = rootObject.getZ() - cameraZ;", + " distanceSq = deltaX * deltaX + deltaY * deltaY + deltaZ * deltaZ;", + " }", + " getLightManager(", + " //@ts-ignore", + " runtimeScene", + " ).applyVisibilityAndShadow(object, distanceSq);", " }", - " getLightManager(", - " //@ts-ignore", - " runtimeScene", - " ).applyVisibilityAndShadow(object, distanceSq);", " }", "", " // Force the visibility to be checked every frame", @@ -603,6 +603,22 @@ " }", "}", "", + "const editorLightCountMax = {", + " value: 4", + "};", + "", + "const lightCountMax = {", + " value: 20", + "};", + "", + "const editorLightShadowCountMax = {", + " value: 1", + "};", + "", + "const lightShadowCountMax = {", + " value: 4", + "};", + "", "gdjs.registerRuntimeScenePreEventsCallback(", " runtimeScene => {", " getLightManager(", @@ -620,22 +636,6 @@ " });", "}", "", - "const editorLightCountMax = {", - " value: 4", - "};", - "", - "const lightCountMax = {", - " value: 20", - "};", - "", - "const editorLightShadowCountMax = {", - " value: 1", - "};", - "", - "const lightShadowCountMax = {", - " value: 4", - "};", - "", "/**", " * Get the platforms manager of an instance container.", " * @param {gdjs.RuntimeScene & {__lightManager: LightManager}} runtimeScene", @@ -2063,6 +2063,23 @@ "parameterObjects": "Object", "useStrict": true, "eventsSheetExpanded": false + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "Light3D::SpotLight3D::SetConeLength" + }, + "parameters": [ + "Object", + "=", + "ConeLength", + "" + ] + } + ] } ], "parameters": [ @@ -2403,21 +2420,18 @@ "=", "Value" ] + }, + { + "type": { + "value": "Light3D::SpotLight3D::ApplyShadowCameraFarPlane" + }, + "parameters": [ + "Object", + "min(Value, ConeLength)", + "" + ] } ] - }, - { - "type": "BuiltinCommonInstructions::JsCode", - "inlineCode": [ - "const object = objects[0];\r", - "const value = eventsFunctionContext.getArgument(\"Value\");\r", - "\r", - "object.__spotLightAdapter.setShadowCameraFarPlane(value);\r", - "" - ], - "parameterObjects": "Object", - "useStrict": true, - "eventsSheetExpanded": false } ], "parameters": [ @@ -2486,8 +2500,167 @@ "Value" ] } + ], + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "IsCastingShadow", + "=", + "" + ] + } + ], + "actions": [ + { + "type": { + "value": "Light3D::SpotLight3D::ApplyConeLength" + }, + "parameters": [ + "Object", + "Value", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "When the shadow is disable by culling, the light should not go through." + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "BooleanVariable" + }, + "parameters": [ + "IsCastingShadow", + "True", + "" + ] + } + ], + "actions": [], + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "Value", + "=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "Light3D::SpotLight3D::ApplyConeLength" + }, + "parameters": [ + "Object", + "ShadowCameraFarPlane", + "" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [ + { + "type": { + "value": "NumberVariable" + }, + "parameters": [ + "Value", + "!=", + "0" + ] + } + ], + "actions": [ + { + "type": { + "value": "Light3D::SpotLight3D::ApplyConeLength" + }, + "parameters": [ + "Object", + "min(Value, ShadowCameraFarPlane)", + "" + ] + } + ] + } + ] + } ] }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Three.js reduces the far plane to match the light range.\nSo, we need to set it again or it will stay at the lowest value the range took.\nWe do the min() on our side to in order:\n- to avoid a side effect when Three.js is upgraded\n- because Three.js keeps the old value in cache and doesn't apply it back unless we set the lower value explicitly ourselves." + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "Light3D::SpotLight3D::ApplyShadowCameraFarPlane" + }, + "parameters": [ + "Object", + "min(Value, ShadowCameraFarPlane)", + "" + ] + } + ] + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Light3D::SpotLight3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "Apply cone length", + "functionType": "Action", + "name": "ApplyConeLength", + "private": true, + "sentence": "Apply cone length of _PARAM0_ to _PARAM1_", + "events": [ { "type": "BuiltinCommonInstructions::JsCode", "inlineCode": [ @@ -2508,6 +2681,48 @@ "name": "Object", "supplementaryInformation": "Light3D::SpotLight3D", "type": "object" + }, + { + "description": "Value", + "name": "Value", + "type": "expression" + } + ], + "objectGroups": [] + }, + { + "fullName": "Apply shadow camera far plane", + "functionType": "Action", + "group": "3D spot light shadow configuration", + "name": "ApplyShadowCameraFarPlane", + "private": true, + "sentence": "Apply shadow camera far plane of _PARAM0_ to _PARAM1_", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const value = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__spotLightAdapter.setShadowCameraFarPlane(value);\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Light3D::SpotLight3D", + "type": "object" + }, + { + "description": "Value", + "name": "Value", + "type": "expression" } ], "objectGroups": [] @@ -2569,6 +2784,9 @@ { "functionName": "SetConeLength" }, + { + "functionName": "ApplyConeLength" + }, { "functionName": "ConeAngle" }, @@ -2607,6 +2825,9 @@ }, { "functionName": "SetShadowCameraFarPlane" + }, + { + "functionName": "ApplyShadowCameraFarPlane" } ] } @@ -3561,6 +3782,17 @@ "parameters": [ "True" ] + }, + { + "type": { + "value": "Light3D::PointLight3D::SetRange" + }, + "parameters": [ + "Object", + "=", + "Range", + "" + ] } ] } @@ -3823,6 +4055,23 @@ "parameterObjects": "Object", "useStrict": true, "eventsSheetExpanded": false + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "Light3D::PointLight3D::SetRange" + }, + "parameters": [ + "Object", + "=", + "Range", + "" + ] + } + ] } ], "parameters": [ @@ -3892,21 +4141,18 @@ "=", "Value" ] + }, + { + "type": { + "value": "Light3D::PointLight3D::ApplyShadowCameraFarPlane" + }, + "parameters": [ + "Object", + "min(Value, Range)", + "" + ] } ] - }, - { - "type": "BuiltinCommonInstructions::JsCode", - "inlineCode": [ - "const object = objects[0];\r", - "const value = eventsFunctionContext.getArgument(\"Value\");\r", - "\r", - "object.__pointLightAdapter.setShadowCameraFarPlane(value);\r", - "" - ], - "parameterObjects": "Object", - "useStrict": true, - "eventsSheetExpanded": false } ], "parameters": [ @@ -3988,6 +4234,34 @@ "parameterObjects": "Object", "useStrict": true, "eventsSheetExpanded": false + }, + { + "type": "BuiltinCommonInstructions::Comment", + "color": { + "b": 109, + "g": 230, + "r": 255, + "textB": 0, + "textG": 0, + "textR": 0 + }, + "comment": "Three.js reduces the far plane to match the light range.\nSo, we need to set it again or it will stay at the lowest value the range took.\nWe do the min() on our side to in order:\n- to avoid a side effect when Three.js is upgraded\n- because Three.js keeps the old value in cache and doesn't apply it back unless we set the lower value explicitly ourselves." + }, + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "Light3D::PointLight3D::ApplyShadowCameraFarPlane" + }, + "parameters": [ + "Object", + "min(Value, ShadowCameraFarPlane)", + "" + ] + } + ] } ], "parameters": [ @@ -3999,6 +4273,43 @@ } ], "objectGroups": [] + }, + { + "fullName": "Apply shadow camera far plane", + "functionType": "Action", + "group": "3D point light shadow configuration", + "name": "ApplyShadowCameraFarPlane", + "private": true, + "sentence": "Apply shadow camera far plane of _PARAM0_ to _PARAM1_", + "events": [ + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const value = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__pointLightAdapter.setShadowCameraFarPlane(value);\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Light3D::PointLight3D", + "type": "object" + }, + { + "description": "Value", + "name": "Value", + "type": "expression" + } + ], + "objectGroups": [] } ], "eventsFunctionsFolderStructure": { @@ -4069,6 +4380,9 @@ }, { "functionName": "SetShadowCameraFarPlane" + }, + { + "functionName": "ApplyShadowCameraFarPlane" } ] } @@ -4141,12 +4455,11 @@ "name": "ShadowCameraFarPlane" }, { - "value": "0", + "value": "2000", "type": "Number", "unit": "Pixel", "label": "Range", "description": "0 means no limit.", - "advanced": true, "name": "Range" } ], diff --git a/scripts/lib/ExtensionsValidatorExceptions.js b/scripts/lib/ExtensionsValidatorExceptions.js index b0266726a..9de03b3fb 100644 --- a/scripts/lib/ExtensionsValidatorExceptions.js +++ b/scripts/lib/ExtensionsValidatorExceptions.js @@ -528,8 +528,7 @@ const extensionsAllowedProperties = { '__spotLight3DExtension', '__pointLight3DExtension', 'getScene', - '__spotLightManager', - '__pointLightManager', + '__lightManager', ], javaScriptObjectAllowedProperties: ['getPrototypeOf'], }, From cb843dc941f5f0a6bf846139a7ef7a549edad1b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Davy=20H=C3=A9lard?= Date: Fri, 3 Apr 2026 18:04:32 +0200 Subject: [PATCH 17/22] Fix sptot light intensity change when enabling shadow --- extensions/reviewed/Light3D.json | 160 +++---------------------------- 1 file changed, 11 insertions(+), 149 deletions(-) diff --git a/extensions/reviewed/Light3D.json b/extensions/reviewed/Light3D.json index 02a91b20b..f4f90d20c 100644 --- a/extensions/reviewed/Light3D.json +++ b/extensions/reviewed/Light3D.json @@ -2503,115 +2503,17 @@ ], "events": [ { - "type": "BuiltinCommonInstructions::Standard", - "conditions": [ - { - "type": { - "value": "BooleanVariable" - }, - "parameters": [ - "IsCastingShadow", - "=", - "" - ] - } - ], - "actions": [ - { - "type": { - "value": "Light3D::SpotLight3D::ApplyConeLength" - }, - "parameters": [ - "Object", - "Value", - "" - ] - } - ] - }, - { - "type": "BuiltinCommonInstructions::Comment", - "color": { - "b": 109, - "g": 230, - "r": 255, - "textB": 0, - "textG": 0, - "textR": 0 - }, - "comment": "When the shadow is disable by culling, the light should not go through." - }, - { - "type": "BuiltinCommonInstructions::Standard", - "conditions": [ - { - "type": { - "value": "BooleanVariable" - }, - "parameters": [ - "IsCastingShadow", - "True", - "" - ] - } + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const value = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__spotLightAdapter.setConeLength(value);\r", + "" ], - "actions": [], - "events": [ - { - "type": "BuiltinCommonInstructions::Standard", - "conditions": [ - { - "type": { - "value": "NumberVariable" - }, - "parameters": [ - "Value", - "=", - "0" - ] - } - ], - "actions": [ - { - "type": { - "value": "Light3D::SpotLight3D::ApplyConeLength" - }, - "parameters": [ - "Object", - "ShadowCameraFarPlane", - "" - ] - } - ] - }, - { - "type": "BuiltinCommonInstructions::Standard", - "conditions": [ - { - "type": { - "value": "NumberVariable" - }, - "parameters": [ - "Value", - "!=", - "0" - ] - } - ], - "actions": [ - { - "type": { - "value": "Light3D::SpotLight3D::ApplyConeLength" - }, - "parameters": [ - "Object", - "min(Value, ShadowCameraFarPlane)", - "" - ] - } - ] - } - ] + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false } ] }, @@ -2654,42 +2556,6 @@ ], "objectGroups": [] }, - { - "fullName": "Apply cone length", - "functionType": "Action", - "name": "ApplyConeLength", - "private": true, - "sentence": "Apply cone length of _PARAM0_ to _PARAM1_", - "events": [ - { - "type": "BuiltinCommonInstructions::JsCode", - "inlineCode": [ - "const object = objects[0];\r", - "const value = eventsFunctionContext.getArgument(\"Value\");\r", - "\r", - "object.__spotLightAdapter.setConeLength(value);\r", - "" - ], - "parameterObjects": "Object", - "useStrict": true, - "eventsSheetExpanded": false - } - ], - "parameters": [ - { - "description": "Object", - "name": "Object", - "supplementaryInformation": "Light3D::SpotLight3D", - "type": "object" - }, - { - "description": "Value", - "name": "Value", - "type": "expression" - } - ], - "objectGroups": [] - }, { "fullName": "Apply shadow camera far plane", "functionType": "Action", @@ -2784,9 +2650,6 @@ { "functionName": "SetConeLength" }, - { - "functionName": "ApplyConeLength" - }, { "functionName": "ConeAngle" }, @@ -2929,13 +2792,12 @@ "name": "ShadowCameraFarPlane" }, { - "value": "0", + "value": "2000", "type": "Number", "unit": "Pixel", "label": "Cone length", "description": "0 means no limit.", "group": "Cone", - "advanced": true, "name": "ConeLength" } ], From 036f96ced77833ddd148b45090f5fad5538e9fb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Davy=20H=C3=A9lard?= Date: Fri, 3 Apr 2026 18:47:36 +0200 Subject: [PATCH 18/22] Add shadow bias --- extensions/reviewed/Light3D.json | 251 ++++++++++++++++++++++++++++++- 1 file changed, 244 insertions(+), 7 deletions(-) diff --git a/extensions/reviewed/Light3D.json b/extensions/reviewed/Light3D.json index f4f90d20c..28d86b9eb 100644 --- a/extensions/reviewed/Light3D.json +++ b/extensions/reviewed/Light3D.json @@ -3,15 +3,15 @@ "category": "Visual effect", "dimension": "3D", "extensionNamespace": "", - "fullName": "3D spot light", + "fullName": "3D lights", "gdevelopVersion": ">=5.5.222", "helpPath": "", "iconUrl": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0ibWRpLWxpZ2h0YnVsYi1vbi1vdXRsaW5lIiB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZD0iTTIwLDExSDIzVjEzSDIwVjExTTEsMTFINFYxM0gxVjExTTEzLDFWNEgxMVYxSDEzTTQuOTIsMy41TDcuMDUsNS42NEw1LjYzLDcuMDVMMy41LDQuOTNMNC45MiwzLjVNMTYuOTUsNS42M0wxOS4wNywzLjVMMjAuNSw0LjkzTDE4LjM3LDcuMDVMMTYuOTUsNS42M00xMiw2QTYsNiAwIDAsMSAxOCwxMkMxOCwxNC4yMiAxNi43OSwxNi4xNiAxNSwxNy4yVjE5QTEsMSAwIDAsMSAxNCwyMEgxMEExLDEgMCAwLDEgOSwxOVYxNy4yQzcuMjEsMTYuMTYgNiwxNC4yMiA2LDEyQTYsNiAwIDAsMSAxMiw2TTE0LDIxVjIyQTEsMSAwIDAsMSAxMywyM0gxMUExLDEgMCAwLDEgMTAsMjJWMjFIMTRNMTEsMThIMTNWMTUuODdDMTQuNzMsMTUuNDMgMTYsMTMuODYgMTYsMTJBNCw0IDAgMCwwIDEyLDhBNCw0IDAgMCwwIDgsMTJDOCwxMy44NiA5LjI3LDE1LjQzIDExLDE1Ljg3VjE4WiIgLz48L3N2Zz4=", "name": "Light3D", "previewIconUrl": "https://asset-resources.gdevelop.io/public-resources/Icons/f237ae4e3b857c556846c7b2c0b132556fd4bcdeff217034b4d9c97dc1aab1d6_lightbulb-on-outline.svg", - "shortDescription": "Light up a cone like a flashlight.", + "shortDescription": "A collection of light object for 3D.", "version": "1.0.0", - "description": "Light up a cone like a flashlight.", + "description": "A collection of light object for 3D.", "tags": [ "3d", "light" @@ -267,6 +267,13 @@ " }", "", " /**", + " * @param value {number}", + " */", + " setShadowBias(value) {", + " this.spotLight.shadow.bias = -value;", + " }", + "", + " /**", " * @param resourceName {string}", " */", " setImage(resourceName) {", @@ -415,7 +422,6 @@ " * @param value {number}", " */", " setRange(value) {", - " const old = this.pointLight.shadow.camera.far;", " this.pointLight.distance = value;", " }", "", @@ -467,9 +473,15 @@ " * @param value {number}", " */", " setShadowCameraFarPlane(value) {", - " const old = this.pointLight.shadow.camera.far;", " this.pointLight.shadow.camera.far = value;", " }", + "", + " /**", + " * @param value {number}", + " */", + " setShadowBias(value) {", + " this.pointLight.shadow.bias = -value;", + " }", "}", "", "//@ts-ignore", @@ -1392,6 +1404,17 @@ "" ] }, + { + "type": { + "value": "Light3D::SpotLight3D::SetShadowBias" + }, + "parameters": [ + "Object", + "=", + "ShadowBias", + "" + ] + }, { "type": { "value": "Light3D::SpotLight3D::UpdateImage" @@ -2592,6 +2615,90 @@ } ], "objectGroups": [] + }, + { + "description": "the shadow bias of the light. Use this to avoid \"shadow acne\" due to depth buffer precision. Choose a value small enough like 0.001 to avoid creating distance between shadows and objects but not too small to avoid shadow glitches on low/medium quality. This value is used for high quality, and multiplied by 1.25 for medium quality and 2 for low quality.", + "fullName": "Shadow bias", + "functionType": "ExpressionAndCondition", + "group": "3D spot light shadow configuration", + "name": "ShadowBias", + "sentence": "the shadow bias", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "ShadowBias" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Light3D::SpotLight3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "ShadowBias", + "group": "3D spot light shadow configuration", + "name": "SetShadowBias", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "ShadowBias", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const value = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__spotLightAdapter.setShadowBias(value);\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Light3D::SpotLight3D", + "type": "object" + } + ], + "objectGroups": [] } ], "eventsFunctionsFolderStructure": { @@ -2691,6 +2798,12 @@ }, { "functionName": "ApplyShadowCameraFarPlane" + }, + { + "functionName": "ShadowBias" + }, + { + "functionName": "SetShadowBias" } ] } @@ -2799,6 +2912,15 @@ "description": "0 means no limit.", "group": "Cone", "name": "ConeLength" + }, + { + "value": "0", + "type": "Number", + "label": "Shadow bias", + "description": "Use this to avoid \"shadow acne\" due to depth buffer precision. Choose a value small enough like 0.001 to avoid creating distance between shadows and objects but not too small to avoid shadow glitches on low/medium quality. This value is used for high quality, and multiplied by 1.25 for medium quality and 2 for low quality.", + "group": "Shadow", + "advanced": true, + "name": "ShadowBias" } ], "propertiesFolderStructure": { @@ -2844,6 +2966,9 @@ }, { "propertyName": "ShadowCameraFarPlane" + }, + { + "propertyName": "ShadowBias" } ] } @@ -2858,8 +2983,8 @@ "areaMinY": -20, "areaMinZ": -20, "defaultName": "PointLight", - "description": "A collection of light object for 3D.", - "fullName": "3D spot light and point light", + "description": "Light up a cone like a flashlight.", + "fullName": "3D point light", "helpPath": "", "iconUrl": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0ibWRpLXRvcmNoIiB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZD0iTTguNiA5LjZDOSAxMC4yIDkuNSAxMC43IDEwLjIgMTFIMTQuMkMxNC41IDEwLjkgMTQuNyAxMC43IDE0LjkgMTAuNUMxNS45IDkuNSAxNi4zIDggMTUuOCA2LjdMMTUuNyA2LjVDMTUuNiA2LjIgMTUuNCA2IDE1LjIgNS44QzE1LjEgNS42IDE0LjkgNS41IDE0LjggNS4zQzE0LjQgNSAxNCA0LjcgMTMuNiA0LjNDMTIuNyAzLjQgMTIuNiAyIDEzLjEgMUMxMi42IDEuMSAxMi4xIDEuNCAxMS43IDEuOEMxMC4yIDMgOS42IDUuMSAxMC4zIDdWNy4yQzEwLjMgNy4zIDEwLjIgNy40IDEwLjEgNy41QzEwIDcuNiA5LjggNy41IDkuNyA3LjRMOS42IDcuM0M5IDYuNSA4LjkgNS4zIDkuMyA0LjNDOC40IDUuMSA3LjkgNi40IDggNy43QzggOCA4LjEgOC4zIDguMiA4LjZDOC4yIDguOSA4LjQgOS4zIDguNiA5LjZNMTIuMyA4LjFDMTIuNCA3LjYgMTIuMiA3LjIgMTIuMSA2LjhDMTIgNi40IDEyIDYgMTIuMiA1LjZMMTIuNSA2LjJDMTIuOSA2LjggMTMuNiA3IDEzLjggNy44VjguMUMxMy44IDguNiAxMy42IDkuMSAxMy4zIDkuNEMxMy4xIDkuNSAxMi45IDkuNyAxMi43IDkuN0MxMi4xIDkuOSAxMS40IDkuNiAxMSA5LjJDMTEuOCA5LjIgMTIuMiA4LjYgMTIuMyA4LjFNMTUgMTJWMTRIMTRMMTMgMjJIMTFMMTAgMTRIOVYxMkgxNVoiIC8+PC9zdmc+", "is3D": true, @@ -3262,6 +3387,17 @@ "" ] }, + { + "type": { + "value": "Light3D::PointLight3D::SetShadowBias" + }, + "parameters": [ + "Object", + "=", + "ShadowBias", + "" + ] + }, { "type": { "value": "Light3D::PointLight3D::UpdateHelper" @@ -4172,6 +4308,90 @@ } ], "objectGroups": [] + }, + { + "description": "the shadow bias of the light. Use this to avoid \"shadow acne\" due to depth buffer precision. Choose a value small enough like 0.001 to avoid creating distance between shadows and objects but not too small to avoid shadow glitches on low/medium quality. This value is used for high quality, and multiplied by 1.25 for medium quality and 2 for low quality.", + "fullName": "Shadow bias", + "functionType": "ExpressionAndCondition", + "group": "3D point light shadow configuration", + "name": "ShadowBias", + "sentence": "the shadow bias", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetReturnNumber" + }, + "parameters": [ + "ShadowBias" + ] + } + ] + } + ], + "expressionType": { + "type": "expression" + }, + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Light3D::PointLight3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "", + "functionType": "ActionWithOperator", + "getterName": "ShadowBias", + "group": "3D point light shadow configuration", + "name": "SetShadowBias", + "sentence": "", + "events": [ + { + "type": "BuiltinCommonInstructions::Standard", + "conditions": [], + "actions": [ + { + "type": { + "value": "SetNumberVariable" + }, + "parameters": [ + "ShadowBias", + "=", + "Value" + ] + } + ] + }, + { + "type": "BuiltinCommonInstructions::JsCode", + "inlineCode": [ + "const object = objects[0];\r", + "const value = eventsFunctionContext.getArgument(\"Value\");\r", + "\r", + "object.__pointLightAdapter.setShadowBias(value);\r", + "" + ], + "parameterObjects": "Object", + "useStrict": true, + "eventsSheetExpanded": false + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Light3D::PointLight3D", + "type": "object" + } + ], + "objectGroups": [] } ], "eventsFunctionsFolderStructure": { @@ -4245,6 +4465,12 @@ }, { "functionName": "ApplyShadowCameraFarPlane" + }, + { + "functionName": "ShadowBias" + }, + { + "functionName": "SetShadowBias" } ] } @@ -4323,6 +4549,14 @@ "label": "Range", "description": "0 means no limit.", "name": "Range" + }, + { + "value": "0", + "type": "Number", + "label": "Shadow Bias", + "description": "Use this to avoid \"shadow acne\" due to depth buffer precision. Choose a value small enough like 0.001 to avoid creating distance between shadows and objects but not too small to avoid shadow glitches on low/medium quality. This value is used for high quality, and multiplied by 1.25 for medium quality and 2 for low quality.", + "group": "Shadow", + "name": "ShadowBias" } ], "propertiesFolderStructure": { @@ -4354,6 +4588,9 @@ }, { "propertyName": "ShadowCameraFarPlane" + }, + { + "propertyName": "ShadowBias" } ] } From 42b7aae295b9e6f65195cd77ced774ee84ec33e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Davy=20H=C3=A9lard?= Date: Sat, 4 Apr 2026 14:17:08 +0200 Subject: [PATCH 19/22] Use bigger texture for the point light since it's a cube --- extensions/reviewed/Light3D.json | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/extensions/reviewed/Light3D.json b/extensions/reviewed/Light3D.json index 28d86b9eb..1220f35d1 100644 --- a/extensions/reviewed/Light3D.json +++ b/extensions/reviewed/Light3D.json @@ -237,6 +237,9 @@ " case \"High\":", " size = 1024;", " break;", + " case \"VeryHigh\":", + " size = 2048;", + " break;", " }", " const shadow = this.spotLight.shadow;", " const mapSize = shadow.mapSize;", @@ -439,13 +442,13 @@ " let size = 512;", " switch (shadowQuality) {", " case \"Low\":", - " size = 256;", + " size = 512;", " break;", " case \"Medium\":", - " size = 512;", + " size = 1024;", " break;", " case \"High\":", - " size = 1024;", + " size = 2048;", " break;", " }", " const shadow = this.pointLight.shadow;", @@ -2872,16 +2875,20 @@ "group": "Shadow", "choices": [ { - "label": "", + "label": "Low", "value": "Low" }, { - "label": "", + "label": "Medium", "value": "Medium" }, { - "label": "", + "label": "High", "value": "High" + }, + { + "label": "Very high", + "value": "VeryHigh" } ], "name": "ShadowQuality" From 663f0903bb535d1379d115f4700bea8326183558 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Davy=20H=C3=A9lard?= Date: Tue, 7 Apr 2026 14:47:36 +0200 Subject: [PATCH 20/22] Set shadow bias according to quality and angle --- extensions/reviewed/Light3D.json | 175 ++++++++++++++++++++++++++++--- 1 file changed, 161 insertions(+), 14 deletions(-) diff --git a/extensions/reviewed/Light3D.json b/extensions/reviewed/Light3D.json index 1220f35d1..1a9643e47 100644 --- a/extensions/reviewed/Light3D.json +++ b/extensions/reviewed/Light3D.json @@ -223,7 +223,7 @@ " }", "", " /**", - " * @param shadowQuality {\"Low\" | \"Medium\" | \"High\"}", + " * @param shadowQuality {\"Low\" | \"Medium\" | \"High\" | \"VeryHigh\"}", " */", " setShadowQuality(shadowQuality) {", " let size = 512;", @@ -271,9 +271,19 @@ "", " /**", " * @param value {number}", + " * @param quality {\"Low\" | \"Medium\" | \"High\"}", + " * @param coneAngle {number}", " */", - " setShadowBias(value) {", - " this.spotLight.shadow.bias = -value;", + " setShadowBias(value, quality, coneAngle) {", + " const biasMultiplier =", + " quality === 'Low'", + " ? 8", + " : quality === 'Medium'", + " ? 4", + " : quality === 'High'", + " ? 2 : 1;", + " this.spotLight.shadow.bias = -value * biasMultiplier *", + " Math.tan(gdjs.toRad(gdjs.evtTools.common.clamp(coneAngle, 0, 89)));", " }", "", " /**", @@ -337,8 +347,8 @@ " * @param {PointLightRuntimeObject} object", "\t */", " constructor(object) {", - "\t\tconst geometry = new THREE.SphereGeometry( 16, 4, 2 );", - "\t\tconst material = new THREE.MeshBasicMaterial( { wireframe: true, fog: false, toneMapped: false } );", + " const geometry = new THREE.SphereGeometry(16, 4, 2);", + " const material = new THREE.MeshBasicMaterial({ wireframe: true, fog: false, toneMapped: false });", " super(geometry, material);", " this.object = object;", " this.update();", @@ -348,8 +358,8 @@ " }", "", " dispose() {", - "\t\tthis.geometry.dispose();", - "\t\tthis.material.dispose();", + " this.geometry.dispose();", + " this.material.dispose();", " }", "", " update() {", @@ -481,9 +491,16 @@ "", " /**", " * @param value {number}", + " * @param quality {\"Low\" | \"Medium\" | \"High\"}", " */", - " setShadowBias(value) {", - " this.pointLight.shadow.bias = -value;", + " setShadowBias(value, quality) {", + " const biasMultiplier =", + " quality === 'Low'", + " ? 4", + " : quality === 'Medium'", + " ? 2", + " : 1;", + " this.pointLight.shadow.bias = -value * biasMultiplier;", " }", "}", "", @@ -1620,6 +1637,18 @@ "=", "Value" ] + }, + { + "type": { + "value": "Light3D::SpotLight3D::ApplyShadowBias" + }, + "parameters": [ + "Object", + "ShadowBias", + "ShadowQuality", + "ConeAngle", + "" + ] } ] }, @@ -2279,6 +2308,18 @@ "=", "Value" ] + }, + { + "type": { + "value": "Light3D::SpotLight3D::ApplyShadowBias" + }, + "parameters": [ + "Object", + "ShadowBias", + "ShadowQuality", + "ConeAngle", + "" + ] } ] }, @@ -2676,16 +2717,49 @@ "=", "Value" ] + }, + { + "type": { + "value": "Light3D::SpotLight3D::ApplyShadowBias" + }, + "parameters": [ + "Object", + "Value", + "ShadowQuality", + "ConeAngle", + "" + ] } ] - }, + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Light3D::SpotLight3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "Apply shadow bias", + "functionType": "Action", + "group": "3D spot light shadow configuration", + "name": "ApplyShadowBias", + "private": true, + "sentence": "Apply shadow bias of _PARAM0_ to _PARAM1_ for a _PARAM2_ quality and cone angle _PARAM3_", + "events": [ { "type": "BuiltinCommonInstructions::JsCode", "inlineCode": [ "const object = objects[0];\r", "const value = eventsFunctionContext.getArgument(\"Value\");\r", + "const quality = eventsFunctionContext.getArgument(\"Quality\");\r", + "const coneAngle = eventsFunctionContext.getArgument(\"Angle\");\r", "\r", - "object.__spotLightAdapter.setShadowBias(value);\r", + "object.__spotLightAdapter.setShadowBias(value, quality, coneAngle);\r", "" ], "parameterObjects": "Object", @@ -2699,6 +2773,21 @@ "name": "Object", "supplementaryInformation": "Light3D::SpotLight3D", "type": "object" + }, + { + "description": "Value", + "name": "Value", + "type": "expression" + }, + { + "description": "Quality", + "name": "Quality", + "type": "string" + }, + { + "description": "Cone angle", + "name": "Angle", + "type": "expression" } ], "objectGroups": [] @@ -2807,6 +2896,9 @@ }, { "functionName": "SetShadowBias" + }, + { + "functionName": "ApplyShadowBias" } ] } @@ -3962,6 +4054,17 @@ "=", "Value" ] + }, + { + "type": { + "value": "Light3D::PointLight3D::ApplyShadowBias" + }, + "parameters": [ + "Object", + "ShadowBias", + "ShadowQuality", + "" + ] } ] }, @@ -4373,16 +4476,47 @@ "=", "Value" ] + }, + { + "type": { + "value": "Light3D::PointLight3D::ApplyShadowBias" + }, + "parameters": [ + "Object", + "Value", + "ShadowQuality", + "" + ] } ] - }, + } + ], + "parameters": [ + { + "description": "Object", + "name": "Object", + "supplementaryInformation": "Light3D::PointLight3D", + "type": "object" + } + ], + "objectGroups": [] + }, + { + "fullName": "Apply shadow bias", + "functionType": "Action", + "group": "3D point light shadow configuration", + "name": "ApplyShadowBias", + "private": true, + "sentence": "Apply shadow bias of _PARAM0_ to _PARAM1_ for a _PARAM2_ quality", + "events": [ { "type": "BuiltinCommonInstructions::JsCode", "inlineCode": [ "const object = objects[0];\r", "const value = eventsFunctionContext.getArgument(\"Value\");\r", + "const quality = eventsFunctionContext.getArgument(\"Quality\");\r", "\r", - "object.__pointLightAdapter.setShadowBias(value);\r", + "object.__pointLightAdapter.setShadowBias(value, quality);\r", "" ], "parameterObjects": "Object", @@ -4396,6 +4530,16 @@ "name": "Object", "supplementaryInformation": "Light3D::PointLight3D", "type": "object" + }, + { + "description": "Value", + "name": "Value", + "type": "expression" + }, + { + "description": "Quality", + "name": "Quality", + "type": "string" } ], "objectGroups": [] @@ -4478,6 +4622,9 @@ }, { "functionName": "SetShadowBias" + }, + { + "functionName": "ApplyShadowBias" } ] } @@ -4605,4 +4752,4 @@ } } ] -} \ No newline at end of file +} From 03f690d8440cf48ed82b9e533a4ba865ebfd1900 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Davy=20H=C3=A9lard?= Date: Tue, 7 Apr 2026 15:12:40 +0200 Subject: [PATCH 21/22] Fix bias description. --- extensions/reviewed/Light3D.json | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/extensions/reviewed/Light3D.json b/extensions/reviewed/Light3D.json index 1a9643e47..a4566ccbf 100644 --- a/extensions/reviewed/Light3D.json +++ b/extensions/reviewed/Light3D.json @@ -2661,7 +2661,7 @@ "objectGroups": [] }, { - "description": "the shadow bias of the light. Use this to avoid \"shadow acne\" due to depth buffer precision. Choose a value small enough like 0.001 to avoid creating distance between shadows and objects but not too small to avoid shadow glitches on low/medium quality. This value is used for high quality, and multiplied by 1.25 for medium quality and 2 for low quality.", + "description": "the shadow bias of the light. Use this to avoid \"shadow acne\" due to depth buffer precision. Choose a value small enough like 0.001 to avoid creating distance between shadows and objects but not too small to avoid shadow glitches on low/medium quality. This value is used for high quality, and multiplied by 2 for medium quality and 4 for low quality.", "fullName": "Shadow bias", "functionType": "ExpressionAndCondition", "group": "3D spot light shadow configuration", @@ -3016,7 +3016,7 @@ "value": "0", "type": "Number", "label": "Shadow bias", - "description": "Use this to avoid \"shadow acne\" due to depth buffer precision. Choose a value small enough like 0.001 to avoid creating distance between shadows and objects but not too small to avoid shadow glitches on low/medium quality. This value is used for high quality, and multiplied by 1.25 for medium quality and 2 for low quality.", + "description": "Use this to avoid \"shadow acne\" due to depth buffer precision. Choose a value small enough like 0.001 to avoid creating distance between shadows and objects but not too small to avoid shadow glitches on low/medium quality. This value is used for high quality, and multiplied by 2 for medium quality and 4 for low quality.", "group": "Shadow", "advanced": true, "name": "ShadowBias" @@ -4420,7 +4420,7 @@ "objectGroups": [] }, { - "description": "the shadow bias of the light. Use this to avoid \"shadow acne\" due to depth buffer precision. Choose a value small enough like 0.001 to avoid creating distance between shadows and objects but not too small to avoid shadow glitches on low/medium quality. This value is used for high quality, and multiplied by 1.25 for medium quality and 2 for low quality.", + "description": "the shadow bias of the light. Use this to avoid \"shadow acne\" due to depth buffer precision. Choose a value small enough like 0.001 to avoid creating distance between shadows and objects but not too small to avoid shadow glitches on low/medium quality. This value is used for high quality, and multiplied by 2 for medium quality and 4 for low quality.", "fullName": "Shadow bias", "functionType": "ExpressionAndCondition", "group": "3D point light shadow configuration", @@ -4708,8 +4708,9 @@ "value": "0", "type": "Number", "label": "Shadow Bias", - "description": "Use this to avoid \"shadow acne\" due to depth buffer precision. Choose a value small enough like 0.001 to avoid creating distance between shadows and objects but not too small to avoid shadow glitches on low/medium quality. This value is used for high quality, and multiplied by 1.25 for medium quality and 2 for low quality.", + "description": "Use this to avoid \"shadow acne\" due to depth buffer precision. Choose a value small enough like 0.001 to avoid creating distance between shadows and objects but not too small to avoid shadow glitches on low/medium quality. This value is used for high quality, and multiplied by 2 for medium quality and 4 for low quality.", "group": "Shadow", + "advanced": true, "name": "ShadowBias" } ], From c69405847b84f114d19a70edb27c12d9fffcd3c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Davy=20H=C3=A9lard?= Date: Tue, 7 Apr 2026 15:35:23 +0200 Subject: [PATCH 22/22] Make spot lights work better with a bias of 0.0001 --- extensions/reviewed/Light3D.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/extensions/reviewed/Light3D.json b/extensions/reviewed/Light3D.json index a4566ccbf..47cc62ee9 100644 --- a/extensions/reviewed/Light3D.json +++ b/extensions/reviewed/Light3D.json @@ -271,7 +271,7 @@ "", " /**", " * @param value {number}", - " * @param quality {\"Low\" | \"Medium\" | \"High\"}", + " * @param quality {\"Low\" | \"Medium\" | \"High\" | \"VeryHigh\"}", " * @param coneAngle {number}", " */", " setShadowBias(value, quality, coneAngle) {", @@ -282,7 +282,8 @@ " ? 4", " : quality === 'High'", " ? 2 : 1;", - " this.spotLight.shadow.bias = -value * biasMultiplier *", + " // Multiply by 0.125 to make values around 0.001 work best.", + " this.spotLight.shadow.bias = -value * 0.125 * biasMultiplier *", " Math.tan(gdjs.toRad(gdjs.evtTools.common.clamp(coneAngle, 0, 89)));", " }", "",