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

@ -23,7 +23,7 @@ GameLib.API.Component = function(
loaded = false;
}
this.loaded = loaded;
if (GameLib.Utils.UndefinedOrNull(parentEntity)) {
parentEntity = null;
}

View File

@ -22,6 +22,10 @@ GameLib.Component = function(
this.idToObject = {};
this.parentObjects = [];
this.build = true;
this.linkedObjects.parentEntity = GameLib.Entity;
};
@ -55,6 +59,7 @@ GameLib.Component.COMPONENT_SKELETON = 0x19;
GameLib.Component.COMPONENT_TEXTURE = 0x1a;
GameLib.Component.COMPONENT_ENTITY_MANAGER = 0x1b;
GameLib.Component.COMPONENT_DOM_ELEMENT = 0x1c;
GameLib.Component.COMPONENT_IMAGE_FACTORY = 0x1d;
/**
* 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() {
if (!this.build) {
return;
}
this.build = false;
this.idToObject = {};
for (var property in this.linkedObjects) {
if (
this.linkedObjects.hasOwnProperty(property) &&
this.hasOwnProperty(property)
this.hasOwnProperty(property) &&
this[property]
) {
if (this.linkedObjects[property] instanceof Array) {
this.idToObject = GameLib.Utils.LoadIdsFromArrayToIdObject(this[property], this.idToObject);
@ -82,4 +94,115 @@ GameLib.Component.prototype.buildIdToObject = function() {
}
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],
'lights' : [GameLib.D3.Light],
'textures' : [GameLib.D3.Texture],
'materials' : [GameLib.D3.Material],
'imageFactory' : GameLib.D3.ImageFactory
'materials' : [GameLib.D3.Material]
},
false,
parentEntity

View File

@ -41,7 +41,7 @@ GameLib.D3.Helper = function Helper(
object instanceof GameLib.D3.Mesh &&
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) {
@ -92,7 +92,7 @@ GameLib.D3.Helper.prototype.createInstance = function(update) {
}
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) {

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
* @param graphics GameLib.D3.Graphics
* @param baseUrl
* @param apiImageFactory GameLib.D3.API.ImageFactory
* @param progressCallback
* @returns {Function}
* @constructor
*/
GameLib.D3.ImageFactory = function (
graphics,
baseUrl
apiImageFactory,
progressCallback
) {
graphics.isNotThreeThrow();
this.graphics = graphics;
var promiseList = {};
return function(imagePath, progressCallback) {
if (!imagePath) {
console.log('Attempted to download bad URL : ' + imagePath);
throw new Error('Bad URL : ' + imagePath);
}
if (promiseList[imagePath]) {
return promiseList[imagePath];
}
var defer = Q.defer();
promiseList[imagePath] = defer.promise;
GameLib.D3.ImageFactory.LoadImage(graphics, baseUrl + imagePath, defer, progressCallback);
return promiseList[imagePath];
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;
};
/**
* Loads an image and resolves the defered promise once it succeeded (or failed)
* @param graphics
* @param url
* @param defer
* @param progressCallback
* 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.LoadImage = function (
graphics,
url,
defer,
progressCallback
GameLib.D3.ImageFactory.prototype.loadImage = function(
imagePath
) {
var loader = new graphics.instance.ImageLoader();
imagePath = imagePath.replace(new RegExp('\/*'), '/');
loader.crossOrigin = '';
if (!imagePath) {
console.log('Attempted to download bad URL : ' + imagePath);
throw new Error('Bad URL : ' + imagePath);
}
loader.load(
url + '?ts=' + Date.now(),
if (this.promiseList[imagePath]) {
return this.promiseList[imagePath];
}
var defer = Q.defer();
this.promiseList[imagePath] = defer.promise;
this.instance.load(
this.baseUrl + imagePath + '?ts=' + Date.now(),
function (image) {
defer.resolve(image);
},
}.bind(this),
function onProgress(xhr) {
if (progressCallback) {
progressCallback((xhr.loaded / xhr.total * 100));
if (this.progressCallback) {
this.progressCallback((xhr.loaded / xhr.total * 100));
}
},
}.bind(this),
function onError() {
defer.reject('Failed to download image : ' + url);
}
defer.reject('Failed to download image : ' + this.baseUrl + imagePath);
}.bind(this)
);
return this.promiseList[imagePath];
};
/**
* Converts a GameLib.D3.ImageFactory to a GameLib.D3.API.ImageFactory
* @returns {GameLib.D3.API.ImageFactory}
*/
GameLib.D3.ImageFactory.prototype.toApiImageFactory = function() {
return new GameLib.D3.API.ImageFactory(
this.id,
this.name,
this.baseUrl,
GameLib.Utils.IdOrNull(this.parentEntity)
);
};
/**
* 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(
graphics,
this,
this.specular
this.specular,
this
);
this.color = new GameLib.Color(
graphics,
this,
this.color
this.color,
this
);
this.emissive = new GameLib.Color(
graphics,
this,
this.emissive
this.emissive,
this
);
if (this.alphaMap) {
@ -273,6 +273,8 @@ GameLib.D3.Material = function Material(
}
}
this.needsUpdate = false;
this.buildIdToObject();
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) {
instance = new THREE.Mesh(instanceGeometry, this.materials[0].instance);
instance = new THREE.Mesh(instanceGeometry);
}
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) {
@ -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);
@ -402,6 +402,10 @@ GameLib.D3.Mesh.prototype.createInstance = function(update) {
instance.rotateZ(this.localRotation.z);
}
if (this.materials.length == 1 && this.materials[0].instance) {
instance.material = this.materials[0].instance;
}
instance.renderOrder = this.renderOrder;
return instance;

View File

@ -54,8 +54,8 @@ GameLib.D3.Scene = function (
return new GameLib.D3.Mesh(
this.graphics,
apiMesh,
this.computeNormals,
this.imageFactory
this.imageFactory,
this.computeNormals
);
} else {
@ -106,11 +106,15 @@ GameLib.D3.Scene = function (
function(apiTexture) {
if (apiTexture instanceof GameLib.D3.API.Texture) {
return new GameLib.D3.Texture(
var texture = new GameLib.D3.Texture(
this.graphics,
apiTexture,
this.imageFactory
);
this.idToObject[texture.id] = texture;
return texture;
} else {
console.warn('apiTexture not an instance of API.Texture');
throw new Error('apiTexture not an instance of API.Texture');
@ -123,20 +127,39 @@ GameLib.D3.Scene = function (
function(apiMaterial) {
if (apiMaterial instanceof GameLib.D3.API.Material) {
return new GameLib.D3.Material(
var material = new GameLib.D3.Material(
this.graphics,
apiMaterial,
this.imageFactory
);
this.idToObject[material.id] = material;
return material;
} else {
console.warn('apiMaterial not an instance of API.Material');
throw new Error('apiMaterial not an instance of API.Material');
}
}.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();
@ -146,24 +169,6 @@ GameLib.D3.Scene = function (
GameLib.D3.Scene.prototype = Object.create(GameLib.D3.API.Scene.prototype);
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
* @returns {THREE.Scene}

View File

@ -77,14 +77,22 @@ GameLib.D3.Texture.prototype.constructor = GameLib.D3.Texture;
*/
GameLib.D3.Texture.prototype.loadTexture = function() {
this.imageData = this.imageFactory(this.imagePath);
this.imageData = this.imageFactory.loadImage(this.imagePath);
this.imageData.then(
function (imageInstance){
this.imageInstance = imageInstance;
this.instance = this.createInstance();
this.parentObjects.map(
function(parentObject){
if (parentObject instanceof GameLib.D3.Material && parentObject.updateInstance) {
parentObject.updateInstance();
}
}
)
}.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
* @returns {GameLib.D3.Texture|THREE.SoftwareRenderer.Texture|THREE.Texture|*|Texture}
* @param update
* @returns {*}
*/
GameLib.D3.Texture.prototype.createInstance = function(update) {
@ -213,8 +222,6 @@ GameLib.D3.Texture.prototype.createInstance = function(update) {
instance.premultiplyAlpha = this.premultiplyAlpha;
instance.textureType = this.textureType;
instance.needsUpdate = true;
return instance;
};