mirror of
https://github.com/4ian/GDevelop.git
synced 2025-10-15 10:19:04 +00:00
Compare commits
46 Commits
d474c2a47e
...
shadowmapp
Author | SHA1 | Date | |
---|---|---|---|
![]() |
a19d1a4022 | ||
![]() |
d54fe7c017 | ||
![]() |
3ed4cf1e81 | ||
![]() |
f063e43019 | ||
![]() |
0043bf7aa0 | ||
![]() |
23d261bf78 | ||
![]() |
ff126b4781 | ||
![]() |
4a6f3222a8 | ||
![]() |
939024ee45 | ||
![]() |
19ddd479dc | ||
![]() |
c73befc0b4 | ||
![]() |
d8136e0331 | ||
![]() |
15ff4bd0b3 | ||
![]() |
c7fab9c15e | ||
![]() |
b427c1ae57 | ||
![]() |
1cb1ba7d62 | ||
![]() |
dbe3d39ab1 | ||
![]() |
a6f67dc9d9 | ||
![]() |
867141aa9a | ||
![]() |
bbc3aafa00 | ||
![]() |
4abc089776 | ||
![]() |
f7194e29c5 | ||
![]() |
6e620b47a4 | ||
![]() |
7da6770a24 | ||
![]() |
97a60a0a04 | ||
![]() |
62b3ebfe63 | ||
![]() |
b41c4ee7d5 | ||
![]() |
572721f13f | ||
![]() |
f04118fd43 | ||
![]() |
e9c8eedbaa | ||
![]() |
eac9f97ea1 | ||
![]() |
e346e79a8b | ||
![]() |
16491fe2b0 | ||
![]() |
50b9f1f7e7 | ||
![]() |
f67b543331 | ||
![]() |
69f637b3ba | ||
![]() |
01de04bae7 | ||
![]() |
02552511d6 | ||
![]() |
d68f6a9b28 | ||
![]() |
5d4e0d3af6 | ||
![]() |
789357a9a6 | ||
![]() |
f3c47c3ed2 | ||
![]() |
0fae553fe2 | ||
![]() |
a6e795a05f | ||
![]() |
de2b9cce21 | ||
![]() |
2fe40a9bf7 |
@@ -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 {
|
||||
|
@@ -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];
|
||||
|
@@ -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();
|
||||
}
|
||||
})();
|
||||
}
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
|
@@ -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");
|
||||
|
@@ -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
|
||||
|
@@ -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;
|
||||
|
@@ -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:
|
||||
|
@@ -116,6 +116,9 @@ namespace gdjs {
|
||||
}
|
||||
}
|
||||
|
||||
getRuntimeLayer(): gdjs.RuntimeLayer {
|
||||
return this;
|
||||
}
|
||||
getRenderer(): gdjs.LayerRenderer {
|
||||
return this._renderer;
|
||||
}
|
||||
|
@@ -17,6 +17,7 @@ namespace gdjs {
|
||||
getName: () => string;
|
||||
getRendererObject: () => RendererObjectInterface | null | undefined;
|
||||
get3DRendererObject: () => THREE.Object3D | null | undefined;
|
||||
getRuntimeLayer?: () => gdjs.RuntimeLayer;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -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(
|
||||
|
@@ -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.
|
||||
|
@@ -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(
|
||||
|
@@ -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>
|
||||
|
@@ -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">
|
||||
|
@@ -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 => {
|
||||
|
Reference in New Issue
Block a user