From 5f1affaff94454b4a79bf3d69f0ff070b88c0468 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 8 Sep 2015 00:51:54 -0700 Subject: [PATCH] Procedural skybox work --- cmake/macros/AutoScribeShader.cmake | 2 +- cmake/macros/SetupHifiLibrary.cmake | 6 +- examples/shaders/exampleSkyboxUserDataV2.json | 6 + .../shaders/shadertoys/relentlessSkybox.fs | 144 +++++++++ interface/CMakeLists.txt | 2 +- libraries/entities-renderer/CMakeLists.txt | 2 +- .../src/EntityTreeRenderer.cpp | 18 +- .../src/RenderableBoxEntityItem.cpp | 13 +- .../src/RenderableBoxEntityItem.h | 9 +- .../src/RenderableProceduralItem.h | 60 ---- .../src/RenderableSphereEntityItem.cpp | 13 +- .../src/RenderableSphereEntityItem.h | 10 +- libraries/gpu-networking/CMakeLists.txt | 11 + .../gpu-networking/GpuNetworkingLogging.cpp | 11 + .../src/gpu-networking/GpuNetworkingLogging.h | 11 + .../src/gpu-networking}/ShaderCache.cpp | 0 .../src/gpu-networking}/ShaderCache.h | 0 .../src/gpu-networking}/TextureCache.cpp | 21 +- .../src/gpu-networking/TextureCache.h | 167 +++++++++++ libraries/gpu/CMakeLists.txt | 4 + libraries/model/CMakeLists.txt | 2 +- libraries/model/src/model/Skybox.cpp | 71 +++-- libraries/model/src/model/Skybox.h | 9 +- libraries/model/src/model/Skybox.slf | 18 +- libraries/procedural/CMakeLists.txt | 10 + .../src/procedural/Procedural.cpp} | 130 ++++----- .../procedural/src/procedural/Procedural.h | 60 ++++ .../src/procedural/ProceduralShaders.h | 276 ++++++++++++++++++ libraries/render-utils/CMakeLists.txt | 2 +- libraries/render-utils/src/TextureCache.h | 173 +---------- libraries/render-utils/src/simple.slf | 37 ++- libraries/script-engine/CMakeLists.txt | 2 +- tests/gpu-test/CMakeLists.txt | 2 +- 33 files changed, 937 insertions(+), 365 deletions(-) create mode 100644 examples/shaders/exampleSkyboxUserDataV2.json create mode 100644 examples/shaders/shadertoys/relentlessSkybox.fs delete mode 100644 libraries/entities-renderer/src/RenderableProceduralItem.h create mode 100644 libraries/gpu-networking/CMakeLists.txt create mode 100644 libraries/gpu-networking/src/gpu-networking/GpuNetworkingLogging.cpp create mode 100644 libraries/gpu-networking/src/gpu-networking/GpuNetworkingLogging.h rename libraries/{render-utils/src => gpu-networking/src/gpu-networking}/ShaderCache.cpp (100%) rename libraries/{render-utils/src => gpu-networking/src/gpu-networking}/ShaderCache.h (100%) rename libraries/{render-utils/src => gpu-networking/src/gpu-networking}/TextureCache.cpp (97%) create mode 100644 libraries/gpu-networking/src/gpu-networking/TextureCache.h create mode 100644 libraries/procedural/CMakeLists.txt rename libraries/{entities-renderer/src/RenderableProceduralItem.cpp => procedural/src/procedural/Procedural.cpp} (66%) create mode 100644 libraries/procedural/src/procedural/Procedural.h create mode 100644 libraries/procedural/src/procedural/ProceduralShaders.h diff --git a/cmake/macros/AutoScribeShader.cmake b/cmake/macros/AutoScribeShader.cmake index bec80d55b7..90a304ac50 100755 --- a/cmake/macros/AutoScribeShader.cmake +++ b/cmake/macros/AutoScribeShader.cmake @@ -73,7 +73,7 @@ endfunction() macro(AUTOSCRIBE_SHADER_LIB) - + message("Autoscribe running for ${TARGET_NAME}") file(RELATIVE_PATH RELATIVE_LIBRARY_DIR_PATH ${CMAKE_CURRENT_SOURCE_DIR} "${HIFI_LIBRARY_DIR}") foreach(HIFI_LIBRARY ${ARGN}) #if (NOT TARGET ${HIFI_LIBRARY}) diff --git a/cmake/macros/SetupHifiLibrary.cmake b/cmake/macros/SetupHifiLibrary.cmake index aba8d500a5..2e943f9b12 100644 --- a/cmake/macros/SetupHifiLibrary.cmake +++ b/cmake/macros/SetupHifiLibrary.cmake @@ -9,7 +9,8 @@ macro(SETUP_HIFI_LIBRARY) - project(${TARGET_NAME}) + project(${TARGET_NAME}) + message("Setting up project ${TARGET_NAME}") # grab the implemenation and header files file(GLOB_RECURSE LIB_SRCS "src/*.h" "src/*.cpp" "src/*.c") @@ -33,5 +34,8 @@ macro(SETUP_HIFI_LIBRARY) foreach(QT_MODULE ${${TARGET_NAME}_DEPENDENCY_QT_MODULES}) target_link_libraries(${TARGET_NAME} Qt5::${QT_MODULE}) endforeach() + + # Don't make scribed shaders cumulative + set(AUTOSCRIBE_SHADER_LIB_SRC "") endmacro(SETUP_HIFI_LIBRARY) \ No newline at end of file diff --git a/examples/shaders/exampleSkyboxUserDataV2.json b/examples/shaders/exampleSkyboxUserDataV2.json new file mode 100644 index 0000000000..2839baea4c --- /dev/null +++ b/examples/shaders/exampleSkyboxUserDataV2.json @@ -0,0 +1,6 @@ +{ + "ProceduralEntity": + { + "shaderUrl": "https://s3.amazonaws.com/Oculus/shadertoys/relentlessSkybox.fs" + } +} \ No newline at end of file diff --git a/examples/shaders/shadertoys/relentlessSkybox.fs b/examples/shaders/shadertoys/relentlessSkybox.fs new file mode 100644 index 0000000000..79e38bf9aa --- /dev/null +++ b/examples/shaders/shadertoys/relentlessSkybox.fs @@ -0,0 +1,144 @@ +// 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 + +// ******************************************************************************************* +// Please do NOT use this shader in your own productions/videos/games without my permission! +// If you'd still like to do so, please drop me a mail (stral@aon.at) +// ******************************************************************************************* +float time = iGlobalTime; + +vec2 rotate(vec2 p, float a) { + return vec2(p.x * cos(a) - p.y * sin(a), p.x * sin(a) + p.y * cos(a)); +} + +float box(vec2 p, vec2 b, float r) { + return length(max(abs(p) - b, 0.0)) - r; +} + +// iq's ray-plane-intersection code +vec3 intersect(in vec3 o, in vec3 d, vec3 c, vec3 u, vec3 v) +{ + vec3 q = o - c; + return vec3( + dot(cross(u, v), q), + dot(cross(q, u), d), + dot(cross(v, q), d)) / dot(cross(v, u), d); +} + +// 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))); +} + +float noise11(float p) { + float fl = floor(p); + return mix(rand11(fl), rand11(fl + 1.0), fract(p)); //smoothstep(0.0, 1.0, fract(p))); +} +float fbm11(float p) { + return noise11(p) * 0.5 + noise11(p * 2.0) * 0.25 + noise11(p * 5.0) * 0.125; +} +vec3 noise31(float p) { + return vec3(noise11(p), noise11(p + 18.952), noise11(p - 11.372)) * 2.0 - 1.0; +} + +// something that looks a bit like godrays coming from the surface +float sky(vec3 p) { + float a = atan(p.x, p.z); + float t = time * 0.1; + float v = rand11(floor(a * 4.0 + t)) * 0.5 + rand11(floor(a * 8.0 - t)) * 0.25 + + rand11(floor(a * 16.0 + t)) * 0.125; + return v; +} + +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); +} + + + +vec3 getSkyboxColor() { + vec3 rd = normalize(_normal); + vec3 ro = vec3(0, 0, 1); + float inten = 0.0; + + // background + float sd = dot(rd, vec3(0.0, 1.0, 0.0)); + inten = pow(1.0 - abs(sd), 20.0) + pow(sky(rd), 5.0) * step(0.0, rd.y) * 0.2; + + vec3 its; + float v, g; + + // voronoi floor layers + for(int i = 0; i < 4; i ++) + { + float layer = float(i); + its = intersect(ro, rd, vec3(0.0, -5.0 - layer * 5.0, 0.0), vec3(1.0, 0.0, 0.0), vec3(0.0, 0.0, 1.0)); + if(its.x > 0.0) + { + vec3 vo = voronoi((its.yz) * 0.05 + 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 * 1.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 ct = vec3(0.6, 0.8, 9.0); + // find a color for the computed intensity + vec3 col = pow(vec3(inten), ct); + return col; +} diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index d7ee9228f4..05a937eef8 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -113,7 +113,7 @@ endif() target_link_libraries(${TARGET_NAME} ${BULLET_LIBRARIES}) # link required hifi libraries -link_hifi_libraries(shared octree environment gpu model render fbx networking entities avatars +link_hifi_libraries(shared octree environment gpu gpu-networking procedural model render fbx networking entities avatars audio audio-client animation script-engine physics render-utils entities-renderer ui auto-updater plugins display-plugins input-plugins) diff --git a/libraries/entities-renderer/CMakeLists.txt b/libraries/entities-renderer/CMakeLists.txt index c4dddb8971..3787beb32b 100644 --- a/libraries/entities-renderer/CMakeLists.txt +++ b/libraries/entities-renderer/CMakeLists.txt @@ -26,4 +26,4 @@ find_package(PolyVox REQUIRED) target_include_directories(${TARGET_NAME} SYSTEM PUBLIC ${POLYVOX_INCLUDE_DIRS}) target_link_libraries(${TARGET_NAME} ${POLYVOX_LIBRARIES}) -link_hifi_libraries(shared gpu script-engine render render-utils) +link_hifi_libraries(shared gpu gpu-networking procedural script-engine render render-utils) diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 72400dcefb..fa8c0eb633 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include "EntityTreeRenderer.h" @@ -454,13 +455,24 @@ void EntityTreeRenderer::applyZonePropertiesToScene(std::shared_ptrendOverrideEnvironmentData(); auto stage = scene->getSkyStage(); if (zone->getBackgroundMode() == BACKGROUND_MODE_SKYBOX) { - stage->getSkybox()->setColor(zone->getSkyboxProperties().getColorVec3()); + auto skybox = stage->getSkybox(); + skybox->setColor(zone->getSkyboxProperties().getColorVec3()); + static QString userData; + if (userData != zone->getUserData()) { + userData = zone->getUserData(); + QSharedPointer procedural(new Procedural(userData)); + if (procedural->_enabled) { + skybox->setProcedural(procedural); + } else { + skybox->setProcedural(QSharedPointer()); + } + } if (zone->getSkyboxProperties().getURL().isEmpty()) { - stage->getSkybox()->setCubemap(gpu::TexturePointer()); + skybox->setCubemap(gpu::TexturePointer()); } else { // Update the Texture of the Skybox with the one pointed by this zone auto cubeMap = DependencyManager::get()->getTexture(zone->getSkyboxProperties().getURL(), CUBE_TEXTURE); - stage->getSkybox()->setCubemap(cubeMap->getGPUTexture()); + skybox->setCubemap(cubeMap->getGPUTexture()); } stage->setBackgroundMode(model::SunSkyStage::SKY_BOX); } else { diff --git a/libraries/entities-renderer/src/RenderableBoxEntityItem.cpp b/libraries/entities-renderer/src/RenderableBoxEntityItem.cpp index 738c677104..525226b0e8 100644 --- a/libraries/entities-renderer/src/RenderableBoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableBoxEntityItem.cpp @@ -21,6 +21,8 @@ #include #include "RenderableDebugableEntityItem.h" +#include "../render-utils/simple_vert.h" +#include "../render-utils/simple_frag.h" EntityItemPointer RenderableBoxEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { return std::make_shared(entityID, properties); @@ -42,11 +44,18 @@ void RenderableBoxEntityItem::render(RenderArgs* args) { glm::vec4 cubeColor(toGlm(getXColor()), getLocalRenderAlpha()); if (!_procedural) { - _procedural.reset(new ProceduralInfo(this)); + _procedural.reset(new Procedural(this->getUserData())); + _procedural->_vertexSource = simple_vert; + _procedural->_fragmentSource = simple_frag; + _procedural->_state->setCullMode(gpu::State::CULL_NONE); + _procedural->_state->setDepthTest(true, true, gpu::LESS_EQUAL); + _procedural->_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); } if (_procedural->ready()) { - _procedural->prepare(batch); + _procedural->prepare(batch, this->getDimensions()); DependencyManager::get()->renderSolidCube(batch, 1.0f, _procedural->getColor(cubeColor)); } else { DependencyManager::get()->renderSolidCube(batch, 1.0f, cubeColor); diff --git a/libraries/entities-renderer/src/RenderableBoxEntityItem.h b/libraries/entities-renderer/src/RenderableBoxEntityItem.h index e317163683..838022c7d4 100644 --- a/libraries/entities-renderer/src/RenderableBoxEntityItem.h +++ b/libraries/entities-renderer/src/RenderableBoxEntityItem.h @@ -13,10 +13,11 @@ #define hifi_RenderableBoxEntityItem_h #include -#include "RenderableEntityItem.h" -#include "RenderableProceduralItem.h" +#include -class RenderableBoxEntityItem : public BoxEntityItem, RenderableProceduralItem { +#include "RenderableEntityItem.h" + +class RenderableBoxEntityItem : public BoxEntityItem { public: static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); @@ -28,6 +29,8 @@ public: virtual void setUserData(const QString& value); SIMPLE_RENDERABLE() +private: + QSharedPointer _procedural; }; diff --git a/libraries/entities-renderer/src/RenderableProceduralItem.h b/libraries/entities-renderer/src/RenderableProceduralItem.h deleted file mode 100644 index 37e827d304..0000000000 --- a/libraries/entities-renderer/src/RenderableProceduralItem.h +++ /dev/null @@ -1,60 +0,0 @@ -// -// 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 -#include - -class EntityItem; -class QJsonObject; - -class RenderableProceduralItem { -protected: - // FIXME better encapsulation - // FIXME better mechanism for extending to things rendered using shaders other than simple.slv - struct ProceduralInfo { - ProceduralInfo(EntityItem* entity); - void parse(); - void parse(const QJsonObject&); - bool ready(); - void prepare(gpu::Batch& batch); - glm::vec4 getColor(const glm::vec4& entityColor); - - bool _enabled{ false }; - uint8_t _version{ 1 }; - 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; - QJsonObject _uniforms; - }; - - QSharedPointer _procedural; -}; - -#endif diff --git a/libraries/entities-renderer/src/RenderableSphereEntityItem.cpp b/libraries/entities-renderer/src/RenderableSphereEntityItem.cpp index 3b58397a82..82257c67fb 100644 --- a/libraries/entities-renderer/src/RenderableSphereEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableSphereEntityItem.cpp @@ -21,6 +21,8 @@ #include #include "RenderableDebugableEntityItem.h" +#include "../render-utils/simple_vert.h" +#include "../render-utils/simple_frag.h" EntityItemPointer RenderableSphereEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { return std::make_shared(entityID, properties); @@ -47,12 +49,19 @@ void RenderableSphereEntityItem::render(RenderArgs* args) { static const int SLICES = 15, STACKS = 15; if (!_procedural) { - _procedural.reset(new ProceduralInfo(this)); + _procedural.reset(new Procedural(getUserData())); + _procedural->_vertexSource = simple_vert; + _procedural->_fragmentSource = simple_frag; + _procedural->_state->setCullMode(gpu::State::CULL_NONE); + _procedural->_state->setDepthTest(true, true, gpu::LESS_EQUAL); + _procedural->_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); } glm::vec4 sphereColor(toGlm(getXColor()), getLocalRenderAlpha()); if (_procedural->ready()) { - _procedural->prepare(batch); + _procedural->prepare(batch, getDimensions()); DependencyManager::get()->renderSphere(batch, 0.5f, SLICES, STACKS, _procedural->getColor(sphereColor)); } else { DependencyManager::get()->renderSolidSphere(batch, 0.5f, SLICES, STACKS, sphereColor); diff --git a/libraries/entities-renderer/src/RenderableSphereEntityItem.h b/libraries/entities-renderer/src/RenderableSphereEntityItem.h index 5036354c04..293ae79029 100644 --- a/libraries/entities-renderer/src/RenderableSphereEntityItem.h +++ b/libraries/entities-renderer/src/RenderableSphereEntityItem.h @@ -13,10 +13,11 @@ #define hifi_RenderableSphereEntityItem_h #include -#include "RenderableEntityItem.h" -#include "RenderableProceduralItem.h" +#include -class RenderableSphereEntityItem : public SphereEntityItem, RenderableProceduralItem { +#include "RenderableEntityItem.h" + +class RenderableSphereEntityItem : public SphereEntityItem { public: static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); @@ -28,6 +29,9 @@ public: virtual void setUserData(const QString& value); SIMPLE_RENDERABLE(); + +private: + QSharedPointer _procedural; }; diff --git a/libraries/gpu-networking/CMakeLists.txt b/libraries/gpu-networking/CMakeLists.txt new file mode 100644 index 0000000000..836afac371 --- /dev/null +++ b/libraries/gpu-networking/CMakeLists.txt @@ -0,0 +1,11 @@ +set(TARGET_NAME gpu-networking) + +# use setup_hifi_library macro to setup our project and link appropriate Qt modules +setup_hifi_library() + +add_dependency_external_projects(glm) +find_package(GLM REQUIRED) +target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS}) + +link_hifi_libraries(shared networking gpu) + diff --git a/libraries/gpu-networking/src/gpu-networking/GpuNetworkingLogging.cpp b/libraries/gpu-networking/src/gpu-networking/GpuNetworkingLogging.cpp new file mode 100644 index 0000000000..38da22969b --- /dev/null +++ b/libraries/gpu-networking/src/gpu-networking/GpuNetworkingLogging.cpp @@ -0,0 +1,11 @@ +// +// Created by Bradley Austin Davis on 2015/08/07 +// 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 "GpuNetworkingLogging.h" + +Q_LOGGING_CATEGORY(gpunetwork, "hifi.gpu-network") diff --git a/libraries/gpu-networking/src/gpu-networking/GpuNetworkingLogging.h b/libraries/gpu-networking/src/gpu-networking/GpuNetworkingLogging.h new file mode 100644 index 0000000000..7499340a9b --- /dev/null +++ b/libraries/gpu-networking/src/gpu-networking/GpuNetworkingLogging.h @@ -0,0 +1,11 @@ +// +// Created by Bradley Austin Davis on 2015/08/07 +// 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 + +Q_DECLARE_LOGGING_CATEGORY(gpunetwork) diff --git a/libraries/render-utils/src/ShaderCache.cpp b/libraries/gpu-networking/src/gpu-networking/ShaderCache.cpp similarity index 100% rename from libraries/render-utils/src/ShaderCache.cpp rename to libraries/gpu-networking/src/gpu-networking/ShaderCache.cpp diff --git a/libraries/render-utils/src/ShaderCache.h b/libraries/gpu-networking/src/gpu-networking/ShaderCache.h similarity index 100% rename from libraries/render-utils/src/ShaderCache.h rename to libraries/gpu-networking/src/gpu-networking/ShaderCache.h diff --git a/libraries/render-utils/src/TextureCache.cpp b/libraries/gpu-networking/src/gpu-networking/TextureCache.cpp similarity index 97% rename from libraries/render-utils/src/TextureCache.cpp rename to libraries/gpu-networking/src/gpu-networking/TextureCache.cpp index c4fb4c9989..e2462946a4 100644 --- a/libraries/render-utils/src/TextureCache.cpp +++ b/libraries/gpu-networking/src/gpu-networking/TextureCache.cpp @@ -1,7 +1,4 @@ // -// TextureCache.cpp -// interface/src/renderer -// // Created by Andrzej Kapolka on 8/6/13. // Copyright 2013 High Fidelity, Inc. // @@ -21,13 +18,11 @@ #include #include #include -#include "PathUtils.h" +#include #include - - -#include "RenderUtilsLogging.h" +#include "GpuNetworkingLogging.h" TextureCache::TextureCache() { const qint64 TEXTURE_DEFAULT_UNUSED_MAX_SIZE = DEFAULT_UNUSED_MAX_SIZE; @@ -255,7 +250,7 @@ void listSupportedImageFormats() { foreach(const QByteArray& f, supportedFormats) { formats += QString(f) + ","; } - qCDebug(renderutils) << "List of supported Image formats:" << formats; + qCDebug(gpunetwork) << "List of supported Image formats:" << formats; }); } @@ -323,9 +318,9 @@ void ImageReader::run() { if (originalWidth == 0 || originalHeight == 0 || imageFormat == QImage::Format_Invalid) { if (filenameExtension.empty()) { - qCDebug(renderutils) << "QImage failed to create from content, no file extension:" << _url; + qCDebug(gpunetwork) << "QImage failed to create from content, no file extension:" << _url; } else { - qCDebug(renderutils) << "QImage failed to create from content" << _url; + qCDebug(gpunetwork) << "QImage failed to create from content" << _url; } return; } @@ -333,7 +328,7 @@ void ImageReader::run() { int imageArea = image.width() * image.height(); auto ntex = dynamic_cast(&*texture); if (ntex && (ntex->getType() == CUBE_TEXTURE)) { - qCDebug(renderutils) << "Cube map size:" << _url << image.width() << image.height(); + qCDebug(gpunetwork) << "Cube map size:" << _url << image.width() << image.height(); } int opaquePixels = 0; @@ -384,7 +379,7 @@ void ImageReader::run() { } } if (opaquePixels == imageArea) { - qCDebug(renderutils) << "Image with alpha channel is completely opaque:" << _url; + qCDebug(gpunetwork) << "Image with alpha channel is completely opaque:" << _url; image = image.convertToFormat(QImage::Format_RGB888); } @@ -532,7 +527,7 @@ void ImageReader::run() { faces.push_back(image.copy(QRect(layout._faceZPos._x * faceWidth, layout._faceZPos._y * faceWidth, faceWidth, faceWidth)).mirrored(layout._faceZPos._horizontalMirror, layout._faceZPos._verticalMirror)); faces.push_back(image.copy(QRect(layout._faceZNeg._x * faceWidth, layout._faceZNeg._y * faceWidth, faceWidth, faceWidth)).mirrored(layout._faceZNeg._horizontalMirror, layout._faceZNeg._verticalMirror)); } else { - qCDebug(renderutils) << "Failed to find a known cube map layout from this image:" << _url; + qCDebug(gpunetwork) << "Failed to find a known cube map layout from this image:" << _url; return; } diff --git a/libraries/gpu-networking/src/gpu-networking/TextureCache.h b/libraries/gpu-networking/src/gpu-networking/TextureCache.h new file mode 100644 index 0000000000..f0abda8e66 --- /dev/null +++ b/libraries/gpu-networking/src/gpu-networking/TextureCache.h @@ -0,0 +1,167 @@ +// +// Created by Andrzej Kapolka on 8/6/13. +// Copyright 2013 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 +// + +#ifndef hifi_TextureCache_h +#define hifi_TextureCache_h + +#include + +#include +#include +#include + +#include +#include + +namespace gpu { +class Batch; +} +class NetworkTexture; + +typedef QSharedPointer NetworkTexturePointer; + +enum TextureType { DEFAULT_TEXTURE, NORMAL_TEXTURE, SPECULAR_TEXTURE, EMISSIVE_TEXTURE, SPLAT_TEXTURE, CUBE_TEXTURE }; + +/// Stores cached textures, including render-to-texture targets. +class TextureCache : public ResourceCache, public Dependency { + Q_OBJECT + SINGLETON_DEPENDENCY + +public: + /// Returns the ID of the permutation/normal texture used for Perlin noise shader programs. This texture + /// has two lines: the first, a set of random numbers in [0, 255] to be used as permutation offsets, and + /// the second, a set of random unit vectors to be used as noise gradients. + const gpu::TexturePointer& getPermutationNormalTexture(); + + /// Returns an opaque white texture (useful for a default). + const gpu::TexturePointer& getWhiteTexture(); + + /// Returns an opaque gray texture (useful for a default). + const gpu::TexturePointer& getGrayTexture(); + + /// Returns the a pale blue texture (useful for a normal map). + const gpu::TexturePointer& getBlueTexture(); + + /// Returns the a black texture (useful for a default). + const gpu::TexturePointer& getBlackTexture(); + + // Returns a map used to compress the normals through a fitting scale algorithm + const gpu::TexturePointer& getNormalFittingTexture(); + + /// Returns a texture version of an image file + static gpu::TexturePointer getImageTexture(const QString& path); + + /// Loads a texture from the specified URL. + NetworkTexturePointer getTexture(const QUrl& url, TextureType type = DEFAULT_TEXTURE, bool dilatable = false, + const QByteArray& content = QByteArray()); + +protected: + + virtual QSharedPointer createResource(const QUrl& url, + const QSharedPointer& fallback, bool delayLoad, const void* extra); + +private: + TextureCache(); + virtual ~TextureCache(); + friend class DilatableNetworkTexture; + + gpu::TexturePointer _permutationNormalTexture; + gpu::TexturePointer _whiteTexture; + gpu::TexturePointer _grayTexture; + gpu::TexturePointer _blueTexture; + gpu::TexturePointer _blackTexture; + gpu::TexturePointer _normalFittingTexture; + + QHash > _dilatableNetworkTextures; +}; + +/// A simple object wrapper for an OpenGL texture. +class Texture { +public: + friend class TextureCache; + friend class DilatableNetworkTexture; + Texture(); + ~Texture(); + + const gpu::TexturePointer& getGPUTexture() const { return _gpuTexture; } + +protected: + gpu::TexturePointer _gpuTexture; + +private: +}; + +/// A texture loaded from the network. + +class NetworkTexture : public Resource, public Texture { + Q_OBJECT + +public: + + NetworkTexture(const QUrl& url, TextureType type, const QByteArray& content); + + /// Checks whether it "looks like" this texture is translucent + /// (majority of pixels neither fully opaque or fully transparent). + bool isTranslucent() const { return _translucent; } + + /// Returns the lazily-computed average texture color. + const QColor& getAverageColor() const { return _averageColor; } + + int getOriginalWidth() const { return _originalWidth; } + int getOriginalHeight() const { return _originalHeight; } + int getWidth() const { return _width; } + int getHeight() const { return _height; } + TextureType getType() const { return _type; } +protected: + + virtual void downloadFinished(QNetworkReply* reply); + + Q_INVOKABLE void loadContent(const QByteArray& content); + // FIXME: This void* should be a gpu::Texture* but i cannot get it to work for now, moving on... + Q_INVOKABLE void setImage(const QImage& image, void* texture, bool translucent, const QColor& averageColor, int originalWidth, + int originalHeight); + + virtual void imageLoaded(const QImage& image); + + TextureType _type; + +private: + bool _translucent; + QColor _averageColor; + int _originalWidth; + int _originalHeight; + int _width; + int _height; +}; + +/// Caches derived, dilated textures. +class DilatableNetworkTexture : public NetworkTexture { + Q_OBJECT + +public: + + DilatableNetworkTexture(const QUrl& url, const QByteArray& content); + + /// Returns a pointer to a texture with the requested amount of dilation. + QSharedPointer getDilatedTexture(float dilation); + +protected: + + virtual void imageLoaded(const QImage& image); + virtual void reinsert(); + +private: + + QImage _image; + int _innerRadius; + int _outerRadius; + + QMap > _dilatedTextures; +}; + +#endif // hifi_TextureCache_h diff --git a/libraries/gpu/CMakeLists.txt b/libraries/gpu/CMakeLists.txt index 7a88580f7f..38fe5cb22f 100644 --- a/libraries/gpu/CMakeLists.txt +++ b/libraries/gpu/CMakeLists.txt @@ -7,6 +7,10 @@ setup_hifi_library() link_hifi_libraries(shared) +add_dependency_external_projects(glm) +find_package(GLM REQUIRED) +target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS}) + add_dependency_external_projects(glew) find_package(GLEW REQUIRED) add_definitions(-DGLEW_STATIC) diff --git a/libraries/model/CMakeLists.txt b/libraries/model/CMakeLists.txt index 701c132e61..8acb4b0a71 100755 --- a/libraries/model/CMakeLists.txt +++ b/libraries/model/CMakeLists.txt @@ -9,4 +9,4 @@ add_dependency_external_projects(glm) find_package(GLM REQUIRED) target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS}) -link_hifi_libraries(shared gpu octree) +link_hifi_libraries(shared networking gpu gpu-networking procedural octree) diff --git a/libraries/model/src/model/Skybox.cpp b/libraries/model/src/model/Skybox.cpp index 314492881f..c17bf1df72 100755 --- a/libraries/model/src/model/Skybox.cpp +++ b/libraries/model/src/model/Skybox.cpp @@ -10,10 +10,12 @@ // #include "Skybox.h" -#include "gpu/Batch.h" -#include "gpu/Context.h" -#include "ViewFrustum.h" +#include +#include +#include +#include + #include "Skybox_vert.h" #include "Skybox_frag.h" @@ -38,19 +40,54 @@ void Skybox::setColor(const Color& color) { _color = color; } +void Skybox::setProcedural(QSharedPointer procedural) { + _procedural = procedural; + if (_procedural) { + _procedural->_vertexSource = Skybox_vert; + _procedural->_fragmentSource = Skybox_frag; + // No pipeline state customization + } +} + void Skybox::setCubemap(const gpu::TexturePointer& cubemap) { _cubemap = cubemap; } + void Skybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum, const Skybox& skybox) { + static gpu::BufferPointer theBuffer; + static gpu::Stream::FormatPointer theFormat; - if (skybox.getCubemap()) { - if (skybox.getCubemap()->isDefined()) { + if (skybox._procedural || skybox.getCubemap()) { + if (!theBuffer) { + const float CLIP = 1.0f; + const glm::vec2 vertices[4] = { { -CLIP, -CLIP }, { CLIP, -CLIP }, { -CLIP, CLIP }, { CLIP, CLIP } }; + theBuffer = std::make_shared(sizeof(vertices), (const gpu::Byte*) vertices); + theFormat = std::make_shared(); + theFormat->setAttribute(gpu::Stream::POSITION, gpu::Stream::POSITION, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::XYZ)); + } - static gpu::PipelinePointer thePipeline; - static gpu::BufferPointer theBuffer; - static gpu::Stream::FormatPointer theFormat; + glm::mat4 projMat; + viewFrustum.evalProjectionMatrix(projMat); + + Transform viewTransform; + viewFrustum.evalViewTransform(viewTransform); + batch.setProjectionTransform(projMat); + batch.setViewTransform(viewTransform); + batch.setModelTransform(Transform()); // only for Mac + batch.setInputBuffer(gpu::Stream::POSITION, theBuffer, 0, 8); + batch.setInputFormat(theFormat); + + if (skybox._procedural && skybox._procedural->_enabled && skybox._procedural->ready()) { + if (skybox.getCubemap() && skybox.getCubemap()->isDefined()) { + batch.setResourceTexture(0, skybox.getCubemap()); + } + + skybox._procedural->prepare(batch, glm::vec3(1)); + batch.draw(gpu::TRIANGLE_STRIP, 4); + } else if (skybox.getCubemap() && skybox.getCubemap()->isDefined()) { static gpu::BufferPointer theConstants; + static gpu::PipelinePointer thePipeline; static int SKYBOX_CONSTANTS_SLOT = 0; // need to be defined by the compilation of the shader if (!thePipeline) { auto skyVS = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(Skybox_vert))); @@ -72,23 +109,10 @@ void Skybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum, const Sky thePipeline = gpu::PipelinePointer(gpu::Pipeline::create(skyShader, skyState)); - const float CLIP = 1.0f; - const glm::vec2 vertices[4] = { {-CLIP, -CLIP}, {CLIP, -CLIP}, {-CLIP, CLIP}, {CLIP, CLIP}}; - theBuffer = std::make_shared(sizeof(vertices), (const gpu::Byte*) vertices); - - theFormat = std::make_shared(); - theFormat->setAttribute(gpu::Stream::POSITION, gpu::Stream::POSITION, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::XYZ)); - auto color = glm::vec4(1.0f); theConstants = std::make_shared(sizeof(color), (const gpu::Byte*) &color); } - glm::mat4 projMat; - viewFrustum.evalProjectionMatrix(projMat); - - Transform viewTransform; - viewFrustum.evalViewTransform(viewTransform); - if (glm::all(glm::equal(skybox.getColor(), glm::vec3(0.0f)))) { auto color = glm::vec4(1.0f); theConstants->setSubData(0, sizeof(color), (const gpu::Byte*) &color); @@ -96,13 +120,8 @@ void Skybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum, const Sky theConstants->setSubData(0, sizeof(Color), (const gpu::Byte*) &skybox.getColor()); } - batch.setProjectionTransform(projMat); - batch.setViewTransform(viewTransform); - batch.setModelTransform(Transform()); // only for Mac batch.setPipeline(thePipeline); - batch.setInputBuffer(gpu::Stream::POSITION, theBuffer, 0, 8); batch.setUniformBuffer(SKYBOX_CONSTANTS_SLOT, theConstants, 0, theConstants->getSize()); - batch.setInputFormat(theFormat); batch.setResourceTexture(0, skybox.getCubemap()); batch.draw(gpu::TRIANGLE_STRIP, 4); } diff --git a/libraries/model/src/model/Skybox.h b/libraries/model/src/model/Skybox.h index 7a4550d75a..809ec7e3b0 100755 --- a/libraries/model/src/model/Skybox.h +++ b/libraries/model/src/model/Skybox.h @@ -11,12 +11,13 @@ #ifndef hifi_model_Skybox_h #define hifi_model_Skybox_h -#include "gpu/Texture.h" +#include +#include #include "Light.h" class ViewFrustum; -//class Transform; +struct Procedural; namespace gpu { class Batch; } namespace model { @@ -35,11 +36,13 @@ public: void setCubemap(const gpu::TexturePointer& cubemap); const gpu::TexturePointer& getCubemap() const { return _cubemap; } + void setProcedural(QSharedPointer procedural); + static void render(gpu::Batch& batch, const ViewFrustum& frustum, const Skybox& skybox); protected: gpu::TexturePointer _cubemap; - + QSharedPointer _procedural; Color _color{1.0f, 1.0f, 1.0f}; }; typedef std::shared_ptr< Skybox > SkyboxPointer; diff --git a/libraries/model/src/model/Skybox.slf b/libraries/model/src/model/Skybox.slf index 47444b21bd..382801f52d 100755 --- a/libraries/model/src/model/Skybox.slf +++ b/libraries/model/src/model/Skybox.slf @@ -22,13 +22,29 @@ uniform skyboxBuffer { }; in vec3 _normal; - out vec4 _fragColor; +//PROCEDURAL_COMMON_BLOCK + +#line 1001 +//PROCEDURAL_BLOCK + +#line 2033 void main(void) { + +#ifdef PROCEDURAL + + vec3 color = getSkyboxColor(); + _fragColor = vec4(color, 0.0); + +#else + vec3 coord = normalize(_normal); vec3 texel = texture(cubeMap, coord).rgb; vec3 color = texel * _skybox._color.rgb; vec3 pixel = pow(color, vec3(1.0/2.2)); // manual Gamma correction _fragColor = vec4(pixel, 0.0); + +#endif + } diff --git a/libraries/procedural/CMakeLists.txt b/libraries/procedural/CMakeLists.txt new file mode 100644 index 0000000000..bd53f0abb9 --- /dev/null +++ b/libraries/procedural/CMakeLists.txt @@ -0,0 +1,10 @@ +set(TARGET_NAME procedural) + +# use setup_hifi_library macro to setup our project and link appropriate Qt modules +setup_hifi_library() + +add_dependency_external_projects(glm) +find_package(GLM REQUIRED) +target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS}) + +link_hifi_libraries(shared gpu networking gpu-networking) diff --git a/libraries/entities-renderer/src/RenderableProceduralItem.cpp b/libraries/procedural/src/procedural/Procedural.cpp similarity index 66% rename from libraries/entities-renderer/src/RenderableProceduralItem.cpp rename to libraries/procedural/src/procedural/Procedural.cpp index c88d1410a5..c8433ef341 100644 --- a/libraries/entities-renderer/src/RenderableProceduralItem.cpp +++ b/libraries/procedural/src/procedural/Procedural.cpp @@ -6,7 +6,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include "RenderableProceduralItem.h" +#include "Procedural.h" #include #include @@ -14,14 +14,12 @@ #include #include -#include -#include -#include -#include +#include #include +#include +#include -#include "RenderableProceduralItemShader.h" -#include "../render-utils/simple_vert.h" +#include "ProceduralShaders.h" static const char* const UNIFORM_TIME_NAME= "iGlobalTime"; static const char* const UNIFORM_SCALE_NAME = "iWorldScale"; @@ -30,43 +28,46 @@ static const QString PROCEDURAL_USER_DATA_KEY = "ProceduralEntity"; static const QString URL_KEY = "shaderUrl"; static const QString VERSION_KEY = "version"; static const QString UNIFORMS_KEY = "uniforms"; +static const std::string PROCEDURAL_BLOCK = "//PROCEDURAL_BLOCK"; +static const std::string PROCEDURAL_COMMON_BLOCK = "//PROCEDURAL_COMMON_BLOCK"; +static const std::string PROCEDURAL_VERSION = "//PROCEDURAL_VERSION"; -RenderableProceduralItem::ProceduralInfo::ProceduralInfo(EntityItem* entity) : _entity(entity) { - parse(); + +// Example +//{ +// "ProceduralEntity": { +// "shaderUrl": "file:///C:/Users/bdavis/Git/hifi/examples/shaders/test.fs", +// } +//} +QJsonValue Procedural::getProceduralData(const QString& proceduralJson) { + if (proceduralJson.isEmpty()) { + return QJsonValue(); + } + + QJsonParseError parseError; + auto doc = QJsonDocument::fromJson(proceduralJson.toUtf8(), &parseError); + if (parseError.error != QJsonParseError::NoError) { + return QJsonValue(); + } + + return doc.object()[PROCEDURAL_USER_DATA_KEY]; } -void RenderableProceduralItem::ProceduralInfo::parse() { + +Procedural::Procedural(const QString& userDataJson) { + parse(userDataJson); + _state = std::make_shared(); +} + +void Procedural::parse(const QString& userDataJson) { _enabled = false; - 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; - } - userData = doc.object(); + auto proceduralData = getProceduralData(userDataJson); + if (proceduralData.isObject()) { + parse(proceduralData.toObject()); } - - // 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; - } - - parse(proceduralData.toObject()); } -void RenderableProceduralItem::ProceduralInfo::parse(const QJsonObject& proceduralData) { +void Procedural::parse(const QJsonObject& proceduralData) { // grab the version number { auto version = proceduralData[VERSION_KEY]; @@ -106,7 +107,7 @@ void RenderableProceduralItem::ProceduralInfo::parse(const QJsonObject& procedur _enabled = true; } -bool RenderableProceduralItem::ProceduralInfo::ready() { +bool Procedural::ready() { if (!_enabled) { return false; } @@ -122,7 +123,7 @@ bool RenderableProceduralItem::ProceduralInfo::ready() { return false; } -void RenderableProceduralItem::ProceduralInfo::prepare(gpu::Batch& batch) { +void Procedural::prepare(gpu::Batch& batch, const glm::vec3& size) { if (_shaderUrl.isLocalFile()) { auto lastModified = QFileInfo(_shaderPath).lastModified().toMSecsSinceEpoch(); if (lastModified > _shaderModified) { @@ -139,31 +140,33 @@ void RenderableProceduralItem::ProceduralInfo::prepare(gpu::Batch& batch) { if (!_pipeline || _pipelineDirty) { _pipelineDirty = true; if (!_vertexShader) { - _vertexShader = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(simple_vert))); + _vertexShader = gpu::ShaderPointer(gpu::Shader::createVertex(_vertexSource)); } - QString framentShaderSource; - switch (_version) { - case 1: - framentShaderSource = SHADER_TEMPLATE_V1.arg(_shaderSource); - break; - default: - case 2: - framentShaderSource = SHADER_TEMPLATE_V2.arg(_shaderSource); - break; + // Build the fragment shader + std::string fragmentShaderSource = _fragmentSource; + size_t replaceIndex = fragmentShaderSource.find(PROCEDURAL_COMMON_BLOCK); + if (replaceIndex != std::string::npos) { + fragmentShaderSource.replace(replaceIndex, PROCEDURAL_COMMON_BLOCK.size(), SHADER_COMMON); } - _fragmentShader = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(framentShaderSource.toLocal8Bit().data()))); + + replaceIndex = fragmentShaderSource.find(PROCEDURAL_VERSION); + if (replaceIndex != std::string::npos) { + if (_version == 1) { + fragmentShaderSource.replace(replaceIndex, PROCEDURAL_VERSION.size(), "#define PROCEDURAL_V1 1"); + } else if (_version == 2) { + fragmentShaderSource.replace(replaceIndex, PROCEDURAL_VERSION.size(), "#define PROCEDURAL_V2 1"); + } + } + replaceIndex = fragmentShaderSource.find(PROCEDURAL_BLOCK); + if (replaceIndex != std::string::npos) { + fragmentShaderSource.replace(replaceIndex, PROCEDURAL_BLOCK.size(), _shaderSource.toLocal8Bit().data()); + } + qDebug() << "FragmentShader:\n" << fragmentShaderSource.c_str(); + _fragmentShader = gpu::ShaderPointer(gpu::Shader::createPixel(fragmentShaderSource)); _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)); + gpu::Shader::makeProgram(*_shader); + _pipeline = gpu::PipelinePointer(gpu::Pipeline::create(_shader, _state)); _timeSlot = _shader->getUniforms().findLocation(UNIFORM_TIME_NAME); _scaleSlot = _shader->getUniforms().findLocation(UNIFORM_SCALE_NAME); _start = usecTimestampNow(); @@ -221,15 +224,12 @@ void RenderableProceduralItem::ProceduralInfo::prepare(gpu::Batch& batch) { // Minimize floating point error by doing an integer division to milliseconds, before the floating point division to seconds float time = (float)((usecTimestampNow() - _start) / USECS_PER_MSEC) / MSECS_PER_SECOND; batch._glUniform1f(_timeSlot, time); - // FIXME move into the 'set once' section, since this doesn't change over time - auto scale = _entity->getDimensions(); - batch._glUniform3f(_scaleSlot, scale.x, scale.y, scale.z); - batch.setResourceTexture(DeferredLightingEffect::NORMAL_FITTING_MAP_SLOT, DependencyManager::get()->getNormalFittingTexture()); + batch._glUniform3f(_scaleSlot, size.x, size.y, size.z); } -glm::vec4 RenderableProceduralItem::ProceduralInfo::getColor(const glm::vec4& entityColor) { +glm::vec4 Procedural::getColor(const glm::vec4& entityColor) { if (_version == 1) { return glm::vec4(1); } diff --git a/libraries/procedural/src/procedural/Procedural.h b/libraries/procedural/src/procedural/Procedural.h new file mode 100644 index 0000000000..bb6a0ad44d --- /dev/null +++ b/libraries/procedural/src/procedural/Procedural.h @@ -0,0 +1,60 @@ +// +// 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 +#include + + +// FIXME better encapsulation +// FIXME better mechanism for extending to things rendered using shaders other than simple.slv +struct Procedural { + static QJsonValue getProceduralData(const QString& proceduralJson); + + Procedural(const QString& userDataJson); + void parse(const QString& userDataJson); + void parse(const QJsonObject&); + bool ready(); + void prepare(gpu::Batch& batch, const glm::vec3& size); + glm::vec4 getColor(const glm::vec4& entityColor); + + bool _enabled{ false }; + uint8_t _version{ 1 }; + + std::string _vertexSource; + std::string _fragmentSource; + + 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; + QJsonObject _uniforms; + + gpu::PipelinePointer _pipeline; + gpu::ShaderPointer _vertexShader; + gpu::ShaderPointer _fragmentShader; + gpu::ShaderPointer _shader; + gpu::StatePointer _state; +}; + +#endif diff --git a/libraries/procedural/src/procedural/ProceduralShaders.h b/libraries/procedural/src/procedural/ProceduralShaders.h new file mode 100644 index 0000000000..9943a322cc --- /dev/null +++ b/libraries/procedural/src/procedural/ProceduralShaders.h @@ -0,0 +1,276 @@ +// +// 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 std::string SHADER_COMMON = R"SHADER( + +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); +} + +// 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 + +#define PROCEDURAL 1 + +//PROCEDURAL_VERSION +)SHADER"; diff --git a/libraries/render-utils/CMakeLists.txt b/libraries/render-utils/CMakeLists.txt index 0ea71e54e3..4d33c6f1c1 100644 --- a/libraries/render-utils/CMakeLists.txt +++ b/libraries/render-utils/CMakeLists.txt @@ -40,4 +40,4 @@ add_dependency_external_projects(oglplus) find_package(OGLPLUS REQUIRED) target_include_directories(${TARGET_NAME} PUBLIC ${OGLPLUS_INCLUDE_DIRS}) -link_hifi_libraries(animation fbx shared gpu model render environment) +link_hifi_libraries(shared gpu gpu-networking procedural model render environment animation fbx) diff --git a/libraries/render-utils/src/TextureCache.h b/libraries/render-utils/src/TextureCache.h index eeb17f07b9..23ac11d7b0 100644 --- a/libraries/render-utils/src/TextureCache.h +++ b/libraries/render-utils/src/TextureCache.h @@ -1,171 +1,2 @@ -// -// TextureCache.h -// interface/src/renderer -// -// Created by Andrzej Kapolka on 8/6/13. -// Copyright 2013 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 -// - -#ifndef hifi_TextureCache_h -#define hifi_TextureCache_h - -#include -#include - -#include -#include -#include - -#include -#include - -namespace gpu { -class Batch; -} -class NetworkTexture; - -typedef QSharedPointer NetworkTexturePointer; - -enum TextureType { DEFAULT_TEXTURE, NORMAL_TEXTURE, SPECULAR_TEXTURE, EMISSIVE_TEXTURE, SPLAT_TEXTURE, CUBE_TEXTURE }; - -/// Stores cached textures, including render-to-texture targets. -class TextureCache : public ResourceCache, public Dependency { - Q_OBJECT - SINGLETON_DEPENDENCY - -public: - /// Returns the ID of the permutation/normal texture used for Perlin noise shader programs. This texture - /// has two lines: the first, a set of random numbers in [0, 255] to be used as permutation offsets, and - /// the second, a set of random unit vectors to be used as noise gradients. - const gpu::TexturePointer& getPermutationNormalTexture(); - - /// Returns an opaque white texture (useful for a default). - const gpu::TexturePointer& getWhiteTexture(); - - /// Returns an opaque gray texture (useful for a default). - const gpu::TexturePointer& getGrayTexture(); - - /// Returns the a pale blue texture (useful for a normal map). - const gpu::TexturePointer& getBlueTexture(); - - /// Returns the a black texture (useful for a default). - const gpu::TexturePointer& getBlackTexture(); - - // Returns a map used to compress the normals through a fitting scale algorithm - const gpu::TexturePointer& getNormalFittingTexture(); - - /// Returns a texture version of an image file - static gpu::TexturePointer getImageTexture(const QString& path); - - /// Loads a texture from the specified URL. - NetworkTexturePointer getTexture(const QUrl& url, TextureType type = DEFAULT_TEXTURE, bool dilatable = false, - const QByteArray& content = QByteArray()); - -protected: - - virtual QSharedPointer createResource(const QUrl& url, - const QSharedPointer& fallback, bool delayLoad, const void* extra); - -private: - TextureCache(); - virtual ~TextureCache(); - friend class DilatableNetworkTexture; - - gpu::TexturePointer _permutationNormalTexture; - gpu::TexturePointer _whiteTexture; - gpu::TexturePointer _grayTexture; - gpu::TexturePointer _blueTexture; - gpu::TexturePointer _blackTexture; - gpu::TexturePointer _normalFittingTexture; - - QHash > _dilatableNetworkTextures; -}; - -/// A simple object wrapper for an OpenGL texture. -class Texture { -public: - friend class TextureCache; - friend class DilatableNetworkTexture; - Texture(); - ~Texture(); - - const gpu::TexturePointer& getGPUTexture() const { return _gpuTexture; } - -protected: - gpu::TexturePointer _gpuTexture; - -private: -}; - -/// A texture loaded from the network. - -class NetworkTexture : public Resource, public Texture { - Q_OBJECT - -public: - - NetworkTexture(const QUrl& url, TextureType type, const QByteArray& content); - - /// Checks whether it "looks like" this texture is translucent - /// (majority of pixels neither fully opaque or fully transparent). - bool isTranslucent() const { return _translucent; } - - /// Returns the lazily-computed average texture color. - const QColor& getAverageColor() const { return _averageColor; } - - int getOriginalWidth() const { return _originalWidth; } - int getOriginalHeight() const { return _originalHeight; } - int getWidth() const { return _width; } - int getHeight() const { return _height; } - TextureType getType() const { return _type; } -protected: - - virtual void downloadFinished(QNetworkReply* reply); - - Q_INVOKABLE void loadContent(const QByteArray& content); - // FIXME: This void* should be a gpu::Texture* but i cannot get it to work for now, moving on... - Q_INVOKABLE void setImage(const QImage& image, void* texture, bool translucent, const QColor& averageColor, int originalWidth, - int originalHeight); - - virtual void imageLoaded(const QImage& image); - - TextureType _type; - -private: - bool _translucent; - QColor _averageColor; - int _originalWidth; - int _originalHeight; - int _width; - int _height; -}; - -/// Caches derived, dilated textures. -class DilatableNetworkTexture : public NetworkTexture { - Q_OBJECT - -public: - - DilatableNetworkTexture(const QUrl& url, const QByteArray& content); - - /// Returns a pointer to a texture with the requested amount of dilation. - QSharedPointer getDilatedTexture(float dilation); - -protected: - - virtual void imageLoaded(const QImage& image); - virtual void reinsert(); - -private: - - QImage _image; - int _innerRadius; - int _outerRadius; - - QMap > _dilatedTextures; -}; - -#endif // hifi_TextureCache_h +// Compatibility +#include diff --git a/libraries/render-utils/src/simple.slf b/libraries/render-utils/src/simple.slf index 31d33a73e4..5901a72838 100644 --- a/libraries/render-utils/src/simple.slf +++ b/libraries/render-utils/src/simple.slf @@ -18,12 +18,39 @@ // the interpolated normal in vec3 _normal; in vec3 _color; +in vec2 _texCoord0; +in vec4 _position; +//PROCEDURAL_COMMON_BLOCK + +#line 1001 +//PROCEDURAL_BLOCK + +#line 2030 void main(void) { Material material = getMaterial(); - packDeferredFragment( - normalize(_normal.xyz), - glowIntensity, - _color.rgb, - DEFAULT_SPECULAR, DEFAULT_SHININESS); + vec3 normal = normalize(_normal.xyz); + vec3 diffuse = _color.rgb; + vec3 specular = DEFAULT_SPECULAR; + float shininess = DEFAULT_SHININESS; + float emissiveAmount = 0.0; + +#ifdef PROCEDURAL + +#ifdef PROCEDURAL_V1 + specular = getProceduralColor().rgb; + emissiveAmount = 1.0; +#else + emissiveAmount = getProceduralColors(diffuse, specular, shininess); +#endif + +#endif + + if (emissiveAmount > 0.0) { + packDeferredFragmentLightmap( + normal, glowIntensity, diffuse, specular, shininess, specular); + } else { + packDeferredFragment( + normal, glowIntensity, diffuse, specular, shininess); + } } diff --git a/libraries/script-engine/CMakeLists.txt b/libraries/script-engine/CMakeLists.txt index 139b99e426..1acfb57829 100644 --- a/libraries/script-engine/CMakeLists.txt +++ b/libraries/script-engine/CMakeLists.txt @@ -7,4 +7,4 @@ add_dependency_external_projects(glm) find_package(GLM REQUIRED) target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS}) -link_hifi_libraries(shared octree gpu model fbx entities animation audio physics) +link_hifi_libraries(shared networking octree gpu gpu-networking procedural model fbx entities animation audio physics) diff --git a/tests/gpu-test/CMakeLists.txt b/tests/gpu-test/CMakeLists.txt index abdbfc07f9..3d42364132 100644 --- a/tests/gpu-test/CMakeLists.txt +++ b/tests/gpu-test/CMakeLists.txt @@ -10,6 +10,6 @@ set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/") #include_oglplus() # link in the shared libraries -link_hifi_libraries(render-utils gpu shared networking fbx model animation script-engine) +link_hifi_libraries(networking gpu gpu-networking procedural shared fbx model animation script-engine render-utils ) copy_dlls_beside_windows_executable() \ No newline at end of file