UNPKG

playcanvas

Version:

PlayCanvas WebGL game engine

897 lines (894 loc) 35.5 kB
import { Debug, DebugHelper } from '../core/debug.js'; import { BoundingBox } from '../core/shape/bounding-box.js'; import { BoundingSphere } from '../core/shape/bounding-sphere.js'; import { BindGroup } from '../platform/graphics/bind-group.js'; import { UniformBuffer } from '../platform/graphics/uniform-buffer.js'; import { SHADERDEF_NOSHADOW, SHADERDEF_BATCH, SHADERDEF_SKIN, SHADERDEF_MORPH_POSITION, SHADERDEF_MORPH_NORMAL, SHADERDEF_MORPH_TEXTURE_BASED_INT, SHADERDEF_SCREENSPACE, SORTKEY_FORWARD, BLEND_NORMAL, BLEND_NONE, SHADERDEF_INSTANCING, MASK_AFFECT_LIGHTMAPPED, MASK_AFFECT_DYNAMIC, LAYER_WORLD, RENDERSTYLE_SOLID, SHADERDEF_UV0, SHADERDEF_UV1, SHADERDEF_VCOLOR, SHADERDEF_TANGENTS } from './constants.js'; import { GraphNode } from './graph-node.js'; import { getDefaultMaterial } from './materials/default-material.js'; import { LightmapCache } from './graphics/lightmap-cache.js'; import { DebugGraphics } from '../platform/graphics/debug-graphics.js'; import { hash32Fnv1a } from '../core/hash.js'; import { array } from '../core/array-utils.js'; /** * @import { BindGroupFormat } from '../platform/graphics/bind-group-format.js' * @import { Camera } from './camera.js' * @import { GSplatInstance } from './gsplat/gsplat-instance.js' * @import { GraphicsDevice } from '../platform/graphics/graphics-device.js' * @import { Material } from './materials/material.js' * @import { Mesh } from './mesh.js' * @import { MorphInstance } from './morph-instance.js' * @import { CameraShaderParams } from './camera-shader-params.js' * @import { Scene } from './scene.js' * @import { ScopeId } from '../platform/graphics/scope-id.js' * @import { Shader } from '../platform/graphics/shader.js' * @import { SkinInstance } from './skin-instance.js' * @import { StencilParameters } from '../platform/graphics/stencil-parameters.js' * @import { Texture } from '../platform/graphics/texture.js' * @import { UniformBufferFormat } from '../platform/graphics/uniform-buffer-format.js' * @import { Vec3 } from '../core/math/vec3.js' * @import { VertexBuffer } from '../platform/graphics/vertex-buffer.js' */ var id = 0; var _tmpAabb = new BoundingBox(); var _tempBoneAabb = new BoundingBox(); var _tempSphere = new BoundingSphere(); /** @type {Set<Mesh>} */ var _meshSet = new Set(); // internal array used to evaluate the hash for the shader instance var lookupHashes = new Uint32Array(4); /** * Internal data structure used to store data used by hardware instancing. */ class InstancingData { destroy() { if (this._destroyVertexBuffer) { var _this_vertexBuffer; (_this_vertexBuffer = this.vertexBuffer) == null ? undefined : _this_vertexBuffer.destroy(); } this.vertexBuffer = null; } /** * @param {number} numObjects - The number of objects instanced. */ constructor(numObjects){ /** @type {VertexBuffer|null} */ this.vertexBuffer = null; /** * True if the vertex buffer is destroyed when the mesh instance is destroyed. * * @type {boolean} */ this._destroyVertexBuffer = false; this.count = numObjects; } } /** * Internal helper class for storing the shader and related mesh bind group in the shader cache. */ class ShaderInstance { /** * Returns the mesh bind group for the shader. * * @param {GraphicsDevice} device - The graphics device. * @returns {BindGroup} - The mesh bind group. */ getBindGroup(device) { // create bind group if (!this.bindGroup) { var shader = this.shader; Debug.assert(shader); var bindGroupFormat = shader.meshBindGroupFormat; Debug.assert(bindGroupFormat); this.bindGroup = new BindGroup(device, bindGroupFormat); DebugHelper.setName(this.bindGroup, "MeshBindGroup_" + this.bindGroup.id); } return this.bindGroup; } /** * Returns the uniform buffer for the shader. * * @param {GraphicsDevice} device - The graphics device. * @returns {UniformBuffer} - The uniform buffer. */ getUniformBuffer(device) { // create uniform buffer if (!this.uniformBuffer) { var shader = this.shader; Debug.assert(shader); var ubFormat = shader.meshUniformBufferFormat; Debug.assert(ubFormat); this.uniformBuffer = new UniformBuffer(device, ubFormat, false); } return this.uniformBuffer; } destroy() { var _this_bindGroup, _this_uniformBuffer; (_this_bindGroup = this.bindGroup) == null ? undefined : _this_bindGroup.destroy(); this.bindGroup = null; (_this_uniformBuffer = this.uniformBuffer) == null ? undefined : _this_uniformBuffer.destroy(); this.uniformBuffer = null; } constructor(){ /** * A bind group storing mesh textures / samplers for the shader. but not the uniform buffer. * * @type {BindGroup|null} */ this.bindGroup = null; /** * A uniform buffer storing mesh uniforms for the shader. * * @type {UniformBuffer|null} */ this.uniformBuffer = null; } } /** * Callback used by {@link Layer} to calculate the "sort distance" for a {@link MeshInstance}, * which determines its place in the render order. * * @callback CalculateSortDistanceCallback * @param {MeshInstance} meshInstance - The mesh instance. * @param {Vec3} cameraPosition - The position of the camera. * @param {Vec3} cameraForward - The forward vector of the camera. */ /** * An instance of a {@link Mesh}. A single mesh can be referenced by many mesh instances that can * have different transforms and materials. * * @category Graphics */ class MeshInstance { /** * Sets the render style of the mesh instance. Can be: * * - {@link RENDERSTYLE_SOLID} * - {@link RENDERSTYLE_WIREFRAME} * - {@link RENDERSTYLE_POINTS} * * Defaults to {@link RENDERSTYLE_SOLID}. * * @type {number} */ set renderStyle(renderStyle) { this._renderStyle = renderStyle; this.mesh.prepareRenderState(renderStyle); } /** * Gets the render style of the mesh instance. * * @type {number} */ get renderStyle() { return this._renderStyle; } /** * Sets the graphics mesh being instanced. * * @type {Mesh} */ set mesh(mesh) { if (mesh === this._mesh) { return; } if (this._mesh) { this._mesh.decRefCount(); } this._mesh = mesh; if (mesh) { mesh.incRefCount(); } } /** * Gets the graphics mesh being instanced. * * @type {Mesh} */ get mesh() { return this._mesh; } /** * Sets the world space axis-aligned bounding box for this mesh instance. * * @type {BoundingBox} */ set aabb(aabb) { this._aabb = aabb; } /** * Gets the world space axis-aligned bounding box for this mesh instance. * * @type {BoundingBox} */ get aabb() { // use specified world space aabb if (!this._updateAabb) { return this._aabb; } // callback function returning world space aabb if (this._updateAabbFunc) { return this._updateAabbFunc(this._aabb); } // use local space override aabb if specified var localAabb = this._customAabb; var toWorldSpace = !!localAabb; // otherwise evaluate local aabb if (!localAabb) { localAabb = _tmpAabb; if (this.skinInstance) { // Initialize local bone AABBs if needed if (!this.mesh.boneAabb) { var morphTargets = this._morphInstance ? this._morphInstance.morph._targets : null; this.mesh._initBoneAabbs(morphTargets); } // evaluate local space bounds based on all active bones var boneUsed = this.mesh.boneUsed; var first = true; for(var i = 0; i < this.mesh.boneAabb.length; i++){ if (boneUsed[i]) { // transform bone AABB by bone matrix _tempBoneAabb.setFromTransformedAabb(this.mesh.boneAabb[i], this.skinInstance.matrices[i]); // add them up if (first) { first = false; localAabb.center.copy(_tempBoneAabb.center); localAabb.halfExtents.copy(_tempBoneAabb.halfExtents); } else { localAabb.add(_tempBoneAabb); } } } toWorldSpace = true; } else if (this.node._aabbVer !== this._aabbVer || this.mesh._aabbVer !== this._aabbMeshVer) { // local space bounding box - either from mesh or empty if (this.mesh) { localAabb.center.copy(this.mesh.aabb.center); localAabb.halfExtents.copy(this.mesh.aabb.halfExtents); } else { localAabb.center.set(0, 0, 0); localAabb.halfExtents.set(0, 0, 0); } // update local space bounding box by morph targets if (this.mesh && this.mesh.morph) { var morphAabb = this.mesh.morph.aabb; localAabb._expand(morphAabb.getMin(), morphAabb.getMax()); } toWorldSpace = true; this._aabbVer = this.node._aabbVer; this._aabbMeshVer = this.mesh._aabbVer; } } // store world space bounding box if (toWorldSpace) { this._aabb.setFromTransformedAabb(localAabb, this.node.getWorldTransform()); } return this._aabb; } /** * Clear the internal shader cache. * * @ignore */ clearShaders() { this._shaderCache.forEach((shaderInstance)=>{ shaderInstance.destroy(); }); this._shaderCache.clear(); } /** * Returns the shader instance for the specified shader pass and light hash that is compatible * with this mesh instance. * * @param {number} shaderPass - The shader pass index. * @param {number} lightHash - The hash value of the lights that are affecting this mesh instance. * @param {Scene} scene - The scene. * @param {CameraShaderParams} cameraShaderParams - The camera shader parameters. * @param {UniformBufferFormat} [viewUniformFormat] - The format of the view uniform buffer. * @param {BindGroupFormat} [viewBindGroupFormat] - The format of the view bind group. * @param {any} [sortedLights] - Array of arrays of lights. * @returns {ShaderInstance} - the shader instance. * @ignore */ getShaderInstance(shaderPass, lightHash, scene, cameraShaderParams, viewUniformFormat, viewBindGroupFormat, sortedLights) { var shaderDefs = this._shaderDefs; // unique hash for the required shader lookupHashes[0] = shaderPass; lookupHashes[1] = lightHash; lookupHashes[2] = shaderDefs; lookupHashes[3] = cameraShaderParams.hash; var hash = hash32Fnv1a(lookupHashes); // look up the cache var shaderInstance = this._shaderCache.get(hash); // cache miss in the shader cache of the mesh instance if (!shaderInstance) { var mat = this._material; // get the shader from the material shaderInstance = new ShaderInstance(); shaderInstance.shader = mat.variants.get(hash); shaderInstance.hashes = new Uint32Array(lookupHashes); // cache miss in the material variants if (!shaderInstance.shader) { var _this_mesh_vertexBuffer; // marker to allow us to see the source node for shader alloc DebugGraphics.pushGpuMarker(this.mesh.device, "Node: " + this.node.name); var shader = mat.getShaderVariant({ device: this.mesh.device, scene: scene, objDefs: shaderDefs, cameraShaderParams: cameraShaderParams, pass: shaderPass, sortedLights: sortedLights, viewUniformFormat: viewUniformFormat, viewBindGroupFormat: viewBindGroupFormat, vertexFormat: (_this_mesh_vertexBuffer = this.mesh.vertexBuffer) == null ? undefined : _this_mesh_vertexBuffer.format }); DebugGraphics.popGpuMarker(this.mesh.device); // add it to the material variants cache mat.variants.set(hash, shader); shaderInstance.shader = shader; } // add it to the mesh instance cache this._shaderCache.set(hash, shaderInstance); } Debug.call(()=>{ // due to a small number of shaders in the cache, and to avoid performance hit, we're not // handling the hash collision. This is very unlikely but still possible. Check and report // if it happens in the debug mode, allowing us to fix the issue. if (!array.equals(shaderInstance.hashes, lookupHashes)) { Debug.errorOnce('Hash collision in the shader cache for mesh instance. This is very unlikely but still possible. Please report this issue.'); } }); return shaderInstance; } /** * Sets the material used by this mesh instance. * * @type {Material} */ set material(material) { this.clearShaders(); var prevMat = this._material; // Remove the material's reference to this mesh instance if (prevMat) { prevMat.removeMeshInstanceRef(this); } this._material = material; if (material) { // Record that the material is referenced by this mesh instance material.addMeshInstanceRef(this); // update transparent flag based on material this.transparent = material.transparent; this.updateKey(); } } /** * Gets the material used by this mesh instance. * * @type {Material} */ get material() { return this._material; } set layer(layer) { this._layer = layer; this.updateKey(); } get layer() { return this._layer; } /** * @param {number} shaderDefs - The shader definitions to set. * @private */ _updateShaderDefs(shaderDefs) { if (shaderDefs !== this._shaderDefs) { this._shaderDefs = shaderDefs; this.clearShaders(); } } /** * Sets the callback to calculate sort distance. In some circumstances mesh instances are * sorted by a distance calculation to determine their rendering order. Set this callback to * override the default distance calculation, which gives the dot product of the camera forward * vector and the vector between the camera position and the center of the mesh instance's * axis-aligned bounding box. This option can be particularly useful for rendering transparent * meshes in a better order than the default. * * @type {CalculateSortDistanceCallback|null} */ set calculateSortDistance(calculateSortDistance) { this._calculateSortDistance = calculateSortDistance; } /** * Gets the callback to calculate sort distance. * * @type {CalculateSortDistanceCallback|null} */ get calculateSortDistance() { return this._calculateSortDistance; } set receiveShadow(val) { if (this._receiveShadow !== val) { this._receiveShadow = val; this._updateShaderDefs(val ? this._shaderDefs & -2 : this._shaderDefs | SHADERDEF_NOSHADOW); } } get receiveShadow() { return this._receiveShadow; } set batching(val) { this._updateShaderDefs(val ? this._shaderDefs | SHADERDEF_BATCH : this._shaderDefs & -16385); } get batching() { return (this._shaderDefs & SHADERDEF_BATCH) !== 0; } /** * Sets the skin instance managing skinning of this mesh instance. Set to null if skinning is * not used. * * @type {SkinInstance|null} */ set skinInstance(val) { this._skinInstance = val; this._updateShaderDefs(val ? this._shaderDefs | SHADERDEF_SKIN : this._shaderDefs & -3); this._setupSkinUpdate(); } /** * Gets the skin instance managing skinning of this mesh instance. * * @type {SkinInstance|null} */ get skinInstance() { return this._skinInstance; } /** * Sets the morph instance managing morphing of this mesh instance. Set to null if morphing is * not used. * * @type {MorphInstance|null} */ set morphInstance(val) { var // release existing _this__morphInstance; (_this__morphInstance = this._morphInstance) == null ? undefined : _this__morphInstance.destroy(); // assign new this._morphInstance = val; var shaderDefs = this._shaderDefs; shaderDefs = val && val.morph.morphPositions ? shaderDefs | SHADERDEF_MORPH_POSITION : shaderDefs & -1025; shaderDefs = val && val.morph.morphNormals ? shaderDefs | SHADERDEF_MORPH_NORMAL : shaderDefs & -2049; shaderDefs = val && val.morph.intRenderFormat ? shaderDefs | SHADERDEF_MORPH_TEXTURE_BASED_INT : shaderDefs & -8193; this._updateShaderDefs(shaderDefs); } /** * Gets the morph instance managing morphing of this mesh instance. * * @type {MorphInstance|null} */ get morphInstance() { return this._morphInstance; } set screenSpace(val) { if (this._screenSpace !== val) { this._screenSpace = val; this._updateShaderDefs(val ? this._shaderDefs | SHADERDEF_SCREENSPACE : this._shaderDefs & -257); } } get screenSpace() { return this._screenSpace; } set key(val) { this._key[SORTKEY_FORWARD] = val; } get key() { return this._key[SORTKEY_FORWARD]; } /** * Sets the mask controlling which {@link LightComponent}s light this mesh instance, which * {@link CameraComponent} sees it and in which {@link Layer} it is rendered. Defaults to 1. * * @type {number} */ set mask(val) { var toggles = this._shaderDefs & 0x0000FFFF; this._updateShaderDefs(toggles | val << 16); } /** * Gets the mask controlling which {@link LightComponent}s light this mesh instance, which * {@link CameraComponent} sees it and in which {@link Layer} it is rendered. * * @type {number} */ get mask() { return this._shaderDefs >> 16; } /** * Sets the number of instances when using hardware instancing to render the mesh. * * @type {number} */ set instancingCount(value) { if (this.instancingData) { this.instancingData.count = value; } } /** * Gets the number of instances when using hardware instancing to render the mesh. * * @type {number} */ get instancingCount() { return this.instancingData ? this.instancingData.count : 0; } destroy() { var _this__skinInstance, _this_morphInstance, _this_instancingData; var mesh = this.mesh; if (mesh) { // this decreases ref count on the mesh this.mesh = null; // destroy mesh if (mesh.refCount < 1) { mesh.destroy(); } } // release ref counted lightmaps this.setRealtimeLightmap(MeshInstance.lightmapParamNames[0], null); this.setRealtimeLightmap(MeshInstance.lightmapParamNames[1], null); (_this__skinInstance = this._skinInstance) == null ? undefined : _this__skinInstance.destroy(); this._skinInstance = null; (_this_morphInstance = this.morphInstance) == null ? undefined : _this_morphInstance.destroy(); this.morphInstance = null; this.clearShaders(); // make sure material clears references to this meshInstance this.material = null; (_this_instancingData = this.instancingData) == null ? undefined : _this_instancingData.destroy(); } /** * Sets the render style for an array of mesh instances. * * @param {MeshInstance[]} meshInstances - The mesh instances to set the render style for. * @param {number} renderStyle - The render style to set. * @ignore */ static _prepareRenderStyleForArray(meshInstances, renderStyle) { if (meshInstances) { for(var i = 0; i < meshInstances.length; i++){ // switch mesh instance to the requested style meshInstances[i]._renderStyle = renderStyle; // process all unique meshes var mesh = meshInstances[i].mesh; if (!_meshSet.has(mesh)) { _meshSet.add(mesh); mesh.prepareRenderState(renderStyle); } } _meshSet.clear(); } } /** * Test if meshInstance is visible by camera. It requires the frustum of the camera to be up to * date, which forward-renderer takes care of. This function should not be called elsewhere. * * @param {Camera} camera - The camera to test visibility against. * @returns {boolean} - True if the mesh instance is visible by the camera, false otherwise. * @ignore */ _isVisible(camera) { if (this.visible) { // custom visibility method of MeshInstance if (this.isVisibleFunc) { return this.isVisibleFunc(camera); } _tempSphere.center = this.aabb.center; // this line evaluates aabb _tempSphere.radius = this._aabb.halfExtents.length(); return camera.frustum.containsSphere(_tempSphere) > 0; } return false; } updateKey() { // render alphatest/atoc after opaque var material = this.material; var blendType = material.alphaToCoverage || material.alphaTest ? BLEND_NORMAL : material.blendType; // Key definition: // Bit // 31 : sign bit (leave) // 27 - 30 : layer // 26 : translucency type (opaque/transparent) // 25 : unused // 0 - 24 : Material ID (if opaque) or 0 (if transparent - will be depth) this._key[SORTKEY_FORWARD] = (this.layer & 0x0f) << 27 | (blendType === BLEND_NONE ? 1 : 0) << 26 | (material.id & 0x1ffffff) << 0; } /** * Sets up {@link MeshInstance} to be rendered using Hardware Instancing. * * @param {VertexBuffer|null} vertexBuffer - Vertex buffer to hold per-instance vertex data * (usually world matrices). Pass null to turn off hardware instancing. * @param {boolean} cull - Whether to perform frustum culling on this instance. If true, the whole * instance will be culled by the camera frustum. This often involves setting * {@link RenderComponent#customAabb} containing all instances. Defaults to false, which means * the whole instance is always rendered. */ setInstancing(vertexBuffer, cull) { if (cull === undefined) cull = false; if (vertexBuffer) { this.instancingData = new InstancingData(vertexBuffer.numVertices); this.instancingData.vertexBuffer = vertexBuffer; // mark vertex buffer as instancing data vertexBuffer.format.instancing = true; // set up culling this.cull = cull; } else { this.instancingData = null; this.cull = true; } this._updateShaderDefs(vertexBuffer ? this._shaderDefs | SHADERDEF_INSTANCING : this._shaderDefs & -33); } ensureMaterial(device) { if (!this.material) { Debug.warn("Mesh attached to entity '" + this.node.name + "' does not have a material, using a default one."); this.material = getDefaultMaterial(device); } } // Parameter management clearParameters() { this.parameters = {}; } getParameters() { return this.parameters; } /** * Retrieves the specified shader parameter from a mesh instance. * * @param {string} name - The name of the parameter to query. * @returns {object} The named parameter. */ getParameter(name) { return this.parameters[name]; } /** * Sets a shader parameter on a mesh instance. Note that this parameter will take precedence * over parameter of the same name if set on Material this mesh instance uses for rendering. * * @param {string} name - The name of the parameter to set. * @param {number|number[]|Texture|Float32Array} data - The value for the specified parameter. * @param {number} [passFlags] - Mask describing which passes the material should be included * in. Defaults to 0xFFFFFFFF (all passes). */ setParameter(name, data, passFlags) { if (passFlags === undefined) passFlags = 0xFFFFFFFF; var param = this.parameters[name]; if (param) { param.data = data; param.passFlags = passFlags; } else { this.parameters[name] = { scopeId: null, data: data, passFlags: passFlags }; } } /** * A wrapper over settings parameter specifically for realtime baked lightmaps. This handles * reference counting of lightmaps and releases them when no longer referenced. * * @param {string} name - The name of the parameter to set. * @param {Texture|null} texture - The lightmap texture to set. * @ignore */ setRealtimeLightmap(name, texture) { // no change var old = this.getParameter(name); if (old === texture) { return; } // remove old if (old) { LightmapCache.decRef(old.data); } // assign new if (texture) { LightmapCache.incRef(texture); this.setParameter(name, texture); } else { this.deleteParameter(name); } } /** * Deletes a shader parameter on a mesh instance. * * @param {string} name - The name of the parameter to delete. */ deleteParameter(name) { if (this.parameters[name]) { delete this.parameters[name]; } } /** * Used to apply parameters from this mesh instance into scope of uniforms, called internally * by forward-renderer. * * @param {GraphicsDevice} device - The graphics device. * @param {number} passFlag - The pass flag for the current render pass. * @ignore */ setParameters(device, passFlag) { var parameters = this.parameters; for(var paramName in parameters){ var parameter = parameters[paramName]; if (parameter.passFlags & passFlag) { if (!parameter.scopeId) { parameter.scopeId = device.scope.resolve(paramName); } parameter.scopeId.setValue(parameter.data); } } } /** * @param {boolean} value - True to enable lightmapped rendering, false to disable. * @ignore */ setLightmapped(value) { if (value) { this.mask = (this.mask | MASK_AFFECT_LIGHTMAPPED) & -6; } else { this.setRealtimeLightmap(MeshInstance.lightmapParamNames[0], null); this.setRealtimeLightmap(MeshInstance.lightmapParamNames[1], null); this._shaderDefs &= -4289; this.mask = (this.mask | MASK_AFFECT_DYNAMIC) & -7; } } /** * @param {BoundingBox|null} aabb - The custom axis-aligned bounding box or null to reset to * the mesh's bounding box. * @ignore */ setCustomAabb(aabb) { if (aabb) { // store the override aabb if (this._customAabb) { this._customAabb.copy(aabb); } else { this._customAabb = aabb.clone(); } } else { // no override, force refresh the actual one this._customAabb = null; this._aabbVer = -1; } this._setupSkinUpdate(); } /** @private */ _setupSkinUpdate() { // set if bones need to be updated before culling if (this._skinInstance) { this._skinInstance._updateBeforeCull = !this._customAabb; } } /** * Create a new MeshInstance instance. * * @param {Mesh} mesh - The graphics mesh to instance. * @param {Material} material - The material to use for this mesh instance. * @param {GraphNode} [node] - The graph node defining the transform for this instance. This * parameter is optional when used with {@link RenderComponent} and will use the node the * component is attached to. * @example * // Create a mesh instance pointing to a 1x1x1 'cube' mesh * const mesh = pc.Mesh.fromGeometry(app.graphicsDevice, new pc.BoxGeometry()); * const material = new pc.StandardMaterial(); * * const meshInstance = new pc.MeshInstance(mesh, material); * * const entity = new pc.Entity(); * entity.addComponent('render', { * meshInstances: [meshInstance] * }); * * // Add the entity to the scene hierarchy * this.app.scene.root.addChild(entity); */ constructor(mesh, material, node = null){ /** * Enable shadow casting for this mesh instance. Use this property to enable/disable shadow * casting without overhead of removing from scene. Note that this property does not add the * mesh instance to appropriate list of shadow casters on a {@link Layer}, but allows mesh to * be skipped from shadow casting while it is in the list already. Defaults to false. * * @type {boolean} */ this.castShadow = false; /** * Controls whether the mesh instance can be culled by frustum culling (see * {@link CameraComponent#frustumCulling}). Defaults to true. * * @type {boolean} */ this.cull = true; /** * Determines the rendering order of mesh instances. Only used when mesh instances are added to * a {@link Layer} with {@link Layer#opaqueSortMode} or {@link Layer#transparentSortMode} * (depending on the material) set to {@link SORTMODE_MANUAL}. * * @type {number} */ this.drawOrder = 0; /** * Enable rendering for this mesh instance. Use visible property to enable/disable rendering * without overhead of removing from scene. But note that the mesh instance is still in the * hierarchy and still in the draw call list. * * @type {boolean} */ this.visible = true; /** * Read this value in {@link Scene#EVENT_POSTCULL} event to determine if the object is actually * going to be rendered. * * @type {boolean} */ this.visibleThisFrame = false; /** * Negative scale batching support. * * @type {number} * @ignore */ this.flipFacesFactor = 1; /** * @type {GSplatInstance|null} * @ignore */ this.gsplatInstance = null; /** @ignore */ this.id = id++; /** * Custom function used to customize culling (e.g. for 2D UI elements). * * @type {Function|null} * @ignore */ this.isVisibleFunc = null; /** * @type {InstancingData|null} * @ignore */ this.instancingData = null; /** * @type {Record<string, {scopeId: ScopeId|null, data: any, passFlags: number}>} * @ignore */ this.parameters = {}; /** * True if the mesh instance is pickable by the {@link Picker}. Defaults to true. * * @type {boolean} * @ignore */ this.pick = true; /** * The stencil parameters for front faces or null if no stencil is enabled. * * @type {StencilParameters|null} * @ignore */ this.stencilFront = null; /** * The stencil parameters for back faces or null if no stencil is enabled. * * @type {StencilParameters|null} * @ignore */ this.stencilBack = null; /** * True if the material of the mesh instance is transparent. Optimization to avoid accessing * the material. Updated by the material instance itself. * * @ignore */ this.transparent = false; /** @private */ this._aabb = new BoundingBox(); /** @private */ this._aabbVer = -1; /** @private */ this._aabbMeshVer = -1; /** * @type {BoundingBox|null} * @private */ this._customAabb = null; /** @private */ this._updateAabb = true; /** @private */ this._updateAabbFunc = null; /** @private */ this._key = [ 0, 0 ]; /** @private */ this._layer = LAYER_WORLD; /** * @type {Material|null} * @private */ this._material = null; /** * @type {SkinInstance|null} * @private */ this._skinInstance = null; /** * @type {MorphInstance|null} * @private */ this._morphInstance = null; /** @private */ this._receiveShadow = true; /** @private */ this._renderStyle = RENDERSTYLE_SOLID; /** @private */ this._screenSpace = false; /** * The cache of shaders, indexed by a hash value. * * @type {Map<number, ShaderInstance>} * @private */ this._shaderCache = new Map(); /** * 2 byte toggles, 2 bytes light mask; Default value is no toggles and mask = pc.MASK_AFFECT_DYNAMIC * * @private */ this._shaderDefs = MASK_AFFECT_DYNAMIC << 16; /** * @type {CalculateSortDistanceCallback|null} * @private */ this._calculateSortDistance = null; Debug.assert(!(mesh instanceof GraphNode), 'Incorrect parameters for MeshInstance\'s constructor. Use new MeshInstance(mesh, material, node)'); this.node = node; // The node that defines the transform of the mesh instance this._mesh = mesh; // The mesh that this instance renders mesh.incRefCount(); this.material = material; // The material with which to render this instance if (mesh.vertexBuffer) { var format = mesh.vertexBuffer.format; this._shaderDefs |= format.hasUv0 ? SHADERDEF_UV0 : 0; this._shaderDefs |= format.hasUv1 ? SHADERDEF_UV1 : 0; this._shaderDefs |= format.hasColor ? SHADERDEF_VCOLOR : 0; this._shaderDefs |= format.hasTangents ? SHADERDEF_TANGENTS : 0; } // 64-bit integer key that defines render order of this mesh instance this.updateKey(); } } // shader uniform names for lightmaps MeshInstance.lightmapParamNames = [ 'texture_lightMap', 'texture_dirLightMap' ]; export { MeshInstance };