Compare commits

...

46 Commits

Author SHA1 Message Date
Florian Rival
a19d1a4022 Add line break 2025-07-02 15:35:04 +02:00
Neyl
d54fe7c017 update jsExtensions.js 2025-07-02 14:46:03 +02:00
Neyl
3ed4cf1e81 Update CreateProject.js 2025-07-02 11:03:56 +02:00
Neyl
f063e43019 format 2025-07-01 16:47:18 +02:00
Neyl
0043bf7aa0 Merge branch 'master' into shadowmapping-test 2025-07-01 16:39:11 +02:00
Neyl
23d261bf78 Update CreateProject.js 2025-07-01 15:50:43 +02:00
Neyl
ff126b4781 Update CreateProject.js 2025-07-01 15:48:23 +02:00
Neyl
4a6f3222a8 line correction 2025-07-01 14:51:48 +02:00
Neyl
939024ee45 Update runtimescene-pixi-renderer.ts 2025-07-01 14:50:03 +02:00
Neyl
19ddd479dc Update runtimescene-pixi-renderer.ts 2025-07-01 14:47:48 +02:00
Neyl
c73befc0b4 Update DirectionalLight.ts 2025-07-01 14:46:22 +02:00
Neyl
d8136e0331 corrected line transfer 2025-07-01 14:45:16 +02:00
Neyl
15ff4bd0b3 change base value for shadow booleans and material for 3DCube 2025-07-01 14:43:38 +02:00
Neyl
c7fab9c15e change shadow booleans base value 2025-07-01 10:48:37 +02:00
Neyl
b427c1ae57 cleaning base value 2025-07-01 09:56:41 +02:00
Neyl
1cb1ba7d62 Update DirectionalLight.ts 2025-06-30 16:23:07 +02:00
Neyl
dbe3d39ab1 changed layer base light effects 2025-06-30 14:31:56 +02:00
Neyl
a6f67dc9d9 security for get layers 2025-06-30 11:10:20 +02:00
Florian Rival
867141aa9a Fix flow + fix rendering/styling + add back comments 2025-06-30 10:49:34 +02:00
Neyl
bbc3aafa00 Update EnumerateEffects.js 2025-06-30 10:19:18 +02:00
Neyl
4abc089776 update for cleaniness 2025-06-27 17:49:06 +02:00
Neyl
f7194e29c5 add advanced properties support for effect 2025-06-27 17:02:54 +02:00
Neyl
6e620b47a4 added distance from camera & frustum size proprieties + better shadow camera helper support 2025-06-26 17:53:15 +02:00
Neyl
7da6770a24 clean + hide shadow camera helper 2025-06-26 15:12:28 +02:00
Neyl
97a60a0a04 tried sign inversion for y axis 2025-06-26 14:13:48 +02:00
Neyl
62b3ebfe63 removed constructor useless line (need update) 2025-06-26 14:06:00 +02:00
Neyl
b41c4ee7d5 removed shadow camera position calculation, and constructor useless lines 2025-06-26 14:04:12 +02:00
Neyl
572721f13f format for okay-ish test push 2025-06-26 10:27:03 +02:00
Neyl
f04118fd43 okay-ish push, working on x, not on y 2025-06-26 10:25:54 +02:00
Neyl
e9c8eedbaa Update DirectionalLight.ts 2025-06-25 17:43:58 +02:00
Neyl
eac9f97ea1 Update DirectionalLight.ts 2025-06-25 16:38:30 +02:00
Neyl
e346e79a8b pre fix light position calculation 2025-06-25 16:25:31 +02:00
Neyl
16491fe2b0 safer shadowmap resize management + format 2025-06-25 10:16:02 +02:00
Neyl
50b9f1f7e7 added cast shadow ? support on directionnal light + resolution parameter 2025-06-24 17:02:27 +02:00
Neyl
f67b543331 fix data transfer from object to renderer 2025-06-24 10:54:52 +02:00
Neyl
69f637b3ba pre fix model3D object data transfering for cast and receive shadow 2025-06-24 09:55:49 +02:00
Neyl
01de04bae7 format 2025-06-23 16:10:22 +02:00
Neyl
02552511d6 fixed cube shadow casting/receiving update on hot reload 2025-06-23 15:37:16 +02:00
Neyl
d68f6a9b28 fix property configuration for checkboxes on 3d models 2025-06-23 14:29:12 +02:00
Neyl
5d4e0d3af6 format 2025-06-23 14:13:43 +02:00
Neyl
789357a9a6 quasi functional push : needs branching all different elements + repairing light position adaptation to camera position 2025-06-23 14:08:57 +02:00
Neyl
f3c47c3ed2 final cleaner comit 2025-06-02 15:11:40 +02:00
Neyl
0fae553fe2 working shadowmap (on directional light, hemisphere light doesnt support shadows) 2025-06-02 15:00:19 +02:00
Neyl
a6e795a05f format 2025-05-05 10:48:01 +02:00
Neyl
de2b9cce21 FAILED implementation test of shadowmapping of threeJS 2025-05-05 10:39:05 +02:00
Neyl
2fe40a9bf7 base commit for shadow (does not work) 2025-04-30 10:37:59 +02:00
16 changed files with 411 additions and 65 deletions

View File

@@ -25,6 +25,8 @@ namespace gdjs {
topFaceVisible: boolean;
bottomFaceVisible: boolean;
tint: string | undefined;
isCastingShadow: boolean;
isReceivingShadow: boolean;
materialType: 'Basic' | 'StandardWithoutMetalness';
};
}
@@ -71,8 +73,10 @@ namespace gdjs {
string,
];
_materialType: gdjs.Cube3DRuntimeObject.MaterialType =
gdjs.Cube3DRuntimeObject.MaterialType.Basic;
gdjs.Cube3DRuntimeObject.MaterialType.StandardWithoutMetalness;
_tint: string;
_isCastingShadow: boolean = true;
_isReceivingShadow: boolean = true;
constructor(
instanceContainer: gdjs.RuntimeInstanceContainer,
@@ -121,6 +125,8 @@ namespace gdjs {
];
this._tint = objectData.content.tint || '255;255;255';
this._isCastingShadow = objectData.content.isCastingShadow || false;
this._isReceivingShadow = objectData.content.isReceivingShadow || false;
this._materialType = this._convertMaterialType(
objectData.content.materialType
@@ -430,6 +436,18 @@ namespace gdjs {
) {
this.setMaterialType(newObjectData.content.materialType);
}
if (
oldObjectData.content.isCastingShadow !==
newObjectData.content.isCastingShadow
) {
this.updateShadowCasting(newObjectData.content.isCastingShadow);
}
if (
oldObjectData.content.isReceivingShadow !==
newObjectData.content.isReceivingShadow
) {
this.updateShadowReceiving(newObjectData.content.isReceivingShadow);
}
return true;
}
@@ -531,6 +549,14 @@ namespace gdjs {
this._materialType = newMaterialType;
this._renderer._updateMaterials();
}
updateShadowCasting(value: boolean) {
this._isCastingShadow = value;
this._renderer.updateShadowCasting();
}
updateShadowReceiving(value: boolean) {
this._isReceivingShadow = value;
this._renderer.updateShadowReceiving();
}
}
export namespace Cube3DRuntimeObject {

View File

@@ -81,13 +81,14 @@ namespace gdjs {
.map((_, index) =>
getFaceMaterial(runtimeObject, materialIndexToFaceIndex[index])
);
const boxMesh = new THREE.Mesh(geometry, materials);
super(runtimeObject, instanceContainer, boxMesh);
this._boxMesh = boxMesh;
this._cube3DRuntimeObject = runtimeObject;
boxMesh.receiveShadow = this._cube3DRuntimeObject._isCastingShadow;
boxMesh.castShadow = this._cube3DRuntimeObject._isReceivingShadow;
this.updateSize();
this.updatePosition();
this.updateRotation();
@@ -114,6 +115,13 @@ namespace gdjs {
new THREE.BufferAttribute(new Float32Array(tints), 3)
);
}
updateShadowCasting() {
this._boxMesh.castShadow = this._cube3DRuntimeObject._isCastingShadow;
}
updateShadowReceiving() {
this._boxMesh.receiveShadow =
this._cube3DRuntimeObject._isReceivingShadow;
}
updateFace(faceIndex: integer) {
const materialIndex = faceIndexToMaterialIndex[faceIndex];

View File

@@ -6,6 +6,7 @@ namespace gdjs {
r: number;
t: string;
}
const shadowHelper = false;
gdjs.PixiFiltersTools.registerFilterCreator(
'Scene3D::DirectionalLight',
new (class implements gdjs.PixiFiltersTools.FilterCreator {
@@ -18,18 +19,35 @@ namespace gdjs {
}
return new (class implements gdjs.PixiFiltersTools.Filter {
light: THREE.DirectionalLight;
rotationObject: THREE.Group;
_isEnabled: boolean = false;
top: string = 'Y-';
elevation: float = 45;
rotation: float = 0;
shadowSize: float = 1024;
distanceFromCamera: float = 1500;
frustumSize: float = 4000;
shadowCameraHelper: THREE.CameraHelper | null;
constructor() {
this.light = new THREE.DirectionalLight();
this.light.position.set(1, 0, 0);
this.rotationObject = new THREE.Group();
this.rotationObject.add(this.light);
this.updateRotation();
this.light.shadow.mapSize.width = this.shadowSize;
this.light.shadow.mapSize.height = this.shadowSize;
this.light.shadow.camera.near = 1;
this.light.shadow.camera.far = this.distanceFromCamera + 10000;
this.light.shadow.camera.right = this.frustumSize / 2;
this.light.shadow.camera.left = -this.frustumSize / 2;
this.light.shadow.camera.top = this.frustumSize / 2;
this.light.shadow.camera.bottom = -this.frustumSize / 2;
if (shadowHelper) {
this.shadowCameraHelper = new THREE.CameraHelper(
this.light.shadow.camera
);
} else {
this.shadowCameraHelper = null;
}
this.light.shadow.camera.updateProjectionMatrix();
}
isEnabled(target: EffectsTarget): boolean {
@@ -53,7 +71,12 @@ namespace gdjs {
if (!scene) {
return false;
}
scene.add(this.rotationObject);
scene.add(this.light);
scene.add(this.light.target);
if (this.shadowCameraHelper) {
scene.add(this.shadowCameraHelper);
}
this._isEnabled = true;
return true;
}
@@ -65,20 +88,72 @@ namespace gdjs {
if (!scene) {
return false;
}
scene.remove(this.rotationObject);
scene.remove(this.light);
scene.remove(this.light.target);
if (this.shadowCameraHelper) {
scene.remove(this.shadowCameraHelper);
}
this._isEnabled = false;
return true;
}
updatePreRender(target: gdjs.EffectsTarget): any {}
updatePreRender(target: gdjs.EffectsTarget): any {
if (!target.getRuntimeLayer) {
return;
}
const layer = target.getRuntimeLayer();
const x = layer.getCameraX();
const y = layer.getCameraY();
const z = layer.getCameraZ(layer.getInitialCamera3DFieldOfView());
const roundedX = Math.floor(x / 100) * 100;
const roundedY = Math.floor(y / 100) * 100;
const roundedZ = Math.floor(z / 100) * 100;
if (this.top === 'Y-') {
const posLightX =
roundedX +
this.distanceFromCamera *
Math.cos(gdjs.toRad(this.rotation + 90)) *
Math.cos(gdjs.toRad(this.elevation));
const posLightY =
roundedY -
this.distanceFromCamera * Math.sin(gdjs.toRad(this.elevation));
const posLightZ =
roundedZ +
this.distanceFromCamera *
Math.sin(gdjs.toRad(this.rotation + 90)) *
Math.cos(gdjs.toRad(this.elevation));
this.light.position.set(posLightX, posLightY, posLightZ);
this.light.target.position.set(roundedX, roundedY, roundedZ);
} else {
const posLightX =
roundedX +
this.distanceFromCamera *
Math.cos(gdjs.toRad(this.rotation + 90)) *
Math.cos(gdjs.toRad(this.elevation));
const posLightY =
roundedY +
this.distanceFromCamera *
Math.sin(gdjs.toRad(this.rotation + 90)) *
Math.cos(gdjs.toRad(this.elevation));
const posLightZ =
roundedZ +
this.distanceFromCamera * Math.sin(gdjs.toRad(this.elevation));
this.light.position.set(posLightX, posLightY, posLightZ);
this.light.target.position.set(roundedX, roundedY, roundedZ);
}
}
updateDoubleParameter(parameterName: string, value: number): void {
if (parameterName === 'intensity') {
this.light.intensity = value;
} else if (parameterName === 'elevation') {
this.elevation = value;
this.updateRotation();
} else if (parameterName === 'rotation') {
this.rotation = value;
this.updateRotation();
} else if (parameterName === 'distanceFromCamera') {
this.distanceFromCamera = value;
} else if (parameterName === 'frustumSize') {
this.frustumSize = value;
}
}
getDoubleParameter(parameterName: string): number {
@@ -88,6 +163,10 @@ namespace gdjs {
return this.elevation;
} else if (parameterName === 'rotation') {
return this.rotation;
} else if (parameterName === 'distanceFromCamera') {
return this.distanceFromCamera;
} else if (parameterName === 'frustumSize') {
return this.frustumSize;
}
return 0;
}
@@ -97,9 +176,25 @@ namespace gdjs {
gdjs.rgbOrHexStringToNumber(value)
);
}
if (parameterName === 'top') {
this.top = value;
this.updateRotation();
if (parameterName === 'shadowQuality') {
if (value === 'Low') {
this.light.shadow.mapSize.set(512, 512);
this.light.shadow.map?.dispose(); //force the recreation of the shadow texture
this.light.shadow.map = null;
this.light.shadow.needsUpdate = true;
}
if (value === 'Medium') {
this.light.shadow.mapSize.set(1024, 1024);
this.light.shadow.map?.dispose(); //force the recreation of the shadow texture
this.light.shadow.map = null;
this.light.shadow.needsUpdate = true;
}
if (value === 'High') {
this.light.shadow.mapSize.set(2048, 2048);
this.light.shadow.map?.dispose(); //force the recreation of the shadow texture
this.light.shadow.map = null;
this.light.shadow.needsUpdate = true;
}
}
}
updateColorParameter(parameterName: string, value: number): void {
@@ -113,16 +208,9 @@ namespace gdjs {
}
return 0;
}
updateBooleanParameter(parameterName: string, value: boolean): void {}
updateRotation() {
if (this.top === 'Z+') {
// 0° is a light from the right of the screen.
this.rotationObject.rotation.z = gdjs.toRad(this.rotation);
this.rotationObject.rotation.y = -gdjs.toRad(this.elevation);
} else {
// 0° becomes a light from Z+.
this.rotationObject.rotation.y = gdjs.toRad(this.rotation - 90);
this.rotationObject.rotation.z = -gdjs.toRad(this.elevation);
updateBooleanParameter(parameterName: string, value: boolean): void {
if (parameterName === 'isCastingShadow') {
this.light.castShadow = value;
}
}
getNetworkSyncData(): DirectionalLightFilterNetworkSyncData {
@@ -140,7 +228,6 @@ namespace gdjs {
this.elevation = syncData.e;
this.rotation = syncData.r;
this.top = syncData.t;
this.updateRotation();
}
})();
}

View File

@@ -859,7 +859,9 @@ module.exports = {
propertyName === 'rightFaceResourceRepeat' ||
propertyName === 'topFaceResourceRepeat' ||
propertyName === 'bottomFaceResourceRepeat' ||
propertyName === 'enableTextureTransparency'
propertyName === 'enableTextureTransparency' ||
propertyName === 'isCastingShadow' ||
propertyName === 'isReceivingShadow'
) {
objectContent[propertyName] = newValue === '1';
return true;
@@ -1083,12 +1085,26 @@ module.exports = {
objectProperties
.getOrCreate('materialType')
.setValue(objectContent.materialType || 'Basic')
.setValue(objectContent.materialType || 'StandardWithoutMetalness')
.setType('choice')
.addExtraInfo('Basic')
.addExtraInfo('StandardWithoutMetalness')
.setLabel(_('Material type'));
objectProperties
.getOrCreate('isCastingShadow')
.setValue(objectContent.isCastingShadow ? 'true' : 'false')
.setType('boolean')
.setLabel(_('Shadow casting'))
.setGroup(_('Shadows'));
objectProperties
.getOrCreate('isReceivingShadow')
.setValue(objectContent.isReceivingShadow ? 'true' : 'false')
.setType('boolean')
.setLabel(_('Shadow receiving'))
.setGroup(_('Shadows'));
return objectProperties;
};
Cube3DObject.content = {
@@ -1116,8 +1132,10 @@ module.exports = {
rightFaceResourceRepeat: false,
topFaceResourceRepeat: false,
bottomFaceResourceRepeat: false,
materialType: 'Basic',
materialType: 'StandardWithoutMetalness',
tint: '255;255;255',
isCastingShadow: true,
isReceivingShadow: true,
};
Cube3DObject.updateInitialInstanceProperty = function (
@@ -1913,6 +1931,34 @@ module.exports = {
.setLabel(_('Rotation (in degrees)'))
.setType('number')
.setGroup(_('Orientation'));
properties
.getOrCreate('isCastingShadow')
.setValue('true')
.setLabel(_('Shadow casting'))
.setType('boolean')
.setGroup(_('Shadows'));
properties
.getOrCreate('shadowQuality')
.setValue('Medium')
.addExtraInfo('Low')
.addExtraInfo('Medium')
.addExtraInfo('High')
.setLabel(_('Shadow quality'))
.setType('choice')
.setGroup(_('Shadows'));
properties
.getOrCreate('frustumSize')
.setValue('4000')
.setLabel(_('Shadow frustum size'))
.setType('number')
.setGroup(_('Shadows'))
.setAdvanced(true);
properties
.getOrCreate('distanceFromCamera')
.setValue('1500')
.setLabel(_("Distance from layer's camera"))
.setType('number')
.setAdvanced(true);
}
{
const effect = extension
@@ -3210,6 +3256,8 @@ module.exports = {
this._threeObject = new THREE.Group();
this._threeObject.rotation.order = 'ZYX';
this._threeObject.castShadow = true;
this._threeObject.receiveShadow = true;
this._threeGroup.add(this._threeObject);
}

View File

@@ -23,7 +23,7 @@ Model3DObjectConfiguration::Model3DObjectConfiguration()
: width(100), height(100), depth(100), rotationX(0), rotationY(0),
rotationZ(0), modelResourceName(""), materialType("StandardWithoutMetalness"),
originLocation("ModelOrigin"), centerLocation("ModelOrigin"),
keepAspectRatio(true), crossfadeDuration(0.1f) {}
keepAspectRatio(true), crossfadeDuration(0.1f), isCastingShadow(true), isReceivingShadow(true) {}
bool Model3DObjectConfiguration::UpdateProperty(const gd::String &propertyName,
const gd::String &newValue) {
@@ -75,6 +75,16 @@ bool Model3DObjectConfiguration::UpdateProperty(const gd::String &propertyName,
crossfadeDuration = newValue.To<double>();
return true;
}
if(propertyName == "isCastingShadow")
{
isCastingShadow = newValue == "1";
return true;
}
if(propertyName == "isReceivingShadow")
{
isReceivingShadow = newValue == "1";
return true;
}
return false;
}
@@ -178,6 +188,20 @@ Model3DObjectConfiguration::GetProperties() const {
.SetGroup(_("Animations"))
.SetMeasurementUnit(gd::MeasurementUnit::GetSecond());
objectProperties["isCastingShadow"]
.SetValue(isCastingShadow ? "true" : "false")
.SetType("boolean")
.SetLabel(_("Shadow casting"))
.SetGroup(_("Shadows"));
objectProperties["isReceivingShadow"]
.SetValue(isReceivingShadow ? "true" : "false")
.SetType("boolean")
.SetLabel(_("Shadow receiving"))
.SetGroup(_("Shadows"));
return objectProperties;
}
@@ -210,6 +234,8 @@ void Model3DObjectConfiguration::DoUnserializeFrom(
centerLocation = content.GetStringAttribute("centerLocation");
keepAspectRatio = content.GetBoolAttribute("keepAspectRatio");
crossfadeDuration = content.GetDoubleAttribute("crossfadeDuration");
isCastingShadow = content.GetBoolAttribute("isCastingShadow");
isReceivingShadow = content.GetBoolAttribute("isReceivingShadow");
RemoveAllAnimations();
auto &animationsElement = content.GetChild("animations");
@@ -239,6 +265,8 @@ void Model3DObjectConfiguration::DoSerializeTo(
content.SetAttribute("centerLocation", centerLocation);
content.SetAttribute("keepAspectRatio", keepAspectRatio);
content.SetAttribute("crossfadeDuration", crossfadeDuration);
content.SetAttribute("isCastingShadow", isCastingShadow);
content.SetAttribute("isReceivingShadow", isReceivingShadow);
auto &animationsElement = content.AddChild("animations");
animationsElement.ConsiderAsArrayOf("animation");

View File

@@ -160,6 +160,8 @@ public:
const gd::String& GetCenterLocation() const { return centerLocation; };
bool shouldKeepAspectRatio() const { return keepAspectRatio; };
bool shouldCastShadow() const { return isCastingShadow; };
bool shouldReceiveShadow() const { return isReceivingShadow; };
///@}
protected:
@@ -182,6 +184,8 @@ private:
gd::String centerLocation;
bool keepAspectRatio;
bool isCastingShadow;
bool isReceivingShadow;
std::vector<Model3DAnimation> animations;
static Model3DAnimation badAnimation; //< Bad animation when an out of bound

View File

@@ -38,6 +38,8 @@ namespace gdjs {
| 'BottomCenterY';
animations: Model3DAnimation[];
crossfadeDuration: float;
isCastingShadow: boolean;
isReceivingShadow: boolean;
};
}
@@ -101,6 +103,8 @@ namespace gdjs {
_animationSpeedScale: float = 1;
_animationPaused: boolean = false;
_crossfadeDuration: float = 0;
_isCastingShadow: boolean = true;
_isReceivingShadow: boolean = true;
constructor(
instanceContainer: gdjs.RuntimeInstanceContainer,
@@ -123,6 +127,8 @@ namespace gdjs {
objectData.content.materialType
);
this.setIsCastingShadow(objectData.content.isCastingShadow);
this.setIsReceivingShadow(objectData.content.isReceivingShadow);
this.onModelChanged(objectData);
this._crossfadeDuration = objectData.content.crossfadeDuration || 0;
@@ -195,6 +201,18 @@ namespace gdjs {
newObjectData.content.centerLocation
);
}
if (
oldObjectData.content.isCastingShadow !==
newObjectData.content.isCastingShadow
) {
this.setIsCastingShadow(newObjectData.content.isCastingShadow);
}
if (
oldObjectData.content.isReceivingShadow !==
newObjectData.content.isReceivingShadow
) {
this.setIsReceivingShadow(newObjectData.content.isReceivingShadow);
}
return true;
}
@@ -358,6 +376,16 @@ namespace gdjs {
return this._renderer.hasAnimationEnded();
}
setIsCastingShadow(value: boolean): void {
this._isCastingShadow = value;
this._renderer._updateShadow();
}
setIsReceivingShadow(value: boolean): void {
this._isReceivingShadow = value;
this._renderer._updateShadow();
}
setCrossfadeDuration(duration: number): void {
if (this._crossfadeDuration === duration) return;
this._crossfadeDuration = duration;

View File

@@ -286,6 +286,7 @@ namespace gdjs {
this.get3DRendererObject().remove(this._threeObject);
this.get3DRendererObject().add(threeObject);
this._threeObject = threeObject;
this._updateShadow();
// Start the current animation on the new 3D object.
this._animationMixer = new THREE.AnimationMixer(root);
@@ -323,6 +324,13 @@ namespace gdjs {
return this._originalModel.animations[animationIndex].name;
}
_updateShadow() {
this._threeObject.traverse((child) => {
child.castShadow = this._model3DRuntimeObject._isCastingShadow;
child.receiveShadow = this._model3DRuntimeObject._isReceivingShadow;
});
}
/**
* Return true if animation has ended.
* The animation had ended if:

View File

@@ -116,6 +116,9 @@ namespace gdjs {
}
}
getRuntimeLayer(): gdjs.RuntimeLayer {
return this;
}
getRenderer(): gdjs.LayerRenderer {
return this._renderer;
}

View File

@@ -17,6 +17,7 @@ namespace gdjs {
getName: () => string;
getRendererObject: () => RendererObjectInterface | null | undefined;
get3DRendererObject: () => THREE.Object3D | null | undefined;
getRuntimeLayer?: () => gdjs.RuntimeLayer;
}
/**

View File

@@ -97,6 +97,8 @@ namespace gdjs {
!gdjs.evtTools.common.isMobile()),
preserveDrawingBuffer: true, // Keep to true to allow screenshots.
});
this._threeRenderer.shadowMap.enabled = true;
this._threeRenderer.shadowMap.type = THREE.PCFSoftShadowMap;
this._threeRenderer.useLegacyLights = true;
this._threeRenderer.autoClear = false;
this._threeRenderer.setSize(

View File

@@ -52,6 +52,10 @@ export type ValueFieldCommonProperties = {|
onEditButtonClick?: () => void,
getValueFromDisplayedValue?: string => string,
getDisplayedValueFromValue?: string => string,
// Only used for effects for now:
advanced?: boolean,
defaultValue?: string | number | boolean,
|};
// "Primitive" value fields are "simple" fields.

View File

@@ -49,6 +49,8 @@ export const enumerateEffectsMetadata = (
const getLabel = () => propertyLabel;
const getDescription = () => propertyDescription;
const getExtraDescription = () => parameterName;
const advanced = property.isAdvanced();
const defaultValue = property.getValue();
if (valueType === 'number') {
return {
@@ -61,6 +63,8 @@ export const enumerateEffectsMetadata = (
getLabel,
getDescription,
getExtraDescription,
advanced,
defaultValue: parseFloat(defaultValue) || 0,
};
} else if (valueType === 'boolean') {
return {
@@ -73,6 +77,8 @@ export const enumerateEffectsMetadata = (
getLabel,
getDescription,
getExtraDescription,
advanced,
defaultValue: defaultValue === 'true',
};
} else if (valueType === 'resource') {
// Resource is a "string" (with a selector in the UI)
@@ -90,6 +96,8 @@ export const enumerateEffectsMetadata = (
getLabel,
getDescription,
getExtraDescription,
advanced,
defaultValue,
};
} else if (valueType === 'color') {
return {
@@ -102,6 +110,8 @@ export const enumerateEffectsMetadata = (
getLabel,
getDescription,
getExtraDescription,
advanced,
defaultValue,
};
} else if (valueType === 'choice') {
const choices = property
@@ -119,6 +129,8 @@ export const enumerateEffectsMetadata = (
getLabel,
getDescription,
getExtraDescription,
advanced,
defaultValue,
};
} else {
console.error(

View File

@@ -53,6 +53,8 @@ import PasteIcon from '../UI/CustomSvgIcons/Clipboard';
import CopyIcon from '../UI/CustomSvgIcons/Copy';
import { type ConnectDragSource } from 'react-dnd';
import ResponsiveFlatButton from '../UI/ResponsiveFlatButton';
import { Accordion, AccordionHeader, AccordionBody } from '../UI/Accordion';
import { type Field } from '../CompactPropertiesEditor';
const gd: libGDevelop = global.gd;
@@ -132,14 +134,12 @@ const Effect = React.forwardRef(
ref
) => {
const gdevelopTheme = React.useContext(GDevelopThemeContext);
const preferences = React.useContext(PreferencesContext);
const showEffectParameterNames =
preferences.values.showEffectParameterNames;
const setShowEffectParameterNames = preferences.setShowEffectParameterNames;
const forceUpdate = useForceUpdate();
const isClipboardContainingEffects = Clipboard.has(EFFECTS_CLIPBOARD_KIND);
const renameEffect = React.useCallback(
@@ -190,6 +190,42 @@ const Effect = React.forwardRef(
effectType
);
const parametersSchema = effectMetadata && effectMetadata.parametersSchema;
const basicPropertiesSchema = React.useMemo(
() =>
parametersSchema
? parametersSchema.filter(param => param.valueType && !param.advanced)
: [],
[parametersSchema]
);
const advancedPropertiesSchema = React.useMemo(
() =>
parametersSchema
? parametersSchema.filter(param => param.valueType && param.advanced)
: [],
[parametersSchema]
);
const areAdvancedPropertiesModified = React.useMemo(
() => {
return advancedPropertiesSchema.some((field: Field) => {
const name = field.valueType ? field.name : null;
if (!name) return false;
const current =
field.valueType === 'number'
? effect.getDoubleParameter(name)
: field.valueType === 'boolean'
? effect.getBooleanParameter(name)
: effect.getStringParameter(name);
const defaultValue = field.valueType ? field.defaultValue : null;
return current !== defaultValue;
});
},
[advancedPropertiesSchema, effect]
);
return (
<I18n>
{({ i18n }) => (
@@ -286,33 +322,61 @@ const Effect = React.forwardRef(
/>
<Spacer />
</div>
{effectType && (
{effectMetadata && (
<Line expand noMargin>
<Column expand>
{effectMetadata ? (
<React.Fragment>
<Line>
<BackgroundText>
<MarkdownText source={effectMetadata.description} />
</BackgroundText>
</Line>
<PropertiesEditor
key={effect.getEffectType()}
instances={[effect]}
schema={effectMetadata.parametersSchema}
project={project}
resourceManagementProps={resourceManagementProps}
renderExtraDescriptionText={
showEffectParameterNames
? parameterName =>
i18n._(
t`Property name in events: \`${parameterName}\` `
)
: undefined
}
/>
</React.Fragment>
) : null}
<Line>
<BackgroundText>
<MarkdownText source={effectMetadata.description} />
</BackgroundText>
</Line>
<PropertiesEditor
key={effect.getEffectType() + '-basic'}
instances={[effect]}
schema={basicPropertiesSchema}
project={project}
resourceManagementProps={resourceManagementProps}
renderExtraDescriptionText={
showEffectParameterNames
? parameterName =>
i18n._(
t`Property name in events: \`${parameterName}\` `
)
: undefined
}
/>
{advancedPropertiesSchema.length > 0 && (
<Accordion
defaultExpanded={areAdvancedPropertiesModified}
noMargin
>
<AccordionHeader noMargin>
<Text size="sub-title">
<Trans>Advanced properties</Trans>
</Text>
</AccordionHeader>
<AccordionBody disableGutters>
<Column expand noMargin>
<PropertiesEditor
key={effect.getEffectType() + '-advanced'}
instances={[effect]}
schema={advancedPropertiesSchema}
project={project}
resourceManagementProps={resourceManagementProps}
renderExtraDescriptionText={
showEffectParameterNames
? parameterName =>
i18n._(
t`Property name in events: \`${parameterName}\` `
)
: undefined
}
/>
</Column>
</AccordionBody>
</Accordion>
)}
<Spacer />
</Column>
</Line>

View File

@@ -674,6 +674,17 @@ const Model3DEditor = ({
propertyName="crossfadeDuration"
/>
</Column>
<Text size="block-title">Shadows</Text>
<Column noMargin expand>
<PropertyCheckbox
objectConfiguration={objectConfiguration}
propertyName="isCastingShadow"
/>
<PropertyCheckbox
objectConfiguration={objectConfiguration}
propertyName="isReceivingShadow"
/>
</Column>
<Column noMargin expand useFullHeight>
{model3DConfiguration.getAnimationsCount() === 0 ? (
<Column noMargin expand justifyContent="center">

View File

@@ -29,14 +29,26 @@ const getNewProjectSourceFromUrl = (projectUrl: string): NewProjectSource => {
};
export const addDefaultLightToLayer = (layer: gdLayer): void => {
const light = layer.getEffects().insertNewEffect('3D Light', 0);
light.setEffectType('Scene3D::HemisphereLight');
light.setStringParameter('skyColor', '255;255;255');
light.setStringParameter('groundColor', '64;64;64');
light.setDoubleParameter('intensity', 1);
light.setStringParameter('top', 'Y-');
light.setDoubleParameter('elevation', 45);
light.setDoubleParameter('rotation', 0);
const directionalLight = layer
.getEffects()
.insertNewEffect('3D Sun Light', 0);
directionalLight.setEffectType('Scene3D::DirectionalLight');
directionalLight.setStringParameter('color', '255;255;255');
directionalLight.setStringParameter('shadowQuality', 'Medium');
directionalLight.setDoubleParameter('distanceFromCamera', 1500);
directionalLight.setDoubleParameter('frustumSize', 4000);
directionalLight.setDoubleParameter('intensity', 0.75);
directionalLight.setStringParameter('top', 'Z+');
directionalLight.setDoubleParameter('elevation', 45);
directionalLight.setDoubleParameter('rotation', 0);
directionalLight.setBooleanParameter('isCastingShadow', true);
const ambientLight = layer
.getEffects()
.insertNewEffect('3D Ammbient Light', 0);
ambientLight.setEffectType('Scene3D::AmbientLight');
ambientLight.setStringParameter('color', '255;255;255');
ambientLight.setDoubleParameter('intensity', 0.25);
};
export const addDefaultLightToAllLayers = (layout: gdLayout): void => {