playcanvas
Version:
PlayCanvas WebGL game engine
112 lines (109 loc) • 5.01 kB
JavaScript
import { RefCountedObject } from '../core/ref-counted-object.js';
import { SkinInstance } from './skin-instance.js';
// Class used as an entry in the ref-counted skin instance cache
class SkinInstanceCachedObject extends RefCountedObject {
constructor(skin, skinInstance){
super();
this.skin = skin;
this.skinInstance = skinInstance;
}
}
// Pure static class, implementing the cache of skin instances used by render component.
class SkinInstanceCache {
static{
// map of SkinInstances allowing those to be shared between
// (specifically a single glb with multiple render components)
// It maps a rootBone to an array of SkinInstanceCachedObject
// this allows us to find if a skin instance already exists for a rootbone, and a specific skin
this._skinInstanceCache = new Map();
}
// function that logs out the state of the skin instances cache
static logCachedSkinInstances() {
console.log('CachedSkinInstances');
SkinInstanceCache._skinInstanceCache.forEach((array, rootBone)=>{
console.log(`${rootBone.name}: Array(${array.length})`);
for(let i = 0; i < array.length; i++){
console.log(` ${i}: RefCount ${array[i].refCount}`);
}
});
}
// returns cached or creates a skin instance for the skin and a rootBone, to be used by render component
// on the specified entity
static createCachedSkinInstance(skin, rootBone, entity) {
// try and get skin instance from the cache
let skinInst = SkinInstanceCache.getCachedSkinInstance(skin, rootBone);
// don't have skin instance for this skin
if (!skinInst) {
skinInst = new SkinInstance(skin);
skinInst.resolve(rootBone, entity);
// add it to the cache
SkinInstanceCache.addCachedSkinInstance(skin, rootBone, skinInst);
}
return skinInst;
}
// returns already created skin instance from skin, for use on the rootBone
// ref count of existing skinInstance is increased
static getCachedSkinInstance(skin, rootBone) {
let skinInstance = null;
// get an array of cached object for the rootBone
const cachedObjArray = SkinInstanceCache._skinInstanceCache.get(rootBone);
if (cachedObjArray) {
// find matching skin
const cachedObj = cachedObjArray.find((element)=>element.skin === skin);
if (cachedObj) {
cachedObj.incRefCount();
skinInstance = cachedObj.skinInstance;
}
}
return skinInstance;
}
// adds skin instance to the cache, and increases ref count on it
static addCachedSkinInstance(skin, rootBone, skinInstance) {
// get an array for the rootBone
let cachedObjArray = SkinInstanceCache._skinInstanceCache.get(rootBone);
if (!cachedObjArray) {
cachedObjArray = [];
SkinInstanceCache._skinInstanceCache.set(rootBone, cachedObjArray);
}
// find entry for the skin
let cachedObj = cachedObjArray.find((element)=>element.skin === skin);
if (!cachedObj) {
cachedObj = new SkinInstanceCachedObject(skin, skinInstance);
cachedObjArray.push(cachedObj);
}
cachedObj.incRefCount();
}
// removes skin instance from the cache. This decreases ref count, and when that reaches 0 it gets destroyed
static removeCachedSkinInstance(skinInstance) {
if (skinInstance) {
const rootBone = skinInstance.rootBone;
if (rootBone) {
// an array for boot bone
const cachedObjArray = SkinInstanceCache._skinInstanceCache.get(rootBone);
if (cachedObjArray) {
// actual skin instance
const cachedObjIndex = cachedObjArray.findIndex((element)=>element.skinInstance === skinInstance);
if (cachedObjIndex >= 0) {
// dec ref on the object
const cachedObj = cachedObjArray[cachedObjIndex];
cachedObj.decRefCount();
// last reference, needs to be destroyed
if (cachedObj.refCount === 0) {
cachedObjArray.splice(cachedObjIndex, 1);
// if the array is empty
if (!cachedObjArray.length) {
SkinInstanceCache._skinInstanceCache.delete(rootBone);
}
// destroy the skin instance
if (skinInstance) {
skinInstance.destroy();
cachedObj.skinInstance = null;
}
}
}
}
}
}
}
}
export { SkinInstanceCache };