diff --git a/examples/VR-VJ/cartridgesSpawner.js b/examples/VR-VJ/cartridgesSpawner.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/unpublishedScripts/DomainContent/Home/plant/flower.fs b/unpublishedScripts/DomainContent/Home/plant/flower.fs new file mode 100644 index 0000000000..b67c910074 --- /dev/null +++ b/unpublishedScripts/DomainContent/Home/plant/flower.fs @@ -0,0 +1,94 @@ +// +// flowers.fs +// examples/homeContent/plant +// +// Created by Eric Levin on 3/7/16. +// Copyright 2016 High Fidelity, Inc. +// +// This fragment shader is designed to shader a sphere to create the effect of a blooming flower +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html + + +#define TWO_PI 6.28318530718 + +uniform float iBloomPct = 0.2; +uniform vec3 iHSLColor = vec3(0.7, 0.5, 0.5); + + +float hue2rgb(float f1, float f2, float hue) { + if (hue < 0.0) + hue += 1.0; + else if (hue > 1.0) + hue -= 1.0; + float res; + if ((6.0 * hue) < 1.0) + res = f1 + (f2 - f1) * 6.0 * hue; + else if ((2.0 * hue) < 1.0) + res = f2; + else if ((3.0 * hue) < 2.0) + res = f1 + (f2 - f1) * ((2.0 / 3.0) - hue) * 6.0; + else + res = f1; + return res; +} + +vec3 hsl2rgb(vec3 hsl) { + vec3 rgb; + + if (hsl.y == 0.0) { + rgb = vec3(hsl.z); // Luminance + } else { + float f2; + + if (hsl.z < 0.5) + f2 = hsl.z * (1.0 + hsl.y); + else + f2 = hsl.z + hsl.y - hsl.y * hsl.z; + + float f1 = 2.0 * hsl.z - f2; + + rgb.r = hue2rgb(f1, f2, hsl.x + (1.0/3.0)); + rgb.g = hue2rgb(f1, f2, hsl.x); + rgb.b = hue2rgb(f1, f2, hsl.x - (1.0/3.0)); + } + return rgb; +} + +vec3 hsl2rgb(float h, float s, float l) { + return hsl2rgb(vec3(h, s, l)); +} + +void mainImage( out vec4 fragColor, in vec2 fragCoord ) { + vec2 st = fragCoord.xy/iWorldScale.xz; + vec3 color = vec3(0.0, 0.0, 0.0); + + vec2 toCenter = vec2(0.5) - st; + float angle = atan(toCenter.y, toCenter.x); + float radius = length(toCenter) * 2.0; + + // Second check is so we discard the top half of the sphere + if ( iBloomPct < radius || _position.y > 0) { + discard; + } + + // simulate ambient occlusion + float brightness = pow(radius, 0.8); + vec3 hslColor = iHSLColor + (abs(angle) * 0.02); + hslColor.z = 0.15 + pow(radius, 2.); + vec3 rgbColor = hsl2rgb(hslColor); + fragColor = vec4(rgbColor, 1.0); +} + + + + +vec4 getProceduralColor() { + vec4 result; + vec2 position = _position.xz; + position += 0.5; + + mainImage(result, position * iWorldScale.xz); + + return result; +} \ No newline at end of file diff --git a/unpublishedScripts/DomainContent/Home/plant/growingPlantEntityScript.js b/unpublishedScripts/DomainContent/Home/plant/growingPlantEntityScript.js new file mode 100644 index 0000000000..585c603d02 --- /dev/null +++ b/unpublishedScripts/DomainContent/Home/plant/growingPlantEntityScript.js @@ -0,0 +1,154 @@ +// +// growingPlantEntityScript.js +// examples/homeContent/plant +// +// Created by Eric Levin on 2/10/16. +// Copyright 2016 High Fidelity, Inc. +// +// This entity script handles the logic for growing a plant when it has water poured on it +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html + + + +(function() { + Script.include('../../../../libraries/utils.js'); + + var _this; + GrowingPlant = function() { + _this = this; + _this.flowers = []; + // _this.STARTING_FLOWER_DIMENSIONS = {x: 0.1, y: 0.001, z: 0.1} + _this.STARTING_FLOWER_DIMENSIONS = { + x: 0.001, + y: 0.001, + z: 0.001 + } + + _this.MAX_FLOWERS = 50; + _this.MIN_FLOWER_TO_FLOWER_DISTANCE = 0.03; + + _this.debounceRange = { + min: 500, + max: 1000 + }; + _this.canCreateFlower = true; + _this.SHADER_URL = "https://s3-us-west-1.amazonaws.com/hifi-content/eric/shaders/flower.fs"; + // _this.SHADER_URL = "file:///C:/Users/Eric/hifi/unpublishedScripts/DomainContent/Home/plant/flower.fs"; + + _this.flowerHSLColors = [{ + hue: 19 / 360, + saturation: 0.92, + light: 0.31 + }, { + hue: 161 / 360, + saturation: 0.28, + light: 0.62 + }]; + + }; + + GrowingPlant.prototype = { + + + continueWatering: function(entityID, data) { + // we're being watered- every now and then spawn a new flower to add to our growing list + // If we don't have any flowers yet, immediately grow one + var data = JSON.parse(data[0]); + + if (_this.canCreateFlower && _this.flowers.length < _this.MAX_FLOWERS) { + _this.createFlower(data.position, data.surfaceNormal); + _this.canCreateFlower = false; + Script.setTimeout(function() { + _this.canCreateFlower = true; + }, randFloat(_this.debounceRange.min, this.debounceRange.max)); + + } + + _this.flowers.forEach(function(flower) { + flower.grow(); + }); + + + }, + + createFlower: function(position, surfaceNormal) { + if (_this.previousFlowerPosition && Vec3.distance(position, _this.previousFlowerPosition) < _this.MIN_FLOWER_TO_FLOWER_DISTANCE) { + // Reduces flower overlap + return; + } + var xzGrowthRate = randFloat(0.00006, 0.00016); + var growthRate = {x: xzGrowthRate, y: randFloat(0.001, 0.003), z: xzGrowthRate}; + + var flower = { + dimensions: { + x: _this.STARTING_FLOWER_DIMENSIONS.x, + y: _this.STARTING_FLOWER_DIMENSIONS.y, + z: _this.STARTING_FLOWER_DIMENSIONS.z + }, + startingPosition: position, + rotation: Quat.rotationBetween(Vec3.UNIT_Y, surfaceNormal), + maxYDimension: randFloat(0.4, 1.1), + // startingHSLColor: { + // hue: 80 / 360, + // saturation: 0.47, + // light: 0.48 + // }, + // endingHSLColor: { + // hue: 19 / 260, + // saturation: 0.92, + // light: 0.41 + // }, + hslColor: Math.random() < 0.5 ? _this.flowerHSLColors[0] : _this.flowerHSLColors[1], + growthRate: growthRate + }; + flower.userData = { + ProceduralEntity: { + shaderUrl: _this.SHADER_URL, + uniforms: { + iBloomPct: randFloat(0.4, 0.8), + iHSLColor: [flower.hslColor.hue, flower.hslColor.saturation, flower.hslColor.light] + } + } + }; + flower.id = Entities.addEntity({ + type: "Sphere", + name: "flower", + lifetime: 3600, + position: position, + collisionless: true, + rotation: flower.rotation, + dimensions: _this.STARTING_FLOWER_DIMENSIONS, + userData: JSON.stringify(flower.userData) + }); + flower.grow = function() { + // grow flower a bit + if (flower.dimensions.y > flower.maxYDimension) { + return; + } + flower.dimensions = Vec3.sum(flower.dimensions, flower.growthRate); + flower.position = Vec3.sum(flower.startingPosition, Vec3.multiply(Quat.getUp(flower.rotation), flower.dimensions.y / 2)); + //As we grow we must also move ourselves in direction we grow! + //TODO: Add this color changing back once we fix bug https://app.asana.com/0/inbox/31759584831096/96943843100173/98022172055918 + // var newHue = map(flower.dimensions.y, _this.STARTING_FLOWER_DIMENSIONS.y, flower.maxYDimension, flower.startingHSLColor.hue, flower.endingHSLColor.hue); + // var newSaturation = map(flower.dimensions.y, _this.STARTING_FLOWER_DIMENSIONS.y, flower.maxYDimension, flower.startingHSLColor.saturation, flower.endingHSLColor.saturation); + // var newLight = map(flower.dimensions.y, _this.STARTING_FLOWER_DIMENSIONS.y, flower.maxYDimension, flower.startingHSLColor.light, flower.endingHSLColor.light); + // flower.userData.PrsoceduralEntity.uniforms.iHSLColor = [newHue, newSaturation, newLight]; + Entities.editEntity(flower.id, { + dimensions: flower.dimensions, + position: flower.position, + }); + } + _this.flowers.push(flower); + _this.previousFlowerPosition = position; + }, + + preload: function(entityID) { + _this.entityID = entityID; + }, + + }; + + // entity scripts always need to return a newly constructed object of our type + return new GrowingPlant(); +}); \ No newline at end of file diff --git a/unpublishedScripts/DomainContent/Home/plant/growingPlantSpawner.js b/unpublishedScripts/DomainContent/Home/plant/growingPlantSpawner.js new file mode 100644 index 0000000000..a48708eb8a --- /dev/null +++ b/unpublishedScripts/DomainContent/Home/plant/growingPlantSpawner.js @@ -0,0 +1,139 @@ +// +// growingPlantSpawner.js +// examples/homeContent/plant +// +// Created by Eric Levin on 2/10/16. +// Copyright 2016 High Fidelity, Inc. +// +// This entity script handles the logic for growing a plant when it has water poured on it +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html + + +var orientation = Camera.getOrientation(); +orientation = Quat.safeEulerAngles(orientation); +orientation.x = 0; +orientation = Quat.fromVec3Degrees(orientation); + + +var bowlPosition = Vec3.sum(MyAvatar.position, Vec3.multiply(2, Quat.getFront(orientation))); +var BOWL_MODEL_URL = "http://hifi-content.s3.amazonaws.com/alan/dev/Flowers--Bowl.fbx"; +var bowlDimensions = { + x: 0.518, + y: 0.1938, + z: 0.5518 +}; +var bowl = Entities.addEntity({ + type: "Model", + modelURL: BOWL_MODEL_URL, + dimensions: bowlDimensions, + name: "plant bowl", + position: bowlPosition +}); + + +var PLANT_MODEL_URL = "http://hifi-content.s3.amazonaws.com/alan/dev/Flowers--Moss-Rock.fbx"; +var PLANT_SCRIPT_URL = Script.resolvePath("growingPlantEntityScript.js?v1" + Math.random().toFixed(2)); +var plantDimensions = { + x: 0.52, + y: 0.2600, + z: 0.52 +}; +var plantPosition = Vec3.sum(bowlPosition, { + x: 0, + y: plantDimensions.y / 2, + z: 0 +}); +var plant = Entities.addEntity({ + type: "Model", + modelURL: PLANT_MODEL_URL, + name: "hifi-growable-plant", + dimensions: plantDimensions, + position: plantPosition, + script: PLANT_SCRIPT_URL, + parentID: bowl +}); + +var WATER_CAN_MODEL_URL = "https://s3-us-west-1.amazonaws.com/hifi-content/eric/models/waterCan.fbx?v1" + Math.random(); +var WATER_CAN_SCRIPT_URL = Script.resolvePath("waterCanEntityScript.js?v2" + Math.random().toFixed()); +var waterCanPosition = Vec3.sum(plantPosition, Vec3.multiply(0.6, Quat.getRight(orientation))); +var waterCanRotation = orientation; +var waterCan = Entities.addEntity({ + type: "Model", + shapeType: 'box', + name: "hifi-water-can", + modelURL: WATER_CAN_MODEL_URL, + script: WATER_CAN_SCRIPT_URL, + dimensions: { + x: 0.1859, + y: 0.2762, + z: 0.4115 + }, + position: waterCanPosition, + angularDamping: 1, + dynamic: true, + gravity: { + x: 0.0, + y: -2.0, + z: 0 + }, + rotation: waterCanRotation, + userData: JSON.stringify({ + wearable: { + joints: { + RightHand: [{ + x: 0.024, + y: 0.173, + z: 0.152 + }, { + x: 0.374, + y: 0.636, + z: -0.638, + w: -0.215 + }], + LeftHand: [{ + x: -0.0348, + y: 0.201, + z: 0.166 + }, { + x: 0.4095, + y: -0.625, + z: 0.616, + w: -0.247 + }] + } + } + }) +}); + + +var waterSpoutPosition = Vec3.sum(waterCanPosition, Vec3.multiply(0.2, Quat.getFront(orientation))) +var waterSpoutRotation = Quat.multiply(waterCanRotation, Quat.fromPitchYawRollDegrees(10, 0, 0)); +var waterSpout = Entities.addEntity({ + type: "Box", + name: "hifi-water-spout", + dimensions: { + x: 0.02, + y: 0.02, + z: 0.07 + }, + color: { + red: 200, + green: 10, + blue: 200 + }, + position: waterSpoutPosition, + rotation: waterSpoutRotation, + parentID: waterCan, + visible: false +}); + +function cleanup() { + // Entities.deleteEntity(plant); + // Entities.deleteEntity(bowl); + // Entities.deleteEntity(waterCan); + // Entities.deleteEntity(waterSpout); +} + + +Script.scriptEnding.connect(cleanup); \ No newline at end of file diff --git a/unpublishedScripts/DomainContent/Home/plant/waterCanEntityScript.js b/unpublishedScripts/DomainContent/Home/plant/waterCanEntityScript.js new file mode 100644 index 0000000000..d306cf8b6b --- /dev/null +++ b/unpublishedScripts/DomainContent/Home/plant/waterCanEntityScript.js @@ -0,0 +1,242 @@ +// +// waterCanEntityScript.js +// examples/homeContent/plant +// +// Created by Eric Levin on 2/15/16. +// Copyright 2016 High Fidelity, Inc. +// +// This entity script handles the logic for pouring water when a user tilts the entity the script is attached too. +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html + + +(function() { + + Script.include('../../../../libraries/utils.js'); + + var _this; + WaterSpout = function() { + _this = this; + _this.waterSound = SoundCache.getSound("https://s3-us-west-1.amazonaws.com/hifi-content/eric/Sounds/shower.wav"); + _this.POUR_ANGLE_THRESHOLD = 0; + _this.waterPouring = false; + _this.WATER_SPOUT_NAME = "hifi-water-spout"; + _this.GROWABLE_ENTITIES_SEARCH_RANGE = 100; + + }; + + WaterSpout.prototype = { + + startNearGrab: function() { + _this.startHold(); + }, + + startEquip: function() { + _this.startHold(); + }, + + startHold: function() { + _this.findGrowableEntities(); + }, + + releaseEquip: function() { + _this.releaseHold(); + }, + + releaseGrab: function() { + _this.releaseHold(); + }, + + releaseHold: function() { + _this.stopPouring(); + }, + + stopPouring: function() { + Entities.editEntity(_this.waterEffect, { + isEmitting: false + }); + _this.waterPouring = false; + //water no longer pouring... + if (_this.waterInjector) { + _this.waterInjector.stop(); + } + Entities.callEntityMethod(_this.mostRecentIntersectedGrowableEntity, 'stopWatering'); + }, + continueEquip: function() { + _this.continueHolding(); + }, + + continueNearGrab: function() { + _this.continueHolding(); + }, + + continueHolding: function() { + if (!_this.waterSpout) { + return; + } + // Check rotation of water can along it's z axis. If it's beyond a threshold, then start spraying water + _this.castRay(); + var rotation = Entities.getEntityProperties(_this.entityID, "rotation").rotation; + var pitch = Quat.safeEulerAngles(rotation).x; + if (pitch < _this.POUR_ANGLE_THRESHOLD) { + // Water is pouring + var spoutProps = Entities.getEntityProperties(_this.waterSpout, ["rotation", "position"]); + if (!_this.waterPouring) { + Entities.editEntity(_this.waterEffect, { + isEmitting: true + }); + _this.waterPouring = true; + if (!_this.waterInjector) { + _this.waterInjector = Audio.playSound(_this.waterSound, { + position: spoutProps.position, + loop: true + }); + + } else { + _this.waterInjector.restart(); + } + } + _this.waterSpoutRotation = spoutProps.rotation; + var waterEmitOrientation = Quat.multiply(_this.waterSpoutRotation, Quat.fromPitchYawRollDegrees(0, 180, 0)); + Entities.editEntity(_this.waterEffect, { + emitOrientation: waterEmitOrientation + }); + } else if (pitch > _this.POUR_ANGLE_THRESHOLD && _this.waterPouring) { + _this.stopPouring(); + } + }, + + castRay: function() { + var spoutProps = Entities.getEntityProperties(_this.waterSpout, ["position, rotation"]); + var direction = Quat.getFront(spoutProps.rotation) + var end = Vec3.sum(spoutProps.position, Vec3.multiply(5, direction)); + + var pickRay = { + origin: spoutProps.position, + direction: direction + }; + var intersection = Entities.findRayIntersection(pickRay, true, _this.growableEntities); + + if (intersection.intersects) { + //We've intersected with a waterable object + var data = JSON.stringify({ + position: intersection.intersection, + surfaceNormal: intersection.surfaceNormal + }); + _this.mostRecentIntersectedGrowableEntity = intersection.entityID; + Entities.callEntityMethod(intersection.entityID, 'continueWatering', [data]); + } + + }, + + + + createWaterEffect: function() { + var waterEffectPosition = Vec3.sum(_this.waterSpoutPosition, Vec3.multiply(Quat.getFront(_this.waterSpoutRotation), -0.04)); + _this.waterEffect = Entities.addEntity({ + type: "ParticleEffect", + name: "water particle effect", + position: waterEffectPosition, + isEmitting: false, + parentID: _this.waterSpout, + colorStart: { + red: 90, + green: 90, + blue: 110 + }, + color: { + red: 70, + green: 70, + blue: 130 + }, + colorFinish: { + red: 23, + green: 195, + blue: 206 + }, + maxParticles: 20000, + lifespan: 2, + emitRate: 2000, + emitSpeed: .3, + speedSpread: 0.1, + emitDimensions: { + x: 0.0, + y: 0.0, + z: 0.0 + }, + emitAcceleration: { + x: 0.0, + y: 0, + z: 0 + }, + polarStart: 0.0, + polarFinish: 0.1, + accelerationSpread: { + x: 0.01, + y: 0.0, + z: 0.01 + }, + emitOrientation: Quat.fromPitchYawRollDegrees(0, 0, 0), + radiusSpread: 0.0001, + radiusStart: 0.005, + particleRadius: 0.003, + radiusFinish: 0.001, + alphaSpread: 0, + alphaStart: 0.1, + alpha: 1.0, + alphaFinish: 1.0, + emitterShouldTrail: true, + textures: "https://s3-us-west-1.amazonaws.com/hifi-content/eric/images/raindrop.png", + }); + + }, + + findGrowableEntities: function() { + _this.growableEntities = []; + var entities = Entities.findEntities(_this.position, _this.GROWABLE_ENTITIES_SEARCH_RANGE); + entities.forEach(function(entity) { + var name = Entities.getEntityProperties(entity, "name").name; + if (name.length > 0 && name.indexOf("growable") !== -1) { + _this.growableEntities.push(entity); + } + }); + + }, + + preload: function(entityID) { + _this.entityID = entityID; + _this.position = Entities.getEntityProperties(_this.entityID, "position").position; + // Wait a a bit for spout to spawn for case where preload is initial spawn, then save it + Script.setTimeout(function() { + var entities = Entities.findEntities(_this.position, 1); + entities.forEach(function(entity) { + var name = Entities.getEntityProperties(entity, "name").name; + if (name === _this.WATER_SPOUT_NAME) { + _this.waterSpout = entity; + } + }); + + if (_this.waterSpout) { + _this.waterSpoutPosition = Entities.getEntityProperties(_this.waterSpout, "position").position; + _this.waterSpoutRotation = Entities.getEntityProperties(_this.waterSpout, "rotation").rotation; + _this.createWaterEffect(); + } + + }, 3000); + + }, + + + unload: function() { + Entities.deleteEntity(_this.waterEffect); + if (_this.waterInjector) { + _this.waterInjector.stop(); + delete _this.waterInjector; + } + } + + }; + + // entity scripts always need to return a newly constructed object of our type + return new WaterSpout(); +}); \ No newline at end of file