Skip to content

Commit

Permalink
resolve named textures using final merged scene
Browse files Browse the repository at this point in the history
textures referenced as global properties inside imported scenes now resolve against the base scene path
  • Loading branch information
bcamper committed Dec 5, 2020
1 parent b6ea8bc commit 4ea5523
Show file tree
Hide file tree
Showing 4 changed files with 181 additions and 101 deletions.
37 changes: 27 additions & 10 deletions src/scene/globals.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,33 @@
import log from '../utils/log';
import { getPropertyPathTarget } from '../utils/props';

// prefix used to identify global property references
const GLOBAL_PREFIX = 'global.';
const GLOBAL_PREFIX_LENGTH = GLOBAL_PREFIX.length;

// name of 'hidden' (non-enumerable) property used to track global property references on an object
const GLOBAL_REGISTRY = '__global_prop';

// Property name references a global property?
export function isGlobalReference (val) {
if (val && val.slice(0, 7) === 'global.') {
if (val && val.slice(0, GLOBAL_PREFIX_LENGTH) === GLOBAL_PREFIX) {
return true;
}
return false;
}

// Has object property been substitued with a value from a global reference?
// Property provided as a single-depth string name, or nested path array (`a.b.c` => ['a', 'b', 'c'])
export function isGlobalSubstitution (object, prop_or_path) {
const path = Array.isArray(prop_or_path) ? prop_or_path : [prop_or_path];
const target = getPropertyPathTarget(object, path);
const prop = path[path.length - 1];
return target?.[GLOBAL_REGISTRY]?.[prop] !== undefined;
}

// Flatten nested global properties for simpler string look-ups
export function flattenGlobalProperties (obj, prefix = null, globals = {}) {
prefix = prefix ? (prefix + '.') : 'global.';
prefix = prefix ? (prefix + '.') : GLOBAL_PREFIX;

for (const p in obj) {
const key = prefix + p;
Expand All @@ -29,26 +46,26 @@ export function applyGlobalProperties (globals, obj, target, key) {
let prop;

// Check for previously applied global substitution
if (target != null && typeof target === 'object' && target._global_prop && target._global_prop[key]) {
prop = target._global_prop[key];
if (target?.[GLOBAL_REGISTRY]?.[key]) {
prop = target[GLOBAL_REGISTRY][key];
}
// Check string for new global substitution
else if (typeof obj === 'string' && obj.slice(0, 7) === 'global.') {
else if (typeof obj === 'string' && obj.slice(0, GLOBAL_PREFIX_LENGTH) === GLOBAL_PREFIX) {
prop = obj;
}

// Found global property to substitute
if (prop) {
// Mark property as global substitution
if (target._global_prop == null) {
Object.defineProperty(target, '_global_prop', { value: {} });
if (target[GLOBAL_REGISTRY] == null) {
Object.defineProperty(target, GLOBAL_REGISTRY, { value: {} });
}
target._global_prop[key] = prop;
target[GLOBAL_REGISTRY][key] = prop;

// Get current global value
let val = globals[prop];
let stack;
while (typeof val === 'string' && val.slice(0, 7) === 'global.') {
while (typeof val === 'string' && val.slice(0, GLOBAL_PREFIX_LENGTH) === GLOBAL_PREFIX) {
// handle globals that refer to other globals, detecting any cyclical references
stack = stack || [prop];
if (stack.indexOf(val) > -1) {
Expand All @@ -68,7 +85,7 @@ export function applyGlobalProperties (globals, obj, target, key) {
},
set: function (v) {
// clear the global substitution and remove the getter/setter
delete target._global_prop[key];
delete target[GLOBAL_REGISTRY][key];
delete target[key];
target[key] = v; // save the new value
}
Expand Down
47 changes: 26 additions & 21 deletions src/scene/scene.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ export default class Scene {
// Options:
// `base_path`: base URL against which scene resources should be resolved (useful for Play) (default nulll)
// `blocking`: should rendering block on scene load completion (default true)
load(config_source = null, options = {}) {
load (config_source = null, options = {}) {
if (this.initializing) {
return this.initializing;
}
Expand All @@ -140,8 +140,9 @@ export default class Scene {
this.createCanvas();
this.prev_textures = this.config && Object.keys(this.config.textures); // save textures from last scene
this.initializing = this.loadScene(config_source, options)
.then(() => this.createWorkers())
.then(() => {
.then(async ({ texture_nodes }) => {
await this.createWorkers();

// Clean up resources from prior scene
this.destroyFeatureSelection();
WorkerBroker.postMessage(this.workers, 'self.clearFunctionStringCache');
Expand All @@ -150,11 +151,17 @@ export default class Scene {
// which need to be serialized, while one loaded only from a URL does not.
const serialize_funcs = ((typeof this.config_source === 'object') || this.hasSubscribersFor('load'));

const updating = this.updateConfig({ serialize_funcs, normalize: false, loading: true, fade_in: true });
const updating = this.updateConfig({
texture_nodes,
serialize_funcs,
normalize: false,
loading: true,
fade_in: true });

if (options.blocking === true) {
return updating;
await updating;
}
}).then(() => {

this.freePreviousTextures();
this.updating--;
this.initializing = null;
Expand Down Expand Up @@ -980,7 +987,7 @@ export default class Scene {
Load (or reload) the scene config
@return {Promise}
*/
loadScene(config_source = null, { base_path, file_type } = {}) {
async loadScene(config_source = null, { base_path, file_type } = {}) {
this.config_source = config_source || this.config_source;

if (typeof this.config_source === 'string') {
Expand All @@ -994,11 +1001,13 @@ export default class Scene {
// TODO: schedule for deprecation
this.config_path = this.base_path;

return SceneLoader.loadScene(this.config_source, { path: this.base_path, type: file_type }).then(({config, bundle}) => {
this.config = config;
this.config_bundle = bundle;
return this.config;
});
const { config, bundle, texture_nodes } = await SceneLoader.loadScene(
this.config_source,
{ path: this.base_path, type: file_type });

this.config = config;
this.config_bundle = bundle;
return { texture_nodes }; // pass along texture nodes for resolution after global property subtistution
}

// Add source to a scene, arguments `name` and `config` need to be provided:
Expand Down Expand Up @@ -1217,22 +1226,18 @@ export default class Scene {

// Update scene config, and optionally rebuild geometry
// rebuild can be boolean, or an object containing rebuild options to passthrough
updateConfig({ loading = false, rebuild = true, serialize_funcs, normalize = true, fade_in = false } = {}) {
updateConfig({ loading = false, rebuild = true, serialize_funcs, texture_nodes = {}, normalize = true, fade_in = false } = {}) {
this.generation = ++Scene.generation;
this.updating++;

// Apply globals, finalize textures and other resource paths if needed
this.config = SceneLoader.applyGlobalProperties(this.config);
if (normalize) {
// normalize whole scene
SceneLoader.normalize(this.config, this.config_bundle);
// normalize whole scene if requested - usually when user is making run-time updates to scene
SceneLoader.normalize(this.config, this.config_bundle, texture_nodes);
}
else {
// special handling for shader uniforms that are globals
SceneLoader.hoistStyleShaderUniformTextures(this.config, this.config_bundle, { include_globals: true });
SceneLoader.hoistTextureNodes(this.config, this.config_bundle, texture_nodes);

// just normalize top-level textures - necessary for adding base path to globals
SceneLoader.normalizeTextures(this.config, this.config_bundle);
}
this.trigger(loading ? 'load' : 'update', { config: this.config });

this.style_manager.init();
Expand Down
Loading

0 comments on commit 4ea5523

Please sign in to comment.