I am not really able to go all day right now, but have managed to implement this except adaptive
. Am not too concerned if I am not posting perfect code, since in order to use as a sub-class requires the ability to have a mesh sub-class & AFAIK I am the only one doing embedded graphics.
Here is the entire scene class
module XXX {
export enum RenderLoopBehaviors { cpu_favored, gpu_favored, adaptive };
export class Scene extends BABYLON.Scene {
constructor(engine: BABYLON.Engine, options?: BABYLON.SceneOptions) {
super(engine, options);
// front run _evaluateActiveMeshes
this["_stockEvaluateActiveMeshes"] = this["_evaluateActiveMeshes"];
this["_evaluateActiveMeshes"] = this["_PRE_EvaluateActiveMeshes"];
}
private _loopBehavior = RenderLoopBehaviors.cpu_favored; // default for back compatablity
private _loopBehaviorState = this._loopBehavior; // current state, never really uses adaptive setting for long
public get loopBehavior() { return this._loopBehavior; }
public set loopBehavior(val : RenderLoopBehaviors) {
this._loopBehavior = val;
this._loopBehaviorState = this._loopBehavior;
if (this._loopBehaviorState !== RenderLoopBehaviors.cpu_favored) {
this.freezeActiveMeshes(false, null, null, false);
} else {
this.unfreezeActiveMeshes();
}
}
public get loopBehaviorState() { return this._loopBehaviorState; }
// a mesh was added / (dis)enabled / layer mask set / changed from - to visibility == 0
public activeMeshesOutOfDate = true;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/**
* @override
* This is near identical to stock, but since it calls _evaluateActiveMeshes()
* that line must be changed. Also, setting alwaysSelectAsActiveMesh = true
*/
public freezeActiveMeshes(skipEvaluateActiveMeshes = false, onSuccess?: () => void, onError?: (message: string) => void, freezeMeshes = true): Scene {
this.executeWhenReady(() => {
if (!this.activeCamera) {
onError && onError('No active camera found');
return;
}
if (!this._frustumPlanes) {
this.setTransformMatrix(this.activeCamera.getViewMatrix(), this.activeCamera.getProjectionMatrix());
}
// this must occur BEFORE running eval
for (const mesh of this.meshes) {
mesh.alwaysSelectAsActiveMesh = true;
}
this["_stockEvaluateActiveMeshes"](); // calling the stock version
this._activeMeshesFrozen = true;
this["skipEvaluateActiveMeshesCompletely"] = skipEvaluateActiveMeshes;
if (freezeMeshes) {
const actives = <BABYLON.SmartArray<BABYLON.AbstractMesh>> this["_activeMeshes"];
for (var index = 0; index < actives.length; index++) {
actives.data[index]._freeze();
}
}
onSuccess && onSuccess();
});
return this;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/**
* @override
* Setting alwaysSelectAsActiveMesh = false
*/
public unfreezeActiveMeshes(): BABYLON.Scene {
for (const mesh of this.meshes) {
mesh.alwaysSelectAsActiveMesh = false;
}
return super.unfreezeActiveMeshes();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
private _PRE_EvaluateActiveMeshes() : void {
// in gpu mode & meshes out of date
if (this._loopBehaviorState !== RenderLoopBehaviors.cpu_favored && this.activeMeshesOutOfDate) {
this._activeMeshesFrozen = false; // quicker than actually bothering to unfreeze
this.freezeActiveMeshes(false, null, null, false); // already does an eval call
this.activeMeshesOutOfDate = false;
// in cpu mode or meshes still up to date
} else {
this["_stockEvaluateActiveMeshes"]();
}
}
}
}
Here are the relevant adds to my mesh sub-class
constructor(name: string, scene: BABYLON.Scene, parent: BABYLON.Node = null, source?: Mesh, doNotCloneChildren?: boolean) {
super(name, scene, parent, source, doNotCloneChildren);
// still allow for use of the stock scene class
if (scene instanceof XXX.Scene) {
this.onMeshReadyObservable.add(() => { scene.activeMeshesOutOfDate = true; } );
}
. . .
}
// ============================ support overrides for RenderLoop =============================
/**
* @override
*/
public setEnabled(value : boolean) : void {
super.setEnabled(value);
const scene = this.getScene();
if (scene instanceof XXX.Scene) scene.activeMeshesOutOfDate = true;
}
/**
* @override
*/
public set layerMask(value: number) {
super.layerMask = value;
const scene = this.getScene();
if (scene instanceof XXX.Scene) scene.activeMeshesOutOfDate = true;
}
/**
* @override
* If you overide a setter, you must also override the getter
*/
public get layerMask() : number {
return super.layerMask;
}
/**
* @override
*/
public set visibility(value: number) {
super.visibility = value;
const scene = this.getScene();
if (scene instanceof XXX.Scene) scene.activeMeshesOutOfDate = true;
}
/**
* @override
* If you overide a setter, you must also override the getter
*/
public get visibility() : number {
return super.visibility;
}
/**
* isVisible is a property, not a setter, so cannot override excactly.
* Implementing this as a PR should change this to a getter / setter.
*/
public setIsVisible(value : boolean) :void {
this.isVisible = value;
const scene = this.getScene();
if (scene instanceof XXX.Scene) scene.activeMeshesOutOfDate = true;
}
In coming days, I will do some more testing. Probably not implement adaptive for my own needs unless I determine I need it myself. Will put any code changes discovered in as edits.