diff --git a/examples/shaders/grid.fs b/examples/shaders/grid.fs
new file mode 100644
index 0000000000..57dcb7ad5d
--- /dev/null
+++ b/examples/shaders/grid.fs
@@ -0,0 +1,102 @@
+#line 2
+
+// https://www.shadertoy.com/view/lss3WS
+
+// srtuss, 2013
+
+// collecting some design ideas for a new game project.
+// no raymarching is used.
+
+// if i could add a custom soundtrack, it'd use this one (essential for desired sensation)
+// http://www.youtube.com/watch?v=1uFAu65tZpo
+
+//#define GREEN_VERSION
+
+// ** improved camera shaking
+// ** cleaned up code
+// ** added stuff to the gates
+
+// srtuss, 2013
+float time = iGlobalTime;
+
+// some noise functions for fast developing
+float rand11(float p) {
+ return fract(sin(p * 591.32) * 43758.5357);
+}
+
+float rand12(vec2 p) {
+ return fract(sin(dot(p.xy, vec2(12.9898, 78.233))) * 43758.5357);
+}
+
+vec2 rand21(float p) {
+ return fract(vec2(sin(p * 591.32), cos(p * 391.32)));
+}
+
+vec2 rand22(in vec2 p)
+{
+ return fract(vec2(sin(p.x * 591.32 + p.y * 154.077),
+ cos(p.x * 391.32 + p.y * 49.077)));
+}
+
+vec3 voronoi(in vec2 x) {
+ vec2 n = floor(x); // grid cell id
+ vec2 f = fract(x);// grid internal position
+ vec2 mg;// shortest distance...
+ vec2 mr;// ..and second shortest distance
+ float md = 8.0, md2 = 8.0;
+ for(int j = -1; j <= 1; j ++)
+ {
+ for(int i = -1; i <= 1; i ++)
+ {
+ vec2 g = vec2(float(i), float(j)); // cell id
+ vec2 o = rand22(n + g);// offset to edge point
+ vec2 r = g + o - f;
+
+ float d = max(abs(r.x), abs(r.y));// distance to the edge
+
+ if(d < md)
+ { md2 = md; md = d; mr = r; mg = g;}
+ else if(d < md2)
+ { md2 = d;}
+ }
+ }
+ return vec3(n + mg, md2 - md);
+}
+
+
+vec4 getProceduralColor() {
+ float inten = 0.0;
+ vec3 its;
+ float v, g;
+ // voronoi floor layers
+ for (int i = 0; i < 4; i++) {
+ float layer = float(i);
+ vec2 pos = _position.xz * 100.0;
+ if (i == 2) {
+ pos.x += time * 0.05;
+ } else if (i == 1) {
+ pos.y += time * 0.07;
+ }
+ vec3 vo = voronoi(pos + 8.0 * rand21(float(i)));
+
+ v = exp(-100.0 * (vo.z - 0.02));
+ float fx = 0.0;
+
+ // add some special fx to lowest layer
+ if (i == 3) {
+ float crd = 0.0; //fract(time * 0.2) * 50.0 - 25.0;
+ float fxi = cos(vo.x * 0.2 + time * 0.5); //abs(crd - vo.x);
+ fx = clamp(smoothstep(0.9, 1.0, fxi), 0.0, 0.9) * 1.0 * rand12(vo.xy);
+ fx *= exp(-3.0 * vo.z) * 2.0;
+ }
+ inten += v * 0.1 + fx;
+ }
+ inten *= 0.4 + (sin(time) * 0.5 + 0.5) * 0.6;
+ vec3 cr = vec3(0.15, 2.0, 9.0);
+ vec3 cg = vec3(2.0, 0.15, 9.0);
+ vec3 cb = vec3(9.0, 2.0, 0.15);
+ vec3 ct = vec3(9.0, 0.25, 0.3);
+ vec3 cy = vec3(0.25, 0.3, 9.3);
+ vec3 col = pow(vec3(inten), 1.5 * cy);
+ return vec4(col, 1.0);
+}
diff --git a/examples/shaders/hex.fs b/examples/shaders/hex.fs
new file mode 100644
index 0000000000..2264d4bf05
--- /dev/null
+++ b/examples/shaders/hex.fs
@@ -0,0 +1,48 @@
+#line 2
+// Created by inigo quilez - iq/2014
+// License Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.
+
+// { 2d cell id, distance to border, distnace to center )
+vec4 hexagon(vec2 p) {
+ vec2 q = vec2(p.x * 2.0 * 0.5773503, p.y + p.x * 0.5773503);
+
+ vec2 pi = floor(q);
+ vec2 pf = fract(q);
+
+ float v = mod(pi.x + pi.y, 3.0);
+
+ float ca = step(1.0, v);
+ float cb = step(2.0, v);
+ vec2 ma = step(pf.xy, pf.yx);
+
+ // distance to borders
+ float e = dot(ma,
+ 1.0 - pf.yx + ca * (pf.x + pf.y - 1.0) + cb * (pf.yx - 2.0 * pf.xy));
+
+ // distance to center
+ p = vec2(q.x + floor(0.5 + p.y / 1.5), 4.0 * p.y / 3.0) * 0.5 + 0.5;
+ float f = length((fract(p) - 0.5) * vec2(1.0, 0.85));
+
+ return vec4(pi + ca - cb * ma, e, f);
+}
+
+
+float hash1(vec2 p) {
+ float n = dot(p, vec2(127.1, 311.7));
+ return fract(sin(n) * 43758.5453);
+}
+
+vec4 getProceduralColor() {
+ vec2 uv = _position.xz + 0.5;
+ vec2 pos = _position.xz * iWorldScale.xz;
+ // gray
+ vec4 h = hexagon(8.0 * pos + 0.5);
+ float n = snoise(vec3(0.3 * h.xy + iGlobalTime * 0.1, iGlobalTime));
+ vec3 col = 0.15 + 0.15 * hash1(h.xy + 1.2) * vec3(1.0);
+ col *= smoothstep(0.10, 0.11, h.z);
+ col *= smoothstep(0.10, 0.11, h.w);
+ col *= 1.0 + 0.15 * sin(40.0 * h.z);
+ col *= 0.75 + 0.5 * h.z * n;
+ col *= pow(16.0 * uv.x * (1.0 - uv.x) * uv.y * (1.0 - uv.y), 0.1);
+ return vec4(col, 1.0);
+}
diff --git a/examples/shaders/test.fs b/examples/shaders/test.fs
new file mode 100644
index 0000000000..07db8f409e
--- /dev/null
+++ b/examples/shaders/test.fs
@@ -0,0 +1,48 @@
+#line 2
+// Created by inigo quilez - iq/2014
+// License Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.
+
+// { 2d cell id, distance to border, distnace to center )
+vec4 hexagon(vec2 p) {
+ vec2 q = vec2(p.x * 2.0 * 0.5773503, p.y + p.x * 0.5773503);
+
+ vec2 pi = floor(q);
+ vec2 pf = fract(q);
+
+ float v = mod(pi.x + pi.y, 3.0);
+
+ float ca = step(1.0, v);
+ float cb = step(2.0, v);
+ vec2 ma = step(pf.xy, pf.yx);
+
+ // distance to borders
+ float e = dot(ma,
+ 1.0 - pf.yx + ca * (pf.x + pf.y - 1.0) + cb * (pf.yx - 2.0 * pf.xy));
+
+ // distance to center
+ p = vec2(q.x + floor(0.5 + p.y / 1.5), 4.0 * p.y / 3.0) * 0.5 + 0.5;
+ float f = length((fract(p) - 0.5) * vec2(1.0, 0.85));
+
+ return vec4(pi + ca - cb * ma, e, f);
+}
+
+
+float hash1(vec2 p) {
+ float n = dot(p, vec2(127.1, 311.7));
+ return fract(sin(n) * 43758.5453);
+}
+
+vec4 getProceduralColor() {
+ vec2 uv = _position.xz + 0.5;
+ vec2 pos = _position.xz * 40.0;
+ // gray
+ vec4 h = hexagon(8.0 * pos + 0.5);
+ float n = snoise(vec3(0.3 * h.xy + iGlobalTime * 0.1, iGlobalTime));
+ vec3 col = 0.15 + 0.15 * hash1(h.xy + 1.2) * vec3(1.0);
+ col *= smoothstep(0.10, 0.11, h.z);
+ col *= smoothstep(0.10, 0.11, h.w);
+ col *= 1.0 + 0.15 * sin(40.0 * h.z);
+ col *= 0.75 + 0.5 * h.z * n;
+ col *= pow(16.0 * uv.x * (1.0 - uv.x) * uv.y * (1.0 - uv.y), 0.1);
+ return vec4(col, 1.0);
+}
diff --git a/examples/toys/proceduralEdit.js b/examples/toys/proceduralEdit.js
new file mode 100644
index 0000000000..0792344063
--- /dev/null
+++ b/examples/toys/proceduralEdit.js
@@ -0,0 +1,12 @@
+Script.include("../toys/magBalls/constants.js");
+
+OmniToolModuleType = "ProceduralEdit"
+
+OmniToolModules.ProceduralEdit = function(omniTool, entityId) {
+ this.omniTool = omniTool;
+ this.entityId = entityId;
+
+ var window = new WebWindow('Procedural', "file:///C:/Users/bdavis/Git/hifi/examples/toys/proceduralEdit/ui.html", 640, 480, false);
+ window.setVisible(true);
+
+}
diff --git a/examples/toys/proceduralEdit/ui.html b/examples/toys/proceduralEdit/ui.html
new file mode 100644
index 0000000000..04d4417912
--- /dev/null
+++ b/examples/toys/proceduralEdit/ui.html
@@ -0,0 +1,29 @@
+
+
+
+ACE in Action
+
+
+
+
+function foo(items) {
+ var x = "All this is syntax highlighted";
+ return x;
+}
+
+
+
+
+
\ No newline at end of file
diff --git a/libraries/entities-renderer/src/RenderableBoxEntityItem.cpp b/libraries/entities-renderer/src/RenderableBoxEntityItem.cpp
index 774a371726..f73b99d059 100644
--- a/libraries/entities-renderer/src/RenderableBoxEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableBoxEntityItem.cpp
@@ -16,6 +16,7 @@
#include
#include
+#include
#include
#include
@@ -25,15 +26,29 @@ EntityItemPointer RenderableBoxEntityItem::factory(const EntityItemID& entityID,
return std::make_shared(entityID, properties);
}
+void RenderableBoxEntityItem::setUserData(const QString& value) {
+ BoxEntityItem::setUserData(value);
+ _procedural.reset();
+}
+
void RenderableBoxEntityItem::render(RenderArgs* args) {
PerformanceTimer perfTimer("RenderableBoxEntityItem::render");
Q_ASSERT(getType() == EntityTypes::Box);
- glm::vec4 cubeColor(toGlm(getXColor()), getLocalRenderAlpha());
-
Q_ASSERT(args->_batch);
gpu::Batch& batch = *args->_batch;
batch.setModelTransform(getTransformToCenter()); // we want to include the scale as well
- DependencyManager::get()->renderSolidCube(batch, 1.0f, cubeColor);
+
+ if (!_procedural) {
+ _procedural.reset(new ProceduralInfo(this));
+ }
+
+ if (_procedural->ready()) {
+ _procedural->prepare(batch);
+ DependencyManager::get()->renderUnitCube(batch);
+ } else {
+ glm::vec4 cubeColor(toGlm(getXColor()), getLocalRenderAlpha());
+ DependencyManager::get()->renderSolidCube(batch, 1.0f, cubeColor);
+ }
RenderableDebugableEntityItem::render(this, args);
};
diff --git a/libraries/entities-renderer/src/RenderableBoxEntityItem.h b/libraries/entities-renderer/src/RenderableBoxEntityItem.h
index b14da9ee22..e317163683 100644
--- a/libraries/entities-renderer/src/RenderableBoxEntityItem.h
+++ b/libraries/entities-renderer/src/RenderableBoxEntityItem.h
@@ -14,8 +14,9 @@
#include
#include "RenderableEntityItem.h"
+#include "RenderableProceduralItem.h"
-class RenderableBoxEntityItem : public BoxEntityItem {
+class RenderableBoxEntityItem : public BoxEntityItem, RenderableProceduralItem {
public:
static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
@@ -24,6 +25,7 @@ public:
{ }
virtual void render(RenderArgs* args);
+ virtual void setUserData(const QString& value);
SIMPLE_RENDERABLE()
};
diff --git a/libraries/entities-renderer/src/RenderableProceduralItem.cpp b/libraries/entities-renderer/src/RenderableProceduralItem.cpp
new file mode 100644
index 0000000000..6b0a3db1da
--- /dev/null
+++ b/libraries/entities-renderer/src/RenderableProceduralItem.cpp
@@ -0,0 +1,142 @@
+//
+// Created by Bradley Austin Davis on 2015/09/05
+// Copyright 2013-2015 High Fidelity, Inc.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+#include "RenderableProceduralItem.h"
+
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+#include "RenderableProceduralItemShader.h"
+#include "../render-utils/simple_vert.h"
+
+static const char* const UNIFORM_TIME_NAME= "iGlobalTime";
+static const char* const UNIFORM_SCALE_NAME = "iWorldScale";
+static const QString PROCEDURAL_USER_DATA_KEY = "ProceduralEntity";
+
+RenderableProceduralItem::ProceduralInfo::ProceduralInfo(EntityItem* entity) : _entity(entity) {
+ QJsonObject userData;
+ {
+ const QString& userDataJson = entity->getUserData();
+ if (userDataJson.isEmpty()) {
+ return;
+ }
+ QJsonParseError parseError;
+ auto doc = QJsonDocument::fromJson(userDataJson.toUtf8(), &parseError);
+ if (parseError.error != QJsonParseError::NoError) {
+ return;
+ }
+ qDebug() << "Found JSON user data: " << userDataJson;
+ userData = doc.object();
+ }
+
+ // Example
+ //{
+ // "ProceduralEntity": {
+ // "shaderUrl": "file:///C:/Users/bdavis/Git/hifi/examples/shaders/test.fs",
+ // "color" : "#FFFFFF"
+ // }
+ //}
+
+ auto proceduralData = userData[PROCEDURAL_USER_DATA_KEY];
+ if (proceduralData.isNull()) {
+ return;
+ }
+
+ auto proceduralDataObject = proceduralData.toObject();
+ foreach(auto key, proceduralDataObject) {
+ qDebug() << key;
+ }
+ qDebug() << "Procedural data object " << proceduralDataObject;
+ QString shaderUrl = proceduralDataObject["shaderUrl"].toString();
+ qDebug() << "Shader url: " << shaderUrl;
+ _shaderUrl = QUrl(shaderUrl);
+ if (!_shaderUrl.isValid()) {
+ return;
+ }
+
+ if (_shaderUrl.isLocalFile()) {
+ _shaderPath = _shaderUrl.toLocalFile();
+ qDebug() << "Shader path: " << _shaderPath;
+ if (!QFile(_shaderPath).exists()) {
+ return;
+ }
+ } else {
+ _networkShader = ShaderCache::instance().getShader(_shaderUrl);
+ }
+
+ _enabled = true;
+}
+
+bool RenderableProceduralItem::ProceduralInfo::ready() {
+ if (!_enabled) {
+ return false;
+ }
+
+ if (!_shaderPath.isEmpty()) {
+ return true;
+ }
+
+ if (_networkShader) {
+ return _networkShader->isLoaded();
+ }
+
+ return false;
+}
+
+void RenderableProceduralItem::ProceduralInfo::prepare(gpu::Batch& batch) {
+ if (_shaderUrl.isLocalFile()) {
+ auto lastModified = QFileInfo(_shaderPath).lastModified().toMSecsSinceEpoch();
+ if (lastModified > _shaderModified) {
+ QFile file(_shaderPath);
+ file.open(QIODevice::ReadOnly);
+ _shaderSource = QTextStream(&file).readAll();
+ _pipelineDirty = true;
+ _shaderModified = lastModified;
+ }
+ } else if (_networkShader && _networkShader->isLoaded()) {
+ _shaderSource = _networkShader->_source;
+ }
+
+ if (!_pipeline || _pipelineDirty) {
+ _pipelineDirty = false;
+ if (!_vertexShader) {
+ _vertexShader = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(simple_vert)));
+ }
+ QString framentShaderSource = SHADER_TEMPLATE.arg(_shaderSource);
+ _fragmentShader = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(framentShaderSource.toLocal8Bit().data())));
+ _shader = gpu::ShaderPointer(gpu::Shader::createProgram(_vertexShader, _fragmentShader));
+ gpu::Shader::BindingSet slotBindings;
+ slotBindings.insert(gpu::Shader::Binding(std::string("normalFittingMap"), DeferredLightingEffect::NORMAL_FITTING_MAP_SLOT));
+ gpu::Shader::makeProgram(*_shader, slotBindings);
+ auto state = std::make_shared();
+ state->setCullMode(gpu::State::CULL_NONE);
+ state->setDepthTest(true, true, gpu::LESS_EQUAL);
+ state->setBlendFunction(false,
+ gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
+ gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
+ _pipeline = gpu::PipelinePointer(gpu::Pipeline::create(_shader, state));
+ _timeSlot = _shader->getUniforms().findLocation(UNIFORM_TIME_NAME);
+ _scaleSlot = _shader->getUniforms().findLocation(UNIFORM_SCALE_NAME);
+ _start = usecTimestampNow();
+ }
+
+ batch.setPipeline(_pipeline);
+ float time = (float)((usecTimestampNow() - _start) / USECS_PER_MSEC) / MSECS_PER_SECOND;
+ batch._glUniform1f(_timeSlot, time);
+ auto scale = _entity->getDimensions();
+ batch._glUniform3f(_scaleSlot, scale.x, scale.y, scale.z);
+ batch.setResourceTexture(DeferredLightingEffect::NORMAL_FITTING_MAP_SLOT, DependencyManager::get()->getNormalFittingTexture());
+}
diff --git a/libraries/entities-renderer/src/RenderableProceduralItem.h b/libraries/entities-renderer/src/RenderableProceduralItem.h
new file mode 100644
index 0000000000..e42cc81b64
--- /dev/null
+++ b/libraries/entities-renderer/src/RenderableProceduralItem.h
@@ -0,0 +1,51 @@
+//
+// Created by Bradley Austin Davis on 2015/09/05
+// Copyright 2013-2015 High Fidelity, Inc.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+#pragma once
+#ifndef hifi_RenderableProcedrualItem_h
+#define hifi_RenderableProcedrualItem_h
+
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+class EntityItem;
+
+class RenderableProceduralItem {
+protected:
+ struct ProceduralInfo {
+ ProceduralInfo(EntityItem* entity);
+ bool ready();
+ void prepare(gpu::Batch& batch);
+
+ bool _enabled{ false };
+ gpu::PipelinePointer _pipeline;
+ gpu::ShaderPointer _vertexShader;
+ gpu::ShaderPointer _fragmentShader;
+ gpu::ShaderPointer _shader;
+ QString _shaderSource;
+ QString _shaderPath;
+ QUrl _shaderUrl;
+ quint64 _shaderModified{ 0 };
+ bool _pipelineDirty{ true };
+ int32_t _timeSlot{ gpu::Shader::INVALID_LOCATION };
+ int32_t _scaleSlot{ gpu::Shader::INVALID_LOCATION };
+ uint64_t _start{ 0 };
+ NetworkShaderPointer _networkShader;
+ EntityItem* _entity;
+ };
+
+ QSharedPointer _procedural;
+};
+
+#endif
diff --git a/libraries/entities-renderer/src/RenderableProceduralItemShader.h b/libraries/entities-renderer/src/RenderableProceduralItemShader.h
new file mode 100644
index 0000000000..82ba91cc42
--- /dev/null
+++ b/libraries/entities-renderer/src/RenderableProceduralItemShader.h
@@ -0,0 +1,329 @@
+//
+// Created by Bradley Austin Davis on 2015/09/05
+// Copyright 2013-2015 High Fidelity, Inc.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+// Shader includes portions of webgl-noise:
+// Description : Array and textureless GLSL 2D/3D/4D simplex
+// noise functions.
+// Author : Ian McEwan, Ashima Arts.
+// Maintainer : ijm
+// Lastmod : 20110822 (ijm)
+// License : Copyright (C) 2011 Ashima Arts. All rights reserved.
+// Distributed under the MIT License. See LICENSE file.
+// https://github.com/ashima/webgl-noise
+//
+const QString SHADER_TEMPLATE = R"SCRIBE(#version 410 core
+layout(location = 0) out vec4 _fragColor0;
+layout(location = 1) out vec4 _fragColor1;
+layout(location = 2) out vec4 _fragColor2;
+
+// the glow intensity
+uniform float glowIntensity;
+
+// the alpha threshold
+uniform float alphaThreshold;
+
+uniform sampler2D normalFittingMap;
+
+vec3 bestFitNormal(vec3 normal) {
+ vec3 absNorm = abs(normal);
+ float maxNAbs = max(absNorm.z, max(absNorm.x, absNorm.y));
+
+ vec2 texcoord = (absNorm.z < maxNAbs ?
+ (absNorm.y < maxNAbs ? absNorm.yz : absNorm.xz) :
+ absNorm.xy);
+ texcoord = (texcoord.x < texcoord.y ? texcoord.yx : texcoord.xy);
+ texcoord.y /= texcoord.x;
+ vec3 cN = normal / maxNAbs;
+ float fittingScale = texture(normalFittingMap, texcoord).a;
+ cN *= fittingScale;
+ return (cN * 0.5 + 0.5);
+}
+
+
+const vec3 DEFAULT_SPECULAR = vec3(0.1);
+const float DEFAULT_SHININESS = 10;
+
+void packDeferredFragmentLightmap(vec3 normal, float alpha, vec3 diffuse, vec3 specular, float shininess, vec3 emissive) {
+ if (alpha != glowIntensity) {
+ discard;
+ }
+
+ _fragColor0 = vec4(diffuse.rgb, alpha);
+ _fragColor1 = vec4(bestFitNormal(normal), 0.5);
+ _fragColor2 = vec4(emissive, shininess / 128.0);
+}
+
+float mod289(float x) {
+ return x - floor(x * (1.0 / 289.0)) * 289.0;
+}
+
+vec2 mod289(vec2 x) {
+ return x - floor(x * (1.0 / 289.0)) * 289.0;
+}
+
+vec3 mod289(vec3 x) {
+ return x - floor(x * (1.0 / 289.0)) * 289.0;
+}
+
+vec4 mod289(vec4 x) {
+ return x - floor(x * (1.0 / 289.0)) * 289.0;
+}
+
+float permute(float x) {
+ return mod289(((x*34.0)+1.0)*x);
+}
+
+vec3 permute(vec3 x) {
+ return mod289(((x*34.0)+1.0)*x);
+}
+
+vec4 permute(vec4 x) {
+ return mod289(((x*34.0)+1.0)*x);
+}
+
+float taylorInvSqrt(float r) {
+ return 1.79284291400159 - 0.85373472095314 * r;
+}
+
+vec4 taylorInvSqrt(vec4 r) {
+ return 1.79284291400159 - 0.85373472095314 * r;
+}
+
+vec4 grad4(float j, vec4 ip) {
+ const vec4 ones = vec4(1.0, 1.0, 1.0, -1.0);
+ vec4 p, s;
+
+ p.xyz = floor(fract(vec3(j) * ip.xyz) * 7.0) * ip.z - 1.0;
+ p.w = 1.5 - dot(abs(p.xyz), ones.xyz);
+ s = vec4(lessThan(p, vec4(0.0)));
+ p.xyz = p.xyz + (s.xyz * 2.0 - 1.0) * s.www;
+
+ return p;
+}
+
+// (sqrt(5) - 1)/4 = F4, used once below
+#define F4 0.309016994374947451
+
+float snoise(vec4 v) {
+ const vec4 C = vec4(0.138196601125011, // (5 - sqrt(5))/20 G4
+ 0.276393202250021, // 2 * G4
+ 0.414589803375032, // 3 * G4
+ -0.447213595499958); // -1 + 4 * G4
+
+ // First corner
+ vec4 i = floor(v + dot(v, vec4(F4)));
+ vec4 x0 = v - i + dot(i, C.xxxx);
+
+ // Other corners
+
+ // Rank sorting originally contributed by Bill Licea-Kane, AMD (formerly ATI)
+ vec4 i0;
+ vec3 isX = step(x0.yzw, x0.xxx);
+ vec3 isYZ = step(x0.zww, x0.yyz);
+ i0.x = isX.x + isX.y + isX.z;
+ i0.yzw = 1.0 - isX;
+ i0.y += isYZ.x + isYZ.y;
+ i0.zw += 1.0 - isYZ.xy;
+ i0.z += isYZ.z;
+ i0.w += 1.0 - isYZ.z;
+
+ // i0 now contains the unique values 0,1,2,3 in each channel
+ vec4 i3 = clamp(i0, 0.0, 1.0);
+ vec4 i2 = clamp(i0 - 1.0, 0.0, 1.0);
+ vec4 i1 = clamp(i0 - 2.0, 0.0, 1.0);
+
+ vec4 x1 = x0 - i1 + C.xxxx;
+ vec4 x2 = x0 - i2 + C.yyyy;
+ vec4 x3 = x0 - i3 + C.zzzz;
+ vec4 x4 = x0 + C.wwww;
+
+ // Permutations
+ i = mod289(i);
+ float j0 = permute(permute(permute(permute(i.w) + i.z) + i.y) + i.x);
+ vec4 j1 = permute(
+ permute(
+ permute(
+ permute(i.w + vec4(i1.w, i2.w, i3.w, 1.0)) + i.z
+ + vec4(i1.z, i2.z, i3.z, 1.0)) + i.y
+ + vec4(i1.y, i2.y, i3.y, 1.0)) + i.x
+ + vec4(i1.x, i2.x, i3.x, 1.0));
+
+ // Gradients: 7x7x6 points over a cube, mapped onto a 4-cross polytope
+ // 7*7*6 = 294, which is close to the ring size 17*17 = 289.
+ vec4 ip = vec4(1.0 / 294.0, 1.0 / 49.0, 1.0 / 7.0, 0.0);
+
+ vec4 p0 = grad4(j0, ip);
+ vec4 p1 = grad4(j1.x, ip);
+ vec4 p2 = grad4(j1.y, ip);
+ vec4 p3 = grad4(j1.z, ip);
+ vec4 p4 = grad4(j1.w, ip);
+
+ // Normalise gradients
+ vec4 norm = taylorInvSqrt(
+ vec4(dot(p0, p0), dot(p1, p1), dot(p2, p2), dot(p3, p3)));
+ p0 *= norm.x;
+ p1 *= norm.y;
+ p2 *= norm.z;
+ p3 *= norm.w;
+ p4 *= taylorInvSqrt(dot(p4, p4));
+
+ // Mix contributions from the five corners
+ vec3 m0 = max(0.6 - vec3(dot(x0, x0), dot(x1, x1), dot(x2, x2)), 0.0);
+ vec2 m1 = max(0.6 - vec2(dot(x3, x3), dot(x4, x4)), 0.0);
+ m0 = m0 * m0;
+ m1 = m1 * m1;
+ return 49.0
+ * (dot(m0 * m0, vec3(dot(p0, x0), dot(p1, x1), dot(p2, x2)))
+ + dot(m1 * m1, vec2(dot(p3, x3), dot(p4, x4))));
+
+}
+
+float snoise(vec3 v) {
+ const vec2 C = vec2(1.0 / 6.0, 1.0 / 3.0);
+ const vec4 D = vec4(0.0, 0.5, 1.0, 2.0);
+
+ // First corner
+ vec3 i = floor(v + dot(v, C.yyy));
+ vec3 x0 = v - i + dot(i, C.xxx);
+
+ // Other corners
+ vec3 g = step(x0.yzx, x0.xyz);
+ vec3 l = 1.0 - g;
+ vec3 i1 = min(g.xyz, l.zxy);
+ vec3 i2 = max(g.xyz, l.zxy);
+
+ vec3 x1 = x0 - i1 + C.xxx;
+ vec3 x2 = x0 - i2 + C.yyy; // 2.0*C.x = 1/3 = C.y
+ vec3 x3 = x0 - D.yyy; // -1.0+3.0*C.x = -0.5 = -D.y
+
+ // Permutations
+ i = mod289(i);
+ vec4 p = permute(
+ permute(
+ permute(i.z + vec4(0.0, i1.z, i2.z, 1.0)) + i.y
+ + vec4(0.0, i1.y, i2.y, 1.0)) + i.x
+ + vec4(0.0, i1.x, i2.x, 1.0));
+
+ // Gradients: 7x7 points over a square, mapped onto an octahedron.
+ // The ring size 17*17 = 289 is close to a multiple of 49 (49*6 = 294)
+ float n_ = 0.142857142857; // 1.0/7.0
+ vec3 ns = n_ * D.wyz - D.xzx;
+
+ vec4 j = p - 49.0 * floor(p * ns.z * ns.z); // mod(p,7*7)
+
+ vec4 x_ = floor(j * ns.z);
+ vec4 y_ = floor(j - 7.0 * x_); // mod(j,N)
+
+ vec4 x = x_ * ns.x + ns.yyyy;
+ vec4 y = y_ * ns.x + ns.yyyy;
+ vec4 h = 1.0 - abs(x) - abs(y);
+
+ vec4 b0 = vec4(x.xy, y.xy);
+ vec4 b1 = vec4(x.zw, y.zw);
+
+ //vec4 s0 = vec4(lessThan(b0,0.0))*2.0 - 1.0;
+ //vec4 s1 = vec4(lessThan(b1,0.0))*2.0 - 1.0;
+ vec4 s0 = floor(b0) * 2.0 + 1.0;
+ vec4 s1 = floor(b1) * 2.0 + 1.0;
+ vec4 sh = -step(h, vec4(0.0));
+
+ vec4 a0 = b0.xzyw + s0.xzyw * sh.xxyy;
+ vec4 a1 = b1.xzyw + s1.xzyw * sh.zzww;
+
+ vec3 p0 = vec3(a0.xy, h.x);
+ vec3 p1 = vec3(a0.zw, h.y);
+ vec3 p2 = vec3(a1.xy, h.z);
+ vec3 p3 = vec3(a1.zw, h.w);
+
+ //Normalise gradients
+ vec4 norm = taylorInvSqrt(
+ vec4(dot(p0, p0), dot(p1, p1), dot(p2, p2), dot(p3, p3)));
+ p0 *= norm.x;
+ p1 *= norm.y;
+ p2 *= norm.z;
+ p3 *= norm.w;
+
+ // Mix final noise value
+ vec4 m = max(0.6 - vec4(dot(x0, x0), dot(x1, x1), dot(x2, x2), dot(x3, x3)),
+ 0.0);
+ m = m * m;
+ return 42.0
+ * dot(m * m, vec4(dot(p0, x0), dot(p1, x1), dot(p2, x2), dot(p3, x3)));
+}
+
+float snoise(vec2 v) {
+ const vec4 C = vec4(0.211324865405187, // (3.0-sqrt(3.0))/6.0
+ 0.366025403784439, // 0.5*(sqrt(3.0)-1.0)
+ -0.577350269189626, // -1.0 + 2.0 * C.x
+ 0.024390243902439); // 1.0 / 41.0
+ // First corner
+ vec2 i = floor(v + dot(v, C.yy));
+ vec2 x0 = v - i + dot(i, C.xx);
+
+ // Other corners
+ vec2 i1;
+ i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);
+ vec4 x12 = x0.xyxy + C.xxzz;
+ x12.xy -= i1;
+
+ // Permutations
+ i = mod289(i); // Avoid truncation effects in permutation
+ vec3 p = permute(
+ permute(i.y + vec3(0.0, i1.y, 1.0)) + i.x + vec3(0.0, i1.x, 1.0));
+
+ vec3 m = max(0.5 - vec3(dot(x0, x0), dot(x12.xy, x12.xy), dot(x12.zw, x12.zw)),
+ 0.0);
+ m = m * m;
+ m = m * m;
+
+ // Gradients: 41 points uniformly over a line, mapped onto a diamond.
+ // The ring size 17*17 = 289 is close to a multiple of 41 (41*7 = 287)
+
+ vec3 x = 2.0 * fract(p * C.www) - 1.0;
+ vec3 h = abs(x) - 0.5;
+ vec3 ox = floor(x + 0.5);
+ vec3 a0 = x - ox;
+
+ // Normalise gradients implicitly by scaling m
+ // Approximation of: m *= inversesqrt( a0*a0 + h*h );
+ m *= 1.79284291400159 - 0.85373472095314 * (a0 * a0 + h * h);
+
+ // Compute final noise value at P
+ vec3 g;
+ g.x = a0.x * x0.x + h.x * x0.y;
+ g.yz = a0.yz * x12.xz + h.yz * x12.yw;
+ return 130.0 * dot(m, g);
+}
+
+// the interpolated normal
+in vec3 _normal;
+in vec3 _color;
+in vec2 _texCoord0;
+in vec4 _position;
+
+// TODO add more uniforms
+uniform float iGlobalTime; // shader playback time (in seconds)
+uniform vec3 iWorldScale; // the dimensions of the object being rendered
+
+// TODO add support for textures
+// TODO document available inputs other than the uniforms
+// TODO provide world scale in addition to the untransformed position
+
+%1
+
+void main(void) {
+ vec4 texel = getProceduralColor();
+
+ packDeferredFragmentLightmap(
+ normalize(_normal),
+ glowIntensity * texel.a,
+ _color.rgb,
+ DEFAULT_SPECULAR, DEFAULT_SHININESS,
+ texel.rgb);
+}
+)SCRIBE";
diff --git a/libraries/entities-renderer/src/RenderableSphereEntityItem.cpp b/libraries/entities-renderer/src/RenderableSphereEntityItem.cpp
index 50d46f4527..da7832f103 100644
--- a/libraries/entities-renderer/src/RenderableSphereEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableSphereEntityItem.cpp
@@ -17,6 +17,7 @@
#include
#include
+#include
#include
#include "RenderableDebugableEntityItem.h"
@@ -25,21 +26,36 @@ EntityItemPointer RenderableSphereEntityItem::factory(const EntityItemID& entity
return std::make_shared(entityID, properties);
}
+void RenderableSphereEntityItem::setUserData(const QString& value) {
+ SphereEntityItem::setUserData(value);
+ _procedural.reset();
+}
+
void RenderableSphereEntityItem::render(RenderArgs* args) {
PerformanceTimer perfTimer("RenderableSphereEntityItem::render");
Q_ASSERT(getType() == EntityTypes::Sphere);
- glm::vec4 sphereColor(toGlm(getXColor()), getLocalRenderAlpha());
-
+ Q_ASSERT(args->_batch);
+ gpu::Batch& batch = *args->_batch;
+ batch.setModelTransform(getTransformToCenter()); // use a transform with scale, rotation, registration point and translation
+
// TODO: it would be cool to select different slices/stacks geometry based on the size of the sphere
// and the distance to the viewer. This would allow us to reduce the triangle count for smaller spheres
// that aren't close enough to see the tessellation and use larger triangle count for spheres that would
// expose that effect
- const int SLICES = 15, STACKS = 15;
+ static const int SLICES = 15, STACKS = 15;
- Q_ASSERT(args->_batch);
- gpu::Batch& batch = *args->_batch;
- batch.setModelTransform(getTransformToCenter()); // use a transform with scale, rotation, registration point and translation
- DependencyManager::get()->renderSolidSphere(batch, 0.5f, SLICES, STACKS, sphereColor);
+ if (!_procedural) {
+ _procedural.reset(new ProceduralInfo(this));
+ }
+
+ if (_procedural->ready()) {
+ _procedural->prepare(batch);
+ DependencyManager::get()->renderSphere(batch, 0.5f, SLICES, STACKS, vec3(1));
+ } else {
+ glm::vec4 sphereColor(toGlm(getXColor()), getLocalRenderAlpha());
+ DependencyManager::get()->renderSolidSphere(batch, 0.5f, SLICES, STACKS, sphereColor);
+ }
+
RenderableDebugableEntityItem::render(this, args);
};
diff --git a/libraries/entities-renderer/src/RenderableSphereEntityItem.h b/libraries/entities-renderer/src/RenderableSphereEntityItem.h
index 6331b35706..5036354c04 100644
--- a/libraries/entities-renderer/src/RenderableSphereEntityItem.h
+++ b/libraries/entities-renderer/src/RenderableSphereEntityItem.h
@@ -13,10 +13,10 @@
#define hifi_RenderableSphereEntityItem_h
#include
-
#include "RenderableEntityItem.h"
+#include "RenderableProceduralItem.h"
-class RenderableSphereEntityItem : public SphereEntityItem {
+class RenderableSphereEntityItem : public SphereEntityItem, RenderableProceduralItem {
public:
static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
@@ -25,6 +25,7 @@ public:
{ }
virtual void render(RenderArgs* args);
+ virtual void setUserData(const QString& value);
SIMPLE_RENDERABLE();
};
diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h
index e420d08709..54a524b5a2 100644
--- a/libraries/entities/src/EntityItem.h
+++ b/libraries/entities/src/EntityItem.h
@@ -334,7 +334,7 @@ public:
void setLocked(bool value) { _locked = value; }
const QString& getUserData() const { return _userData; }
- void setUserData(const QString& value) { _userData = value; }
+ virtual void setUserData(const QString& value) { _userData = value; }
const SimulationOwner& getSimulationOwner() const { return _simulationOwner; }
void setSimulationOwner(const QUuid& id, quint8 priority);
diff --git a/libraries/render-utils/src/ShaderCache.cpp b/libraries/render-utils/src/ShaderCache.cpp
new file mode 100644
index 0000000000..e4ef8250b9
--- /dev/null
+++ b/libraries/render-utils/src/ShaderCache.cpp
@@ -0,0 +1,32 @@
+//
+// Created by Bradley Austin Davis on 2015/05/26
+// Copyright 2015 High Fidelity, Inc.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+#include "ShaderCache.h"
+
+NetworkShader::NetworkShader(const QUrl& url, bool delayLoad)
+ : Resource(url, delayLoad) {};
+
+void NetworkShader::downloadFinished(QNetworkReply* reply) {
+ if (reply) {
+ _source = reply->readAll();
+ reply->deleteLater();
+ }
+}
+
+ShaderCache& ShaderCache::instance() {
+ static ShaderCache _instance;
+ return _instance;
+}
+
+NetworkShaderPointer ShaderCache::getShader(const QUrl& url) {
+ return ResourceCache::getResource(url, QUrl(), false, nullptr).staticCast();
+}
+
+QSharedPointer ShaderCache::createResource(const QUrl& url, const QSharedPointer& fallback, bool delayLoad, const void* extra) {
+ return QSharedPointer(new NetworkShader(url, delayLoad), &Resource::allReferencesCleared);
+}
+
diff --git a/libraries/render-utils/src/ShaderCache.h b/libraries/render-utils/src/ShaderCache.h
new file mode 100644
index 0000000000..7698252924
--- /dev/null
+++ b/libraries/render-utils/src/ShaderCache.h
@@ -0,0 +1,34 @@
+//
+// Created by Bradley Austin Davis on 2015/05/26
+// Copyright 2015 High Fidelity, Inc.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+#pragma once
+#ifndef hifi_ShaderCache_h
+#define hifi_ShaderCache_h
+
+#include
+
+class NetworkShader : public Resource {
+public:
+ NetworkShader(const QUrl& url, bool delayLoad);
+ virtual void downloadFinished(QNetworkReply* reply) override;
+
+ QByteArray _source;
+};
+
+using NetworkShaderPointer = QSharedPointer;
+
+class ShaderCache : public ResourceCache {
+public:
+ static ShaderCache& instance();
+
+ NetworkShaderPointer getShader(const QUrl& url);
+
+protected:
+ virtual QSharedPointer createResource(const QUrl& url, const QSharedPointer& fallback, bool delayLoad, const void* extra) override;
+};
+
+#endif