diff --git a/src/game-lib-a-1-event.js b/src/game-lib-a-1-event.js index 5c0e74d..480d0c5 100644 --- a/src/game-lib-a-1-event.js +++ b/src/game-lib-a-1-event.js @@ -51,6 +51,9 @@ GameLib.Event.MESH_DELETED = 0x23; GameLib.Event.MESH_SELECTED = 0x24; GameLib.Event.MESH_DESELECTED = 0x25; GameLib.Event.COMPONENT_REGISTER = 0x26; +GameLib.Event.IMAGE_NOT_FOUND = 0x27; +GameLib.Event.BLENDER_DATA_RECEIVED = 0x28; +GameLib.Event.IMAGE_UPLOAD_COMPLETE = 0x29; /** * Subscribe to some events diff --git a/src/game-lib-a-2-utils.js b/src/game-lib-a-2-utils.js index 1d7e949..80271b4 100644 --- a/src/game-lib-a-2-utils.js +++ b/src/game-lib-a-2-utils.js @@ -650,4 +650,18 @@ GameLib.Utils.PushUnique = function(array, object) { */ GameLib.Utils.IsEmpty = function(obj) { return (Object.keys(obj).length === 0 && obj.constructor === Object); +}; + +GameLib.Utils.LowerUnderscore = function(name) { + return name.toLowerCase().replace(/\s+/, '_'); +}; + +GameLib.Utils.UpperCaseWordsSpaces = function(word) { + return word.replace(/[-_]/g, ' ').split(' ').reduce( + function(result, word) { + result += word[0].toUpperCase() + word.substr(1); + return result + ' '; + }, + '' + ).trim(); }; \ No newline at end of file diff --git a/src/game-lib-d3-api-image.js b/src/game-lib-d3-api-image.js index 7a17122..abc311e 100644 --- a/src/game-lib-d3-api-image.js +++ b/src/game-lib-d3-api-image.js @@ -2,6 +2,8 @@ * Image * @param id * @param name + * @param fileName + * @param extension * @param path * @param contentType * @param size @@ -11,6 +13,8 @@ GameLib.D3.API.Image = function( id, name, + fileName, + extension, path, contentType, size, @@ -22,12 +26,22 @@ GameLib.D3.API.Image = function( this.id = id; if (GameLib.Utils.UndefinedOrNull(name)) { - name = 'Image ' + GameLib.Utils.RandomId() + '.png'; + name = 'Image ' + id; } this.name = name; - if (GameLib.Utils.UndefinedOrNull(path)) { - path = '/' + this.name.toLowerCase().replace(' ', '_'); + if (GameLib.Utils.UndefinedOrNull(fileName)) { + fileName = GameLib.Utils.LowerUnderscore(name); + } + this.fileName = fileName; + + if (GameLib.Utils.UndefinedOrNull(extension)) { + extension = '.unknown'; + } + this.extension = extension; + + if (GameLib.Utils.UndefinedOrNull(path)) { + path = '/'; } this.path = path; @@ -35,15 +49,15 @@ GameLib.D3.API.Image = function( contentType = 'application/octet-stream'; - if (this.name.match(/(png)$/i)) { + if (this.extension.match(/(png)$/i)) { contentType = 'image/png'; } - if (this.name.match(/(jpg|jpeg)$/i)) { + if (this.extension.match(/(jpg|jpeg)$/i)) { contentType = 'image/jpeg'; } - if (this.name.match(/(gif)$/i)) { + if (this.extension.match(/(gif)$/i)) { contentType = 'image/gif'; } } @@ -72,6 +86,8 @@ GameLib.D3.API.Image.FromObject = function(objectImage) { return new GameLib.D3.API.Image( objectImage.id, objectImage.name, + objectImage.fileName, + objectImage.extension, objectImage.path, objectImage.contentType, objectImage.size, diff --git a/src/game-lib-d3-image.js b/src/game-lib-d3-image.js index d66e9ca..3c7a89e 100644 --- a/src/game-lib-d3-image.js +++ b/src/game-lib-d3-image.js @@ -8,6 +8,9 @@ GameLib.D3.Image = function( graphics, apiImage ) { + this.graphics = graphics; + this.graphics.isNotThreeThrow(); + if (GameLib.Utils.UndefinedOrNull(apiImage)) { apiImage = {}; } @@ -20,6 +23,8 @@ GameLib.D3.Image = function( this, apiImage.id, apiImage.name, + apiImage.fileName, + apiImage.extension, apiImage.path, apiImage.contentType, apiImage.size, @@ -60,6 +65,8 @@ GameLib.D3.Image.prototype.toApiObject = function() { var apiImage = new GameLib.D3.API.Image( this.id, this.name, + this.fileName, + this.extension, this.path, this.contentType, this.size, diff --git a/src/game-lib-d3-texture.js b/src/game-lib-d3-texture.js index 3e3dca4..d760f51 100644 --- a/src/game-lib-d3-texture.js +++ b/src/game-lib-d3-texture.js @@ -173,7 +173,7 @@ GameLib.D3.Texture.prototype.createInstance = function() { ); instance.needsUpdate = true; } else { - return null; + instance = new THREE.CubeTexture(); } if (this.mapping !== GameLib.D3.Texture.TYPE_CUBE_REFLECTION_MAPPING && @@ -190,7 +190,7 @@ GameLib.D3.Texture.prototype.createInstance = function() { ); instance.needsUpdate = true; } else { - return null; + instance = new THREE.Texture(); } if (this.mapping !== GameLib.D3.Texture.TYPE_UV_MAPPING) { diff --git a/src/game-lib-system-linking.js b/src/game-lib-system-linking.js index 124f584..85deb76 100644 --- a/src/game-lib-system-linking.js +++ b/src/game-lib-system-linking.js @@ -24,6 +24,7 @@ GameLib.System.Linking = function( this.resolved = []; + this.imageNotFoundSubscription = null; this.componentCreatedSubscription = null; this.parentSceneChangeSubscription = null; this.parentEntityChangeSubscription = null; @@ -85,6 +86,10 @@ GameLib.System.Linking.prototype.start = function() { this.materialInstanceCreated ); + this.imageNotFoundSubscription = this.subscribe( + GameLib.Event.IMAGE_NOT_FOUND, + this.imageNotFound + ) }; GameLib.System.Linking.prototype.link = function(component, data) { @@ -295,6 +300,44 @@ GameLib.System.Linking.prototype.componentCreated = function(data) { this.resolveDependencies(component); }; + +GameLib.System.Linking.prototype.imageNotFound = function(data) { + + /** + * For blender files (and others) - we need to create the textures without images + */ + var textures = GameLib.EntityManager.Instance.queryComponents(GameLib.D3.Texture); + + textures.map(function(texture){ + + /** + * data.image is a runtime image + */ + if (texture.image === data.image.id) { + + /** + * This texture will never load because the image does not exist - so create + * its instance (without the image) + */ + texture.instance = texture.createInstance(); + + /** + * We override the 'loaded' value - we say its loaded because it will be just with defaults + * @type {boolean} + */ + texture.loaded = true; + + GameLib.Event.Emit( + GameLib.Event.TEXTURE_INSTANCE_CREATED, + { + texture: texture + } + ); + } + }); + +}; + GameLib.System.Linking.prototype.meshInstanceCreated = function(data) { this.resolveDependencies(data.mesh); @@ -421,21 +464,10 @@ GameLib.System.Linking.prototype.onParentEntityChange = function(data) { 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.imageNotFoundSubscription.remove(); this.componentCreatedSubscription.remove(); this.parentSceneChangeSubscription.remove(); this.parentEntityChangeSubscription.remove(); diff --git a/src/game-lib-system-storage.js b/src/game-lib-system-storage.js index 8effc6d..b4b260b 100644 --- a/src/game-lib-system-storage.js +++ b/src/game-lib-system-storage.js @@ -90,6 +90,8 @@ GameLib.System.Storage = function( this.saveSubscription = null; this.loadSubscription = null; this.loadImageSubscription = null; + this.blenderDataSubscription = null + this.imageUploadCompleteSubscription = null; }; GameLib.System.Storage.prototype = Object.create(GameLib.System.prototype); @@ -118,6 +120,16 @@ GameLib.System.Storage.prototype.start = function() { GameLib.Event.LOAD_IMAGE, this.loadImage ); + + this.blenderDataSubscription = this.subscribe( + GameLib.Event.BLENDER_DATA_RECEIVED, + this.processBlenderData + ); + + this.imageUploadCompleteSubscription = this.subscribe( + GameLib.Event.IMAGE_UPLOAD_COMPLETE, + this.imageUploadComplete + ); }; /** @@ -452,6 +464,141 @@ GameLib.System.Storage.prototype.load = function(data) { }; +/** + * Once we have an image uploaded - we should load them all again - if their runtime version already exist, do nothing, + * otherwise, create the runtime version of it + * @param data + */ +GameLib.System.Storage.prototype.imageUploadComplete = function(data) { + + var runtimeImages = GameLib.EntityManager.Instance.queryComponents(GameLib.D3.Image); + + /** + * Process all images - we have to load them in addition to creating their runtime components + */ + data.images.map(function(imageData){ + + var image = runtimeImages.reduce( + function(result, runtimeImage){ + + if (imageData.id === runtimeImage.id) { + result = runtimeImage; + } + + return result; + + }, + null + ); + + if (image) { + /** + * Do Nothing - the runtime version of this image already exists and simply needs to load + */ + } else { + /** + * We don't have this runtime version of the image - create it + * @type {GameLib.D3.Image} + */ + image = GameLib.D3.Image.FromObject(this.graphics, imageData); + } + + /** + * Finally, load the image + */ + GameLib.Event.Emit( + GameLib.Event.LOAD_IMAGE, + { + image : image + } + ); + + }.bind(this)); +}; + +/** + * Process Blender Data - Basically does what 'load' does - but because we already have the data we don't have + * a complicated load pattern - we create the runtime components in the best order we can (images load async unfortunately) + * and announce their creation so the linking system can link them + * @param data + */ +GameLib.System.Storage.prototype.processBlenderData = function(data) { + + console.log('loading blender data'); + + /** + * Process all images - we have to load them in addition to creating their runtime components + */ + data.images.map(function(imageData){ + + var image = GameLib.D3.Image.FromObject(this.graphics, imageData); + + GameLib.Event.Emit( + GameLib.Event.LOAD_IMAGE, + { + image : image + } + ); + + GameLib.Event.Emit( + GameLib.Event.COMPONENT_CREATED, + { + component: image + } + ); + + }.bind(this)); + + /** + * Process all textures + */ + data.textures.map(function(textureData){ + + var texture = GameLib.D3.Texture.FromObject(this.graphics, textureData); + + GameLib.Event.Emit( + GameLib.Event.COMPONENT_CREATED, + { + component: texture + } + ); + }.bind(this)); + + /** + * Process all materials + */ + data.materials.map(function(materialData){ + + var material = GameLib.D3.Material.FromObject(this.graphics, materialData); + + GameLib.Event.Emit( + GameLib.Event.COMPONENT_CREATED, + { + component: material + } + ); + }.bind(this)); + + /** + * Now process all meshes + */ + data.meshes.map(function(meshData){ + + var mesh = GameLib.D3.Mesh.FromObject(this.graphics, meshData); + + GameLib.Event.Emit( + GameLib.Event.COMPONENT_CREATED, + { + component: mesh + } + ); + }.bind(this)); + + /** + * And that should be it... + */ +}; + GameLib.System.Storage.prototype.loadImage = function(data) { console.log('loading image : ' + data.image.name); @@ -464,7 +611,7 @@ GameLib.System.Storage.prototype.loadImage = function(data) { var image = data.image; - var url = this.apiUploadUrl + image.path + '?ts=' + Date.now(); + var url = this.apiUrl + image.path + image.fileName + image.extension + '?ts=' + Date.now(); var preflight = new XMLHttpRequest(); @@ -496,12 +643,29 @@ GameLib.System.Storage.prototype.loadImage = function(data) { var url = window.URL.createObjectURL(this.response); } else { if (onError) { + + GameLib.Event.Emit( + GameLib.Event.IMAGE_NOT_FOUND, + { + image : image + } + ); + onError(image, {message:'Image not found'}); + return; } } } catch (error) { if (onError) { + + GameLib.Event.Emit( + GameLib.Event.IMAGE_NOT_FOUND, + { + image : image + } + ); + onError(image, {message:'Image not found'}); return; } @@ -570,5 +734,7 @@ GameLib.System.Storage.prototype.stop = function() { this.loadSubscription.remove(); this.saveSubscription.remove(); this.loadImageSubscription.remove(); + this.blenderDataSubscription.remove(); + this.imageUploadCompleteSubscription.remove(); };