diff --git a/CHANGES.md b/CHANGES.md index 29d0f6045fc..ca97d63f3d0 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,21 @@ # PlayCanvas Engine Changes +### v0.169.1 +* [FIX] Absolute script URLs + +### v0.169.0 +* Scripts referenced in scene are preloaded during loadScene() and loadSceneHierarchy() +* No longer load scripts during app.preload() + +### v0.168.12 +* [FIX] Jumping pixels on normal mapped reflections + +### v0.168.11 +* [FIX] texCubeLOD path bug introduced in 0.168.9 + +### v0.168.10 +* Improve specular occlusion + ### v0.168.9 * Added Dual-Paraboloid atlased reflections * Use Spherical Harmonics instead of low-res cubemap diff --git a/VERSION b/VERSION index 2c28a1f0572..e9d5c31c064 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.169.0-dev +0.170.0-dev diff --git a/build/build.py b/build/build.py index c430589bedb..3894f4dbccc 100644 --- a/build/build.py +++ b/build/build.py @@ -152,8 +152,8 @@ def insert_versions(path): # open source, read in data and replace with version and revision numbers sf = open(path, 'r') text = sf.read() - text = text.replace("__CURRENT_SDK_VERSION__", get_version()) - text = text.replace("__REVISION__", get_revision()) + text = text.replace("__CURRENT_SDK_VERSION__", get_version().strip()) + text = text.replace("__REVISION__", get_revision().strip()) # Open a temporary destination file dst = path + '.tmp' diff --git a/src/core/core_core.js b/src/core/core_core.js index 691a01ff65e..fbe877e7e73 100644 --- a/src/core/core_core.js +++ b/src/core/core_core.js @@ -9,6 +9,11 @@ * Contains: https://github.com/tildeio/rsvp.js - see page for license information */ var pc = { + + version: "__CURRENT_SDK_VERSION__", + + revision: "__REVISION__", + /** * @name pc.config * @description Configuration data made available to the application from the server diff --git a/src/core/core_log.js b/src/core/core_log.js index 010458a5aff..b7cbbfdce46 100644 --- a/src/core/core_log.js +++ b/src/core/core_log.js @@ -19,7 +19,7 @@ pc.extend(pc, function () { * @param {String} text */ open: function (text) { - pc.log.write(Date()); + pc.log.write("Powered by PlayCanvas " + pc.version + " " + pc.revision); }, /** @@ -106,4 +106,4 @@ var logWARNING = pc.log.warning; var logERROR = pc.log.error; var logALERT = pc.log.alert; -var logASSERT = pc.log.assert; \ No newline at end of file +var logASSERT = pc.log.assert; diff --git a/src/framework/framework_application.js b/src/framework/framework_application.js index 9930fd54b96..e00ab000551 100644 --- a/src/framework/framework_application.js +++ b/src/framework/framework_application.js @@ -59,7 +59,7 @@ pc.extend(pc, function () { this._inTools = false; this._scriptPrefix = options.scriptPrefix || ''; - this._scripts = []; + // this._scripts = []; this.loader.addHandler("animation", new pc.AnimationHandler()); this.loader.addHandler("model", new pc.ModelHandler(this.graphicsDevice)); @@ -115,6 +115,21 @@ pc.extend(pc, function () { }; + + // Mini-object used to measure progress of loading sets + var Progress = function (length) { + this.length = length; + this.count = 0; + + this.inc = function () { + this.count++; + } + + this.done = function () { + return (this.count === this.length); + } + }; + Application.prototype = { /** * @name pc.Application#configure @@ -129,7 +144,6 @@ pc.extend(pc, function () { var priorityScripts = response['priority_scripts']; self._parseApplicationProperties(props, function (err) { - self._parseScripts(scripts, priorityScripts); self._parseAssets(assets); if (!err) { callback(null); @@ -147,29 +161,12 @@ pc.extend(pc, function () { preload: function (callback) { var self = this; - this.systems.script.preloading = true; - // get list of assets to preload var assets = this.assets.list({ preload: true }); - // Mini-object used to measure progress of loading sets - var Progress = function (length) { - this.length = length; - this.count = 0; - - this.inc = function () { - this.count++; - } - - this.done = function () { - return (this.count === this.length); - } - }; - var _assets = new Progress(assets.length); - var _scripts = new Progress(this._scripts.length); var _done = false; @@ -180,17 +177,16 @@ pc.extend(pc, function () { return; } - if (!_done && _assets.done() && _scripts.done()) { + if (!_done && _assets.done()) { _done = true; - self.systems.script.preloading = false; callback(); } }; - // totals loading progress of assets and scripts - var total = assets.length + this._scripts.length; + // totals loading progress of assets + var total = assets.length; var count = function () { - return _assets.count + _scripts.count; + return _assets.count; }; var i; @@ -228,24 +224,6 @@ pc.extend(pc, function () { } else { done(); } - - if (_scripts.length) { - for (i = 0; i < _scripts.length; i++) { - var url = pc.path.join(this._scriptPrefix, this._scripts[i]); - this.loader.load(url, "script", function (err, ScriptType) { - if (err) { - console.error(err); - } - - _scripts.inc(); - if (_scripts.done()) { - done(); - } - }); - } - } else { - done(); - } }, /** @@ -267,22 +245,36 @@ pc.extend(pc, function () { * }); */ loadSceneHierarchy: function (url, callback) { - var parser = new pc.SceneParser(this); + var self = this; + + // Because we need to load scripts before we instance the hierarchy (i.e. before we create script components) + // Split loading into load and open + var handler = this.loader.getHandler("hierarchy"); + + handler.load(url, function (err, data) { + var settings = data.settings + + // called after scripts are preloaded + var _loaded = function () { + var entity = handler.open(url, data); - this.loader.load(url, "hierarchy", function (err, entity) { - // clear from cache because this data is modified by entity operations (e.g. destroy) - this.loader.clearCache(url, "hierarchy"); + // clear from cache because this data is modified by entity operations (e.g. destroy) + self.loader.clearCache(url, "hierarchy"); - // add to hierarchy - this.root.addChild(entity); + // add to hierarchy + self.root.addChild(entity); - // initialize components - pc.ComponentSystem.initialize(entity); - pc.ComponentSystem.postInitialize(entity); + // initialize components + pc.ComponentSystem.initialize(entity); + pc.ComponentSystem.postInitialize(entity); - if (callback) { - callback(err, entity); + if (callback) { + callback(err, entity); + } } + + // load priority and referenced scripts before opening scene + this._preloadScripts(data, _loaded); }.bind(this)); }, @@ -319,6 +311,7 @@ pc.extend(pc, function () { }, loadScene: function (url, callback) { + var self = this; var first = true; // If there is an existing scene destroy it @@ -329,36 +322,45 @@ pc.extend(pc, function () { this.scene = null; } - this.loader.load(url, "scene", function (err, scene) { + var handler = this.loader.getHandler("scene"); + + handler.load(url, function (err, data) { if (!err) { - // clear scene from cache because we'll destroy it when we load another one - // so data will be invalid - this.loader.clearCache(url, "scene"); + var _loaded = function () { + // parse and create scene + var scene = handler.open(url, data); - this.loader.patch({ - resource: scene, - type: "scene" - }, this.assets); + // clear scene from cache because we'll destroy it when we load another one + // so data will be invalid + self.loader.clearCache(url, "scene"); - this.root.addChild(scene.root); + self.loader.patch({ + resource: scene, + type: "scene" + }, self.assets); - // Initialise pack settings - if (this.systems.rigidbody && typeof Ammo !== 'undefined') { - this.systems.rigidbody.setGravity(scene._gravity.x, scene._gravity.y, scene._gravity.z); - } + self.root.addChild(scene.root); - if (!first) { - // if this is not the initial scene - // we need to run component initialization - // first scene is initialized in app.start() - pc.ComponentSystem.initialize(scene.root); - pc.ComponentSystem.postInitialize(scene.root); - } + // Initialise pack settings + if (self.systems.rigidbody && typeof Ammo !== 'undefined') { + self.systems.rigidbody.setGravity(scene._gravity.x, scene._gravity.y, scene._gravity.z); + } - if (callback) { - callback(null, scene); + if (!first) { + // if this is not the initial scene + // we need to run component initialization + // first scene is initialized in app.start() + pc.ComponentSystem.initialize(scene.root); + pc.ComponentSystem.postInitialize(scene.root); + } + + if (callback) { + callback(null, scene); + } } + // preload scripts before opening scene + this._preloadScripts(data, _loaded); } else { if (callback) { callback(err); @@ -367,6 +369,44 @@ pc.extend(pc, function () { }.bind(this)); }, + _preloadScripts: function (sceneData, callback) { + var self = this; + + self.systems.script.preloading = true; + + var scripts = this._getScriptReferences(sceneData); + + var i = 0, l = scripts.length; + var progress = new Progress(l); + var scriptUrl; + var regex = /^http(s)?:\/\//; + + if (l) { + for (i = 0; i < l; i++) { + scriptUrl = scripts[i]; + // support absolute URLs (for now) + if (!regex.test(scriptUrl.toLowerCase())) { + scriptUrl = pc.path.join(this._scriptPrefix, scripts[i]); + } + + this.loader.load(scriptUrl, "script", function (err, ScriptType) { + if (err) { + console.error(err); + } + + progress.inc(); + if (progress.done()) { + self.systems.script.preloading = false; + callback(); + } + }); + } + } else { + self.systems.script.preloading = false; + callback(); + } + }, + // set application properties from data file _parseApplicationProperties: function (props, callback) { this._width = props['width']; @@ -410,25 +450,40 @@ pc.extend(pc, function () { } }, - // copy list of script urls to preload - _parseScripts: function (scripts, priorityScripts) { - var i; + _getScriptReferences: function (scene) { + var i, key; + + var priorityScripts = []; + if (scene.settings.priority_scripts) { + priorityScripts = scene.settings.priority_scripts; + } - this._scripts = []; + var _scripts = []; + var _index = {}; // first add priority scripts - if (priorityScripts) { - for (i = 0; i < priorityScripts.length; i++) { - this._scripts.push(priorityScripts[i]); - } + for (i = 0; i < priorityScripts.length; i++) { + _scripts.push(priorityScripts[i]); + _index[priorityScripts[i]] = true; } - // then add rest of scripts in order - for (i = 0; i < scripts.length; i++) { - if (this._scripts.indexOf(scripts[i]) < 0) { - this._scripts.push(scripts[i]); + // then interate hierarchy to get referenced scripts + var entities = scene.entities; + for (key in entities) { + if (!entities[key].components.script) { + continue; + } + + var scripts = entities[key].components.script.scripts; + for(i = 0; i < scripts.length; i++) { + if (_index[scripts[i].url]) + continue; + _scripts.push(scripts[i].url); + _index[scripts[i].url] = true; } } + + return _scripts; }, /** diff --git a/src/graphics/programlib/chunks/aoSpecOcc.frag b/src/graphics/programlib/chunks/aoSpecOcc.frag index 784c88283e2..787c1fdaf0c 100644 --- a/src/graphics/programlib/chunks/aoSpecOcc.frag +++ b/src/graphics/programlib/chunks/aoSpecOcc.frag @@ -1,12 +1,9 @@ -uniform float material_occludeSpecularContrast; uniform float material_occludeSpecularIntensity; void occludeSpecular(inout psInternalData data) { - // fake specular occlusion from AO - float specPow = exp2(data.glossiness * 4.0); // 0 - 128 - specPow = max(specPow, 0.0001); - float specOcc = saturate(pow(data.ao * (data.glossiness + 1.0), specPow)); - - specOcc = mix(data.ao, specOcc, material_occludeSpecularContrast); + // approximated specular occlusion from AO + float specPow = exp2(data.glossiness * 11.0); + // http://research.tri-ace.com/Data/cedec2011_RealtimePBR_Implementation_e.pptx + float specOcc = saturate(pow(dot(data.normalW, data.viewDirW) + data.ao, 0.01*specPow) - 1.0 + data.ao); specOcc = mix(1.0, specOcc, material_occludeSpecularIntensity); data.specularLight *= specOcc; diff --git a/src/graphics/programlib/chunks/aoSpecOccConst.frag b/src/graphics/programlib/chunks/aoSpecOccConst.frag index ab7868e4dc5..ac516f6abb1 100644 --- a/src/graphics/programlib/chunks/aoSpecOccConst.frag +++ b/src/graphics/programlib/chunks/aoSpecOccConst.frag @@ -1,8 +1,9 @@ -uniform float material_occludeSpecularIntensity; void occludeSpecular(inout psInternalData data) { - // fake specular occlusion from AO - float specOcc = data.ao; - specOcc = mix(1.0, specOcc, material_occludeSpecularIntensity); + // approximated specular occlusion from AO + float specPow = exp2(data.glossiness * 11.0); + // http://research.tri-ace.com/Data/cedec2011_RealtimePBR_Implementation_e.pptx + float specOcc = saturate(pow(dot(data.normalW, data.viewDirW) + data.ao, 0.01*specPow) - 1.0 + data.ao); + data.specularLight *= specOcc; data.reflection *= specOcc; } diff --git a/src/resources/resources_cubemap.js b/src/resources/resources_cubemap.js index 684236a9c5c..d2a6ce522fd 100644 --- a/src/resources/resources_cubemap.js +++ b/src/resources/resources_cubemap.js @@ -86,7 +86,7 @@ pc.extend(pc, function () { if (data.dds) { data.dds.fixCubemapSeams = true; - data.dds.minFilter = pc.FILTER_LINEAR; + data.dds.minFilter = this._device.useTexCubeLod? pc.FILTER_LINEAR_MIPMAP_LINEAR : pc.FILTER_LINEAR; data.dds.magFilter = pc.FILTER_LINEAR; data.dds.addressU = pc.ADDRESS_CLAMP_TO_EDGE; data.dds.addressV = pc.ADDRESS_CLAMP_TO_EDGE; diff --git a/src/resources/resources_loader.js b/src/resources/resources_loader.js index bed33e447a7..ca495f76ce6 100644 --- a/src/resources/resources_loader.js +++ b/src/resources/resources_loader.js @@ -32,6 +32,10 @@ pc.extend(pc, function () { delete this._handlers[type]; }, + getHandler: function (type) { + return this._handlers[type]; + }, + /** * @function * @name pc.ResourceLoader#load diff --git a/src/scene/scene_phongmaterial.js b/src/scene/scene_phongmaterial.js index 9165a86a778..80b274302cd 100644 --- a/src/scene/scene_phongmaterial.js +++ b/src/scene/scene_phongmaterial.js @@ -82,7 +82,6 @@ pc.extend(pc, function () { * @property {pc.Texture} aoMap Baked ambient occlusion map. Modulates ambient color. * @property {Boolean} occludeSpecular Uses aoMap to occlude specular/reflection. It's a hack, because real specular occlusion is view-dependent. However, it's much better than nothing. * @property {Number} occludeSpecularIntensity Controls visibility of specular occlusion. - * @property {Number} occludeSpecularContrast Controls contrast of specular occlusion. * @property {Boolean} specularAntialias Enables Toksvig AA for mipmapped normal maps with specular. * @property {Boolean} conserveEnergy Defines how diffuse and specular components are combined when Fresnel is on. It is recommended that you leave this option enabled, although you may want to disable it in case when all reflection comes only from a few light sources, and you don't use an environment map, therefore having mostly black reflection. @@ -222,7 +221,6 @@ pc.extend(pc, function () { this.specularAntialias = false; this.conserveEnergy = true; this.occludeSpecular = true; - this.occludeSpecularContrast = 1; this.occludeSpecularIntensity = 1; this.shadingModel = pc.SPECULAR_PHONG; this.fresnelModel = pc.FRESNEL_NONE; @@ -434,9 +432,6 @@ pc.extend(pc, function () { if (this.occludeSpecular) { this.setParameter('material_occludeSpecularIntensity', this.occludeSpecularIntensity); - if (this.occludeSpecularContrast > 0) { - this.setParameter('material_occludeSpecularContrast', this.occludeSpecularContrast); - } } if (this.cubeMapProjection===pc.CUBEPROJ_BOX) { @@ -651,7 +646,7 @@ pc.extend(pc, function () { specularAA: this.specularAntialias, conserveEnergy: this.conserveEnergy, occludeSpecular: this.occludeSpecular, - occludeSpecularFloat: (this.occludeSpecularContrast > 0), + occludeSpecularFloat: (this.occludeSpecularIntensity !== 1.0), occludeDirect: this.occludeDirect, shadingModel: this.shadingModel, fresnelModel: this.fresnelModel,