gpu-curtains
Version:
gpu-curtains is a 3D WebGPU rendering engine. It can be used as a standalone 3D engine, but also includes extra classes focused on mapping 3d objects to DOM elements; It allows users to synchronize values such as position, sizing, or scale between them.
178 lines (177 loc) • 6.69 kB
JavaScript
import { generateUUID } from "../../utils/utils.mjs";
import { Vec3 } from "../../math/Vec3.mjs";
import { Object3D } from "../objects3D/Object3D.mjs";
import { isCameraRenderer } from "../renderers/utils.mjs";
import { sRGBToLinear } from "../../math/color-utils.mjs";
//#region src/core/lights/Light.ts
const nullVec3 = new Vec3();
/**
* Used as a base class to create a light.
*/
var Light = class extends Object3D {
/** @ignore */
#intensity;
/**
* A {@link Vec3} holding the {@link Light} {@link color} multiplied by its {@link intensity}.
* @private
*/
#intensityColor;
/**
* Light constructor
* @param renderer - {@link CameraRenderer} or {@link GPUCurtains} used to create this {@link Light}.
* @param parameters - {@link LightParams} used to create this {@link Light}.
*/
constructor(renderer, { label = "", color = new Vec3(1), intensity = 1, type = "lights" } = {}) {
super();
this.type = type;
this.setRenderer(renderer);
this.uuid = generateUUID();
this.options = {
label,
color,
intensity
};
this.color = color;
this.#intensityColor = this.color.clone();
this.color.onChange(() => this.onPropertyChanged("color", this.actualColor));
this.intensity = intensity;
this.userData = {};
}
/**
* Set or reset this light {@link CameraRenderer}.
* @param renderer - New {@link CameraRenderer} or {@link GPUCurtains} instance to use.
*/
setRenderer(renderer) {
const hasRenderer = !!this.renderer;
if (this.renderer) this.renderer.removeLight(this);
renderer = isCameraRenderer(renderer, this.type);
this.renderer = renderer;
if (this.index === void 0) this.index = this.renderer.lights.filter((light) => light.type === this.type).length;
if (!this.renderer.lightsBindingParams || this.index + 1 > this.renderer.lightsBindingParams[this.type].max) this.onMaxLightOverflow(this.type);
this.renderer.addLight(this);
this.setRendererBinding();
if (hasRenderer) this.reset(false);
}
/**
* Set or reset this {@link Light} {@link CameraRenderer} corresponding {@link core/bindings/BufferBinding.BufferBinding | BufferBinding}.
*/
setRendererBinding() {
if (this.renderer.bindings[this.type]) this.rendererBinding = this.renderer.bindings[this.type];
}
/**
* Resend all properties to the {@link CameraRenderer} corresponding {@link core/bindings/BufferBinding.BufferBinding | BufferBinding}. Called when the maximum number of corresponding {@link Light} has been overflowed or when updating the {@link Light} {@link renderer}.
* @param resetShadow - Whether to reset the {@link Light} shadow if any.
*/
reset(resetShadow = true) {
this.setRendererBinding();
this.onPropertyChanged("color", this.actualColor);
}
/**
* Get this {@link Light} intensity.
* @returns - The {@link Light} intensity.
*/
get intensity() {
return this.#intensity;
}
/**
* Set this {@link Light} intensity and update the {@link CameraRenderer} corresponding {@link core/bindings/BufferBinding.BufferBinding | BufferBinding}.
* @param value - The new {@link Light} intensity.
*/
set intensity(value) {
this.#intensity = value;
this.onPropertyChanged("color", this.visible ? this.actualColor : nullVec3);
}
/**
* Get whether this {@link Light} is visible or not.
*/
get visible() {
return super.visible;
}
/**
* Set this {@link Light} visible value, and update its {@link actualColor} property accordingly.
*/
set visible(value) {
super.visible = value;
this.onPropertyChanged("color", value ? this.actualColor : nullVec3);
}
/**
* Get whether all this {@link Light} parents are visible or not. Should not be used directly.
*/
get parentVisibility() {
return super.parentVisibility;
}
/**
* Set this {@link Light} parent visiblity, and update its {@link actualColor} property accordingly. Should not be used directly.
*/
set parentVisibility(value) {
super.parentVisibility = value;
this.onPropertyChanged("color", this.visible ? this.actualColor : nullVec3);
}
/**
* Get the actual {@link Vec3} color used in the shader: convert {@link color} to linear space, then multiply by {@link intensity}.
* @returns - Actual {@link Vec3} color used in the shader.
*/
get actualColor() {
return sRGBToLinear(this.#intensityColor.copy(this.color)).multiplyScalar(this.intensity);
}
/**
* Update the {@link CameraRenderer} corresponding {@link core/bindings/BufferBinding.BufferBinding | BufferBinding} input value and tell the {@link CameraRenderer#cameraLightsBindGroup | renderer camera, lights and shadows} bind group to update.
* @param propertyKey - name of the property to update.
* @param value - new value of the property.
*/
onPropertyChanged(propertyKey, value) {
if (this.rendererBinding && this.rendererBinding.inputs[propertyKey]) {
if (value instanceof Vec3) {
this.rendererBinding.inputs[propertyKey].value[this.index * 3] = value.x;
this.rendererBinding.inputs[propertyKey].value[this.index * 3 + 1] = value.y;
this.rendererBinding.inputs[propertyKey].value[this.index * 3 + 2] = value.z;
} else this.rendererBinding.inputs[propertyKey].value[this.index] = value;
this.rendererBinding.inputs[propertyKey].shouldUpdate = true;
this.renderer.shouldUpdateCameraLightsBindGroup();
}
}
/**
* Tell the {@link renderer} that the maximum number for this {@link type} of light has been overflown.
* @param lightsType - {@link type} of light.
*/
onMaxLightOverflow(lightsType) {
this.renderer.onMaxLightOverflow(lightsType, this.index);
if (this.rendererBinding) this.rendererBinding = this.renderer.bindings[lightsType];
}
/** @ignore */
applyRotation() {}
/** @ignore */
applyScale() {}
/** @ignore */
applyTransformOrigin() {}
/**
* Called by the {@link core/scenes/Scene.Scene | Scene} before updating the matrix stack.
*/
onBeforeRenderScene() {
this._onBeforeRenderCallback && this._onBeforeRenderCallback();
}
/**
* Callback to execute before updating the {@link core/scenes/Scene.Scene | Scene} matrix stack. This means it is called early and allows to update transformations values before actually setting the {@link Light} matrices. The callback won't be called if the {@link renderer} is not ready.
* @param callback - callback to run just before updating the {@link core/scenes/Scene.Scene | Scene} matrix stack.
* @returns - our {@link Light}
*/
onBeforeRender(callback) {
if (callback) this._onBeforeRenderCallback = callback;
return this;
}
/**
* Remove this {@link Light} from the {@link renderer} and destroy it.
*/
remove() {
this.renderer.removeLight(this);
this.destroy();
}
/**
* Destroy this {@link Light}.
*/
destroy() {
super.destroy();
}
};
//#endregion
export { Light };