@needle-tools/engine
Version:
Needle Engine is a web-based runtime for 3D apps. It runs on your machine for development with great integrations into editors like Unity or Blender - and can be deployed onto any device! It is flexible, extensible and networking and XR are built-in.
835 lines • 36.1 kB
JavaScript
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
import { getRaycastMesh } from "@needle-tools/gltf-progressive";
import { AxesHelper, Material, Mesh, Object3D, SkinnedMesh, Vector4 } from "three";
import { showBalloonWarning } from "../engine/debug/index.js";
import { getComponent, getOrAddComponent } from "../engine/engine_components.js";
import { Gizmos } from "../engine/engine_gizmos.js";
import { InstancingUtil, NEED_UPDATE_INSTANCE_KEY } from "../engine/engine_instancing.js";
import { isLocalNetwork } from "../engine/engine_networking_utils.js";
import { serializable } from "../engine/engine_serialization_decorator.js";
import { FrameEvent } from "../engine/engine_setup.js";
import { getTempVector } from "../engine/engine_three_utils.js";
import { getParam } from "../engine/engine_utils.js";
import { NEEDLE_render_objects } from "../engine/extensions/NEEDLE_render_objects.js";
import { setCustomVisibility } from "../engine/js-extensions/Layers.js";
import { Behaviour, GameObject } from "./Component.js";
import { ReflectionProbe } from "./ReflectionProbe.js";
import { InstancingHandler } from "./RendererInstancing.js";
// import { RendererCustomShader } from "./RendererCustomShader.js";
import { RendererLightmap } from "./RendererLightmap.js";
// for staying compatible with old code
export { InstancingUtil } from "../engine/engine_instancing.js";
const debugRenderer = getParam("debugrenderer");
const debugskinnedmesh = getParam("debugskinnedmesh");
const suppressInstancing = getParam("noinstancing");
const showWireframe = getParam("wireframe");
export var ReflectionProbeUsage;
(function (ReflectionProbeUsage) {
ReflectionProbeUsage[ReflectionProbeUsage["Off"] = 0] = "Off";
ReflectionProbeUsage[ReflectionProbeUsage["BlendProbes"] = 1] = "BlendProbes";
ReflectionProbeUsage[ReflectionProbeUsage["BlendProbesAndSkybox"] = 2] = "BlendProbesAndSkybox";
ReflectionProbeUsage[ReflectionProbeUsage["Simple"] = 3] = "Simple";
})(ReflectionProbeUsage || (ReflectionProbeUsage = {}));
export class FieldWithDefault {
path = null;
asset = null;
default;
}
export var RenderState;
(function (RenderState) {
RenderState[RenderState["Both"] = 0] = "Both";
RenderState[RenderState["Back"] = 1] = "Back";
RenderState[RenderState["Front"] = 2] = "Front";
})(RenderState || (RenderState = {}));
// support sharedMaterials[index] assigning materials directly to the objects
class SharedMaterialArray {
_renderer;
_targets = [];
_indexMapMaxIndex;
_indexMap;
_changed = false;
get changed() {
return this._changed;
}
set changed(value) {
if (value === true) {
if (debugRenderer)
console.warn("SharedMaterials have changed: " + this._renderer.name, this);
}
this._changed = value;
}
is(renderer) {
return this._renderer === renderer;
}
constructor(renderer, originalMaterials) {
this._renderer = renderer;
const setMaterial = this.setMaterial.bind(this);
const getMaterial = this.getMaterial.bind(this);
const go = renderer.gameObject;
this._targets = [];
if (go) {
switch (go.type) {
case "Group":
this._targets = [...go.children];
break;
case "SkinnedMesh":
case "Mesh":
this._targets.push(go);
break;
}
}
// this is useful to have an index map when e.g. materials are trying to be assigned by index
let hasMissingMaterials = false;
let indexMap = undefined;
let maxIndex = 0;
for (let i = 0; i < this._targets.length; i++) {
const target = this._targets[i];
if (!target)
continue;
const mat = target.material;
if (!mat)
continue;
// set the shadow side to the same as the side of the material, three flips this for some reason
mat.shadowSide = mat.side;
for (let k = 0; k < originalMaterials.length; k++) {
const orig = originalMaterials[k];
if (!orig) {
hasMissingMaterials = true;
continue;
}
if (mat.name === orig.name) {
if (indexMap === undefined)
indexMap = new Map();
indexMap.set(k, i);
maxIndex = Math.max(maxIndex, k);
// console.log(`Material ${mat.name} at ${k} was found at index ${i} in renderer ${renderer.name}.`)
break;
}
}
}
if (hasMissingMaterials) {
this._indexMapMaxIndex = maxIndex;
this._indexMap = indexMap;
const warningMessage = `Renderer ${renderer.name} was initialized with missing materials - this may lead to unexpected behaviour when trying to access sharedMaterials by index.`;
console.warn(warningMessage);
if (isLocalNetwork())
showBalloonWarning("Found renderer with missing materials: please check the console for details.");
}
// this lets us override the javascript indexer, only works in ES6 tho
// but like that we can use sharedMaterials[index] and it will be assigned to the object directly
return new Proxy(this, {
get(target, key) {
if (typeof key === "string") {
const index = parseInt(key);
if (!isNaN(index)) {
return getMaterial(index);
}
}
return target[key];
},
set(target, key, value) {
if (typeof key === "string")
setMaterial(value, Number.parseInt(key));
// console.log(target, key, value);
if (Reflect.set(target, key, value)) {
if (value instanceof Material)
target.changed = true;
return true;
}
return false;
}
});
}
get length() {
if (this._indexMapMaxIndex !== undefined)
return this._indexMapMaxIndex + 1;
return this._targets.length;
}
// iterator to support: for(const mat of sharedMaterials)
*[Symbol.iterator]() {
for (let i = 0; i < this.length; i++) {
yield this.getMaterial(i);
}
}
resolveIndex(index) {
const map = this._indexMap;
// if we have a index map it means that some materials were missing
if (map) {
if (map.has(index))
return map.get(index);
// return -1;
}
return index;
}
setMaterial(mat, index) {
index = this.resolveIndex(index);
if (index < 0 || index >= this._targets.length)
return;
const target = this._targets[index];
if (!target || target["material"] === undefined)
return;
target["material"] = mat;
this.changed = true;
}
getMaterial(index) {
index = this.resolveIndex(index);
if (index < 0)
return null;
const obj = this._targets;
if (index >= obj.length)
return null;
const target = obj[index];
if (!target)
return null;
return target["material"];
}
}
/**
* @category Rendering
* @group Components
*/
export class Renderer extends Behaviour {
/** Enable or disable instancing for an object. This will create a Renderer component if it does not exist yet.
* @returns the Renderer component that was created or already existed on the object
*/
static setInstanced(obj, enableInstancing) {
const renderer = getOrAddComponent(obj, Renderer);
renderer.setInstancingEnabled(enableInstancing);
return renderer;
}
/** Check if an object is currently rendered using instancing
* @returns true if the object is rendered using instancing
*/
static isInstanced(obj) {
const renderer = getComponent(obj, Renderer);
if (renderer)
return renderer.isInstancingActive;
return InstancingUtil.isUsingInstancing(obj);
}
/** Set the rendering state only of an object (makes it visible or invisible) without affecting component state or child hierarchy visibility! You can also just enable/disable the Renderer component on that object for the same effect!
*
* If you want to activate or deactivate a complete object you can use obj.visible as usual (it acts the same as setActive in Unity) */
static setVisible(obj, visible) {
setCustomVisibility(obj, visible);
}
receiveShadows = false;
shadowCastingMode = ShadowCastingMode.Off;
lightmapIndex = -1;
lightmapScaleOffset = new Vector4(1, 1, 0, 0);
/** If the renderer should use instancing
* If this is a boolean (true) all materials will be instanced or (false) none of them.
* If this is an array of booleans the materials will be instanced based on the index of the material.
*/
enableInstancing = undefined;
renderOrder = undefined;
allowOcclusionWhenDynamic = true;
probeAnchor;
reflectionProbeUsage = ReflectionProbeUsage.Off;
// custom shader
// get materialProperties(): Array<MaterialProperties> | undefined {
// return this._materialProperties;
// }
// set materialProperties(value: Array<MaterialProperties> | undefined) {
// this._materialProperties = value;
// }
// private customShaderHandler: RendererCustomShader | undefined = undefined;
// private _materialProperties: Array<MaterialProperties> | undefined = undefined;
_lightmaps;
/** Get the mesh Object3D for this renderer
* Warn: if this is a multimaterial object it will return the first mesh only
* @returns a mesh object3D.
* */
get sharedMesh() {
if (this.gameObject.type === "Mesh") {
return this.gameObject;
}
else if (this.gameObject.type === "SkinnesMesh") {
return this.gameObject;
}
else if (this.gameObject.type === "Group") {
return this.gameObject.children[0];
}
return undefined;
}
_sharedMeshes = [];
/** Get all the mesh Object3D for this renderer
* @returns an array of mesh object3D.
*/
get sharedMeshes() {
if (this.destroyed || !this.gameObject)
return this._sharedMeshes;
this._sharedMeshes.length = 0;
if (this.gameObject.type === "Group") {
for (const ch of this.gameObject.children) {
if (ch.type === "Mesh" || ch.type === "SkinnedMesh") {
this._sharedMeshes.push(ch);
}
}
}
else if (this.gameObject.type === "Mesh" || this.gameObject.type === "SkinnedMesh") {
this._sharedMeshes.push(this.gameObject);
}
return this._sharedMeshes;
}
get sharedMaterial() {
return this.sharedMaterials[0];
}
set sharedMaterial(mat) {
const cur = this.sharedMaterials[0];
if (cur === mat)
return;
this.sharedMaterials[0] = mat;
this.applyLightmapping();
}
/**@deprecated please use sharedMaterial */
get material() {
return this.sharedMaterials[0];
}
/**@deprecated please use sharedMaterial */
set material(mat) {
this.sharedMaterial = mat;
}
_sharedMaterials;
_originalMaterials;
_probeAnchorLastFrame;
// this is just available during deserialization
set sharedMaterials(_val) {
// TODO: elements in the array might be missing at the moment which leads to problems if an index is serialized
if (!this._originalMaterials) {
this._originalMaterials = _val;
}
else if (_val) {
let didWarn = false;
for (let i = 0; i < this._sharedMaterials.length; i++) {
const mat = i < _val.length ? _val[i] : null;
if (mat && mat instanceof Material) {
this.sharedMaterials[i] = mat;
}
else {
if (!didWarn) {
didWarn = true;
console.warn("Can not assign null as material: " + this.name, mat);
}
}
}
}
}
//@ts-ignore
get sharedMaterials() {
if (!this._sharedMaterials || !this._sharedMaterials.is(this)) {
if (!this._originalMaterials)
this._originalMaterials = [];
this._sharedMaterials = new SharedMaterialArray(this, this._originalMaterials);
}
return this._sharedMaterials;
}
static get shouldSuppressInstancing() {
return suppressInstancing;
}
_lightmapTextureOverride = undefined;
get lightmap() {
if (this._lightmaps?.length) {
return this._lightmaps[0].lightmap;
}
return null;
}
/** set undefined to return to default lightmap */
set lightmap(tex) {
this._lightmapTextureOverride = tex;
if (tex === undefined) {
tex = this.context.lightmaps.tryGetLightmap(this.sourceId, this.lightmapIndex);
}
if (this._lightmaps?.length) {
for (const lm of this._lightmaps) {
lm.lightmap = tex;
}
}
}
get hasLightmap() {
const lm = this.lightmap;
return lm !== null && lm !== undefined;
}
allowProgressiveLoading = true;
_firstFrame = -1;
registering() {
if (!this.enabled) {
this.setVisibility(false);
}
}
awake() {
this._firstFrame = this.context.time.frame;
if (debugRenderer)
console.log("Renderer ", this.name, this);
this.clearInstancingState();
if (this.probeAnchor && debugRenderer)
this.probeAnchor.add(new AxesHelper(.2));
this._reflectionProbe = null;
if (this.isMultiMaterialObject(this.gameObject)) {
for (const child of this.gameObject.children) {
this.context.addBeforeRenderListener(child, this.onBeforeRenderThree);
child.layers.mask = this.gameObject.layers.mask;
}
if (this.renderOrder !== undefined) {
// Objects can have nested renderers (e.g. contain 2 meshes and then again another group)
// or perhaps just regular child objects that have their own renderer component (?)
let index = 0;
for (let i = 0; i < this.gameObject.children.length; i++) {
const ch = this.gameObject.children[i];
// ignore nested groups or objects that have their own renderer (aka their own render order settings)
if (!this.isMeshOrSkinnedMesh(ch) || GameObject.getComponent(ch, Renderer))
continue;
if (this.renderOrder.length <= index) {
console.warn("Incorrect renderOrder element count", this, this.renderOrder.length + " but expected " + this.gameObject.children.length, "Index: " + index, "ChildElement:", ch);
continue;
}
// if(debugRenderer) console.log("Setting render order", ch, this.renderOrder[index])
ch.renderOrder = this.renderOrder[index];
index += 1;
}
}
}
// TODO: custom shader with sub materials
else if (this.isMeshOrSkinnedMesh(this.gameObject)) {
this.context.addBeforeRenderListener(this.gameObject, this.onBeforeRenderThree);
if (this.renderOrder !== undefined && this.renderOrder.length > 0)
this.gameObject.renderOrder = this.renderOrder[0];
}
else {
this.context.addBeforeRenderListener(this.gameObject, this.onBeforeRenderThree);
}
this.applyLightmapping();
if (showWireframe) {
for (let i = 0; i < this.sharedMaterials.length; i++) {
const mat = this.sharedMaterials[i];
if (mat) {
mat.wireframe = true;
}
}
}
}
applyLightmapping() {
if (this.lightmapIndex >= 0) {
const type = this.gameObject.type;
// use the override lightmap if its not undefined
const tex = this._lightmapTextureOverride !== undefined
? this._lightmapTextureOverride
: this.context.lightmaps.tryGetLightmap(this.sourceId, this.lightmapIndex);
if (tex) {
if (!this._lightmaps)
this._lightmaps = [];
if (type === "Mesh") {
const mat = this.gameObject["material"];
if (!mat?.isMeshBasicMaterial) {
if (this._lightmaps.length <= 0) {
const rm = new RendererLightmap(this.gameObject, this.context);
this._lightmaps.push(rm);
}
const rm = this._lightmaps[0];
rm.init(this.lightmapIndex, this.lightmapScaleOffset, tex);
}
else {
if (mat)
console.warn("Lightmapping is not supported on MeshBasicMaterial", mat.name);
}
}
// for multi materials we need to loop through children
// and then we add a lightmap renderer component to each of them
else if (this.isMultiMaterialObject(this.gameObject) && this.sharedMaterials.length > 0) {
for (let i = 0; i < this.gameObject.children.length; i++) {
const child = this.gameObject.children[i];
if (!child["material"]?.isMeshBasicMaterial) {
let rm = undefined;
if (i >= this._lightmaps.length) {
rm = new RendererLightmap(child, this.context);
this._lightmaps.push(rm);
}
else
rm = this._lightmaps[i];
rm.init(this.lightmapIndex, this.lightmapScaleOffset, tex);
}
}
}
}
else {
if (debugRenderer)
console.warn("Lightmap not found", this.sourceId, this.lightmapIndex);
}
}
}
_isInstancingEnabled = false;
_handles = undefined;
/**
* @returns true if this renderer has instanced objects
*/
get isInstancingActive() {
return this._handles != undefined && this._handles.length > 0 && this._isInstancingEnabled;
}
/** @returns the instancing handles */
get instances() {
if (!this._handles || this._handles.length <= 0) {
return null;
}
this._handlesTempArray.length = 0;
if (this._handles) {
for (const h of this._handles) {
this._handlesTempArray.push(h);
}
}
return this._handlesTempArray;
}
_handlesTempArray = [];
/** Enable or disable instancing for this renderer.
* @param enabled true to enable instancing, false to disable it
*/
setInstancingEnabled(enabled) {
if (this._isInstancingEnabled === enabled)
return enabled && (this._handles === undefined || this._handles != null && this._handles.length > 0);
this._isInstancingEnabled = enabled;
if (enabled) {
if (this.enableInstancing === undefined)
this.enableInstancing = true;
if (this._handles === undefined) {
this._handles = InstancingHandler.instance.setup(this, this.gameObject, this.context, null, { rend: this, foundMeshes: 0, useMatrixWorldAutoUpdate: this.useInstanceMatrixWorldAutoUpdate() });
if (this._handles) {
GameObject.markAsInstancedRendered(this.gameObject, true);
return true;
}
}
else if (this._handles !== null) {
for (const handler of this._handles) {
handler.updateInstanceMatrix(true);
handler.add();
}
GameObject.markAsInstancedRendered(this.gameObject, true);
return true;
}
}
else {
if (this._handles) {
for (const handler of this._handles) {
handler.remove(this.destroyed);
}
}
return true;
}
return false;
}
clearInstancingState() {
this._isInstancingEnabled = false;
this._handles = undefined;
}
/** Return true to wrap matrix update events for instanced rendering to update instance matrices automatically when matrixWorld changes
* This is a separate method to be overrideable from user code
*/
useInstanceMatrixWorldAutoUpdate() {
return true;
}
start() {
if (this.enableInstancing && !suppressInstancing) {
this.setInstancingEnabled(true);
// make sure the instance is marked dirty once for cases where e.g. an animator animates the instanced object
// in the first frame we want the updated matrix then to be applied immediately to the instancing
InstancingUtil.markDirty(this.gameObject);
}
this.gameObject.frustumCulled = this.allowOcclusionWhenDynamic;
if (this.isMultiMaterialObject(this.gameObject)) {
for (let i = 0; i < this.gameObject.children.length; i++) {
const ch = this.gameObject.children[i];
ch.frustumCulled = this.allowOcclusionWhenDynamic;
}
}
}
onEnable() {
// ensure shared meshes are initialized
const _ = this.sharedMeshes;
this.setVisibility(true);
// Check if the renderer is using instancing (or any child object is supposed to use instancing)
const isUsingInstancing = this._isInstancingEnabled ||
(this.enableInstancing == true || (Array.isArray(this.enableInstancing) && this.enableInstancing.some(x => x)));
if (isUsingInstancing) {
if (this.__internalDidAwakeAndStart)
this.setInstancingEnabled(true);
}
// if no insancing is used we can apply the stencil settings
// but instancing and stencil at the same time is not supported
else if (this.enabled) {
this.applyStencil();
}
this.updateReflectionProbe();
// this.testIfLODLevelsAreAvailable();
}
onDisable() {
this.setVisibility(false);
if (this._handles && this._handles.length > 0) {
this.setInstancingEnabled(false);
}
}
onDestroy() {
this._handles = null;
if (this.isMultiMaterialObject(this.gameObject)) {
for (const child of this.gameObject.children) {
this.context.removeBeforeRenderListener(child, this.onBeforeRenderThree);
}
}
else {
this.context.removeBeforeRenderListener(this.gameObject, this.onBeforeRenderThree);
}
}
onBeforeRender() {
if (!this.gameObject) {
return;
}
if (this._probeAnchorLastFrame !== this.probeAnchor) {
this._reflectionProbe?.onUnset(this);
this.updateReflectionProbe();
}
if (debugRenderer == this.name && this.gameObject instanceof Mesh) {
this.gameObject.geometry.computeBoundingSphere();
const tempCenter = getTempVector(this.gameObject.geometry.boundingSphere.center).applyMatrix4(this.gameObject.matrixWorld);
Gizmos.DrawWireSphere(tempCenter, this.gameObject.geometry.boundingSphere.radius, 0x00ddff);
}
if (this.isMultiMaterialObject(this.gameObject) && this.gameObject.children?.length > 0) {
for (const ch of this.gameObject.children) {
this.applySettings(ch);
}
}
else {
this.applySettings(this.gameObject);
}
if (this.sharedMaterials.changed) {
this.sharedMaterials.changed = false;
this.applyLightmapping();
}
if (this._handles?.length) {
// if (this.name === "Darbouka")
// console.log(this.name, this.gameObject.matrixWorldNeedsUpdate);
const needsUpdate = this.gameObject[NEED_UPDATE_INSTANCE_KEY] === true; // || this.gameObject.matrixWorldNeedsUpdate;
if (needsUpdate) {
// if (debugInstancing) console.log("UPDATE INSTANCED MATRICES at frame #" + this.context.time.frame);
this.gameObject[NEED_UPDATE_INSTANCE_KEY] = false;
const remove = false; // Math.random() < .01;
for (let i = this._handles.length - 1; i >= 0; i--) {
const h = this._handles[i];
if (remove) {
h.remove(this.destroyed);
this._handles.splice(i, 1);
}
else
h.updateInstanceMatrix();
}
this.gameObject.matrixWorldNeedsUpdate = false;
}
}
if (this._handles && this._handles.length <= 0) {
GameObject.markAsInstancedRendered(this.gameObject, false);
}
if (this._isInstancingEnabled && this._handles) {
for (let i = 0; i < this._handles.length; i++) {
const handle = this._handles[i];
setCustomVisibility(handle.object, false);
}
}
if (this.reflectionProbeUsage !== ReflectionProbeUsage.Off && this._reflectionProbe) {
this._reflectionProbe.onSet(this);
}
// since three 163 we need to set the envMap to the scene envMap if it is not set
// otherwise the envmapIntensity has no effect: https://github.com/mrdoob/three.js/pull/27903
// internal issue: https://linear.app/needle/issue/NE-6363
for (const mat of this._sharedMaterials) {
// If the material has a envMap and is NOT using a reflection probe we set the envMap to the scene environment
if (mat && "envMap" in mat && "envMapIntensity" in mat && !ReflectionProbe.isUsingReflectionProbe(mat)) {
mat.envMap = this.context.scene.environment;
}
}
}
onBeforeRenderThree = (_renderer, _scene, _camera, _geometry, material, _group) => {
if (material.envMapIntensity !== undefined) {
const factor = this.hasLightmap ? Math.PI : 1;
const environmentIntensity = this.context.mainCameraComponent?.environmentIntensity ?? 1;
material.envMapIntensity = Math.max(0, environmentIntensity * this.context.sceneLighting.environmentIntensity / factor);
}
if (this._lightmaps) {
for (const lm of this._lightmaps) {
lm.updateLightmapUniforms(material);
lm.applyLightmap();
}
}
};
onAfterRender() {
if (this._isInstancingEnabled && this._handles) {
for (let i = 0; i < this._handles.length; i++) {
const handle = this._handles[i];
setCustomVisibility(handle.object, true);
}
}
if (this.reflectionProbeUsage !== ReflectionProbeUsage.Off && this._reflectionProbe) {
this._reflectionProbe.onUnset(this);
}
if (this.static && this.gameObject.matrixAutoUpdate) {
this.gameObject.matrixAutoUpdate = false;
}
}
/** Applies stencil settings for this renderer's objects (if stencil settings are available) */
applyStencil() {
NEEDLE_render_objects.applyStencil(this);
}
/** Apply the settings of this renderer to the given object
* Settings include shadow casting and receiving (e.g. this.receiveShadows, this.shadowCastingMode)
*/
applySettings(go) {
go.receiveShadow = this.receiveShadows;
if (this.shadowCastingMode == ShadowCastingMode.On) {
go.castShadow = true;
}
else
go.castShadow = false;
}
_reflectionProbe = null;
updateReflectionProbe() {
// handle reflection probe
this._reflectionProbe = null;
if (this.reflectionProbeUsage !== ReflectionProbeUsage.Off) {
// update the reflection probe right before rendering
// if we do it immediately the reflection probe might not be enabled yet
// (since this method is called from onEnable)
this.startCoroutine(this._updateReflectionProbe(), FrameEvent.LateUpdate);
this._probeAnchorLastFrame = this.probeAnchor;
}
}
*_updateReflectionProbe() {
const obj = this.probeAnchor || this.gameObject;
const isAnchor = this.probeAnchor ? true : false;
this._reflectionProbe = ReflectionProbe.get(obj, this.context, isAnchor, this.probeAnchor);
}
setVisibility(visible) {
if (!this.isMultiMaterialObject(this.gameObject)) {
setCustomVisibility(this.gameObject, visible);
}
else {
for (const ch of this.gameObject.children) {
if (this.isMeshOrSkinnedMesh(ch)) {
setCustomVisibility(ch, visible);
}
}
}
}
isMultiMaterialObject(obj) {
return obj.type === "Group";
}
isMeshOrSkinnedMesh(obj) {
return obj.type === "Mesh" || obj.type === "SkinnedMesh";
}
}
__decorate([
serializable()
], Renderer.prototype, "receiveShadows", void 0);
__decorate([
serializable()
], Renderer.prototype, "shadowCastingMode", void 0);
__decorate([
serializable()
], Renderer.prototype, "lightmapIndex", void 0);
__decorate([
serializable(Vector4)
], Renderer.prototype, "lightmapScaleOffset", void 0);
__decorate([
serializable()
], Renderer.prototype, "enableInstancing", void 0);
__decorate([
serializable()
], Renderer.prototype, "renderOrder", void 0);
__decorate([
serializable()
], Renderer.prototype, "allowOcclusionWhenDynamic", void 0);
__decorate([
serializable(Object3D)
], Renderer.prototype, "probeAnchor", void 0);
__decorate([
serializable()
], Renderer.prototype, "reflectionProbeUsage", void 0);
export class MeshRenderer extends Renderer {
}
export class SkinnedMeshRenderer extends MeshRenderer {
_needUpdateBoundingSphere = false;
// private _lastWorldPosition = new Vector3();
awake() {
super.awake();
if (debugskinnedmesh)
console.log("SkinnedMeshRenderer for \"" + this.name + "\"", this);
// disable skinned mesh occlusion because of https://github.com/mrdoob/js/issues/14499
this.allowOcclusionWhenDynamic = false;
for (const mesh of this.sharedMeshes) {
// If we don't do that here the bounding sphere matrix used for raycasts will be wrong. Not sure *why* this is necessary
mesh.parent?.updateWorldMatrix(false, true);
this.markBoundsDirty();
}
}
onAfterRender() {
super.onAfterRender();
// this.gameObject.parent.position.x += Math.sin(this.context.time.time) * .01;
// if (this.gameObject instanceof SkinnedMesh && this.gameObject.geometry.boundingSphere) {
// const bounds = this.gameObject.geometry.boundingSphere;
// const worldpos = getTempVector().setFromMatrixPosition(this.gameObject.matrixWorld);
// if (worldpos.distanceTo(this._lastWorldPosition) > bounds.radius) {
// this._lastWorldPosition.copy(worldpos);
// this.markBoundsDirty();
// };
// }
if (this._needUpdateBoundingSphere) {
for (const mesh of this.sharedMeshes) {
if (mesh instanceof SkinnedMesh) {
this._needUpdateBoundingSphere = false;
try {
const geometry = mesh.geometry;
const raycastmesh = getRaycastMesh(mesh);
if (raycastmesh) {
mesh.geometry = raycastmesh;
}
mesh.computeBoundingSphere();
mesh.geometry = geometry;
}
catch (err) {
console.error(`Error updating bounding sphere for ${mesh.name}`, err);
}
}
}
}
// if (this.context.time.frame % 30 === 0) this.markBoundsDirty();
if (debugskinnedmesh) {
for (const mesh of this.sharedMeshes) {
if (mesh instanceof SkinnedMesh && mesh.boundingSphere) {
const tempCenter = getTempVector(mesh.boundingSphere.center).applyMatrix4(mesh.matrixWorld);
Gizmos.DrawWireSphere(tempCenter, mesh.boundingSphere.radius, "red");
}
}
}
}
markBoundsDirty() {
this._needUpdateBoundingSphere = true;
}
}
export var ShadowCastingMode;
(function (ShadowCastingMode) {
/// <summary>
/// <para>No shadows are cast from this object.</para>
/// </summary>
ShadowCastingMode[ShadowCastingMode["Off"] = 0] = "Off";
/// <summary>
/// <para>Shadows are cast from this object.</para>
/// </summary>
ShadowCastingMode[ShadowCastingMode["On"] = 1] = "On";
/// <summary>
/// <para>Shadows are cast from this object, treating it as two-sided.</para>
/// </summary>
ShadowCastingMode[ShadowCastingMode["TwoSided"] = 2] = "TwoSided";
/// <summary>
/// <para>Object casts shadows, but is otherwise invisible in the Scene.</para>
/// </summary>
ShadowCastingMode[ShadowCastingMode["ShadowsOnly"] = 3] = "ShadowsOnly";
})(ShadowCastingMode || (ShadowCastingMode = {}));
//# sourceMappingURL=Renderer.js.map