From e8435b6218c6e9ce61a5a079c6b486392a9359fd Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 2 Sep 2015 23:35:52 -0700 Subject: [PATCH] Procedural rendering for box entities --- examples/shaders/grid.fs | 102 ++++++ examples/shaders/hex.fs | 48 +++ examples/shaders/test.fs | 48 +++ examples/toys/proceduralEdit.js | 12 + examples/toys/proceduralEdit/ui.html | 29 ++ .../src/RenderableBoxEntityItem.cpp | 21 +- .../src/RenderableBoxEntityItem.h | 4 +- .../src/RenderableProceduralItem.cpp | 142 ++++++++ .../src/RenderableProceduralItem.h | 51 +++ .../src/RenderableProceduralItemShader.h | 329 ++++++++++++++++++ .../src/RenderableSphereEntityItem.cpp | 30 +- .../src/RenderableSphereEntityItem.h | 5 +- libraries/entities/src/EntityItem.h | 2 +- libraries/render-utils/src/ShaderCache.cpp | 32 ++ libraries/render-utils/src/ShaderCache.h | 34 ++ 15 files changed, 875 insertions(+), 14 deletions(-) create mode 100644 examples/shaders/grid.fs create mode 100644 examples/shaders/hex.fs create mode 100644 examples/shaders/test.fs create mode 100644 examples/toys/proceduralEdit.js create mode 100644 examples/toys/proceduralEdit/ui.html create mode 100644 libraries/entities-renderer/src/RenderableProceduralItem.cpp create mode 100644 libraries/entities-renderer/src/RenderableProceduralItem.h create mode 100644 libraries/entities-renderer/src/RenderableProceduralItemShader.h create mode 100644 libraries/render-utils/src/ShaderCache.cpp create mode 100644 libraries/render-utils/src/ShaderCache.h 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