From 5c5ea74e149189cdfe8e1b763878c8f4ff4ca6c8 Mon Sep 17 00:00:00 2001 From: -=yb4f310 Date: Mon, 9 Oct 2017 10:18:59 +0200 Subject: [PATCH] render to canvas - persist loading in case of errors --- src/game-lib-a-component-a.js | 2 + src/game-lib-d3-api-canvas.js | 59 +++++++++++++++++ src/game-lib-d3-api-render-target.js | 21 ------ src/game-lib-d3-api-renderer.js | 24 +++++++ src/game-lib-d3-api-texture.js | 8 +++ src/game-lib-d3-canvas.js | 95 ++++++++++++++++++++++++++++ src/game-lib-d3-render-target.js | 64 +++++++++---------- src/game-lib-d3-renderer.js | 55 +++++++++++++++- src/game-lib-d3-texture.js | 32 ++++++++-- src/game-lib-system-linking.js | 5 +- src/game-lib-system-storage.js | 26 ++++---- 11 files changed, 320 insertions(+), 71 deletions(-) create mode 100644 src/game-lib-d3-api-canvas.js create mode 100644 src/game-lib-d3-canvas.js diff --git a/src/game-lib-a-component-a.js b/src/game-lib-a-component-a.js index 42e9aac..ff01ec1 100644 --- a/src/game-lib-a-component-a.js +++ b/src/game-lib-a-component-a.js @@ -187,6 +187,7 @@ GameLib.Component.COMPONENT_CONTROLS_KEYBOARD = 0x39; GameLib.Component.COMPONENT_CONTROLS_MOUSE = 0x3a; GameLib.Component.COMPONENT_MESH_TEXT = 0x3b; GameLib.Component.COMPONENT_FONT = 0x3c; +GameLib.Component.COMPONENT_CANVAS = 0x3d; /** * Returns string name for component number @@ -256,6 +257,7 @@ GameLib.Component.GetComponentName = function(number) { case 0x3a : return 'GameLib.D3.Controls.Mouse'; case 0x3b : return 'GameLib.D3.Mesh.Text'; case 0x3c : return 'GameLib.D3.Font'; + case 0x3d : return 'GameLib.D3.Canvas'; break; } diff --git a/src/game-lib-d3-api-canvas.js b/src/game-lib-d3-api-canvas.js new file mode 100644 index 0000000..a5b66d4 --- /dev/null +++ b/src/game-lib-d3-api-canvas.js @@ -0,0 +1,59 @@ +/** + * Raw Canvas API object - should always correspond with the Canvas Schema + * @param id + * @param name + * @param width + * @param height + * @param parentEntity + * @constructor + */ +GameLib.D3.API.Canvas = function( + id, + name, + width, + height, + parentEntity +) { + if (GameLib.Utils.UndefinedOrNull(id)) { + id = GameLib.Utils.RandomId(); + } + this.id = id; + + if (GameLib.Utils.UndefinedOrNull(name)) { + name = 'Canvas (' + id + ')'; + } + this.name = name; + + if (GameLib.Utils.UndefinedOrNull(width)) { + width = 512; + } + this.width = width; + + if (GameLib.Utils.UndefinedOrNull(height)) { + height = 512; + } + this.height = height; + + if (GameLib.Utils.UndefinedOrNull(parentEntity)){ + parentEntity = null; + } + this.parentEntity = parentEntity; +}; + +GameLib.D3.API.Canvas.prototype = Object.create(GameLib.Component.prototype); +GameLib.D3.API.Canvas.prototype.constructor = GameLib.D3.API.Canvas; + +/** + * Returns an API light from an Object light + * @param objectCanvas + * @constructor + */ +GameLib.D3.API.Canvas.FromObject = function(objectCanvas) { + return new GameLib.D3.API.Canvas( + objectCanvas.id, + objectCanvas.name, + objectCanvas.width, + objectCanvas.height, + objectCanvas.parentEntity + ); +}; diff --git a/src/game-lib-d3-api-render-target.js b/src/game-lib-d3-api-render-target.js index affdc5c..71640bf 100644 --- a/src/game-lib-d3-api-render-target.js +++ b/src/game-lib-d3-api-render-target.js @@ -17,9 +17,6 @@ GameLib.D3.API.RenderTarget = function ( name, width, height, - minFilter, - magFilter, - format, stencilBuffer, texture, parentEntity @@ -44,21 +41,6 @@ GameLib.D3.API.RenderTarget = function ( } this.height = height; - if (GameLib.Utils.UndefinedOrNull(minFilter)) { - minFilter = GameLib.D3.RenderTarget.LINEAR_FILTER; - } - this.minFilter = minFilter; - - if (GameLib.Utils.UndefinedOrNull(magFilter)) { - magFilter = GameLib.D3.RenderTarget.LINEAR_FILTER; - } - this.magFilter = magFilter; - - if (GameLib.Utils.UndefinedOrNull(format)) { - format = GameLib.D3.RenderTarget.RGB_FORMAT; - } - this.format = format; - if (GameLib.Utils.UndefinedOrNull(stencilBuffer)) { stencilBuffer = false; } @@ -90,9 +72,6 @@ GameLib.D3.API.RenderTarget.FromObject = function(objectComponent) { objectComponent.name, objectComponent.width, objectComponent.height, - objectComponent.minFilter, - objectComponent.magFilter, - objectComponent.format, objectComponent.stencilBuffer, objectComponent.texture, objectComponent.parentEntity diff --git a/src/game-lib-d3-api-renderer.js b/src/game-lib-d3-api-renderer.js index ce6d4b8..d4f74c2 100644 --- a/src/game-lib-d3-api-renderer.js +++ b/src/game-lib-d3-api-renderer.js @@ -14,6 +14,9 @@ * @param parentEntity * @param preserveDrawingBuffer * @param clippingPlanes + * @param bufferScene + * @param bufferCamera + * @param renderTarget * @constructor */ GameLib.D3.API.Renderer = function ( @@ -30,6 +33,9 @@ GameLib.D3.API.Renderer = function ( scenes, viewports, clippingPlanes, + bufferScene, + bufferCamera, + renderTarget, parentEntity ) { if (GameLib.Utils.UndefinedOrNull(id)) { @@ -101,6 +107,21 @@ GameLib.D3.API.Renderer = function ( } this.clippingPlanes = clippingPlanes; + if (GameLib.Utils.UndefinedOrNull(bufferScene)) { + bufferScene = null; + } + this.bufferScene = bufferScene; + + if (GameLib.Utils.UndefinedOrNull(bufferCamera)) { + bufferCamera = camera; + } + this.bufferCamera = bufferCamera; + + if (GameLib.Utils.UndefinedOrNull(renderTarget)) { + renderTarget = null; + } + this.renderTarget = renderTarget; + if (GameLib.Utils.UndefinedOrNull(parentEntity)) { parentEntity = null; } @@ -131,6 +152,9 @@ GameLib.D3.API.Renderer.FromObject = function(objectComponent) { objectComponent.scenes, objectComponent.viewports, objectComponent.clippingPlanes, + objectComponent.bufferScene, + objectComponent.bufferCamera, + objectComponent.renderTarget, objectComponent.parentEntity ); }; diff --git a/src/game-lib-d3-api-texture.js b/src/game-lib-d3-api-texture.js index a9f48f9..31a610a 100644 --- a/src/game-lib-d3-api-texture.js +++ b/src/game-lib-d3-api-texture.js @@ -21,6 +21,7 @@ * @param unpackAlignment * @param premultiplyAlpha * @param encoding + * @param canvas * @param parentEntity * @constructor */ @@ -46,6 +47,7 @@ GameLib.D3.API.Texture = function( unpackAlignment, premultiplyAlpha, encoding, + canvas, parentEntity ) { if (GameLib.Utils.UndefinedOrNull(parentEntity)) { @@ -158,6 +160,11 @@ GameLib.D3.API.Texture = function( } this.encoding = encoding; + if (GameLib.Utils.UndefinedOrNull(canvas)) { + canvas = null; + } + this.canvas = canvas; + this.needsUpdate = false; }; @@ -192,6 +199,7 @@ GameLib.D3.API.Texture.FromObject = function(objectTexture) { objectTexture.unpackAlignment, objectTexture.premultiplyAlpha, objectTexture.encoding, + objectTexture.canvas, objectTexture.parentEntity ) }; diff --git a/src/game-lib-d3-canvas.js b/src/game-lib-d3-canvas.js new file mode 100644 index 0000000..00e3444 --- /dev/null +++ b/src/game-lib-d3-canvas.js @@ -0,0 +1,95 @@ +/** + * Canvas Superset - The apiCanvas properties get moved into the Canvas object itself, and then the instance is created + * @param graphics GameLib.D3.Graphics + * @param apiCanvas GameLib.D3.API.Canvas + * @constructor + */ +GameLib.D3.Canvas = function( + graphics, + apiCanvas +) { + this.graphics = graphics; + this.graphics.isNotThreeThrow(); + + if (GameLib.Utils.UndefinedOrNull(apiCanvas)) { + apiCanvas = {}; + } + + if (apiCanvas instanceof GameLib.D3.Canvas) { + return apiCanvas; + } + + GameLib.D3.API.Canvas.call( + this, + apiCanvas.id, + apiCanvas.name, + apiCanvas.width, + apiCanvas.height, + apiCanvas.parentEntity + ); + + GameLib.Component.call( + this, + GameLib.Component.COMPONENT_CANVAS + ); +}; + +GameLib.D3.Canvas.prototype = Object.create(GameLib.D3.API.Canvas.prototype); +GameLib.D3.Canvas.prototype.constructor = GameLib.D3.Canvas; + +/** + * Creates a light instance + * @returns {*} + */ +GameLib.D3.Canvas.prototype.createInstance = function() { + + var instance = document.createElement('canvas'); + + instance.width = this.width; + instance.height = this.height; + + return instance; +}; + +/** + * Updates the instance with the current state + */ +GameLib.D3.Canvas.prototype.updateInstance = function() { + + if (GameLib.Utils.UndefinedOrNull(this.instance)) { + this.instance = document.createElement('canvas'); + this.loaded = true; + } + + this.instance.width = this.width; + this.instance.height = this.height; +}; + +/** + * Converts a GameLib.D3.Canvas to a GameLib.D3.API.Canvas + * @returns {GameLib.D3.API.Canvas} + */ +GameLib.D3.Canvas.prototype.toApiObject = function() { + return new GameLib.D3.API.Canvas( + this.id, + this.name, + this.width, + this.height, + GameLib.Utils.IdOrNull(this.parentEntity) + ); +}; + +/** + * Returns a new GameLib.D3.Canvas from a GameLib.D3.API.Canvas + * @param graphics GameLib.D3.Graphics + * @param objectCanvas GameLib.D3.API.Canvas + * @returns {GameLib.D3.Canvas} + */ +GameLib.D3.Canvas.FromObject = function(graphics, objectCanvas) { + + return new GameLib.D3.Canvas( + graphics, + GameLib.D3.API.Canvas.FromObject(objectCanvas) + ); + +}; \ No newline at end of file diff --git a/src/game-lib-d3-render-target.js b/src/game-lib-d3-render-target.js index f89e898..5848e57 100644 --- a/src/game-lib-d3-render-target.js +++ b/src/game-lib-d3-render-target.js @@ -26,9 +26,6 @@ GameLib.D3.RenderTarget = function ( apiRenderTarget.name, apiRenderTarget.width, apiRenderTarget.height, - apiRenderTarget.minFilter, - apiRenderTarget.magFilter, - apiRenderTarget.format, apiRenderTarget.stencilBuffer, apiRenderTarget.texture ); @@ -45,35 +42,28 @@ GameLib.D3.RenderTarget = function ( GameLib.D3.RenderTarget.prototype = Object.create(GameLib.D3.API.RenderTarget.prototype); GameLib.D3.RenderTarget.prototype.constructor = GameLib.D3.RenderTarget; -/** - * Some constants (based on THREE.js constants - update if needed) - * @type {number} - */ -GameLib.D3.RenderTarget.LINEAR_FILTER = 1006; -GameLib.D3.RenderTarget.RGB_FORMAT = 1022; -GameLib.D3.RenderTarget.RGBA_FORMAT = 1023; - /** * Creates a Render Target instance * @returns {*} */ GameLib.D3.RenderTarget.prototype.createInstance = function() { - var instance = new THREE.WebGLRenderTarget( - this.width, - this.height, - { - minFilter : this.minFilter, - magFilter : this.magFilter, - format : this.format, - stencilBuffer : this.stencilBuffer - } - ); + var instance = null; + + if (this.texture && this.texture.loaded) { + + instance = new THREE.WebGLRenderTarget( + this.width, + this.height, + { + stencilBuffer : this.stencilBuffer + } + ); - if (this.texture instanceof GameLib.D3.Texture && this.texture.instance) { instance.texture = this.texture.instance; } + return instance; }; @@ -81,14 +71,25 @@ GameLib.D3.RenderTarget.prototype.createInstance = function() { * updates instance */ GameLib.D3.RenderTarget.prototype.updateInstance = function() { - this.instance.width = this.width; - this.instance.height = this.height; - this.instance.minFilter = this.minFilter; - this.instance.magFilter = this.magFilter; - this.instance.format = this.format; - this.instance.stencilBuffer = this.stencilBuffer; - this.instance.texture = this.texture.instance; - this.instance.texture.needsUpdate = true; + + if (this.instance) { + this.instance.setSize(this.width, this.height); + this.instance.stencilBuffer = this.stencilBuffer; + if (this.texture && this.texture.loaded) { + this.instance.texture = this.texture.instance; + this.instance.texture.needsUpdate = true; + } else { + this.instance.texture = null; + } + } else { + this.instance = this.createInstance(); + + if (this.instance) { + this.loaded = true; + } + } + + }; /** @@ -102,9 +103,6 @@ GameLib.D3.RenderTarget.prototype.toApiObject = function() { this.name, this.width, this.height, - this.minFilter, - this.magFilter, - this.format, this.stencilBuffer, GameLib.Utils.IdOrNull(this.texture), GameLib.Utils.IdOrNull(this.parentEntity) diff --git a/src/game-lib-d3-renderer.js b/src/game-lib-d3-renderer.js index abb4916..3687d68 100644 --- a/src/game-lib-d3-renderer.js +++ b/src/game-lib-d3-renderer.js @@ -37,6 +37,9 @@ GameLib.D3.Renderer = function ( apiRenderer.scenes, apiRenderer.viewports, apiRenderer.clippingPlanes, + apiRenderer.bufferScene, + apiRenderer.bufferCamera, + apiRenderer.renderTarget, apiRenderer.parentEntity ); @@ -100,6 +103,27 @@ GameLib.D3.Renderer = function ( } }.bind(this)); + if (this.bufferScene instanceof GameLib.D3.API.Scene) { + this.bufferScene = new GameLib.D3.Scene( + this.graphics, + this.bufferScene + ) + } + + if (this.bufferCamera instanceof GameLib.D3.API.Camera) { + this.bufferCamera = new GameLib.D3.Camera( + this.graphics, + this.bufferCamera + ) + } + + if (this.renderTarget instanceof GameLib.D3.API.RenderTarget) { + this.renderTarget = new GameLib.D3.RenderTarget( + this.graphics, + this.renderTarget + ) + } + /** * Only runtime Renderer Components have runtime statistics */ @@ -116,7 +140,10 @@ GameLib.D3.Renderer = function ( 'camera' : GameLib.D3.Camera, 'scenes' : [GameLib.D3.Scene], 'viewports' : [GameLib.D3.Viewport], - 'clippingPlanes': [GameLib.D3.Mesh.Plane] + 'clippingPlanes': [GameLib.D3.Mesh.Plane], + 'bufferScene' : GameLib.D3.Scene, + 'bufferCamera' : GameLib.D3.Camera, + 'renderTarget' : GameLib.D3.RenderTarget } ); @@ -239,6 +266,14 @@ GameLib.D3.Renderer.prototype.toApiObject = function() { this.viewports.map(function(viewport){ return GameLib.Utils.IdOrNull(viewport); }), + this.clippingPlanes.map( + function(clippingPlane) { + return GameLib.Utils.IdOrNull(clippingPlane); + } + ), + GameLib.Utils.IdOrNull(this.bufferScene), + GameLib.Utils.IdOrNull(this.bufferCamera), + GameLib.Utils.IdOrNull(this.renderTarget), GameLib.Utils.IdOrNull(this.parentEntity) ); @@ -285,6 +320,24 @@ GameLib.D3.Renderer.prototype.render = function(delta) { this.instance.clear(); + if ( + this.bufferScene && + this.bufferScene.loaded && + this.bufferCamera && + this.bufferCamera.loaded && + this.renderTarget && + this.renderTarget.loaded + ) { + /** + * We have a buffer that should render to an offscreen render target + */ + this.instance.render( + this.bufferScene.instance, + this.bufferCamera.instance, + this.renderTarget.instance + ); + } + this.viewports.map( function(viewport) { diff --git a/src/game-lib-d3-texture.js b/src/game-lib-d3-texture.js index bb487b2..c150c5e 100644 --- a/src/game-lib-d3-texture.js +++ b/src/game-lib-d3-texture.js @@ -43,6 +43,7 @@ GameLib.D3.Texture = function( apiTexture.unpackAlignment, apiTexture.premultiplyAlpha, apiTexture.encoding, + apiTexture.canvas, apiTexture.parentEntity ); @@ -65,11 +66,19 @@ GameLib.D3.Texture = function( ); } + if (this.canvas instanceof GameLib.D3.API.Canvas) { + this.canvas = new GameLib.D3.Canvas( + this.graphics, + this.canvas + ); + } + GameLib.Component.call( this, GameLib.Component.COMPONENT_TEXTURE, { - 'image' : GameLib.D3.Image + 'image' : GameLib.D3.Image, + 'canvas' : GameLib.D3.Canvas } ); }; @@ -191,6 +200,8 @@ GameLib.D3.Texture.prototype.createInstance = function() { this.image.instance ); instance.needsUpdate = true; + } else if (this.canvas && this.canvas.instance) { + instance = new THREE.Texture(this.canvas.instance); } else { instance = new THREE.Texture(); } @@ -232,14 +243,20 @@ GameLib.D3.Texture.prototype.updateInstance = function() { if (this.image && this.image.instance) { if (this.typeId === GameLib.D3.Texture.TEXTURE_TYPE_NORMAL) { - if (this.instance.image !== this.image.instance) { - this.instance = new THREE.Texture( + + /** + * Image overrides canvas + */ + if (this.instance.image !== this.image.instance) { + + this.instance = new THREE.Texture( this.image.instance ); this.mapping = this.instance.mapping; imageChanged = true; } + } else if (this.typeId === GameLib.D3.Texture.TEXTURE_TYPE_CUBE) { if (this.instance.image[0] !== this.image.instance) { this.instance = new THREE.CubeTexture( @@ -257,7 +274,13 @@ GameLib.D3.Texture.prototype.updateInstance = function() { imageChanged = true; } } - } + } else if ( + this.canvas && this.canvas.instance + ) { + if (this.typeId === GameLib.D3.Texture.TEXTURE_TYPE_NORMAL) { + console.log('update canvas instance here'); + } + } this.instance.name = this.name; this.instance.flipY = this.flipY; @@ -315,6 +338,7 @@ GameLib.D3.Texture.prototype.toApiObject = function() { this.unpackAlignment, this.premultiplyAlpha, this.encoding, + GameLib.Utils.IdOrNull(this.canvas), GameLib.Utils.IdOrNull(this.parentEntity) ); diff --git a/src/game-lib-system-linking.js b/src/game-lib-system-linking.js index 805e06d..75517a3 100644 --- a/src/game-lib-system-linking.js +++ b/src/game-lib-system-linking.js @@ -922,7 +922,10 @@ GameLib.System.Linking.prototype.onParentSceneChange = function(data) { if (data.originalScene && data.originalScene.removeObject) { data.originalScene.removeObject(data.object); } - data.newScene.addObject(data.object); + + if (data.newScene) { + data.newScene.addObject(data.object); + } } }; diff --git a/src/game-lib-system-storage.js b/src/game-lib-system-storage.js index 906256d..dbb1596 100644 --- a/src/game-lib-system-storage.js +++ b/src/game-lib-system-storage.js @@ -422,10 +422,10 @@ GameLib.System.Storage.prototype.loadComponent = function(apiUrl, toProcess, inc if (clientErrorCallback) { clientErrorCallback('Could not create a runtime component: ' + component.name); } - throw new Error('Could not create a runtime component: ' + component.name); + //throw new Error('Could not create a runtime component: ' + component.name); } - if (parentEntity !== null) { + if (parentEntity !== null && runtimeComponent) { runtimeComponent.parentEntity = parentEntity; } } @@ -439,9 +439,11 @@ GameLib.System.Storage.prototype.loadComponent = function(apiUrl, toProcess, inc ) } - loaded.push(runtimeComponent); + if (runtimeComponent) { + loaded.push(runtimeComponent); + } - if (includeDependencies) { + if (includeDependencies && runtimeComponent) { /** * Before we announce the creation of this component, we should get * a list of all dependencies of this component, because once we announce @@ -525,16 +527,18 @@ GameLib.System.Storage.prototype.loadComponent = function(apiUrl, toProcess, inc * The Linking system will then kick in and try to resolve all dependencies */ - if (onComponentLoaded) { + if (runtimeComponent && onComponentLoaded) { onComponentLoaded(runtimeComponent); } - GameLib.Event.Emit( - GameLib.Event.COMPONENT_CREATED, - { - component: runtimeComponent - } - ); + if (runtimeComponent) { + GameLib.Event.Emit( + GameLib.Event.COMPONENT_CREATED, + { + component: runtimeComponent + } + ); + } var toProcess = GameLib.Utils.Difference(loaded.map(function(component){return component.id}), loading);