diff --git a/src/framework/components/render/component.js b/src/framework/components/render/component.js index d0b47aca093..53dc3adbf4d 100644 --- a/src/framework/components/render/component.js +++ b/src/framework/components/render/component.js @@ -12,7 +12,9 @@ import { AssetReference } from '../../asset/asset-reference.js'; import { Component } from '../component.js'; -import { EntityReference } from '../../utils/entity-reference.js'; +/** + * @import { Entity } from '../../entity.js' + */ /** * The RenderComponent enables an {@link Entity} to render 3D meshes. The {@link RenderComponent#type} @@ -54,9 +56,6 @@ import { EntityReference } from '../../utils/entity-reference.js'; * - [Primitive Shapes](https://playcanvas.github.io/#/graphics/shapes) * - [Loading Render Assets](https://playcanvas.github.io/#/graphics/render-asset) * - * @property {import('../../entity.js').Entity} rootBone A reference to the entity to be used as - * the root bone for any skinned meshes that are rendered by this component. - * * @category Graphics */ class RenderComponent extends Component { @@ -136,10 +135,13 @@ class RenderComponent extends Component { _material; /** - * @type {EntityReference} + * A reference to the entity to be used as the root bone for any skinned meshes that + * are rendered by this component. + * + * @type {Entity|null} * @private */ - _rootBone; + _rootBone = null; /** * @type {import('../../../core/event-handle.js').EventHandle|null} @@ -170,15 +172,12 @@ class RenderComponent extends Component { * * @param {import('./system.js').RenderComponentSystem} system - The ComponentSystem that * created this Component. - * @param {import('../../entity.js').Entity} entity - The Entity that this Component is - * attached to. + * @param {Entity} entity - The Entity that this Component is attached to. */ constructor(system, entity) { super(system, entity); // the entity that represents the root bone if this render component has skinned meshes - this._rootBone = new EntityReference(this, 'rootBone'); - this._rootBone.on('set:entity', this._onSetRootBone, this); // render asset reference this._assetReference = new AssetReference( @@ -275,7 +274,6 @@ class RenderComponent extends Component { * @type {string} */ set type(value) { - if (this._type !== value) { this._area = null; this._type = value; @@ -312,7 +310,6 @@ class RenderComponent extends Component { * @type {MeshInstance[]} */ set meshInstances(value) { - Debug.assert(Array.isArray(value), 'MeshInstances set to a Render component must be an array.'); this.destroyMeshInstances(); @@ -703,27 +700,49 @@ class RenderComponent extends Component { } /** - * @param {import('../../entity.js').Entity} entity - The entity set as the root bone. - * @private + * Sets the root bone entity (or entity guid) for the render component. + * + * @type {Entity|string|null} */ - _onSetRootBone(entity) { - if (entity) { - this._onRootBoneChanged(); + set rootBone(value) { + if (this._rootBone !== value) { + const isString = typeof value === 'string'; + if (this._rootBone && isString && this._rootBone.getGuid() === value) { + return; + } + + if (this._rootBone) { + this._clearSkinInstances(); + } + + if (value instanceof GraphNode) { + this._rootBone = value; + } else if (isString) { + this._rootBone = this.system.app.getEntityFromIndex(value) || null; + if (!this._rootBone) { + Debug.warn('Failed to find rootBone Entity by GUID'); + } + } else { + this._rootBone = null; + } + + if (this._rootBone) { + this._cloneSkinInstances(); + } } } - /** @private */ - _onRootBoneChanged() { - // remove existing skin instances and create new ones, connected to new root bone - this._clearSkinInstances(); - if (this.enabled && this.entity.enabled) { - this._cloneSkinInstances(); - } + /** + * Gets the root bone entity for the render component. + * + * @type {Entity|null} + */ + get rootBone() { + return this._rootBone; } /** @private */ destroyMeshInstances() { - const meshInstances = this._meshInstances; if (meshInstances) { this.removeFromLayers(); @@ -814,9 +833,9 @@ class RenderComponent extends Component { const scene = app.scene; const layers = scene.layers; - this._rootBone.onParentComponentEnable(); - - this._cloneSkinInstances(); + if (this._rootBone) { + this._cloneSkinInstances(); + } this._evtLayersChanged = scene.on('set:layers', this.onLayersChanged, this); @@ -852,6 +871,10 @@ class RenderComponent extends Component { this._evtLayersChanged?.off(); this._evtLayersChanged = null; + if (this._rootBone) { + this._clearSkinInstances(); + } + if (layers) { this._evtLayerAdded?.off(); this._evtLayerAdded = null; @@ -904,7 +927,6 @@ class RenderComponent extends Component { } _onRenderAssetLoad() { - // remove existing instances this.destroyMeshInstances(); @@ -923,7 +945,6 @@ class RenderComponent extends Component { } _clearSkinInstances() { - for (let i = 0; i < this._meshInstances.length; i++) { const meshInstance = this._meshInstances[i]; @@ -934,23 +955,20 @@ class RenderComponent extends Component { } _cloneSkinInstances() { - - if (this._meshInstances.length && this._rootBone.entity instanceof GraphNode) { - + if (this._meshInstances.length && this._rootBone instanceof GraphNode) { for (let i = 0; i < this._meshInstances.length; i++) { const meshInstance = this._meshInstances[i]; const mesh = meshInstance.mesh; // if skinned but does not have instance created yet if (mesh.skin && !meshInstance.skinInstance) { - meshInstance.skinInstance = SkinInstanceCache.createCachedSkinInstance(mesh.skin, this._rootBone.entity, this.entity); + meshInstance.skinInstance = SkinInstanceCache.createCachedSkinInstance(mesh.skin, this._rootBone, this.entity); } } } } _cloneMeshes(meshes) { - if (meshes && meshes.length) { // cloned mesh instances @@ -1031,10 +1049,9 @@ class RenderComponent extends Component { } resolveDuplicatedEntityReferenceProperties(oldRender, duplicatedIdsMap) { - if (oldRender.rootBone && duplicatedIdsMap[oldRender.rootBone]) { - this.rootBone = duplicatedIdsMap[oldRender.rootBone]; + if (oldRender.rootBone) { + this.rootBone = duplicatedIdsMap[oldRender.rootBone.getGuid()]; } - this._clearSkinInstances(); } } diff --git a/src/framework/components/render/data.js b/src/framework/components/render/data.js index 5ff36ded89e..53ac9f97c13 100644 --- a/src/framework/components/render/data.js +++ b/src/framework/components/render/data.js @@ -1,7 +1,6 @@ class RenderComponentData { constructor() { this.enabled = true; - this.rootBone = null; } } diff --git a/src/framework/components/render/system.js b/src/framework/components/render/system.js index 12688a94cb1..df3d4484cc6 100644 --- a/src/framework/components/render/system.js +++ b/src/framework/components/render/system.js @@ -11,7 +11,6 @@ import { RenderComponent } from './component.js'; import { RenderComponentData } from './data.js'; const _schema = [ - { name: 'rootBone', type: 'entity' }, 'enabled' ]; @@ -30,7 +29,8 @@ const _properties = [ 'type', 'layers', 'isStatic', - 'batchGroupId' + 'batchGroupId', + 'rootBone' ]; /**