mirror of
https://github.com/4ian/GDevelop.git
synced 2025-10-15 10:19:04 +00:00
282 lines
10 KiB
TypeScript
282 lines
10 KiB
TypeScript
namespace gdjs {
|
|
interface DirectionalLightFilterNetworkSyncData {
|
|
i: number;
|
|
c: number;
|
|
e: number;
|
|
r: number;
|
|
t: string;
|
|
}
|
|
const shadowHelper = false;
|
|
gdjs.PixiFiltersTools.registerFilterCreator(
|
|
'Scene3D::DirectionalLight',
|
|
new (class implements gdjs.PixiFiltersTools.FilterCreator {
|
|
makeFilter(
|
|
target: EffectsTarget,
|
|
effectData: EffectData
|
|
): gdjs.PixiFiltersTools.Filter {
|
|
if (typeof THREE === 'undefined') {
|
|
return new gdjs.PixiFiltersTools.EmptyFilter();
|
|
}
|
|
return new (class implements gdjs.PixiFiltersTools.Filter {
|
|
private _top: string = 'Z+';
|
|
private _elevation: float = 45;
|
|
private _rotation: float = 0;
|
|
private _shadowMapSize: float = 1024;
|
|
private _minimumShadowBias: float = 0;
|
|
private _distanceFromCamera: float = 1500;
|
|
private _frustumSize: float = 4000;
|
|
|
|
private _isEnabled: boolean = false;
|
|
private _light: THREE.DirectionalLight;
|
|
private _shadowMapDirty = true;
|
|
private _shadowCameraDirty = true;
|
|
private _shadowCameraHelper: THREE.CameraHelper | null;
|
|
|
|
constructor() {
|
|
this._light = new THREE.DirectionalLight();
|
|
|
|
if (shadowHelper) {
|
|
this._shadowCameraHelper = new THREE.CameraHelper(
|
|
this._light.shadow.camera
|
|
);
|
|
} else {
|
|
this._shadowCameraHelper = null;
|
|
}
|
|
|
|
this._light.shadow.camera.updateProjectionMatrix();
|
|
}
|
|
|
|
private _updateShadowCamera(): void {
|
|
if (!this._shadowCameraDirty) {
|
|
return;
|
|
}
|
|
this._shadowCameraDirty = false;
|
|
|
|
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;
|
|
}
|
|
|
|
private _updateShadowMapSize(): void {
|
|
if (!this._shadowMapDirty) {
|
|
return;
|
|
}
|
|
this._shadowMapDirty = false;
|
|
|
|
this._light.shadow.mapSize.set(
|
|
this._shadowMapSize,
|
|
this._shadowMapSize
|
|
);
|
|
|
|
// Force the recreation of the shadow map texture:
|
|
this._light.shadow.map?.dispose();
|
|
this._light.shadow.map = null;
|
|
this._light.shadow.needsUpdate = true;
|
|
}
|
|
|
|
isEnabled(target: EffectsTarget): boolean {
|
|
return this._isEnabled;
|
|
}
|
|
setEnabled(target: EffectsTarget, enabled: boolean): boolean {
|
|
if (this._isEnabled === enabled) {
|
|
return true;
|
|
}
|
|
if (enabled) {
|
|
return this.applyEffect(target);
|
|
} else {
|
|
return this.removeEffect(target);
|
|
}
|
|
}
|
|
applyEffect(target: EffectsTarget): boolean {
|
|
const scene = target.get3DRendererObject() as
|
|
| THREE.Scene
|
|
| null
|
|
| undefined;
|
|
if (!scene) {
|
|
return false;
|
|
}
|
|
scene.add(this._light);
|
|
scene.add(this._light.target);
|
|
if (this._shadowCameraHelper) {
|
|
scene.add(this._shadowCameraHelper);
|
|
}
|
|
|
|
this._isEnabled = true;
|
|
return true;
|
|
}
|
|
removeEffect(target: EffectsTarget): boolean {
|
|
const scene = target.get3DRendererObject() as
|
|
| THREE.Scene
|
|
| null
|
|
| undefined;
|
|
if (!scene) {
|
|
return false;
|
|
}
|
|
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 {
|
|
// Apply any update to the camera or shadow map size.
|
|
this._updateShadowCamera();
|
|
this._updateShadowMapSize();
|
|
|
|
// Avoid shadow acne due to depth buffer precision.
|
|
const biasMultiplier =
|
|
this._shadowMapSize < 1024
|
|
? 2
|
|
: this._shadowMapSize < 2048
|
|
? 1.25
|
|
: 1;
|
|
this._light.shadow.bias = -this._minimumShadowBias * biasMultiplier;
|
|
|
|
// Apply update to the light position and its target.
|
|
// By doing this, the shadows are "following" the GDevelop camera.
|
|
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)) *
|
|
Math.cos(gdjs.toRad(this._elevation));
|
|
const posLightY =
|
|
roundedY +
|
|
this._distanceFromCamera *
|
|
Math.sin(gdjs.toRad(this._rotation)) *
|
|
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;
|
|
} else if (parameterName === 'rotation') {
|
|
this._rotation = value;
|
|
} else if (parameterName === 'distanceFromCamera') {
|
|
this._distanceFromCamera = value;
|
|
} else if (parameterName === 'frustumSize') {
|
|
this._frustumSize = value;
|
|
} else if (parameterName === 'minimumShadowBias') {
|
|
this._minimumShadowBias = value;
|
|
}
|
|
}
|
|
getDoubleParameter(parameterName: string): number {
|
|
if (parameterName === 'intensity') {
|
|
return this._light.intensity;
|
|
} else if (parameterName === 'elevation') {
|
|
return this._elevation;
|
|
} else if (parameterName === 'rotation') {
|
|
return this._rotation;
|
|
} else if (parameterName === 'distanceFromCamera') {
|
|
return this._distanceFromCamera;
|
|
} else if (parameterName === 'frustumSize') {
|
|
return this._frustumSize;
|
|
} else if (parameterName === 'minimumShadowBias') {
|
|
return this._minimumShadowBias;
|
|
}
|
|
return 0;
|
|
}
|
|
updateStringParameter(parameterName: string, value: string): void {
|
|
if (parameterName === 'color') {
|
|
this._light.color = new THREE.Color(
|
|
gdjs.rgbOrHexStringToNumber(value)
|
|
);
|
|
}
|
|
if (parameterName === 'top') {
|
|
this._top = value;
|
|
}
|
|
if (parameterName === 'shadowQuality') {
|
|
if (value === 'low' && this._shadowMapSize !== 512) {
|
|
this._shadowMapSize = 512;
|
|
this._shadowMapDirty = true;
|
|
}
|
|
if (value === 'medium' && this._shadowMapSize !== 1024) {
|
|
this._shadowMapSize = 1024;
|
|
this._shadowMapDirty = true;
|
|
}
|
|
if (value === 'high' && this._shadowMapSize !== 2048) {
|
|
this._shadowMapSize = 2048;
|
|
this._shadowMapDirty = true;
|
|
}
|
|
}
|
|
}
|
|
updateColorParameter(parameterName: string, value: number): void {
|
|
if (parameterName === 'color') {
|
|
this._light.color.setHex(value);
|
|
}
|
|
}
|
|
getColorParameter(parameterName: string): number {
|
|
if (parameterName === 'color') {
|
|
return this._light.color.getHex();
|
|
}
|
|
return 0;
|
|
}
|
|
updateBooleanParameter(parameterName: string, value: boolean): void {
|
|
if (parameterName === 'isCastingShadow') {
|
|
this._light.castShadow = value;
|
|
}
|
|
}
|
|
getNetworkSyncData(): DirectionalLightFilterNetworkSyncData {
|
|
return {
|
|
i: this._light.intensity,
|
|
c: this._light.color.getHex(),
|
|
e: this._elevation,
|
|
r: this._rotation,
|
|
t: this._top,
|
|
};
|
|
}
|
|
updateFromNetworkSyncData(syncData: any): void {
|
|
this._light.intensity = syncData.i;
|
|
this._light.color.setHex(syncData.c);
|
|
this._elevation = syncData.e;
|
|
this._rotation = syncData.r;
|
|
this._top = syncData.t;
|
|
}
|
|
})();
|
|
}
|
|
})()
|
|
);
|
|
}
|