getting therer -- deep linking objects

beta.r3js.org
Theunis J. Botha 2017-02-01 16:09:34 +01:00
parent 59650c739e
commit dd148226c9
10 changed files with 347 additions and 87 deletions

View File

@ -22,6 +22,10 @@ GameLib.Component = function(
this.idToObject = {}; this.idToObject = {};
this.parentObjects = [];
this.build = true;
this.linkedObjects.parentEntity = GameLib.Entity; this.linkedObjects.parentEntity = GameLib.Entity;
}; };
@ -55,6 +59,7 @@ GameLib.Component.COMPONENT_SKELETON = 0x19;
GameLib.Component.COMPONENT_TEXTURE = 0x1a; GameLib.Component.COMPONENT_TEXTURE = 0x1a;
GameLib.Component.COMPONENT_ENTITY_MANAGER = 0x1b; GameLib.Component.COMPONENT_ENTITY_MANAGER = 0x1b;
GameLib.Component.COMPONENT_DOM_ELEMENT = 0x1c; GameLib.Component.COMPONENT_DOM_ELEMENT = 0x1c;
GameLib.Component.COMPONENT_IMAGE_FACTORY = 0x1d;
/** /**
* Components are linked at runtime - for storing, we just store the ID * Components are linked at runtime - for storing, we just store the ID
@ -66,12 +71,19 @@ GameLib.Component.prototype.toApiComponent = function() {
GameLib.Component.prototype.buildIdToObject = function() { GameLib.Component.prototype.buildIdToObject = function() {
if (!this.build) {
return;
}
this.build = false;
this.idToObject = {}; this.idToObject = {};
for (var property in this.linkedObjects) { for (var property in this.linkedObjects) {
if ( if (
this.linkedObjects.hasOwnProperty(property) && this.linkedObjects.hasOwnProperty(property) &&
this.hasOwnProperty(property) this.hasOwnProperty(property) &&
this[property]
) { ) {
if (this.linkedObjects[property] instanceof Array) { if (this.linkedObjects[property] instanceof Array) {
this.idToObject = GameLib.Utils.LoadIdsFromArrayToIdObject(this[property], this.idToObject); this.idToObject = GameLib.Utils.LoadIdsFromArrayToIdObject(this[property], this.idToObject);
@ -82,4 +94,115 @@ GameLib.Component.prototype.buildIdToObject = function() {
} }
this.idToObject[this.id] = this; this.idToObject[this.id] = this;
this.build = true;
};
/**
* 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) {
p.parentObjects.push(this);
/**
* 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');
}
idToObject[p].parentObjects.push(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[property].parentObjects.push(this);
/**
* This object is already an object
*/
if (this[property].linkObjects) {
this[property].linkObjects(idToObject);
}
return this[property];
} 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]);
}
}
}
}
}; };

View File

@ -0,0 +1,55 @@
/**
* Raw ImageFactory API object - should always correspond with the ImageFactory Schema
* @param id
* @param name
* @param baseUrl String
* @param parentEntity
* @constructor
*/
GameLib.D3.API.ImageFactory = function(
id,
name,
baseUrl,
parentEntity
) {
GameLib.Component.call(
this,
GameLib.Component.COMPONENT_IMAGE_FACTORY,
null,
null,
parentEntity
);
if (GameLib.Utils.UndefinedOrNull(id)) {
id = GameLib.Utils.RandomId();
}
this.id = id;
if (GameLib.Utils.UndefinedOrNull(name)) {
name = 'ImageFactory (' + this.id + ')';
}
this.name = name;
if (GameLib.Utils.UndefinedOrNull(baseUrl)) {
baseUrl = '';
console.warn('No baseURL defined for image factory');
}
this.baseUrl = baseUrl;
};
GameLib.D3.API.ImageFactory.prototype = Object.create(GameLib.Component.prototype);
GameLib.D3.API.ImageFactory.prototype.constructor = GameLib.D3.API.ImageFactory;
/**
* Returns an API ImageFactory from an Object ImageFactory
* @param objectImageFactory
* @constructor
*/
GameLib.D3.API.ImageFactory.FromObjectImageFactory = function(objectImageFactory) {
return new GameLib.D3.API.ImageFactory(
objectImageFactory.id,
objectImageFactory.name,
objectImageFactory.baseUrl,
objectImageFactory.parentEntity
);
};

View File

@ -33,8 +33,7 @@ GameLib.D3.API.Scene = function(
'meshes' : [GameLib.D3.Mesh], 'meshes' : [GameLib.D3.Mesh],
'lights' : [GameLib.D3.Light], 'lights' : [GameLib.D3.Light],
'textures' : [GameLib.D3.Texture], 'textures' : [GameLib.D3.Texture],
'materials' : [GameLib.D3.Material], 'materials' : [GameLib.D3.Material]
'imageFactory' : GameLib.D3.ImageFactory
}, },
false, false,
parentEntity parentEntity

View File

@ -41,7 +41,7 @@ GameLib.D3.Helper = function Helper(
object instanceof GameLib.D3.Mesh && object instanceof GameLib.D3.Mesh &&
object.meshType != GameLib.D3.Mesh.TYPE_CURVE object.meshType != GameLib.D3.Mesh.TYPE_CURVE
) { ) {
helperType = GameLib.D3.Helper.HELPER_TYPE_WIREFRAME; helperType = GameLib.D3.Helper.HELPER_TYPE_EDGES;
} }
if (object instanceof GameLib.D3.Light) { if (object instanceof GameLib.D3.Light) {
@ -92,7 +92,7 @@ GameLib.D3.Helper.prototype.createInstance = function(update) {
} }
if (this.helperType == GameLib.D3.Helper.HELPER_TYPE_EDGES) { if (this.helperType == GameLib.D3.Helper.HELPER_TYPE_EDGES) {
instance = new THREE.WireframeHelper(this.object.instance, 0x007700); instance = new THREE.EdgesHelper(this.object.instance, 0x007700);
} }
if (this.helperType == GameLib.D3.Helper.HELPER_TYPE_DIRECTIONAL_LIGHT) { if (this.helperType == GameLib.D3.Helper.HELPER_TYPE_DIRECTIONAL_LIGHT) {

View File

@ -1,71 +1,136 @@
/** /**
* The image factory takes care that we only make requests for Image URLs which we have not already started downloading * The image factory takes care that we only make requests for Image URLs which we have not already started downloading
* @param graphics GameLib.D3.Graphics * @param graphics GameLib.D3.Graphics
* @param baseUrl * @param apiImageFactory GameLib.D3.API.ImageFactory
* @param progressCallback
* @returns {Function} * @returns {Function}
* @constructor * @constructor
*/ */
GameLib.D3.ImageFactory = function ( GameLib.D3.ImageFactory = function (
graphics, graphics,
baseUrl apiImageFactory,
progressCallback
) {
graphics.isNotThreeThrow();
this.graphics = graphics;
if (GameLib.Utils.UndefinedOrNull(apiImageFactory)) {
apiImageFactory = {};
}
if (GameLib.Utils.UndefinedOrNull(progressCallback)) {
progressCallback = null;
}
this.progressCallback = progressCallback;
GameLib.D3.API.ImageFactory.call(
this,
apiImageFactory.id,
apiImageFactory.name,
apiImageFactory.baseUrl,
apiImageFactory.parentEntity
);
this.buildIdToObject();
this.promiseList = {};
this.instance = this.createInstance();
};
GameLib.D3.ImageFactory.prototype = Object.create(GameLib.D3.API.ImageFactory.prototype);
GameLib.D3.ImageFactory.prototype.constructor = GameLib.D3.ImageFactory;
GameLib.D3.ImageFactory.prototype.createInstance = function(update) {
var instance = null;
if (update) {
instance = this.instance;
} else {
instance = new THREE.ImageLoader();
}
instance.crossOrigin = '';
return instance;
};
/**
* Update instance
*/
GameLib.D3.ImageFactory.prototype.updateInstance = function() {
this.instance = this.createInstance(true);
};
/**
* Loads an image, either returns the image immediately if already loaded, or a promise to load the image
* @param imagePath
* @returns {*}
* @constructor
*/
GameLib.D3.ImageFactory.prototype.loadImage = function(
imagePath
) { ) {
graphics.isNotThreeThrow(); imagePath = imagePath.replace(new RegExp('\/*'), '/');
var promiseList = {};
return function(imagePath, progressCallback) {
if (!imagePath) { if (!imagePath) {
console.log('Attempted to download bad URL : ' + imagePath); console.log('Attempted to download bad URL : ' + imagePath);
throw new Error('Bad URL : ' + imagePath); throw new Error('Bad URL : ' + imagePath);
} }
if (promiseList[imagePath]) { if (this.promiseList[imagePath]) {
return promiseList[imagePath]; return this.promiseList[imagePath];
} }
var defer = Q.defer(); var defer = Q.defer();
promiseList[imagePath] = defer.promise; this.promiseList[imagePath] = defer.promise;
GameLib.D3.ImageFactory.LoadImage(graphics, baseUrl + imagePath, defer, progressCallback); this.instance.load(
this.baseUrl + imagePath + '?ts=' + Date.now(),
return promiseList[imagePath]; function (image) {
defer.resolve(image);
}.bind(this),
function onProgress(xhr) {
if (this.progressCallback) {
this.progressCallback((xhr.loaded / xhr.total * 100));
} }
}.bind(this),
function onError() {
defer.reject('Failed to download image : ' + this.baseUrl + imagePath);
}.bind(this)
);
return this.promiseList[imagePath];
}; };
/** /**
* Loads an image and resolves the defered promise once it succeeded (or failed) * Converts a GameLib.D3.ImageFactory to a GameLib.D3.API.ImageFactory
* @param graphics * @returns {GameLib.D3.API.ImageFactory}
* @param url
* @param defer
* @param progressCallback
* @constructor
*/ */
GameLib.D3.ImageFactory.LoadImage = function ( GameLib.D3.ImageFactory.prototype.toApiImageFactory = function() {
graphics, return new GameLib.D3.API.ImageFactory(
url, this.id,
defer, this.name,
progressCallback this.baseUrl,
) { GameLib.Utils.IdOrNull(this.parentEntity)
var loader = new graphics.instance.ImageLoader();
loader.crossOrigin = '';
loader.load(
url + '?ts=' + Date.now(),
function (image) {
defer.resolve(image);
},
function onProgress(xhr) {
if (progressCallback) {
progressCallback((xhr.loaded / xhr.total * 100));
}
},
function onError() {
defer.reject('Failed to download image : ' + url);
}
); );
}; };
/**
* Returns a new GameLib.D3.ImageFactory from a GameLib.D3.API.ImageFactory
* @param graphics GameLib.D3.Graphics
* @param objectImageFactory GameLib.D3.API.ImageFactory
* @returns {GameLib.D3.ImageFactory}
*/
GameLib.D3.ImageFactory.FromObjectImageFactory = function(graphics, objectImageFactory) {
return new GameLib.D3.ImageFactory(
graphics,
GameLib.D3.API.ImageFactory.FromObjectImageFactory(objectImageFactory)
);
};

View File

@ -101,20 +101,20 @@ GameLib.D3.Material = function Material(
this.specular = new GameLib.Color( this.specular = new GameLib.Color(
graphics, graphics,
this, this.specular,
this.specular this
); );
this.color = new GameLib.Color( this.color = new GameLib.Color(
graphics, graphics,
this, this.color,
this.color this
); );
this.emissive = new GameLib.Color( this.emissive = new GameLib.Color(
graphics, graphics,
this, this.emissive,
this.emissive this
); );
if (this.alphaMap) { if (this.alphaMap) {
@ -273,6 +273,8 @@ GameLib.D3.Material = function Material(
} }
} }
this.needsUpdate = false;
this.buildIdToObject(); this.buildIdToObject();
this.instance = this.createInstance(); this.instance = this.createInstance();

View File

@ -296,11 +296,11 @@ GameLib.D3.Mesh.prototype.createInstance = function(update) {
} }
if (this.meshType == GameLib.D3.Mesh.TYPE_NORMAL) { if (this.meshType == GameLib.D3.Mesh.TYPE_NORMAL) {
instance = new THREE.Mesh(instanceGeometry, this.materials[0].instance); instance = new THREE.Mesh(instanceGeometry);
} }
if (this.meshType == GameLib.D3.Mesh.TYPE_CURVE) { if (this.meshType == GameLib.D3.Mesh.TYPE_CURVE) {
instance = new THREE.Points(instanceGeometry, this.materials[0].instance); instance = new THREE.Points(instanceGeometry);
} }
if (this.meshType == GameLib.D3.Mesh.TYPE_SKINNED) { if (this.meshType == GameLib.D3.Mesh.TYPE_SKINNED) {
@ -333,7 +333,7 @@ GameLib.D3.Mesh.prototype.createInstance = function(update) {
); );
} }
instance = new THREE.SkinnedMesh(instanceGeometry, this.materials[0].instance); instance = new THREE.SkinnedMesh(instanceGeometry);
instance.add(this.skeleton.rootBoneInstance); instance.add(this.skeleton.rootBoneInstance);
@ -402,6 +402,10 @@ GameLib.D3.Mesh.prototype.createInstance = function(update) {
instance.rotateZ(this.localRotation.z); instance.rotateZ(this.localRotation.z);
} }
if (this.materials.length == 1 && this.materials[0].instance) {
instance.material = this.materials[0].instance;
}
instance.renderOrder = this.renderOrder; instance.renderOrder = this.renderOrder;
return instance; return instance;

View File

@ -54,8 +54,8 @@ GameLib.D3.Scene = function (
return new GameLib.D3.Mesh( return new GameLib.D3.Mesh(
this.graphics, this.graphics,
apiMesh, apiMesh,
this.computeNormals, this.imageFactory,
this.imageFactory this.computeNormals
); );
} else { } else {
@ -106,11 +106,15 @@ GameLib.D3.Scene = function (
function(apiTexture) { function(apiTexture) {
if (apiTexture instanceof GameLib.D3.API.Texture) { if (apiTexture instanceof GameLib.D3.API.Texture) {
return new GameLib.D3.Texture( var texture = new GameLib.D3.Texture(
this.graphics, this.graphics,
apiTexture, apiTexture,
this.imageFactory this.imageFactory
); );
this.idToObject[texture.id] = texture;
return texture;
} else { } else {
console.warn('apiTexture not an instance of API.Texture'); console.warn('apiTexture not an instance of API.Texture');
throw new Error('apiTexture not an instance of API.Texture'); throw new Error('apiTexture not an instance of API.Texture');
@ -123,11 +127,17 @@ GameLib.D3.Scene = function (
function(apiMaterial) { function(apiMaterial) {
if (apiMaterial instanceof GameLib.D3.API.Material) { if (apiMaterial instanceof GameLib.D3.API.Material) {
return new GameLib.D3.Material(
var material = new GameLib.D3.Material(
this.graphics, this.graphics,
apiMaterial, apiMaterial,
this.imageFactory this.imageFactory
); );
this.idToObject[material.id] = material;
return material;
} else { } else {
console.warn('apiMaterial not an instance of API.Material'); console.warn('apiMaterial not an instance of API.Material');
throw new Error('apiMaterial not an instance of API.Material'); throw new Error('apiMaterial not an instance of API.Material');
@ -136,7 +146,20 @@ GameLib.D3.Scene = function (
}.bind(this) }.bind(this)
); );
this.linkObjects(); this.idToObject[this.id] = this;
this.linkObjects(this.idToObject);
this.meshes.map(
function(mesh) {
mesh.updateInstance();
mesh.materials.map(
function(material) {
material.updateInstance();
}
)
}
);
this.buildIdToObject(); this.buildIdToObject();
@ -146,24 +169,6 @@ GameLib.D3.Scene = function (
GameLib.D3.Scene.prototype = Object.create(GameLib.D3.API.Scene.prototype); GameLib.D3.Scene.prototype = Object.create(GameLib.D3.API.Scene.prototype);
GameLib.D3.Scene.prototype.constructor = GameLib.D3.Scene; GameLib.D3.Scene.prototype.constructor = GameLib.D3.Scene;
GameLib.D3.Scene.prototype.linkObjects = function() {
this.meshes.map(
function(mesh) {
this.materials = mesh.materials.map(
function(material) {
return this.idToObject[material];
}
);
mesh.materials.map(
function(material) {
material.diffuseMap = this.idToObject[material.diffuseMap];
}
)
}
)
};
/** /**
* Creates an instance scene * Creates an instance scene
* @returns {THREE.Scene} * @returns {THREE.Scene}

View File

@ -77,14 +77,22 @@ GameLib.D3.Texture.prototype.constructor = GameLib.D3.Texture;
*/ */
GameLib.D3.Texture.prototype.loadTexture = function() { GameLib.D3.Texture.prototype.loadTexture = function() {
this.imageData = this.imageFactory(this.imagePath); this.imageData = this.imageFactory.loadImage(this.imagePath);
this.imageData.then( this.imageData.then(
function (imageInstance){ function (imageInstance){
this.imageInstance = imageInstance; this.imageInstance = imageInstance;
this.instance = this.createInstance(); this.instance = this.createInstance();
this.parentObjects.map(
function(parentObject){
if (parentObject instanceof GameLib.D3.Material && parentObject.updateInstance) {
parentObject.updateInstance();
}
}
)
}.bind(this), }.bind(this),
function onRejected() { function onRejected(message) {
console.warn(message);
} }
); );
}; };
@ -173,7 +181,8 @@ GameLib.D3.Texture.TEXTURE_TYPE_SPECULAR = 'specular';
/** /**
* Creates an instance of our texture object * Creates an instance of our texture object
* @returns {GameLib.D3.Texture|THREE.SoftwareRenderer.Texture|THREE.Texture|*|Texture} * @param update
* @returns {*}
*/ */
GameLib.D3.Texture.prototype.createInstance = function(update) { GameLib.D3.Texture.prototype.createInstance = function(update) {
@ -213,8 +222,6 @@ GameLib.D3.Texture.prototype.createInstance = function(update) {
instance.premultiplyAlpha = this.premultiplyAlpha; instance.premultiplyAlpha = this.premultiplyAlpha;
instance.textureType = this.textureType; instance.textureType = this.textureType;
instance.needsUpdate = true;
return instance; return instance;
}; };