From a752f55deb4b1ebb5f70466e59f4f2cc1620852f Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 3 Mar 2016 02:25:41 -0800 Subject: [PATCH 1/5] example of a rainy day/night zone --- examples/entityScripts/lighteningEntity.js | 339 +++++++++++++++++++++ examples/shaders/rainyDayNightSkybox.fs | 126 ++++++++ examples/zones/RainyDayNightZone.json | 27 ++ 3 files changed, 492 insertions(+) create mode 100644 examples/entityScripts/lighteningEntity.js create mode 100644 examples/shaders/rainyDayNightSkybox.fs create mode 100644 examples/zones/RainyDayNightZone.json diff --git a/examples/entityScripts/lighteningEntity.js b/examples/entityScripts/lighteningEntity.js new file mode 100644 index 0000000000..67cc6c8ea6 --- /dev/null +++ b/examples/entityScripts/lighteningEntity.js @@ -0,0 +1,339 @@ +// +// lighteningEntity.js +// examples/entityScripts +// +// Created by Brad Hefta-Gaub on 3/1/16. +// Copyright 2016 High Fidelity, Inc. +// +// This is an example of an entity script which will randomly create a flash of lightening and a thunder sound +// effect, as well as a background rain sound effect. It can be applied to any entity, although it works best +// on a zone entity. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +/*global print, MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, Audio, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, randFloat, randInt, pointInExtents, vec3equal, setEntityCustomData, getEntityCustomData */ +(function () { + "use strict"; + + //////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Various configurable settings. + // + // You can change these values to change some of the various effects of the rain storm. + // These values can also be controlled by setting user properties on the entity that you've attached this script to. + // add a "lightening" section to a JSON encoded portion of the user data... for example: + // { + // "lightening": { + // "flashMax": 20, + // "flashMin": 0, + // "flashMaxRandomness": 10, + // "flashIntensityStepRandomeness": 2, + // "averageLighteningStrikeGap": 120, + // "extraRandomRangeLighteningStrikeGap": 10, + // "thunderURL": "atp:1336efe995398f5e0d46b37585785de8ba872fe9a9b718264db03748cd41c758.wav", + // "thunderVolume": 0.1, + // "rainURL": "atp:e0cc7438aca776636f6e6f731685781d9999b961c945e4e5760d937be5beecdd.wav", + // "rainVolume": 0.05 + // } + // // NOTE: you can have other user data here as well, so long as it's JSON encoded, it won't impact the lightening script + // } + // + //////////////////////////////////////////////////////////////////////////////////////////////////////////// + + var MAX_FLASH_INTENSITY = 20; // this controls how bright the lightening effect appears + var MIN_FLASH_INTENSITY = 0; // this is probably best at 0, but it could be higher, which will make the lightening not fade completely to darkness before going away. + var MAX_FLASH_INTENSITY_RANDOMNESS = 10; // this will add some randomness to the max brightness of the lightening + var FLASH_INTENSITY_STEP_RANDOMNESS = 2; // as the lightening goes from min to max back to min, this will make it more random in it's brightness + var AVERAGE_LIGHTENING_STRIKE_GAP_IN_SECONDS = 120; // how long on average between lighting + var EXTRA_RANDOM_RANGE_LIGHTENING_STRIKE_GAP_IN_SECONDS = 10; // some randomness to the lightening gap + var THUNDER_SOUND_URL = "https://s3.amazonaws.com/hifi-public/brad/rainstorm/thunder-48k.wav"; // thunder sound effect, must be 48k 16bit PCM + var THUNDER_VOLUME = 1; // adjust the volume of the thunder sound effect + var RAIN_SOUND_URL = "https://s3.amazonaws.com/hifi-public/brad/rainstorm/rain.wav"; // the background rain, this will loop + var RAIN_VOLUME = 0.05; // adjust the volume of the rain sound effect. + + //////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Various constants and variables we need access to in all scopes + //////////////////////////////////////////////////////////////////////////////////////////////////////////// + + var MSECS_PER_SECOND = 1000; // number of milliseconds in a second, a universal truth, don't change this. :) + + // This is a little trick for implementing entity scripts. Many of the entity callbacks will have the JavaScript + // "this" variable set, but if you connect to a signal like update, "this" won't correctly point to the instance + // of your entity script, and so we set "_this" and use it in cases we need to access "this". We need to define + // the variable in this scope so that it's not share among all entities. + + var _this; // this is important here... or else the _this will be globally scoped. + + + //////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Various helper functions... + //////////////////////////////////////////////////////////////////////////////////////////////////////////// + + // Helper function for returning either a value, or the default value if the value is undefined. This is + // is handing in parsing JSON where you don't know if the values have been set or not. + function valueOrDefault(value, defaultValue) { + if (value !== undefined) { + return value; + } + return defaultValue; + } + + // return a random float between high and low + function randFloat(low, high) { + return low + Math.random() * (high - low); + } + + // the "constructor" for our class. pretty simple, it just sets our _this, so we can access it later. + function Lightening() { + _this = this; + } + + //////////////////////////////////////////////////////////////////////////////////////////////////////////// + // The class prototype + // + // This is the class definition/prototype for our class. Funtions declared here will be accessible + // via the instance of the entity + // + Lightening.prototype = { + + // preload() + // This is called by every viewer/interface instance that "sees" the entity you've attached this script to. + // If this is a zone entity, then it will surely be called whenever someone enters the zone. + preload: function (entityID) { + + // we always make a point of remember our entityID, so that we can access our entity later + _this.entityID = entityID; + + // set up some of our time related state + var now = Date.now(); + _this.lastUpdate = now; + _this.lastStrike = now; + + // some of our other state related items + _this.lighteningID = false; // this will be the entityID for any lightening that we create + _this.lighteningActive = false; // are we actively managing lightening + + // Get the entities userData property, to see if someone has overridden any of our default settings + var userDataText = Entities.getEntityProperties(entityID, ["userData"]).userData; + var userData = {}; + if (userDataText !== "") { + userData = JSON.parse(userDataText); + } + var lighteningUserData = valueOrDefault(userData.lightening, {}); + _this.flashIntensityStepRandomeness = valueOrDefault(lighteningUserData.flashIntensityStepRandomness, FLASH_INTENSITY_STEP_RANDOMNESS); + _this.flashMax = valueOrDefault(lighteningUserData.flashMax, MAX_FLASH_INTENSITY); + _this.flashMin = valueOrDefault(lighteningUserData.flashMin, MIN_FLASH_INTENSITY); + _this.flashMaxRandomness = valueOrDefault(lighteningUserData.flashMaxRandomness, MAX_FLASH_INTENSITY_RANDOMNESS); + _this.averageLighteningStrikeGap = valueOrDefault(lighteningUserData.averageLighteningStrikeGap, AVERAGE_LIGHTENING_STRIKE_GAP_IN_SECONDS); + _this.extraRandomRangeLighteningStrikeGap = valueOrDefault(lighteningUserData.extraRandomRangeLighteningStrikeGap, EXTRA_RANDOM_RANGE_LIGHTENING_STRIKE_GAP_IN_SECONDS); + + var thunderURL = valueOrDefault(lighteningUserData.thunderURL, THUNDER_SOUND_URL); + _this.thunderSound = SoundCache.getSound(thunderURL); // start downloading the thunder into the cache in case we need it later + _this.thunderVolume = valueOrDefault(lighteningUserData.thunderVolume, THUNDER_VOLUME); + + var rainURL = valueOrDefault(lighteningUserData.rainURL, RAIN_SOUND_URL); + _this.rainSound = SoundCache.getSound(rainURL); // start downloading the rain, we will be using it for sure + _this.rainVolume = valueOrDefault(lighteningUserData.rainVolume, RAIN_VOLUME); + _this.rainPlaying = false; + + Script.update.connect(_this.onUpdate); // connect our onUpdate to a regular update signal from the interface + }, + + // unload() + // This is called by every viewer/interface instance that "sees" the entity when you "stop knowing about" the + // entity. Usually this means the user has left then domain, or maybe the entity has been deleted. We need to + // clean up anything transient that we created here. In this case it means disconnecting from the update signal + // and stopping the rain sound if we started it. + unload: function ( /*entityID*/ ) { + Script.update.disconnect(_this.onUpdate); + if (_this.rainInjector !== undefined) { + _this.rainInjector.stop(); + } + }, + + // plays the rain sound effect. This is played locally, which means it doesn't provide spatialization, which + // we don't really want for the ambient sound effect of rain. Also, since it's playing locally, we don't send + // the stream to the audio mixer. Another subtle side effect of playing locally is that the sound isn't in sync + // for all users in the domain, but that's ok for a sound effect like rain which is more of a white noise like + // sound effect. + playLocalRain: function () { + var myPosition = Entities.getEntityProperties(_this.entityID, "position").position; + _this.rainInjector = Audio.playSound(_this.rainSound, { + position: myPosition, + volume: _this.rainVolume, + loop: true, + localOnly: true + }); + _this.rainPlaying = true; + }, + + // this handles a single "step" of the lightening flash effect. It assumes a light entity has already been created, + // and all it really does is change the intensity of the light based on the settings that are part of this entity's + // userData. + flashLightening: function (lighteningID) { + var lighteningProperties = Entities.getEntityProperties(lighteningID, ["userData", "intensity"]); + var lighteningParameters = JSON.parse(lighteningProperties.userData); + var currentIntensity = lighteningProperties.intensity; + var flashDirection = lighteningParameters.flashDirection; + var flashMax = lighteningParameters.flashMax; + var flashMin = lighteningParameters.flashMin; + var flashIntensityStepRandomeness = lighteningParameters.flashIntensityStepRandomeness; + var newIntensity = currentIntensity + flashDirection + randFloat(-flashIntensityStepRandomeness, flashIntensityStepRandomeness); + + if (flashDirection > 0) { + if (newIntensity >= flashMax) { + flashDirection = -1; // reverse flash + newIntensity = flashMax; + } + } else { + if (newIntensity <= flashMin) { + flashDirection = 1; // reverse flash + newIntensity = flashMin; + } + } + + // if we reached 0 intensity, then we're done with this strike... + if (newIntensity === 0) { + _this.lighteningActive = false; + Entities.deleteEntity(lighteningID); + } + + // FIXME - we probably don't need to re-edit the userData of the light... we're only + // changing direction, the rest are the same... we could just store direction in our + // own local variable state + var newLighteningParameters = JSON.stringify({ + flashDirection: flashDirection, + flashIntensityStepRandomeness: flashIntensityStepRandomeness, + flashMax: flashMax, + flashMin: flashMin + }); + + // this is what actually creates the effect, changing the intensity of the light + Entities.editEntity(lighteningID, {intensity: newIntensity, userData: newLighteningParameters}); + }, + + // findMyLightening() is designed to make the script more robust. Since we're an open editable platform + // it's possible that from the time that we started the lightening effect until "now" when we're attempting + // to change the light, some other client might have deleted our light. Before we proceed in editing + // the light, we check to see if it exists. + findMyLightening: function () { + if (_this.lighteningID !== false) { + var lighteningName = Entities.getEntityProperties(_this.lighteningID, "name").name; + if (lighteningName !== undefined) { + return _this.lighteningID; + } + } + return false; + }, + + // findOtherLightening() is designed to allow this script to work in a "multi-user" environment, which we + // must assume we are in. Since every user/viewer/client that connect to the domain and "sees" our entity + // is going to run this script, any of them could be in charge of flashing the lightening. So before we + // start to flash the lightening, we will first check to see if someone else already is. + // + // returns true if some other lightening exists... likely because some other viewer is flashing it + // returns false if no other lightening exists... + findOtherLightening: function () { + var myPosition = Entities.getEntityProperties(_this.entityID, "position").position; + + // find all entities near me... + var entities = Entities.findEntities(myPosition, 1000); + var checkEntityID; + var checkProperties; + var entity; + for (entity = 0; entity < entities.length; entity++) { + checkEntityID = entities[entity]; + checkProperties = Entities.getEntityProperties(checkEntityID, ["name", "type"]); + + // check to see if they are lightening + if (checkProperties.type === "Light" && checkProperties.name === "lightening for creator:" + _this.entityID) { + return true; + } + } + return false; + }, + + // createNewLightening() actually creates new lightening and plays the thunder sound + createNewLightening: function () { + var myPosition = Entities.getEntityProperties(_this.entityID, "position").position; + _this.lighteningID = Entities.addEntity({ + type: "Light", + name: "lightening for creator:" + _this.entityID, + userData: JSON.stringify({ + flashDirection: 1, + flashIntensityStepRandomeness: _this.flashIntensityStepRandomeness, + flashMax: _this.flashMax + randFloat(-_this.flashMaxRandomness, _this.flashMaxRandomness), + flashMin: _this.flashMin + }), + falloffRadius: 1000, + intensity: 0, + position: myPosition, + collisionless: true, + dimensions: {x: 1000, y: 1000, z: 1000}, + color: {red: 255, green: 255, blue: 255}, + lifetime: 10 // lightening only lasts 10 seconds.... + }); + + // play the thunder... + Audio.playSound(_this.thunderSound, { + position: myPosition, + volume: _this.thunderVolume + }); + + return _this.lighteningID; + }, + + // onUpdate() this will be called regularly, approximately every frame of the simulation. We will use + // it to determine if we need to do a lightening/thunder strike + onUpdate: function () { + var now = Date.now(); + + // check to see if rain is downloaded and not yet playing, if so start playing + if (!_this.rainPlaying && _this.rainSound.downloaded) { + _this.playLocalRain(); + } + + // NOTE: _this.lighteningActive will only be TRUE if we are the one who created + // the lightening and we are in charge of flashing it... + if (_this.lighteningActive) { + var lighteningID = _this.findMyLightening(); + // if for some reason our lightening is gone... then just return to non-active state + if (lighteningID === false) { + _this.lighteningActive = false; + _this.lighteningID = false; + } else { + // otherwise, flash our lightening... + _this.flashLightening(lighteningID); + } + } else { + // whether or not it's time for us to strike, we always keep an eye out for anyone else + // striking... and if we see someone else striking, we will reset our lastStrike time + if (_this.findOtherLightening()) { + _this.lastStrike = now; + } + + var sinceLastStrike = now - _this.lastStrike; + var nextRandomStrikeTime = _this.averageLighteningStrikeGap + + randFloat(-_this.extraRandomRangeLighteningStrikeGap, + _this.extraRandomRangeLighteningStrikeGap); + + if (sinceLastStrike > nextRandomStrikeTime * MSECS_PER_SECOND) { + + // so it's time for a strike... let's see if someone else has lightening... + // if no one else is flashing lightening... then we create it... + if (_this.findOtherLightening()) { + _this.lighteningActive = false; + _this.lighteningID = false; + } else { + _this.createNewLightening(); + _this.lighteningActive = true; + } + + _this.lastStrike = now; + } + } + } + }; + + return new Lightening(); +}); \ No newline at end of file diff --git a/examples/shaders/rainyDayNightSkybox.fs b/examples/shaders/rainyDayNightSkybox.fs new file mode 100644 index 0000000000..359bfb1653 --- /dev/null +++ b/examples/shaders/rainyDayNightSkybox.fs @@ -0,0 +1,126 @@ +// Created by inigo quilez - iq/2013 +// Turbulence and Day/Night cycle added by Michael Olson - OMGparticles/2015 +// rain effect adapted from Rainy London by David Hoskins. - https://www.shadertoy.com/view/XdSGDc +// License Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License. +#line 5 +const float PI = 3.14159; +uniform float rotationSpeed = 0.005; +uniform float gridLevel = 0.0; +uniform float constellationLevel = 0.0; +uniform float constellationBoundaryLevel = 0.0; + +// Axial tilt +const float axialTilt = 0.408407; +const vec2 atsc = vec2(sin(axialTilt), cos(axialTilt)); +const mat3 axialTiltMatrix = mat3( + vec3(atsc.y, -atsc.x, 0.0), + vec3(atsc.x, atsc.y, 0.0), + vec3(0.0, 0.0, 1.0)); + +vec2 directionToSpherical(in vec3 d) { + return vec2(atan(d.x, -d.z), asin(d.y)) * -1.0f; +} + +vec2 directionToUv(in vec3 d) { + vec2 uv = directionToSpherical(d); + uv /= PI; + uv.x /= 2.0; + uv += 0.5; + return uv; +} + +vec3 stars3(in vec3 d) { + //return rd; + vec3 dir = d * axialTiltMatrix; + + float theta = rotationSpeed * iGlobalTime * 4.0; + vec2 sc = vec2(sin(theta), cos(theta)); + dir = dir * mat3( vec3(sc.y, 0.0, sc.x), + vec3(0.0, 1.0, 0.0), + vec3(-sc.x, 0.0, sc.y)); + + vec2 uv = directionToUv(dir); + vec3 starColor = texture2D( iChannel0, uv).xyz; + starColor = pow(starColor, vec3(0.75)); + + if (gridLevel > 0.0) { + starColor += texture2D( iChannel1, uv).xyz * gridLevel; + } + return starColor; +} + +//const vec3 vNightColor = vec3(.15, 0.3, 0.6); +uniform vec3 uDayColor = vec3(0.9,0.7,0.7); +const vec3 vNightColor = vec3(1.0, 1.0, 1.0); +const vec3 vHorizonColor = vec3(0.6, 0.3, 0.4); +const vec3 vSunColor = vec3(1.0,0.8,0.6); +const vec3 vSunRimColor = vec3(1.0,0.66,0.33); + +vec4 render( in vec3 ro, in vec3 rd ) +{ + float fSunSpeed = (rotationSpeed * 10.0 * iGlobalTime) + PI / 2.0 * 3.0; + vec3 sundir = normalize( vec3(cos(fSunSpeed),sin(fSunSpeed),0.0) ); + sundir = sundir * axialTiltMatrix; + float sun = clamp( dot(sundir,rd), 0.0, 1.0 ); + + float fSunHeight = sundir.y; + + // below this height will be full night color + float fNightHeight = -0.8; + // above this height will be full day color + float fDayHeight = 0.3; + + float fHorizonLength = fDayHeight - fNightHeight; + float fInverseHL = 1.0 / fHorizonLength; + float fHalfHorizonLength = fHorizonLength / 2.0; + float fInverseHHL = 1.0 / fHalfHorizonLength; + float fMidPoint = fNightHeight + fHalfHorizonLength; + + float fNightContrib = clamp((fSunHeight - fMidPoint) * (-fInverseHHL), 0.0, 1.0); + float fHorizonContrib = -clamp(abs((fSunHeight - fMidPoint) * (-fInverseHHL)), 0.0, 1.0) + 1.0; + float fDayContrib = clamp((fSunHeight - fMidPoint) * ( fInverseHHL), 0.0, 1.0); + + // sky color + vec3 vSkyColor = vec3(0.0); + vSkyColor += mix(vec3(0.0), vNightColor, fNightContrib); // Night + vSkyColor += mix(vec3(0.0), vHorizonColor, fHorizonContrib); // Horizon + vSkyColor += mix(vec3(0.0), uDayColor, fDayContrib); // Day + vec3 col = vSkyColor; + + // atmosphere brighter near horizon + col -= clamp(rd.y, 0.0, 0.5); + + // draw sun + col += 0.4 * vSunRimColor * pow( sun, 4.0 ); + col += 1.0 * vSunColor * pow( sun, 2000.0 ); + + // stars + float fStarContrib = clamp((fSunHeight - fDayHeight) * (-fInverseHL), 0.0, 1.0); + + vec3 vStarDir = rd; + + col = mix(col, stars3(vStarDir), fStarContrib); + col += stars3(vStarDir) * fStarContrib; + + // Ten layers of rain sheets... + float rainBrightness = 0.15; + vec2 q = vec2(atan(rd.x, -rd.z), asin(rd.y)); + float dis = 1; + int sheets = 12; + for (int i = 0; i < sheets; i++) { + float f = pow(dis, .45) + 0.25; + vec2 st = f * (q * vec2(2.0, .05) + vec2(-iGlobalTime*.01 + q.y * .05, iGlobalTime * 0.12)); + f = texture2D(iChannel2, st * .5, -59.0).x; + f += texture2D(iChannel2, st*.284, -99.0).y; + f = clamp(pow(abs(f)*.5, 29.0) * 140.0, 0.00, q.y*.4+.05); + vec3 bri = vec3(rainBrightness); + col += bri*f; + dis += 3.5; + } + + return vec4( col, 1.0 ); +} + +vec3 getSkyboxColor() { + return render( vec3(0.0), normalize(_normal) ).rgb; +} diff --git a/examples/zones/RainyDayNightZone.json b/examples/zones/RainyDayNightZone.json new file mode 100644 index 0000000000..dc69ca22ac --- /dev/null +++ b/examples/zones/RainyDayNightZone.json @@ -0,0 +1,27 @@ +{ + "Entities": [ + { + "backgroundMode": "skybox", + "dimensions": { + "x": 30000, + "y": 30000, + "z": 30000 + }, + "name": "Rainy Day/Night Cycle", + "rotation": { + "w": 1, + "x": 0, + "y": 0, + "z": 0 + }, + "script": "https://s3.amazonaws.com/hifi-public/brad/rainstorm/lighteningEntity.js", + "shapeType": "box", + "skybox": { + "url": "https://hifi-public.s3.amazonaws.com/images/SkyboxTextures/CloudyDay1.jpg" + }, + "type": "Zone", + "userData":"{\n\"ProceduralEntity\":{\n\"version\":2,\n\"shaderUrl\":\"https://s3.amazonaws.com/hifi-public/brad/rainstorm/rainyDayNightSkybox.fs\",\n\"channels\":[\n\"https://hifi-public.s3.amazonaws.com/austin/assets/images/skybox/starmap_8k.jpg\",\n\"https://hifi-public.s3.amazonaws.com/austin/assets/images/skybox/celestial_grid.jpg\",\n\"https://s3.amazonaws.com/hifi-public/brad/rainstorm/noise.jpg\",\n\"https://s3.amazonaws.com/hifi-public/brad/noise.jpg\"\n],\n\"uniforms\":{\n\"rotationSpeed\":0.001,\n\"uDayColor\":[0.4,0.3,0.3],\n\"constellationLevel\":0.0,\n\"constellationBoundaryLevel\":0.00,\n\"gridLevel\":0.0\n}\n},\n\"lightening\":{\n\"flashMax\":20,\n\"flashMin\":0,\n\"flashIntensityStepRandomeness\":2,\n\"flashMaxRandomness\":10,\n\"averageLighteningStrikeGap\":120,\n\"extraRandomRangeLighteningStrikeGap\":5,\n\"thunderURL\":\"https://s3.amazonaws.com/hifi-public/brad/rainstorm/thunder-48k.wav\",\n\"thunderVolume\":0.1,\n\"rainURL\":\"https://s3.amazonaws.com/hifi-public/brad/rainstorm/rain.wav\",\n\"rainVolume\":0.05\n}\n}" + } + ], + "Version": 57 +} From d8e7c60ce0a5f5d484e83719e65ce63bbccaa5eb Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Fri, 4 Mar 2016 15:47:25 -0800 Subject: [PATCH 2/5] CR feedback --- examples/entityScripts/lighteningEntity.js | 168 ++++++++++----------- examples/shaders/rainyDayNightSkybox.fs | 13 +- examples/zones/RainyDayNightZone.json | 10 +- 3 files changed, 95 insertions(+), 96 deletions(-) diff --git a/examples/entityScripts/lighteningEntity.js b/examples/entityScripts/lighteningEntity.js index 67cc6c8ea6..40c5364a6a 100644 --- a/examples/entityScripts/lighteningEntity.js +++ b/examples/entityScripts/lighteningEntity.js @@ -1,11 +1,11 @@ // -// lighteningEntity.js +// lightningEntity.js // examples/entityScripts // // Created by Brad Hefta-Gaub on 3/1/16. // Copyright 2016 High Fidelity, Inc. // -// This is an example of an entity script which will randomly create a flash of lightening and a thunder sound +// This is an example of an entity script which will randomly create a flash of lightning and a thunder sound // effect, as well as a background rain sound effect. It can be applied to any entity, although it works best // on a zone entity. // @@ -21,31 +21,31 @@ // // You can change these values to change some of the various effects of the rain storm. // These values can also be controlled by setting user properties on the entity that you've attached this script to. - // add a "lightening" section to a JSON encoded portion of the user data... for example: + // add a "lightning" section to a JSON encoded portion of the user data... for example: // { - // "lightening": { + // "lightning": { // "flashMax": 20, // "flashMin": 0, // "flashMaxRandomness": 10, // "flashIntensityStepRandomeness": 2, // "averageLighteningStrikeGap": 120, - // "extraRandomRangeLighteningStrikeGap": 10, + // "extraRandomRangeLightningStrikeGap": 10, // "thunderURL": "atp:1336efe995398f5e0d46b37585785de8ba872fe9a9b718264db03748cd41c758.wav", // "thunderVolume": 0.1, // "rainURL": "atp:e0cc7438aca776636f6e6f731685781d9999b961c945e4e5760d937be5beecdd.wav", // "rainVolume": 0.05 // } - // // NOTE: you can have other user data here as well, so long as it's JSON encoded, it won't impact the lightening script + // // NOTE: you can have other user data here as well, so long as it's JSON encoded, it won't impact the lightning script // } // //////////////////////////////////////////////////////////////////////////////////////////////////////////// - var MAX_FLASH_INTENSITY = 20; // this controls how bright the lightening effect appears - var MIN_FLASH_INTENSITY = 0; // this is probably best at 0, but it could be higher, which will make the lightening not fade completely to darkness before going away. - var MAX_FLASH_INTENSITY_RANDOMNESS = 10; // this will add some randomness to the max brightness of the lightening - var FLASH_INTENSITY_STEP_RANDOMNESS = 2; // as the lightening goes from min to max back to min, this will make it more random in it's brightness - var AVERAGE_LIGHTENING_STRIKE_GAP_IN_SECONDS = 120; // how long on average between lighting - var EXTRA_RANDOM_RANGE_LIGHTENING_STRIKE_GAP_IN_SECONDS = 10; // some randomness to the lightening gap + var MAX_FLASH_INTENSITY = 20; // this controls how bright the lightning effect appears + var MIN_FLASH_INTENSITY = 0; // this is probably best at 0, but it could be higher, which will make the lightning not fade completely to darkness before going away. + var MAX_FLASH_INTENSITY_RANDOMNESS = 10; // this will add some randomness to the max brightness of the lightning + var FLASH_INTENSITY_STEP_RANDOMNESS = 2; // as the lightning goes from min to max back to min, this will make it more random in it's brightness + var AVERAGE_LIGHTNING_STRIKE_GAP_IN_SECONDS = 120; // how long on average between lighting + var EXTRA_RANDOM_RANGE_LIGHTNING_STRIKE_GAP_IN_SECONDS = 10; // some randomness to the lightning gap var THUNDER_SOUND_URL = "https://s3.amazonaws.com/hifi-public/brad/rainstorm/thunder-48k.wav"; // thunder sound effect, must be 48k 16bit PCM var THUNDER_VOLUME = 1; // adjust the volume of the thunder sound effect var RAIN_SOUND_URL = "https://s3.amazonaws.com/hifi-public/brad/rainstorm/rain.wav"; // the background rain, this will loop @@ -84,7 +84,7 @@ } // the "constructor" for our class. pretty simple, it just sets our _this, so we can access it later. - function Lightening() { + function Lightning() { _this = this; } @@ -94,7 +94,7 @@ // This is the class definition/prototype for our class. Funtions declared here will be accessible // via the instance of the entity // - Lightening.prototype = { + Lightning.prototype = { // preload() // This is called by every viewer/interface instance that "sees" the entity you've attached this script to. @@ -110,8 +110,8 @@ _this.lastStrike = now; // some of our other state related items - _this.lighteningID = false; // this will be the entityID for any lightening that we create - _this.lighteningActive = false; // are we actively managing lightening + _this.lightningID = false; // this will be the entityID for any lightning that we create + _this.lightningActive = false; // are we actively managing lightning // Get the entities userData property, to see if someone has overridden any of our default settings var userDataText = Entities.getEntityProperties(entityID, ["userData"]).userData; @@ -119,21 +119,21 @@ if (userDataText !== "") { userData = JSON.parse(userDataText); } - var lighteningUserData = valueOrDefault(userData.lightening, {}); - _this.flashIntensityStepRandomeness = valueOrDefault(lighteningUserData.flashIntensityStepRandomness, FLASH_INTENSITY_STEP_RANDOMNESS); - _this.flashMax = valueOrDefault(lighteningUserData.flashMax, MAX_FLASH_INTENSITY); - _this.flashMin = valueOrDefault(lighteningUserData.flashMin, MIN_FLASH_INTENSITY); - _this.flashMaxRandomness = valueOrDefault(lighteningUserData.flashMaxRandomness, MAX_FLASH_INTENSITY_RANDOMNESS); - _this.averageLighteningStrikeGap = valueOrDefault(lighteningUserData.averageLighteningStrikeGap, AVERAGE_LIGHTENING_STRIKE_GAP_IN_SECONDS); - _this.extraRandomRangeLighteningStrikeGap = valueOrDefault(lighteningUserData.extraRandomRangeLighteningStrikeGap, EXTRA_RANDOM_RANGE_LIGHTENING_STRIKE_GAP_IN_SECONDS); + var lightningUserData = valueOrDefault(userData.lightning, {}); + _this.flashIntensityStepRandomeness = valueOrDefault(lightningUserData.flashIntensityStepRandomness, FLASH_INTENSITY_STEP_RANDOMNESS); + _this.flashMax = valueOrDefault(lightningUserData.flashMax, MAX_FLASH_INTENSITY); + _this.flashMin = valueOrDefault(lightningUserData.flashMin, MIN_FLASH_INTENSITY); + _this.flashMaxRandomness = valueOrDefault(lightningUserData.flashMaxRandomness, MAX_FLASH_INTENSITY_RANDOMNESS); + _this.averageLightningStrikeGap = valueOrDefault(lightningUserData.averageLightningStrikeGap, AVERAGE_LIGHTNING_STRIKE_GAP_IN_SECONDS); + _this.extraRandomRangeLightningStrikeGap = valueOrDefault(lightningUserData.extraRandomRangeLightningStrikeGap, EXTRA_RANDOM_RANGE_LIGHTNING_STRIKE_GAP_IN_SECONDS); - var thunderURL = valueOrDefault(lighteningUserData.thunderURL, THUNDER_SOUND_URL); + var thunderURL = valueOrDefault(lightningUserData.thunderURL, THUNDER_SOUND_URL); _this.thunderSound = SoundCache.getSound(thunderURL); // start downloading the thunder into the cache in case we need it later - _this.thunderVolume = valueOrDefault(lighteningUserData.thunderVolume, THUNDER_VOLUME); + _this.thunderVolume = valueOrDefault(lightningUserData.thunderVolume, THUNDER_VOLUME); - var rainURL = valueOrDefault(lighteningUserData.rainURL, RAIN_SOUND_URL); + var rainURL = valueOrDefault(lightningUserData.rainURL, RAIN_SOUND_URL); _this.rainSound = SoundCache.getSound(rainURL); // start downloading the rain, we will be using it for sure - _this.rainVolume = valueOrDefault(lighteningUserData.rainVolume, RAIN_VOLUME); + _this.rainVolume = valueOrDefault(lightningUserData.rainVolume, RAIN_VOLUME); _this.rainPlaying = false; Script.update.connect(_this.onUpdate); // connect our onUpdate to a regular update signal from the interface @@ -167,17 +167,17 @@ _this.rainPlaying = true; }, - // this handles a single "step" of the lightening flash effect. It assumes a light entity has already been created, + // this handles a single "step" of the lightning flash effect. It assumes a light entity has already been created, // and all it really does is change the intensity of the light based on the settings that are part of this entity's // userData. - flashLightening: function (lighteningID) { - var lighteningProperties = Entities.getEntityProperties(lighteningID, ["userData", "intensity"]); - var lighteningParameters = JSON.parse(lighteningProperties.userData); - var currentIntensity = lighteningProperties.intensity; - var flashDirection = lighteningParameters.flashDirection; - var flashMax = lighteningParameters.flashMax; - var flashMin = lighteningParameters.flashMin; - var flashIntensityStepRandomeness = lighteningParameters.flashIntensityStepRandomeness; + flashLightning: function (lightningID) { + var lightningProperties = Entities.getEntityProperties(lightningID, ["userData", "intensity"]); + var lightningParameters = JSON.parse(lightningProperties.userData); + var currentIntensity = lightningProperties.intensity; + var flashDirection = lightningParameters.flashDirection; + var flashMax = lightningParameters.flashMax; + var flashMin = lightningParameters.flashMin; + var flashIntensityStepRandomeness = lightningParameters.flashIntensityStepRandomeness; var newIntensity = currentIntensity + flashDirection + randFloat(-flashIntensityStepRandomeness, flashIntensityStepRandomeness); if (flashDirection > 0) { @@ -194,14 +194,14 @@ // if we reached 0 intensity, then we're done with this strike... if (newIntensity === 0) { - _this.lighteningActive = false; - Entities.deleteEntity(lighteningID); + _this.lightningActive = false; + Entities.deleteEntity(lightningID); } // FIXME - we probably don't need to re-edit the userData of the light... we're only // changing direction, the rest are the same... we could just store direction in our // own local variable state - var newLighteningParameters = JSON.stringify({ + var newLightningParameters = JSON.stringify({ flashDirection: flashDirection, flashIntensityStepRandomeness: flashIntensityStepRandomeness, flashMax: flashMax, @@ -209,31 +209,31 @@ }); // this is what actually creates the effect, changing the intensity of the light - Entities.editEntity(lighteningID, {intensity: newIntensity, userData: newLighteningParameters}); + Entities.editEntity(lightningID, {intensity: newIntensity, userData: newLightningParameters}); }, - // findMyLightening() is designed to make the script more robust. Since we're an open editable platform - // it's possible that from the time that we started the lightening effect until "now" when we're attempting + // findMyLightning() is designed to make the script more robust. Since we're an open editable platform + // it's possible that from the time that we started the lightning effect until "now" when we're attempting // to change the light, some other client might have deleted our light. Before we proceed in editing // the light, we check to see if it exists. - findMyLightening: function () { - if (_this.lighteningID !== false) { - var lighteningName = Entities.getEntityProperties(_this.lighteningID, "name").name; - if (lighteningName !== undefined) { - return _this.lighteningID; + findMyLightning: function () { + if (_this.lightningID !== false) { + var lightningName = Entities.getEntityProperties(_this.lightningID, "name").name; + if (lightningName !== undefined) { + return _this.lightningID; } } return false; }, - // findOtherLightening() is designed to allow this script to work in a "multi-user" environment, which we + // findOtherLightning() is designed to allow this script to work in a "multi-user" environment, which we // must assume we are in. Since every user/viewer/client that connect to the domain and "sees" our entity - // is going to run this script, any of them could be in charge of flashing the lightening. So before we - // start to flash the lightening, we will first check to see if someone else already is. + // is going to run this script, any of them could be in charge of flashing the lightning. So before we + // start to flash the lightning, we will first check to see if someone else already is. // - // returns true if some other lightening exists... likely because some other viewer is flashing it - // returns false if no other lightening exists... - findOtherLightening: function () { + // returns true if some other lightning exists... likely because some other viewer is flashing it + // returns false if no other lightning exists... + findOtherLightning: function () { var myPosition = Entities.getEntityProperties(_this.entityID, "position").position; // find all entities near me... @@ -245,20 +245,20 @@ checkEntityID = entities[entity]; checkProperties = Entities.getEntityProperties(checkEntityID, ["name", "type"]); - // check to see if they are lightening - if (checkProperties.type === "Light" && checkProperties.name === "lightening for creator:" + _this.entityID) { + // check to see if they are lightning + if (checkProperties.type === "Light" && checkProperties.name === "lightning for creator:" + _this.entityID) { return true; } } return false; }, - // createNewLightening() actually creates new lightening and plays the thunder sound - createNewLightening: function () { + // createNewLightning() actually creates new lightning and plays the thunder sound + createNewLightning: function () { var myPosition = Entities.getEntityProperties(_this.entityID, "position").position; - _this.lighteningID = Entities.addEntity({ + _this.lightningID = Entities.addEntity({ type: "Light", - name: "lightening for creator:" + _this.entityID, + name: "lightning for creator:" + _this.entityID, userData: JSON.stringify({ flashDirection: 1, flashIntensityStepRandomeness: _this.flashIntensityStepRandomeness, @@ -271,7 +271,7 @@ collisionless: true, dimensions: {x: 1000, y: 1000, z: 1000}, color: {red: 255, green: 255, blue: 255}, - lifetime: 10 // lightening only lasts 10 seconds.... + lifetime: 10 // lightning only lasts 10 seconds.... }); // play the thunder... @@ -280,11 +280,11 @@ volume: _this.thunderVolume }); - return _this.lighteningID; + return _this.lightningID; }, // onUpdate() this will be called regularly, approximately every frame of the simulation. We will use - // it to determine if we need to do a lightening/thunder strike + // it to determine if we need to do a lightning/thunder strike onUpdate: function () { var now = Date.now(); @@ -293,40 +293,40 @@ _this.playLocalRain(); } - // NOTE: _this.lighteningActive will only be TRUE if we are the one who created - // the lightening and we are in charge of flashing it... - if (_this.lighteningActive) { - var lighteningID = _this.findMyLightening(); - // if for some reason our lightening is gone... then just return to non-active state - if (lighteningID === false) { - _this.lighteningActive = false; - _this.lighteningID = false; + // NOTE: _this.lightningActive will only be TRUE if we are the one who created + // the lightning and we are in charge of flashing it... + if (_this.lightningActive) { + var lightningID = _this.findMyLightning(); + // if for some reason our lightning is gone... then just return to non-active state + if (lightningID === false) { + _this.lightningActive = false; + _this.lightningID = false; } else { - // otherwise, flash our lightening... - _this.flashLightening(lighteningID); + // otherwise, flash our lightning... + _this.flashLightning(lightningID); } } else { // whether or not it's time for us to strike, we always keep an eye out for anyone else // striking... and if we see someone else striking, we will reset our lastStrike time - if (_this.findOtherLightening()) { + if (_this.findOtherLightning()) { _this.lastStrike = now; } var sinceLastStrike = now - _this.lastStrike; - var nextRandomStrikeTime = _this.averageLighteningStrikeGap - + randFloat(-_this.extraRandomRangeLighteningStrikeGap, - _this.extraRandomRangeLighteningStrikeGap); + var nextRandomStrikeTime = _this.averageLightningStrikeGap + + randFloat(-_this.extraRandomRangeLightningStrikeGap, + _this.extraRandomRangeLightningStrikeGap); if (sinceLastStrike > nextRandomStrikeTime * MSECS_PER_SECOND) { - // so it's time for a strike... let's see if someone else has lightening... - // if no one else is flashing lightening... then we create it... - if (_this.findOtherLightening()) { - _this.lighteningActive = false; - _this.lighteningID = false; + // so it's time for a strike... let's see if someone else has lightning... + // if no one else is flashing lightning... then we create it... + if (_this.findOtherLightning()) { + _this.lightningActive = false; + _this.lightningID = false; } else { - _this.createNewLightening(); - _this.lighteningActive = true; + _this.createNewLightning(); + _this.lightningActive = true; } _this.lastStrike = now; @@ -335,5 +335,5 @@ } }; - return new Lightening(); + return new Lightning(); }); \ No newline at end of file diff --git a/examples/shaders/rainyDayNightSkybox.fs b/examples/shaders/rainyDayNightSkybox.fs index 359bfb1653..a7bcc4b74b 100644 --- a/examples/shaders/rainyDayNightSkybox.fs +++ b/examples/shaders/rainyDayNightSkybox.fs @@ -2,7 +2,7 @@ // Turbulence and Day/Night cycle added by Michael Olson - OMGparticles/2015 // rain effect adapted from Rainy London by David Hoskins. - https://www.shadertoy.com/view/XdSGDc // License Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License. -#line 5 +#line 6 const float PI = 3.14159; uniform float rotationSpeed = 0.005; uniform float gridLevel = 0.0; @@ -96,11 +96,10 @@ vec4 render( in vec3 ro, in vec3 rd ) // stars float fStarContrib = clamp((fSunHeight - fDayHeight) * (-fInverseHL), 0.0, 1.0); - - vec3 vStarDir = rd; - - col = mix(col, stars3(vStarDir), fStarContrib); - col += stars3(vStarDir) * fStarContrib; + if (fStarContrib > 0.0) { + vec3 vStarDir = rd; + col = mix(col, stars3(vStarDir), fStarContrib); + } // Ten layers of rain sheets... float rainBrightness = 0.15; @@ -118,7 +117,7 @@ vec4 render( in vec3 ro, in vec3 rd ) dis += 3.5; } - return vec4( col, 1.0 ); + return vec4(clamp(col,0.0,1.0), 1.0 ); } vec3 getSkyboxColor() { diff --git a/examples/zones/RainyDayNightZone.json b/examples/zones/RainyDayNightZone.json index dc69ca22ac..1efe87c9fb 100644 --- a/examples/zones/RainyDayNightZone.json +++ b/examples/zones/RainyDayNightZone.json @@ -3,9 +3,9 @@ { "backgroundMode": "skybox", "dimensions": { - "x": 30000, - "y": 30000, - "z": 30000 + "x": 10000, + "y": 10000, + "z": 10000 }, "name": "Rainy Day/Night Cycle", "rotation": { @@ -14,13 +14,13 @@ "y": 0, "z": 0 }, - "script": "https://s3.amazonaws.com/hifi-public/brad/rainstorm/lighteningEntity.js", + "script": "https://s3.amazonaws.com/hifi-public/brad/rainstorm/lightningEntity.js", "shapeType": "box", "skybox": { "url": "https://hifi-public.s3.amazonaws.com/images/SkyboxTextures/CloudyDay1.jpg" }, "type": "Zone", - "userData":"{\n\"ProceduralEntity\":{\n\"version\":2,\n\"shaderUrl\":\"https://s3.amazonaws.com/hifi-public/brad/rainstorm/rainyDayNightSkybox.fs\",\n\"channels\":[\n\"https://hifi-public.s3.amazonaws.com/austin/assets/images/skybox/starmap_8k.jpg\",\n\"https://hifi-public.s3.amazonaws.com/austin/assets/images/skybox/celestial_grid.jpg\",\n\"https://s3.amazonaws.com/hifi-public/brad/rainstorm/noise.jpg\",\n\"https://s3.amazonaws.com/hifi-public/brad/noise.jpg\"\n],\n\"uniforms\":{\n\"rotationSpeed\":0.001,\n\"uDayColor\":[0.4,0.3,0.3],\n\"constellationLevel\":0.0,\n\"constellationBoundaryLevel\":0.00,\n\"gridLevel\":0.0\n}\n},\n\"lightening\":{\n\"flashMax\":20,\n\"flashMin\":0,\n\"flashIntensityStepRandomeness\":2,\n\"flashMaxRandomness\":10,\n\"averageLighteningStrikeGap\":120,\n\"extraRandomRangeLighteningStrikeGap\":5,\n\"thunderURL\":\"https://s3.amazonaws.com/hifi-public/brad/rainstorm/thunder-48k.wav\",\n\"thunderVolume\":0.1,\n\"rainURL\":\"https://s3.amazonaws.com/hifi-public/brad/rainstorm/rain.wav\",\n\"rainVolume\":0.05\n}\n}" + "userData":"{\n\"ProceduralEntity\":{\n\"version\":2,\n\"shaderUrl\":\"https://s3.amazonaws.com/hifi-public/brad/rainstorm/rainyDayNightSkybox.fs\",\n\"channels\":[\n\"https://hifi-public.s3.amazonaws.com/austin/assets/images/skybox/starmap_8k.jpg\",\n\"https://hifi-public.s3.amazonaws.com/austin/assets/images/skybox/celestial_grid.jpg\",\n\"https://s3.amazonaws.com/hifi-public/brad/rainstorm/noise.jpg\",\n\"https://s3.amazonaws.com/hifi-public/brad/noise.jpg\"\n],\n\"uniforms\":{\n\"rotationSpeed\":0.001,\n\"uDayColor\":[0.4,0.3,0.3],\n\"constellationLevel\":0.0,\n\"constellationBoundaryLevel\":0.00,\n\"gridLevel\":0.0\n}\n},\n\"lightning\":{\n\"flashMax\":20,\n\"flashMin\":0,\n\"flashIntensityStepRandomeness\":2,\n\"flashMaxRandomness\":10,\n\"averageLightningStrikeGap\":120,\n\"extraRandomRangeLightningStrikeGap\":5,\n\"thunderURL\":\"https://s3.amazonaws.com/hifi-public/brad/rainstorm/thunder-48k.wav\",\n\"thunderVolume\":0.1,\n\"rainURL\":\"https://s3.amazonaws.com/hifi-public/brad/rainstorm/rain.wav\",\n\"rainVolume\":0.05\n}\n}" } ], "Version": 57 From e776bf5819be55bfe374bab367eb06978d34c34b Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Fri, 4 Mar 2016 15:47:50 -0800 Subject: [PATCH 3/5] CR feedback --- .../entityScripts/{lighteningEntity.js => lightningEntity.js} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename examples/entityScripts/{lighteningEntity.js => lightningEntity.js} (100%) diff --git a/examples/entityScripts/lighteningEntity.js b/examples/entityScripts/lightningEntity.js similarity index 100% rename from examples/entityScripts/lighteningEntity.js rename to examples/entityScripts/lightningEntity.js From 2b4d206eca12a909170c1ff0136041689dbe854f Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Fri, 4 Mar 2016 15:54:28 -0800 Subject: [PATCH 4/5] more CR feedback --- examples/shaders/rainyDayNightSkybox.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/shaders/rainyDayNightSkybox.fs b/examples/shaders/rainyDayNightSkybox.fs index a7bcc4b74b..dd3733088b 100644 --- a/examples/shaders/rainyDayNightSkybox.fs +++ b/examples/shaders/rainyDayNightSkybox.fs @@ -117,7 +117,7 @@ vec4 render( in vec3 ro, in vec3 rd ) dis += 3.5; } - return vec4(clamp(col,0.0,1.0), 1.0 ); + return vec4(clamp(col, vec3(0.0), vec3(1.0)), 1.0); } vec3 getSkyboxColor() { From 6d56a0d05167ac09ddc5fe821dd4e83e63b86a0e Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Fri, 4 Mar 2016 15:55:08 -0800 Subject: [PATCH 5/5] more CR feedback --- examples/shaders/rainyDayNightSkybox.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/shaders/rainyDayNightSkybox.fs b/examples/shaders/rainyDayNightSkybox.fs index dd3733088b..265cda3e25 100644 --- a/examples/shaders/rainyDayNightSkybox.fs +++ b/examples/shaders/rainyDayNightSkybox.fs @@ -117,7 +117,7 @@ vec4 render( in vec3 ro, in vec3 rd ) dis += 3.5; } - return vec4(clamp(col, vec3(0.0), vec3(1.0)), 1.0); + return vec4(clamp(col, 0.0, 1.0), 1.0); } vec3 getSkyboxColor() {