linking system

beta.r3js.org
Theunis J. Botha 2017-06-27 13:27:27 +02:00
parent e725fb4694
commit 9b89113ed9
6 changed files with 990 additions and 915 deletions

View File

@ -27,7 +27,6 @@ GameLib.Event.SAVE_COMPONENT_ERROR = 0xa;
GameLib.Event.COMPONENT_SAVED = 0xb;
GameLib.Event.LOAD_COMPONENT = 0xc;
GameLib.Event.LOAD_COMPONENT_ERROR = 0xd;
GameLib.Event.COMPONENT_LOADED = 0xe;
GameLib.Event.LOGGED_IN = 0xf;
GameLib.Event.COMPONENT_CREATED = 0x10;
GameLib.Event.SCENE_INSTANCE_CREATED = 0x11;
@ -42,6 +41,7 @@ GameLib.Event.MESH_INSTANCE_CREATED = 0x19;
GameLib.Event.MESH_INSTANCE_UPDATED = 0x1a;
GameLib.Event.LIGHT_INSTANCE_CREATED = 0x1b;
GameLib.Event.LIGHT_INSTANCE_UPDATED = 0x1c;
GameLib.Event.DELETE_COMPONENT = 0x1d;
/**
* Subscribe to some events

View File

@ -39,17 +39,20 @@ GameLib.Component = function(
this.dependencies = this.getDependencies();
this.publish(
GameLib.Event.COMPONENT_CREATED,
{
component : this
}
);
if (this.dependencies.length === 0) {
delete this.dependencies;
this.loaded = true;
this.instance = this.createInstance();
}
};
GameLib.Component.prototype = Object.create(GameLib.API.Component.prototype);
GameLib.Component.prototype.constructor = GameLib.Component;
GameLib.Component.prototype.createInstance = function() {
};
GameLib.Component.prototype.getDependencies = function() {
var dependencies = [];
@ -241,108 +244,21 @@ GameLib.Component.prototype.buildIdToObject = function() {
this.built = false;
};
/**
* Some components need to be loaded first, and are referenced by ID string from other objects.
* These components should implement this 'linkObjects' function - this will be implementation specific.
* Example: Scene has meshes which have materials which have textures.
* However, we don't store the texture inside the material inside the mesh, since they are re-usable among meshes.
* Instead, we store them on the scene object, reference them by string id from the mesh, and after scene has loaded,
* we link the objects
*/
// GameLib.Component.prototype.linkObjects = function(idToObject) {
//
// if (this.loaded) {
// return;
// }
//
// this.loaded = true;
//
// for (var property in this.linkedObjects) {
// if (
// this.linkedObjects.hasOwnProperty(property) &&
// this.hasOwnProperty(property) &&
// this[property]
// ) {
//
// if (this.linkedObjects[property] instanceof Array) {
// if (this[property] instanceof Array) {
//
// this[property] = this[property].map(
// function(p) {
// if (p instanceof Object) {
//
// /**
// * This object is already an object, does not need to be linked
// */
// if (p.linkObjects) {
// p.linkObjects(idToObject);
// }
//
// return p;
// } else if (typeof p === 'string') {
//
// if (!idToObject[p]) {
// console.warn('Could not locate the object to be linked in array - fix this');
// throw new Error('Could not locate the object to be linked in array - fix this');
// }
//
// /**
// * Perform deep-linking
// */
// if (idToObject[p].linkObjects) {
// idToObject[p].linkObjects(idToObject);
// }
//
// return idToObject[p];
// } else {
// console.warn('Unhandled type : ', p);
// throw new Error('Unhandled type : ', p);
// }
//
// }.bind(this)
// )
// } else {
// console.warn('Incompatible Link Type - should be instance of array');
// throw new Error('Incompatible Link Type - should be instance of array');
// }
// } else {
//
// if (this[property] instanceof Object) {
//
// /**
// * This object is already an object, perform deep linking
// */
// if (this[property].linkObjects) {
// this[property].linkObjects(idToObject);
// }
//
// } else if (typeof this[property] === 'string') {
//
// if (!idToObject[this[property]]) {
// console.warn('Could not locate the object to be linked - fix this');
// throw new Error('Could not locate the object to be linked - fix this');
// }
//
// this[property] = idToObject[this[property]];
//
// //this[property].parentObjects.push(this);
//
// /**
// * Perform deep-linking
// */
// if (this[property].linkObjects) {
// this[property].linkObjects(idToObject);
// }
// } else {
// console.warn('Unhandled property type - fix this : ' + typeof this[property]);
// throw new Error('Unhandled property type - fix this : ' + typeof this[property]);
// }
// }
// }
// }
//
// this.loaded = false;
// };
GameLib.Component.prototype.generateNewIds = function() {
this.buildIdToObject();
for (var property in this.idToObject) {
if (this.idToObject.hasOwnProperty(property)) {
var oldId = this.idToObject[property].id;
var newId = GameLib.Utils.RandomId();
this.idToObject[property].id = newId;
this.idToObject[property].name = this.idToObject[property].name.replace(oldId,newId);
}
}
};
GameLib.Component.prototype.clone = function() {

View File

@ -10,16 +10,6 @@ GameLib.EntityManager = function() {
this.entities = [];
this.loading = [];
this.dependencies = {};
this.subscriptions = [];
this.checkRegister = [];
this.registerCallbacks();
GameLib.Component.call(
this,
GameLib.Component.COMPONENT_ENTITY_MANAGER,
@ -124,14 +114,6 @@ GameLib.EntityManager.prototype.addEntity = function(entity) {
this.entities.push(entity);
};
// /**
// * Adds a system to this manager
// * @param system GameLib.System
// */
// GameLib.EntityManager.prototype.addSystem = function(system) {
// this.systems.push(system);
// };
/**
* Returns entity by name
* @param name
@ -206,27 +188,32 @@ GameLib.EntityManager.prototype.query = function(components) {
*/
GameLib.EntityManager.prototype.queryComponents = function(constructors) {
return this.checkRegister.reduce(
function(result, object) {
return this.entities.reduce(
function(result, entity) {
if (constructors instanceof Array) {
constructors.map(
function(constructor) {
if (object instanceof constructor) {
result.push(object);
entity.components.map(
function(component) {
if (constructors instanceof Array) {
constructors.map(
function(constructor) {
if (component instanceof constructor) {
result.push(component);
}
}
);
} else {
if (component instanceof constructors) {
result.push(component);
}
}
);
} else {
if (object instanceof constructors) {
result.push(object);
}
}
);
return result;
},
[]
);
};
/**
@ -264,413 +251,3 @@ GameLib.EntityManager.FromObject = function(objectEntityManager) {
return entityManager;
};
/**
* Defines what should happen when a parent scene changes
* @param data
*/
GameLib.EntityManager.prototype.onParentSceneChange = function(data) {
if (
data.object instanceof GameLib.D3.Mesh ||
data.object instanceof GameLib.D3.Light
) {
/**
* We remove the helper (if any) from the old scene and add it to the new scene
*/
var helper = this.findHelperByObject(data.object);
if (helper) {
if (data.originalScene && data.originalScene.instance) {
data.originalScene.instance.remove(helper.instance);
}
data.newScene.instance.add(helper.instance);
}
/**
* We remove the mesh from the old scene and add it to the new scene
*/
if (data.originalScene && data.originalScene.removeObject) {
data.originalScene.removeObject(data.object);
}
data.newScene.addObject(data.object);
/**
* We inherit the parent entity of this new scene
*/
var originalEntity = null;
var newEntity = null;
if (data.object.hasOwnProperty('parentEntity')) {
originalEntity = data.object.parentEntity
}
if (data.newScene.hasOwnProperty('parentEntity')) {
newEntity = data.newScene.parentEntity;
}
var gui = null;
if (originalEntity) {
if (originalEntity.removeComponent) {
if (helper) {
originalEntity.removeComponent(helper);
}
originalEntity.removeComponent(data.object);
}
if (originalEntity.getFirstComponent) {
gui = originalEntity.getFirstComponent(GameLib.GUI);
if (gui) {
gui.removeObject(data.object);
gui.build(this);
}
}
}
if (newEntity) {
if (newEntity.addComponent) {
if (helper) {
newEntity.addComponent(helper);
}
newEntity.addComponent(data.object);
}
if (newEntity.getFirstComponent) {
gui = newEntity.getFirstComponent(GameLib.GUI);
if (gui) {
gui.addObject(data.object);
gui.build(this);
}
}
}
}
};
/**
* Change parent entity
* TODO: also change parent entity of children objects
* @param data
*/
GameLib.EntityManager.prototype.onParentEntityChange = function(data) {
if (data.originalEntity) {
data.originalEntity.removeComponent(data.object);
}
data.newEntity.addComponent(data.object);
// - ok not so cool - we may have parent entities of entities -
// - so not all children should inherit the parent entity
// data.object.buildIdToObject();
//
// for (var property in data.object.idToObject) {
// if (data.object.idToObject.hasOwnProperty(property)) {
// if (data.object.idToObject[property].hasOwnProperty('parentEntity')) {
// data.object.idToObject[property].parentEntity = data.newEntity;
// }
// }
// }
};
GameLib.EntityManager.prototype.link = function(component, data) {
for (var property in component.linkedObjects) {
if (component.linkedObjects.hasOwnProperty(property)) {
if (component.linkedObjects[property] instanceof Array) {
component[property] = component[property].map(function (entry) {
if (entry === data.component.id) {
return data.component;
} else {
return entry;
}
});
} else {
if (component[property] &&
component[property] === data.component.id) {
component[property] = data.component;
}
}
}
}
};
GameLib.EntityManager.prototype.componentCreated = function() {
var loading = [];
return function(data) {
/**
* Register this component immediately
*/
this.checkRegister.push(data.component);
/**
* If we notify ourselves - ignore it
*/
if (data.component === this) {
return;
}
/**
* Store this component into our 'loaded' list
*/
loading.push(data.component);
/**
* Store the dependencies too
*/
data.component.dependencies.map(function (id) {
/**
* Check if we already processed a component on which this component is dependent
*/
var processedComponent = this.checkRegister.reduce(
function(result, component){
if (component.id === id){
result = component;
}
return result;
},
null
);
if (processedComponent) {
/**
* Remove this dependency from the dependency list
*/
var index = data.component.dependencies.indexOf(id);
if (index === -1) {
console.log('failed to locate dependency which should exist');
}
data.component.dependencies.splice(index, 1);
/**
* Now link the component
*/
this.link(data.component, {component: processedComponent});
} else {
if (GameLib.Utils.UndefinedOrNull(this.dependencies[id])) {
this.dependencies[id] = [];
}
/**
* Don't store duplicate dependencies
*/
if (this.dependencies[id].indexOf(data.component === -1)) {
this.dependencies[id].push(data.component);
}
}
}.bind(this));
/**
* Now find all the components which depend on this component
*/
if (GameLib.Utils.UndefinedOrNull(this.dependencies[data.component.id])) {
/**
* We don't know about any dependencies on this object - but maybe a component still
* has to load which has dependencies to this object - this object is in the 'checkRegister'
*/
} else {
/**
* Otherwise, now - for each dependency - check if its loaded
*/
this.dependencies[data.component.id] = this.dependencies[data.component.id].reduce(
function (result, component) {
/**
* Remove the actual dependency
*/
var index = component.dependencies.indexOf(data.component.id);
if (index === -1) {
console.warn('dependency mismatch');
} else {
component.dependencies.splice(index, 1);
}
/**
* Find the actual place where this object should be linked - and link them
*/
this.link(component, data);
/**
* If we now managed to link the objects, and this object has no more dependencies
*/
if (component.dependencies.length === 0) {
component.loaded = true;
component.instance = component.createInstance();
this.emitInstanceEvents(component);
delete component.dependencies;
}
/**
* Also remove this from the current dependency list
*/
return result;
}.bind(this),
[]
);
delete this.dependencies[data.component.id];
}
/**
* Now if this new component has no dependencies, load it
*/
if (data.component.dependencies.length === 0) {
data.component.loaded = true;
data.component.instance = data.component.createInstance();
this.emitInstanceEvents(data.component);
delete data.component.dependencies;
}
/**
* Now check if all components are loaded, i.e., no more dependencies - if so - create their instance objects
*/
var loaded = true;
for (var i = 0; i < loading.length; i++) {
if (!loading[i].loaded) {
loaded = false;
break
}
}
/**
* All components loaded
*/
if (loaded) {
loading = [];
}
};
};
GameLib.EntityManager.prototype.emitInstanceEvents = function (component) {
if (
component instanceof GameLib.D3.Mesh
) {
GameLib.Event.Emit(
GameLib.Event.MESH_INSTANCE_CREATED,
{
mesh: component
}
)
}
if (
component instanceof GameLib.D3.Light
) {
GameLib.Event.Emit(
GameLib.Event.LIGHT_INSTANCE_CREATED,
{
light: component
}
)
}
if (
component instanceof GameLib.D3.Scene
) {
GameLib.Event.Emit(
GameLib.Event.SCENE_INSTANCE_CREATED,
{
scene: component
}
);
}
if (
component instanceof GameLib.D3.Material
) {
GameLib.Event.Emit(
GameLib.Event.MATERIAL_INSTANCE_CREATED,
{
material: component
}
);
}
if (
component instanceof GameLib.D3.Texture
) {
GameLib.Event.Emit(
GameLib.Event.TEXTURE_INSTANCE_CREATED,
{
texture: component
}
);
}
};
/**
*
*/
GameLib.EntityManager.prototype.registerCallbacks = function() {
this.subscriptions.push(
this.subscribe(
GameLib.Event.PARENT_SCENE_CHANGE,
this.onParentSceneChange
)
);
this.subscriptions.push(
this.subscribe(
GameLib.Event.PARENT_ENTITY_CHANGE,
this.onParentEntityChange
)
);
this.subscriptions.push(
this.subscribe(
GameLib.Event.COMPONENT_CREATED,
this.componentCreated().bind(this)
)
);
};
/**
* Links object Ids to actual objects
* @param idToObject
*/
// GameLib.EntityManager.prototype.linkObjects = function(idToObject) {
//
// this.entities.map(
// function(entity) {
// entity.components.map(
// function (componentId, index, array) {
// if (componentId instanceof GameLib.Component) {
// array[index] = componentId;
// } else {
// array[index] = idToObject[componentId];
// }
//
// Object.keys(array[index].linkedObjects).map(
// function (propertyName) {
// array[index][propertyName] = idToObject[array[index][propertyName]];
// }
// );
//
// array[index].loaded = true;
// }
// )
// }
// );
//
// };

View File

@ -41,6 +41,7 @@ GameLib.System.SYSTEM_TYPE_INPUT = 0x4;
GameLib.System.SYSTEM_TYPE_STORAGE = 0x8;
GameLib.System.SYSTEM_TYPE_GUI = 0x10;
GameLib.System.SYSTEM_TYPE_PHYSICS = 0x20;
GameLib.System.SYSTEM_TYPE_LINKING = 0x40;
GameLib.System.SYSTEM_TYPE_ALL = 0xFFFF;
GameLib.System.prototype.createInstance = function() {

View File

@ -0,0 +1,710 @@
/**
* Linking System takes care of linking components and dependencies (after they have loaded) -
* and managing the relationships between objects - ex. what happens when a parent entity changes,
* or a parent scene changes.
* @param apiSystem GameLib.API.System
* @constructor
*/
GameLib.System.Linking = function(
apiSystem
) {
GameLib.System.call(
this,
apiSystem
);
/**
* The dependencies of each component is tracked through this dependencies object -
* it maps the id of the object on which a component depends back to the component which depends on it,
* ex. texture.image = 'abcdefghi', then this.dependencies = {'abcdefghi' : [texture]}
* @type {{}}
*/
this.dependencies = {};
/**
* The 'register' array is a register of each component currently loaded - when the linking
* system starts it also loads all the current components from the entity manager
* @type {Array}
*/
this.register = [];
this.componentCreatedSubscription = null;
this.parentSceneChangeSubscription = null;
this.parentEntityChangeSubscription = null;
this.meshInstanceCreatedSubscription = null;
this.lightInstanceCreatedSubscription = null;
this.sceneInstanceCreatedSubscription = null;
this.imageInstanceCreatedSubscription = null;
this.textureInstanceCreatedSubscription = null;
this.materialInstanceCreatedSubscription = null;
};
GameLib.System.Linking.prototype = Object.create(GameLib.System.prototype);
GameLib.System.Linking.prototype.constructor = GameLib.System.Linking;
GameLib.System.Linking.prototype.start = function() {
this.register = GameLib.EntityManager.Instance.queryComponents([GameLib.Component]);
this.componentCreatedSubscription = this.subscribe(
GameLib.Event.COMPONENT_CREATED,
this.componentCreated().bind(this)
);
this.parentSceneChangeSubscription = this.subscribe(
GameLib.Event.PARENT_SCENE_CHANGE,
this.onParentSceneChange
);
this.parentEntityChangeSubscription = this.subscribe(
GameLib.Event.PARENT_ENTITY_CHANGE,
this.onParentEntityChange
);
this.sceneInstanceCreatedSubscription = this.subscribe(
GameLib.Event.SCENE_INSTANCE_CREATED,
this.sceneInstanceCreated
);
this.meshInstanceCreatedSubscription = this.subscribe(
GameLib.Event.MESH_INSTANCE_CREATED,
this.meshInstanceCreated
);
this.lightInstanceCreatedSubscription = this.subscribe(
GameLib.Event.LIGHT_INSTANCE_CREATED,
this.lightInstanceCreated
);
this.imageInstanceCreatedSubscription = this.subscribe(
GameLib.Event.IMAGE_INSTANCE_CREATED,
this.imageInstanceCreated
);
this.textureInstanceCreatedSubscription = this.subscribe(
GameLib.Event.TEXTURE_INSTANCE_CREATED,
this.textureInstanceCreated
);
this.materialInstanceCreatedSubscription = this.subscribe(
GameLib.Event.MATERIAL_INSTANCE_CREATED,
this.materialInstanceCreated
);
};
GameLib.EntityManager.prototype.emitInstanceEvents = function (component) {
if (
component instanceof GameLib.D3.Mesh
) {
GameLib.Event.Emit(
GameLib.Event.MESH_INSTANCE_CREATED,
{
mesh: component
}
)
}
if (
component instanceof GameLib.D3.Light
) {
GameLib.Event.Emit(
GameLib.Event.LIGHT_INSTANCE_CREATED,
{
light: component
}
)
}
if (
component instanceof GameLib.D3.Scene
) {
GameLib.Event.Emit(
GameLib.Event.SCENE_INSTANCE_CREATED,
{
scene: component
}
);
}
if (
component instanceof GameLib.D3.Material
) {
GameLib.Event.Emit(
GameLib.Event.MATERIAL_INSTANCE_CREATED,
{
material: component
}
);
}
if (
component instanceof GameLib.D3.Texture
) {
GameLib.Event.Emit(
GameLib.Event.TEXTURE_INSTANCE_CREATED,
{
texture: component
}
);
}
};
GameLib.System.Linking.prototype.link = function(component, data) {
for (var property in component.linkedObjects) {
if (component.linkedObjects.hasOwnProperty(property)) {
if (component.linkedObjects[property] instanceof Array) {
component[property] = component[property].map(function (entry) {
if (entry === data.component.id) {
return data.component;
} else {
return entry;
}
});
} else {
if (component[property] &&
component[property] === data.component.id) {
component[property] = data.component;
}
}
}
}
};
GameLib.System.Linking.prototype.componentCreated = function(data) {
/**
* Shorthand
*/
var component = data.component;
/**
* Register this component immediately
*/
this.register.push(component);
/**
* We only care about components with unloaded dependencies -
* other components will have already had their instance objects created
*/
if (component.dependencies.length > 0) {
component.dependencies = component.dependencies.reduce(
function(result, id) {
/**
* Check if we already processed a component on which this component is dependent
*/
var processedComponent = this.register.reduce(
function(result, component){
if (component.id === id){
result = component;
}
return result;
},
null
);
if (processedComponent) {
/**
* Link the component
*/
this.link(component, {component: processedComponent});
} else {
/**
* Create a new link if none exists
*/
if (GameLib.Utils.UndefinedOrNull(this.dependencies[id])) {
this.dependencies[id] = [];
}
/**
* Don't store duplicate dependencies
*/
if (this.dependencies[id].indexOf(component === -1)) {
this.dependencies[id].push(component);
}
/**
* Also - we remember that this component has a dependency
*/
result.push(id);
}
return result;
}.bind(this),
[]
);
if (component.dependencies.length === 0) {
component.loaded = true;
component.instance = component.createInstance();
this.emitInstanceEvents(component);
delete component.dependencies;
}
}
var parentComponents = this.dependencies[component.id];
/**
* Now find all the components which depend on this component
*/
if (GameLib.Utils.UndefinedOrNull(parentComponents)) {
/**
* We don't know about components which depend on this component - but it could still load.
* However, it is stored in the register for later use
*/
} else {
parentComponents.map(
function(parentComponent) {
this.link(parentComponent, {component: component});
/**
* Remove the actual dependency
*/
var index = parentComponent.dependencies.indexOf(component.id);
if (index === -1) {
console.warn('dependency mismatch');
} else {
parentComponent.dependencies.splice(index, 1);
}
/**
* If we now managed to link the objects, and this object has no more dependencies
*/
if (parentComponent.dependencies.length === 0) {
parentComponent.loaded = true;
parentComponent.instance = parentComponent.createInstance();
this.emitInstanceEvents(parentComponent);
delete parentComponent.dependencies;
}
}.bind(this)
);
delete this.dependencies[component.id];
}
};
GameLib.System.Linking.prototype.meshInstanceCreated = function(data) {
var scenes = GameLib.EntityManager.Instance.queryComponents([GameLib.D3.Scene]);
scenes.map(function(scene){
if (data.mesh.parentScene === scene) {
scene.addObject(data.mesh);
}
});
};
GameLib.System.Linking.prototype.lightInstanceCreated = function(data) {
var scenes = GameLib.EntityManager.Instance.queryComponents([GameLib.D3.Scene]);
scenes.map(function(scene){
if (data.light.parentScene === scene) {
scene.addObject(data.light);
}
});
};
GameLib.System.Linking.prototype.sceneInstanceCreated = function(data) {
var scene = data.scene;
scene.images.map(
function(image){
GameLib.Event.Emit(
GameLib.Event.LOAD_IMAGE,
{
onLoaded : function(image) {
if (this.onImageLoaded) {
this.onImageLoaded(image);
}
},
onProgress : function(image, progress) {
if (this.onImageProgress) {
this.onImageProgress(image, progress);
}
},
onError : function(image, error) {
if (this.onImageError) {
this.onImageError(image, error);
}
},
image : image
}
);
}.bind(this)
);
/**
* Add all meshes and lights
*/
var object = GameLib.EntityManager.Instance.queryComponents([
GameLib.D3.Mesh,
GameLib.D3.Light
]);
object.map(function(object){
if (
object.parentScene === scene
) {
scene.addObject(object);
}
});
};
GameLib.System.Linking.prototype.imageInstanceCreated = function(data) {
var textures = GameLib.EntityManager.Instance.queryComponents([GameLib.D3.Texture]);
textures.map(
function(texture) {
/**
* Only work with images that belong to this texture
*/
if (
texture.image === data.image
) {
/**
* Update instance, if already an instance
*/
if (texture.instance) {
texture.updateInstance();
GameLib.Event.Emit(
GameLib.Event.TEXTURE_INSTANCE_UPDATED,
{
texture : texture
}
)
} else {
/**
* Create a new instance
*/
texture.instance = texture.createInstance();
GameLib.Event.Emit(
GameLib.Event.TEXTURE_INSTANCE_CREATED,
{
texture : texture
}
)
}
}
}
);
};
GameLib.System.Linking.prototype.textureInstanceCreated = function(data) {
var materials = GameLib.EntityManager.Instance.queryComponents([GameLib.D3.Material]);
materials.map(
function(material) {
if (!material.instance) {
console.log('No material instance for ' + material.name);
return;
}
var modified = false;
/**
* We also need to check if the image of the texture is assigned -
* if not we should disable the map
*/
if (material.alphaMap === data.texture) {
if (data.texture.image) {
material.instance.alphaMap = data.texture.instance;
} else {
material.instance.alphaMap = null;
}
modified = true;
}
if (material.aoMap === data.texture) {
if (data.texture.image) {
material.instance.aoMap = data.texture.instance;
} else {
material.instance.aoMap = null;
}
modified = true;
}
if (material.bumpMap === data.texture) {
if (data.texture.image) {
material.instance.bumpMap = data.texture.instance;
} else {
material.instance.bumpMap = null;
}
modified = true;
}
if (material.diffuseMap === data.texture) {
if (data.texture.image) {
material.instance.map = data.texture.instance;
} else {
material.instance.map = null;
}
modified = true;
}
if (material.displacementMap === data.texture) {
if (data.texture.image) {
material.instance.displacementMap = data.texture.instance;
} else {
material.instance.displacementMap = null;
}
modified = true;
}
if (material.emissiveMap === data.texture) {
if (data.texture.image) {
material.instance.emissiveMap = data.texture.instance;
} else {
material.instance.emissiveMap = null;
}
modified = true;
}
if (material.environmentMap === data.texture) {
if (data.texture.image) {
material.instance.envMap = data.texture.instance;
} else {
material.instance.envMap = null;
}
modified = true;
}
if (material.lightMap === data.texture) {
if (data.texture.image) {
material.instance.lightMap = data.texture.instance;
} else {
material.instance.lightMap = null;
}
modified = true;
}
if (material.metalnessMap === data.texture) {
if (data.texture.image) {
material.instance.metalnessMap = data.texture.instance;
} else {
material.instance.metalnessMap = null;
}
modified = true;
}
if (material.normalMap === data.texture) {
if (data.texture.image) {
material.instance.normalMap = data.texture.instance;
} else {
material.instance.normalMap = null;
}
modified = true;
}
if (material.roughnessMap === data.texture) {
if (data.texture.image) {
material.instance.roughnessMap = data.texture.instance;
} else {
material.instance.roughnessMap = null;
}
modified = true;
}
if (material.specularMap === data.texture) {
if (data.texture.image) {
material.instance.specularMap = data.texture.instance;
} else {
material.instance.specularMap = null;
}
modified = true;
}
if (modified) {
material.updateInstance();
GameLib.Event.Emit(
GameLib.Event.MATERIAL_INSTANCE_UPDATED,
{
material : material
}
)
}
}
);
};
GameLib.System.Linking.prototype.materialInstanceCreated = function(data) {
var meshes = GameLib.EntityManager.Instance.queryComponents([GameLib.D3.Mesh]);
meshes.map(function(mesh){
if (!mesh.instance) {
return;
}
/**
* Only work with materials assigned to us
*/
if (mesh.materials[0] !== data.material) {
return;
}
if (mesh.instance.material === data.material.instance) {
//mesh.instance.geometry.uvsNeedUpdate = true;
return;
}
if (mesh.materials[0] === data.material) {
if (mesh.instance.material !== data.material.instance) {
mesh.instance.material = data.material.instance;
}
//mesh.instance.geometry.uvsNeedUpdate = true;
}
});
};
/**
* Defines what should happen when a parent scene changes
* @param data
*/
GameLib.System.Linking.prototype.onParentSceneChange = function(data) {
if (
data.object instanceof GameLib.D3.Mesh ||
data.object instanceof GameLib.D3.Light
) {
/**
* We remove the helper (if any) from the old scene and add it to the new scene
*/
var helper = this.findHelperByObject(data.object);
if (helper) {
if (data.originalScene && data.originalScene.instance) {
data.originalScene.instance.remove(helper.instance);
}
data.newScene.instance.add(helper.instance);
}
/**
* We remove the mesh from the old scene and add it to the new scene
*/
if (data.originalScene && data.originalScene.removeObject) {
data.originalScene.removeObject(data.object);
}
data.newScene.addObject(data.object);
/**
* We inherit the parent entity of this new scene
*/
var originalEntity = null;
var newEntity = null;
if (data.object.hasOwnProperty('parentEntity')) {
originalEntity = data.object.parentEntity
}
if (data.newScene.hasOwnProperty('parentEntity')) {
newEntity = data.newScene.parentEntity;
}
var gui = null;
if (originalEntity) {
if (originalEntity.removeComponent) {
if (helper) {
originalEntity.removeComponent(helper);
}
originalEntity.removeComponent(data.object);
}
if (originalEntity.getFirstComponent) {
gui = originalEntity.getFirstComponent(GameLib.GUI);
if (gui) {
gui.removeObject(data.object);
gui.build(this);
}
}
}
if (newEntity) {
if (newEntity.addComponent) {
if (helper) {
newEntity.addComponent(helper);
}
newEntity.addComponent(data.object);
}
if (newEntity.getFirstComponent) {
gui = newEntity.getFirstComponent(GameLib.GUI);
if (gui) {
gui.addObject(data.object);
gui.build(this);
}
}
}
}
};
/**
* Change parent entity
* TODO: also change parent entity of children objects
* @param data
*/
GameLib.System.Linking.prototype.onParentEntityChange = function(data) {
if (data.originalEntity) {
data.originalEntity.removeComponent(data.object);
}
data.newEntity.addComponent(data.object);
// - ok not so cool - we may have parent entities of entities -
// - so not all children should inherit the parent entity
// data.object.buildIdToObject();
//
// for (var property in data.object.idToObject) {
// if (data.object.idToObject.hasOwnProperty(property)) {
// if (data.object.idToObject[property].hasOwnProperty('parentEntity')) {
// data.object.idToObject[property].parentEntity = data.newEntity;
// }
// }
// }
};
GameLib.System.Linking.prototype.stop = function() {
this.register = [];
this.componentCreatedSubscription.remove();
this.parentSceneChangeSubscription.remove();
this.parentEntityChangeSubscription.remove();
this.meshInstanceCreatedSubscription.remove();
this.lightInstanceCreatedSubscription.remove();
this.sceneInstanceCreatedSubscription.remove();
this.imageInstanceCreatedSubscription.remove();
this.textureInstanceCreatedSubscription.remove();
this.materialInstanceCreatedSubscription.remove();
};

View File

@ -7,6 +7,9 @@
* @param onImageLoaded
* @param onImageProgress
* @param onImageError
* @param onComponentLoaded
* @param onComponentProgress
* @param onComponentError
* @constructor
*/
GameLib.System.Storage = function(
@ -16,7 +19,10 @@ GameLib.System.Storage = function(
apiUploadUrl,
onImageLoaded,
onImageProgress,
onImageError
onImageError,
onComponentLoaded,
onComponentProgress,
onComponentError
) {
GameLib.System.call(
this,
@ -55,17 +61,25 @@ GameLib.System.Storage = function(
}
this.onImageError = onImageError;
if (GameLib.Utils.UndefinedOrNull(onComponentLoaded)) {
onComponentLoaded = null;
}
this.onComponentLoaded = onComponentLoaded;
if (GameLib.Utils.UndefinedOrNull(onComponentProgress)) {
onComponentProgress = null;
}
this.onComponentProgress = onComponentProgress;
if (GameLib.Utils.UndefinedOrNull(onComponentError)) {
onComponentError = null;
}
this.onComponentError = onComponentError;
this.loginSubscription = null;
this.saveSubscription = null;
this.loadSubscription = null;
this.loadImageSubscription = null;
this.meshInstanceCreatedSubscription = null;
this.lightInstanceCreatedSubscription = null;
this.sceneInstanceCreatedSubscription = null;
this.imageInstanceCreatedSubscription = null;
this.textureInstanceCreatedSubscription = null;
this.materialInstanceCreatedSubscription = null;
};
GameLib.System.Storage.prototype = Object.create(GameLib.System.prototype);
@ -94,36 +108,6 @@ GameLib.System.Storage.prototype.start = function() {
GameLib.Event.LOAD_IMAGE,
this.loadImage
);
this.sceneInstanceCreatedSubscription = this.subscribe(
GameLib.Event.SCENE_INSTANCE_CREATED,
this.sceneInstanceCreated
);
this.meshInstanceCreatedSubscription = this.subscribe(
GameLib.Event.MESH_INSTANCE_CREATED,
this.meshInstanceCreated
);
this.lightInstanceCreatedSubscription = this.subscribe(
GameLib.Event.LIGHT_INSTANCE_CREATED,
this.lightInstanceCreated
);
this.imageInstanceCreatedSubscription = this.subscribe(
GameLib.Event.IMAGE_INSTANCE_CREATED,
this.imageInstanceCreated
);
this.textureInstanceCreatedSubscription = this.subscribe(
GameLib.Event.TEXTURE_INSTANCE_CREATED,
this.textureInstanceCreated
);
this.materialInstanceCreatedSubscription = this.subscribe(
GameLib.Event.MATERIAL_INSTANCE_CREATED,
this.materialInstanceCreated
);
};
/**
@ -194,51 +178,231 @@ GameLib.System.Storage.prototype.load = function(data) {
return;
}
var xhr = new XMLHttpRequest();
/**
* Load all the ids into our 'loading' list
*/
var loading = data.ids.reduce(
function(result, id) {
xhr.open(
'GET',
data.url
if (result.indexOf(id) === -1) {
result.push(id);
}
return result;
},
false
);
xhr.onreadystatechange = function (xhr) {
return function () {
if (xhr.readyState === 4) {
var loaded = [];
try {
var object = JSON.parse(xhr.responseText);
} catch (error) {
this.publish(
GameLib.Event.LOAD_COMPONENT_ERROR,
{
error : error
}
);
return;
var includeDependencies = data.includeDependencies;
var onComponentLoaded = this.onComponentLoaded;
var onComponentProgress = this.onComponentProgress;
var onComponentError = this.onComponentError;
while (loading.length > 0) {
var id = loading.pop();
var xhr = new XMLHttpRequest();
xhr.onload = function () {
try {
var object = JSON.parse(this.responseText);
} catch (error) {
if (onComponentError) {
onComponentError(error);
}
if (object.result !== 'success') {
this.publish(
GameLib.Event.LOAD_COMPONENT_ERROR,
{
error : object
}
);
return;
}
this.publish(
GameLib.Event.COMPONENT_LOADED,
GameLib.Event.Emit(
GameLib.Event.LOAD_COMPONENT_ERROR,
{
response : object,
includeDependencies : data.includeDependencies
error: error
}
);
return;
}
if (object.result !== 'success') {
if (onComponentError) {
onComponentError(error);
}
GameLib.Event.Emit(
GameLib.Event.LOAD_COMPONENT_ERROR,
{
error: error
}
);
return;
}
/**
* Now we need to create the runtime component - this happens systematically.
* First, we create an API object from the Object, then a Runtime object from the API object
* Each component has a function 'FromObject' which essentially does this for you
*/
var componentName = GameLib.Component.GetComponentName(object.componentType);
var componentClass = eval(componentName);
var fn = componentClass['FromObject'];
var runtimeComponent = null;
if (object.componentType === GameLib.Component.COMPONENT_ENTITY) {
runtimeComponent = fn(object, GameLib.EntityManager.Instance);
} else {
runtimeComponent = fn(this.graphics, object);
}
loaded.push(runtimeComponent.id);
if (includeDependencies) {
/**
* Before we announce the creation of this component, we should get
* a list of all dependencies of this object, because once we announce
* the creation of this object - the linking system will attempt to resolve
* all dependencies
*/
var dependencies = runtimeComponent.dependencies.map(function (dependency) {
return dependency;
});
/**
* Now - we should systematically check if we have the dependency already
* loaded (in our runtime environment) - if we have - we just ignore loading this dependency (for now)
* TODO: decide what to do with runtime versions of 'stale' dependencies
*/
var components = GameLib.EntityManager.Instance.queryComponents(componentClass);
dependencies = dependencies.reduce(
function (result, dependency) {
var found = components.reduce(
function (result, component) {
if (component.id === dependency) {
found = true;
}
return result;
},
false
);
if (!found) {
result.push(dependency);
}
return result;
},
[]
);
/**
* Also check if we did not already load this component quite recently
*/
dependencies = dependencies.reduce(
function (result, dependency) {
var found = loaded.reduce(
function (result, id) {
if (id === dependency) {
result = true;
}
return result;
},
false
);
if (!found) {
result.push(dependency);
}
return result;
},
[]
);
/**
* We should now check our 'loading' list and add all dependencies which are not already in there
*/
dependencies.map(
function (dependency) {
if (loading.indexOf(dependency) === -1) {
loading.push(dependency);
}
}
)
}
}
}(xhr).bind(this);
xhr.send();
/**
* Ok - now we have a super good idea of which components still need to load -
* they live in the 'loading' list.
*
* At this point - the runtime components are created, but they are not ready
* to be used. They may have dependencies to other objects, which still need
* to load, or may never be loaded.
*
* It is however safe, to announce, that we created the
* runtime version of it, however it could still have some dependencies.
*
* The Linking system will then kick in and try to resolve all dependencies
*/
if (onComponentLoaded) {
onComponentLoaded(runtimeComponent);
}
GameLib.Event.Emit(
GameLib.Event.COMPONENT_CREATED,
{
component: runtimeComponent
}
)
};
xhr.onprogress = function(__id) {
return function (progressEvent) {
var progress = 0;
if (progressEvent.total !== 0) {
progress = Number(progressEvent.loaded / progressEvent.total);
progress *= 100;
}
if (onComponentProgress) {
onComponentProgress(__id, progress)
}
};
}(id);
xhr.onerror = function(__id) {
return function (error) {
console.warn('component load failed for component ID ' + __id);
if (onComponentError) {
onComponentError(__id, error)
}
};
}(id);
xhr.open(
'GET',
this.apiUrl + '/component/load/' + id
);
xhr.send();
}
};
GameLib.System.Storage.prototype.loadImage = function(data) {
@ -247,7 +411,7 @@ GameLib.System.Storage.prototype.loadImage = function(data) {
var onLoaded = this.onImageLoaded;
var onProgress = this.onImageProgress;;
var onProgress = this.onImageProgress;
var onError = this.onImageError;
@ -353,303 +517,10 @@ GameLib.System.Storage.prototype.loadImage = function(data) {
preflight.send();
};
GameLib.System.Storage.prototype.meshInstanceCreated = function(data) {
var scenes = GameLib.EntityManager.Instance.queryComponents([GameLib.D3.Scene]);
scenes.map(function(scene){
if (data.mesh.parentScene === scene) {
scene.addObject(data.mesh);
}
});
};
GameLib.System.Storage.prototype.lightInstanceCreated = function(data) {
var scenes = GameLib.EntityManager.Instance.queryComponents([GameLib.D3.Scene]);
scenes.map(function(scene){
if (data.light.parentScene === scene) {
scene.addObject(data.light);
}
});
};
GameLib.System.Storage.prototype.sceneInstanceCreated = function(data) {
var scene = data.scene;
scene.images.map(
function(image){
GameLib.Event.Emit(
GameLib.Event.LOAD_IMAGE,
{
onLoaded : function(image) {
if (this.onImageLoaded) {
this.onImageLoaded(image);
}
},
onProgress : function(image, progress) {
if (this.onImageProgress) {
this.onImageProgress(image, progress);
}
},
onError : function(image, error) {
if (this.onImageError) {
this.onImageError(image, error);
}
},
image : image
}
);
}.bind(this)
);
/**
* Add all meshes and lights
*/
var object = GameLib.EntityManager.Instance.queryComponents([
GameLib.D3.Mesh,
GameLib.D3.Light
]);
object.map(function(object){
if (
object.parentScene === scene
) {
scene.addObject(object);
}
});
};
GameLib.System.Storage.prototype.imageInstanceCreated = function(data) {
var textures = GameLib.EntityManager.Instance.queryComponents([GameLib.D3.Texture]);
textures.map(
function(texture) {
/**
* Only work with images that belong to this texture
*/
if (
texture.image === data.image
) {
/**
* Update instance, if already an instance
*/
if (texture.instance) {
texture.updateInstance();
GameLib.Event.Emit(
GameLib.Event.TEXTURE_INSTANCE_UPDATED,
{
texture : texture
}
)
} else {
/**
* Create a new instance
*/
texture.instance = texture.createInstance();
GameLib.Event.Emit(
GameLib.Event.TEXTURE_INSTANCE_CREATED,
{
texture : texture
}
)
}
}
}
);
};
GameLib.System.Storage.prototype.textureInstanceCreated = function(data) {
var materials = GameLib.EntityManager.Instance.queryComponents([GameLib.D3.Material]);
materials.map(
function(material) {
if (!material.instance) {
console.log('No material instance for ' + material.name);
return;
}
var modified = false;
/**
* We also need to check if the image of the texture is assigned -
* if not we should disable the map
*/
if (material.alphaMap === data.texture) {
if (data.texture.image) {
material.instance.alphaMap = data.texture.instance;
} else {
material.instance.alphaMap = null;
}
modified = true;
}
if (material.aoMap === data.texture) {
if (data.texture.image) {
material.instance.aoMap = data.texture.instance;
} else {
material.instance.aoMap = null;
}
modified = true;
}
if (material.bumpMap === data.texture) {
if (data.texture.image) {
material.instance.bumpMap = data.texture.instance;
} else {
material.instance.bumpMap = null;
}
modified = true;
}
if (material.diffuseMap === data.texture) {
if (data.texture.image) {
material.instance.map = data.texture.instance;
} else {
material.instance.map = null;
}
modified = true;
}
if (material.displacementMap === data.texture) {
if (data.texture.image) {
material.instance.displacementMap = data.texture.instance;
} else {
material.instance.displacementMap = null;
}
modified = true;
}
if (material.emissiveMap === data.texture) {
if (data.texture.image) {
material.instance.emissiveMap = data.texture.instance;
} else {
material.instance.emissiveMap = null;
}
modified = true;
}
if (material.environmentMap === data.texture) {
if (data.texture.image) {
material.instance.envMap = data.texture.instance;
} else {
material.instance.envMap = null;
}
modified = true;
}
if (material.lightMap === data.texture) {
if (data.texture.image) {
material.instance.lightMap = data.texture.instance;
} else {
material.instance.lightMap = null;
}
modified = true;
}
if (material.metalnessMap === data.texture) {
if (data.texture.image) {
material.instance.metalnessMap = data.texture.instance;
} else {
material.instance.metalnessMap = null;
}
modified = true;
}
if (material.normalMap === data.texture) {
if (data.texture.image) {
material.instance.normalMap = data.texture.instance;
} else {
material.instance.normalMap = null;
}
modified = true;
}
if (material.roughnessMap === data.texture) {
if (data.texture.image) {
material.instance.roughnessMap = data.texture.instance;
} else {
material.instance.roughnessMap = null;
}
modified = true;
}
if (material.specularMap === data.texture) {
if (data.texture.image) {
material.instance.specularMap = data.texture.instance;
} else {
material.instance.specularMap = null;
}
modified = true;
}
if (modified) {
material.updateInstance();
GameLib.Event.Emit(
GameLib.Event.MATERIAL_INSTANCE_UPDATED,
{
material : material
}
)
}
}
);
};
GameLib.System.Storage.prototype.materialInstanceCreated = function(data) {
var meshes = GameLib.EntityManager.Instance.queryComponents([GameLib.D3.Mesh]);
meshes.map(function(mesh){
if (!mesh.instance) {
return;
}
/**
* Only work with materials assigned to us
*/
if (mesh.materials[0] !== data.material) {
return;
}
if (mesh.instance.material === data.material.instance) {
//mesh.instance.geometry.uvsNeedUpdate = true;
return;
}
if (mesh.materials[0] === data.material) {
if (mesh.instance.material !== data.material.instance) {
mesh.instance.material = data.material.instance;
}
//mesh.instance.geometry.uvsNeedUpdate = true;
}
});
};
GameLib.System.Storage.prototype.stop = function() {
this.loginSubscription.remove();
this.loadSubscription.remove();
this.saveSubscription.remove();
this.loadImageSubscription.remove();
this.meshInstanceCreatedSubscription.remove();
this.lightInstanceCreatedSubscription.remove();
this.sceneInstanceCreatedSubscription.remove();
this.imageInstanceCreatedSubscription.remove();
this.textureInstanceCreatedSubscription.remove();
this.materialInstanceCreatedSubscription.remove();
};