diff --git a/README.md b/README.md index 1bbb2f7c..8ceebd4e 100644 --- a/README.md +++ b/README.md @@ -3,12 +3,13 @@ [![Build Windows](https://github.com/cpvrlab/SLProject4/actions/workflows/build-x86_64-windows.yml/badge.svg)](https://github.com/cpvrlab/SLProject4/actions/workflows/build-x86_64-windows.yml) [![Build macOS](https://github.com/cpvrlab/SLProject4/actions/workflows/build-x86_64-macos.yml/badge.svg)](https://github.com/cpvrlab/SLProject4/actions/workflows/build-x86_64-macos.yml) [![Build Emscripten](https://github.com/cpvrlab/SLProject4/actions/workflows/build-wasm-emscripten.yml/badge.svg)](https://github.com/cpvrlab/SLProject4/actions/workflows/build-wasm-emscripten.yml) +[![Build Online Docs](https://github.com/cpvrlab/SLProject4/actions/workflows/build-docs.yml/badge.svg)](https://github.com/cpvrlab/SLProject4/actions/workflows/build-docs.yml) -SL stands for Scene Library. It is developed at the Berne University of Applied Sciences (BFH) in Switzerland and is used for student projects in the cpvrLab. The various applications show what you can learn in three semesters about 3D computer graphics in real time rendering and ray tracing. The framework is built in C++ and OpenGL ES and can be built for Windows, Linux, macOS (Intel & arm64), Android, Apple iOS and for WebAssembly enabled browsers. The framework can render alternatively with Ray Tracing and Path Tracing which provides in addition high quality transparencies, reflections and soft shadows. For a complete feature list see the [SLProject4 wiki](https://github.com/cpvrlab/SLProject4/wiki). +SL stands for Scene Library. It is developed at the Berne University of Applied Sciences (BFH) in Switzerland and is used for student projects in the cpvrLab. The various applications show what you can learn in three semesters about 3D computer graphics in real-time rendering and ray tracing. The framework is built in C++ and OpenGL ES and can be built for Windows, Linux, macOS (Intel & arm64), Android, Apple iOS, and WebAssembly-enabled browsers. The framework can render alternatively with Ray Tracing and Path Tracing, which provides high-quality transparencies, reflections, and soft shadows. For a complete feature list see the [SLProject4 wiki](https://github.com/cpvrlab/SLProject4/wiki). ## How to get the SLProject4 -The SLProject4 is hosted at GitHub as a GIT repository. +The SLProject4 is hosted on GitHub as a GIT repository. [GIT](http://git-scm.com/) is a distributed versioning control system. To clone SLProject4, use the following command: @@ -18,7 +19,7 @@ cd git clone https://github.com/cpvrlab/SLProject4.git ``` -For people with an aversion to the command line, a GIT GUI tool, such as the [GitHub Desktop Client](https://desktop.github.com), can be used. To get the latest additions to SLProject4, please checkout the develop branch: +For people with an aversion to the command line, a GIT GUI tool, such as the [GitHub Desktop Client](https://desktop.github.com), can be used. To get the latest additions to SLProject4, please check the develop branch: ``` git checkout develop diff --git a/apps/app_demo_slproject/source/AppDemoGui.cpp b/apps/app_demo_slproject/source/AppDemoGui.cpp index e4d20e0d..55d5f5bd 100644 --- a/apps/app_demo_slproject/source/AppDemoGui.cpp +++ b/apps/app_demo_slproject/source/AppDemoGui.cpp @@ -1775,27 +1775,20 @@ void AppDemoGui::buildMenuBar(SLScene* s, SLSceneView* sv) if (ImGui::BeginMenu("Particle Systems")) { - if (stateGL->glHasGeometryShaders()) - { - if (ImGui::MenuItem("First Particle System", nullptr, sid == SID_ParticleSystem_First)) - s->onLoad(am, s, sv, SID_ParticleSystem_First); - if (ImGui::MenuItem("Demo Particle System", nullptr, sid == SID_ParticleSystem_Demo)) - s->onLoad(am, s, sv, SID_ParticleSystem_Demo); - if (ImGui::MenuItem("Dust Storm Particle System", nullptr, sid == SID_ParticleSystem_DustStorm)) - s->onLoad(am, s, sv, SID_ParticleSystem_DustStorm); - if (ImGui::MenuItem("Fountain Particle System", nullptr, sid == SID_ParticleSystem_Fountain)) - s->onLoad(am, s, sv, SID_ParticleSystem_Fountain); - if (ImGui::MenuItem("Sun Particle System", nullptr, sid == SID_ParticleSystem_Sun)) - s->onLoad(am, s, sv, SID_ParticleSystem_Sun); - if (ImGui::MenuItem("Ring of Fire Particle System", nullptr, sid == SID_ParticleSystem_RingOfFire)) - s->onLoad(am, s, sv, SID_ParticleSystem_RingOfFire); - if (ImGui::MenuItem("Complex Fire Particle System", nullptr, sid == SID_ParticleSystem_FireComplex)) - s->onLoad(am, s, sv, SID_ParticleSystem_FireComplex); - } - else - { - ImGui::MenuItem("Particles need OpenGL >= 4.0 or OpenGLES >= 3.1", nullptr, false, false); - } + if (ImGui::MenuItem("First Particle System", nullptr, sid == SID_ParticleSystem_First)) + s->onLoad(am, s, sv, SID_ParticleSystem_First); + if (ImGui::MenuItem("Demo Particle System", nullptr, sid == SID_ParticleSystem_Demo)) + s->onLoad(am, s, sv, SID_ParticleSystem_Demo); + if (ImGui::MenuItem("Dust Storm Particle System", nullptr, sid == SID_ParticleSystem_DustStorm)) + s->onLoad(am, s, sv, SID_ParticleSystem_DustStorm); + if (ImGui::MenuItem("Fountain Particle System", nullptr, sid == SID_ParticleSystem_Fountain)) + s->onLoad(am, s, sv, SID_ParticleSystem_Fountain); + if (ImGui::MenuItem("Sun Particle System", nullptr, sid == SID_ParticleSystem_Sun)) + s->onLoad(am, s, sv, SID_ParticleSystem_Sun); + if (ImGui::MenuItem("Ring of Fire Particle System", nullptr, sid == SID_ParticleSystem_RingOfFire)) + s->onLoad(am, s, sv, SID_ParticleSystem_RingOfFire); + if (ImGui::MenuItem("Complex Fire Particle System", nullptr, sid == SID_ParticleSystem_FireComplex)) + s->onLoad(am, s, sv, SID_ParticleSystem_FireComplex); ImGui::EndMenu(); } @@ -1964,13 +1957,10 @@ void AppDemoGui::buildMenuBar(SLScene* s, SLSceneView* sv) s->onLoad(am, s, sv, SID_Benchmark6_ColumnsLOD); if (ImGui::MenuItem("Jan's Universe", nullptr, sid == SID_Benchmark7_JansUniverse)) s->onLoad(am, s, sv, SID_Benchmark7_JansUniverse); - if (stateGL->glHasGeometryShaders()) - { - if (ImGui::MenuItem("Particle System lot of fire complex", nullptr, sid == SID_Benchmark8_ParticleSystemFireComplex)) - s->onLoad(am, s, sv, SID_Benchmark8_ParticleSystemFireComplex); - if (ImGui::MenuItem("Particle System lot of particle", nullptr, sid == SID_Benchmark9_ParticleSystemManyParticles)) - s->onLoad(am, s, sv, SID_Benchmark9_ParticleSystemManyParticles); - } + if (ImGui::MenuItem("Particle System lot of fire complex", nullptr, sid == SID_Benchmark8_ParticleSystemFireComplex)) + s->onLoad(am, s, sv, SID_Benchmark8_ParticleSystemFireComplex); + if (ImGui::MenuItem("Particle System lot of particle", nullptr, sid == SID_Benchmark9_ParticleSystemManyParticles)) + s->onLoad(am, s, sv, SID_Benchmark9_ParticleSystemManyParticles); ImGui::EndMenu(); } @@ -2393,6 +2383,9 @@ void AppDemoGui::buildMenuBar(SLScene* s, SLSceneView* sv) if (ImGui::MenuItem("Skeleton", "K", sv->drawBits()->get(SL_DB_SKELETON))) sv->drawBits()->toggle(SL_DB_SKELETON); + if (ImGui::MenuItem("GPU Skinning", nullptr, sv->drawBits()->get(SL_DB_GPU_SKINNING))) + sv->drawBits()->toggle(SL_DB_GPU_SKINNING); + if (ImGui::MenuItem("All off")) sv->drawBits()->allOff(); @@ -2407,6 +2400,7 @@ void AppDemoGui::buildMenuBar(SLScene* s, SLSceneView* sv) sv->drawBits()->on(SL_DB_BBOX); sv->drawBits()->on(SL_DB_SKELETON); sv->drawBits()->on(SL_DB_CULLOFF); + sv->drawBits()->on(SL_DB_GPU_SKINNING); } ImGui::PushItemWidth(ImGui::GetWindowWidth() * 0.65f); @@ -3770,6 +3764,13 @@ void AppDemoGui::buildProperties(SLScene* s, SLSceneView* sv) ps->isGenerated(false); } + bool renderInstanced = ps->renderInstanced(); + if (ImGui::Checkbox("Instanced draw", &renderInstanced)) + { + ps->drawInstanced(renderInstanced); + ps->isGenerated(false); + } + // TTL (Time to live) if (ImGui::CollapsingHeader("Time to live")) { @@ -4333,6 +4334,7 @@ void AppDemoGui::buildProperties(SLScene* s, SLSceneView* sv) ImGui::TreePop(); } + if (m->program() != nullptr) { for (auto* shd : m->program()->shaders()) @@ -4367,6 +4369,7 @@ void AppDemoGui::buildProperties(SLScene* s, SLSceneView* sv) } } } + ImGui::TreePop(); } } diff --git a/apps/app_demo_slproject/source/AppDemoLoad.cpp b/apps/app_demo_slproject/source/AppDemoLoad.cpp index 273e2dec..bcc9f6ff 100644 --- a/apps/app_demo_slproject/source/AppDemoLoad.cpp +++ b/apps/app_demo_slproject/source/AppDemoLoad.cpp @@ -499,7 +499,6 @@ SLNode* createComplexFire(SLAssetManager* am, fireFlameMesh->flipbookRows(flipbookRows); fireFlameMesh->doFlipBookTexture(true); fireFlameMesh->doCounterGap(false); // We don't want to have flickering - fireFlameMesh->changeTexture(); // Switch texture, need to be done, to have flipbook texture as active fireFlameMesh->doAlphaOverLT(false); fireFlameMesh->doSizeOverLT(false); @@ -708,7 +707,6 @@ SLNode* createTorchFire(SLAssetManager* am, torchFlame->flipbookRows(flipbookRows); torchFlame->doFlipBookTexture(true); torchFlame->doCounterGap(false); // We don't want to have flickering - torchFlame->changeTexture(); // Switch texture, need to be done, to have flipbook texture as active torchFlame->doAlphaOverLT(false); torchFlame->doSizeOverLT(false); torchFlame->doRotation(false); @@ -3585,6 +3583,9 @@ resolution shadows near the camera and lower resolution shadows further away."); scene->addChild(center); scene->addChild(cam1); + std::uniform_real_distribution dist(0.0f, 1.0f); + std::default_random_engine randEngine; + // create astroboys around the center astroboy SLint size = 4; for (SLint iZ = -size; iZ <= size; ++iZ) @@ -3598,6 +3599,7 @@ resolution shadows near the camera and lower resolution shadows further away."); float zt = float(iZ) * 1.0f + ((shift) ? 0.5f : 0.0f); SLNode* n = center->copyRec(); n->translate(xt, 0, zt, TS_object); + n->scale(0.75f + 0.5f * dist(randEngine)); scene->addChild(n); } } @@ -5864,419 +5866,401 @@ resolution shadows near the camera and lower resolution shadows further away."); } else if (sceneID == SID_ParticleSystem_Demo) //................................................ { - if (stateGL->glHasGeometryShaders()) - { - // Set scene name and info string - s->name("Simple Demo Particle System"); - s->info("This most simple single particle system is meant to be improved by adding more and more features in the properties list."); + // Set scene name and info string + s->name("Simple Demo Particle System"); + s->info("This most simple single particle system is meant to be improved by adding more and more features in the properties list."); - // Create a scene group node - SLNode* scene = new SLNode("scene node"); - s->root3D(scene); + // Create a scene group node + SLNode* scene = new SLNode("scene node"); + s->root3D(scene); - // Create textures and materials - SLGLTexture* texC = new SLGLTexture(am, texPath + "ParticleSmoke_08_C.png"); - SLGLTexture* texFlipbook = new SLGLTexture(am, texPath + "ParticleSmoke_03_8x8_C.png"); + // Create textures and materials + SLGLTexture* texC = new SLGLTexture(am, texPath + "ParticleSmoke_08_C.png"); + SLGLTexture* texFlipbook = new SLGLTexture(am, texPath + "ParticleSmoke_03_8x8_C.png"); - // Create meshes and nodes - SLParticleSystem* ps = new SLParticleSystem(am, - 1, - SLVec3f(0.04f, 0.4f, 0.1f), - SLVec3f(-0.11f, 0.7f, -0.1f), - 4.0f, - texC, - "Particle System", - texFlipbook); - ps->doAlphaOverLT(false); - ps->doSizeOverLT(false); - ps->doRotation(false); - ps->doColor(false); - ps->acceleration(-0.5, 0.0, 0.0); - ps->timeToLive(2.0f); - SLMesh* pSMesh = ps; - SLNode* pSNode = new SLNode(pSMesh, "Particle system node"); - scene->addChild(pSNode); + // Create meshes and nodes + SLParticleSystem* ps = new SLParticleSystem(am, + 1, + SLVec3f(0.04f, 0.4f, 0.1f), + SLVec3f(-0.11f, 0.7f, -0.1f), + 4.0f, + texC, + "Particle System", + texFlipbook); + ps->doAlphaOverLT(false); + ps->doSizeOverLT(false); + ps->doRotation(false); + ps->doColor(false); + ps->acceleration(-0.5, 0.0, 0.0); + ps->timeToLive(2.0f); + SLMesh* pSMesh = ps; + SLNode* pSNode = new SLNode(pSMesh, "Particle system node"); + scene->addChild(pSNode); - // Set background color and the root scene node - sv->sceneViewCamera()->background().colors(SLCol4f(0.8f, 0.8f, 0.8f), - SLCol4f(0.2f, 0.2f, 0.2f)); - // Save energy - sv->doWaitOnIdle(false); - } + // Set background color and the root scene node + sv->sceneViewCamera()->background().colors(SLCol4f(0.8f, 0.8f, 0.8f), + SLCol4f(0.2f, 0.2f, 0.2f)); + // Save energy + sv->doWaitOnIdle(false); } else if (sceneID == SID_ParticleSystem_DustStorm) //........................................... { - if (stateGL->glHasGeometryShaders()) - { - // Set scene name and info string - s->name("Dust storm particle system"); - s->info("This dust storm particle system uses the box shape type for distribution.\n" - "See the properties window for the detailed settings of the particles system"); - - // Create a scene group node - SLNode* scene = new SLNode("scene node"); - s->root3D(scene); - - // Create and add camera - SLCamera* cam1 = new SLCamera("Camera 1"); - cam1->translation(0, 0, 55); - cam1->lookAt(0, 0, 0); - cam1->focalDist(55); - scene->addChild(cam1); - sv->camera(cam1); + // Set scene name and info string + s->name("Dust storm particle system"); + s->info("This dust storm particle system uses the box shape type for distribution.\n" + "See the properties window for the detailed settings of the particles system"); - // Create textures and materials - SLGLTexture* texC = new SLGLTexture(am, texPath + "ParticleSmoke_08_C.png"); - SLGLTexture* texFlipbookSmoke = new SLGLTexture(am, texPath + "ParticleSmoke_03_8x8_C.png"); + // Create a scene group node + SLNode* scene = new SLNode("scene node"); + s->root3D(scene); - // Create meshes and nodes - // Dust storm - SLParticleSystem* ps = new SLParticleSystem(am, - 500, - SLVec3f(-0.1f, -0.5f, -5.0f), - SLVec3f(0.1f, 0.5f, -2.5f), - 3.5f, - texC, - "DustStorm", - texFlipbookSmoke); - ps->doShape(true); - ps->shapeType(ST_Box); - ps->shapeScale(50.0f, 1.0f, 50.0f); - ps->scale(15.0f); - ps->doSizeOverLT(false); - ps->doAlphaOverLT(true); - ps->doAlphaOverLTCurve(true); - ps->bezierStartEndPointAlpha()[1] = 0.0f; - ps->bezierControlPointAlpha()[1] = 0.5f; - ps->bezierControlPointAlpha()[2] = 0.5f; - ps->generateBernsteinPAlpha(); - ps->doRotRange(true); - ps->color(SLCol4f(1.0f, 1.0f, 1.0f, 1.0f)); - ps->doBlendBrightness(false); - ps->frameRateFB(16); + // Create and add camera + SLCamera* cam1 = new SLCamera("Camera 1"); + cam1->translation(0, 0, 55); + cam1->lookAt(0, 0, 0); + cam1->focalDist(55); + scene->addChild(cam1); + sv->camera(cam1); - SLMesh* pSMesh = ps; - SLNode* pSNode = new SLNode(pSMesh, "Particle system node fire2"); - pSNode->translate(3.0f, -0.8f, 0.0f, TS_object); + // Create textures and materials + SLGLTexture* texC = new SLGLTexture(am, texPath + "ParticleSmoke_08_C.png"); + SLGLTexture* texFlipbookSmoke = new SLGLTexture(am, texPath + "ParticleSmoke_03_8x8_C.png"); - scene->addChild(pSNode); + // Create meshes and nodes + // Dust storm + SLParticleSystem* ps = new SLParticleSystem(am, + 500, + SLVec3f(-0.1f, -0.5f, -5.0f), + SLVec3f(0.1f, 0.5f, -2.5f), + 3.5f, + texC, + "DustStorm", + texFlipbookSmoke); + ps->doShape(true); + ps->shapeType(ST_Box); + ps->shapeScale(50.0f, 1.0f, 50.0f); + ps->scale(15.0f); + ps->doSizeOverLT(false); + ps->doAlphaOverLT(true); + ps->doAlphaOverLTCurve(true); + ps->bezierStartEndPointAlpha()[1] = 0.0f; + ps->bezierControlPointAlpha()[1] = 0.5f; + ps->bezierControlPointAlpha()[2] = 0.5f; + ps->generateBernsteinPAlpha(); + ps->doRotRange(true); + ps->color(SLCol4f(1.0f, 1.0f, 1.0f, 1.0f)); + ps->doBlendBrightness(false); + ps->frameRateFB(16); + + SLMesh* pSMesh = ps; + SLNode* pSNode = new SLNode(pSMesh, "Particle system node fire2"); + pSNode->translate(3.0f, -0.8f, 0.0f, TS_object); + + scene->addChild(pSNode); - // Set background color and the root scene node - sv->sceneViewCamera()->background().colors(SLCol4f(0.8f, 0.8f, 0.8f), - SLCol4f(0.2f, 0.2f, 0.2f)); - // Save energy - sv->doWaitOnIdle(false); - } + // Set background color and the root scene node + sv->sceneViewCamera()->background().colors(SLCol4f(0.8f, 0.8f, 0.8f), + SLCol4f(0.2f, 0.2f, 0.2f)); + // Save energy + sv->doWaitOnIdle(false); } else if (sceneID == SID_ParticleSystem_Fountain) //............................................ { - if (stateGL->glHasGeometryShaders()) - { - // Set scene name and info string - s->name("Fountain particle system"); - s->info("This fountain particle system uses acceleration and gravity.\n" - "See the properties window for the detailed settings of the particles system"); + // Set scene name and info string + s->name("Fountain particle system"); + s->info("This fountain particle system uses acceleration and gravity.\n" + "See the properties window for the detailed settings of the particles system"); - // Create a scene group node - SLNode* scene = new SLNode("scene node"); - s->root3D(scene); + // Create a scene group node + SLNode* scene = new SLNode("scene node"); + s->root3D(scene); - // Create and add camera - SLCamera* cam1 = new SLCamera("Camera 1"); - cam1->translation(0, -1, 55); - cam1->lookAt(0, -1, 0); - cam1->focalDist(55); - scene->addChild(cam1); - sv->camera(cam1); + // Create and add camera + SLCamera* cam1 = new SLCamera("Camera 1"); + cam1->translation(0, -1, 55); + cam1->lookAt(0, -1, 0); + cam1->focalDist(55); + scene->addChild(cam1); + sv->camera(cam1); - // Create textures and materials - SLGLTexture* texC = new SLGLTexture(am, texPath + "ParticleCircle_05_C.png"); - SLGLTexture* texFlipbook = new SLGLTexture(am, texPath + "ParticleSmoke_03_8x8_C.png"); - // SLGLTexture* texFlipbook = new SLGLTexture(am, texPath + "ParticleSmoke_04_8x8_C.png"); + // Create textures and materials + SLGLTexture* texC = new SLGLTexture(am, texPath + "ParticleCircle_05_C.png"); + SLGLTexture* texFlipbook = new SLGLTexture(am, texPath + "ParticleSmoke_03_8x8_C.png"); + // SLGLTexture* texFlipbook = new SLGLTexture(am, texPath + "ParticleSmoke_04_8x8_C.png"); - // Create a light source node - SLLightSpot* light1 = new SLLightSpot(am, s, 0.3f); - light1->translation(0, -1, 2); - light1->name("light node"); - scene->addChild(light1); + // Create a light source node + SLLightSpot* light1 = new SLLightSpot(am, s, 0.3f); + light1->translation(0, -1, 2); + light1->name("light node"); + scene->addChild(light1); - // Create meshes and nodes - SLParticleSystem* ps = new SLParticleSystem(am, - 5000, - SLVec3f(5.0f, 15.0f, 5.0f), - SLVec3f(-5.0f, 17.0f, -5.0f), - 5.0f, - texC, - "Fountain", - texFlipbook); - SLMesh* pSMesh = ps; - ps->doGravity(true); - ps->color(SLCol4f(0.0039f, 0.14f, 0.86f, 0.33f)); - ps->doSizeOverLT(false); - ps->doAlphaOverLT(false); - SLNode* pSNode = new SLNode(pSMesh, "Particle system node"); - scene->addChild(pSNode); + // Create meshes and nodes + SLParticleSystem* ps = new SLParticleSystem(am, + 5000, + SLVec3f(5.0f, 15.0f, 5.0f), + SLVec3f(-5.0f, 17.0f, -5.0f), + 5.0f, + texC, + "Fountain", + texFlipbook); + SLMesh* pSMesh = ps; + ps->doGravity(true); + ps->color(SLCol4f(0.0039f, 0.14f, 0.86f, 0.33f)); + ps->doSizeOverLT(false); + ps->doAlphaOverLT(false); + SLNode* pSNode = new SLNode(pSMesh, "Particle system node"); + scene->addChild(pSNode); - // Set background color and the root scene node - sv->sceneViewCamera()->background().colors(SLCol4f(0.8f, 0.8f, 0.8f), - SLCol4f(0.2f, 0.2f, 0.2f)); - // Save energy - sv->doWaitOnIdle(false); - } + // Set background color and the root scene node + sv->sceneViewCamera()->background().colors(SLCol4f(0.8f, 0.8f, 0.8f), + SLCol4f(0.2f, 0.2f, 0.2f)); + // Save energy + sv->doWaitOnIdle(false); } else if (sceneID == SID_ParticleSystem_Sun) //................................................. { - if (stateGL->glHasGeometryShaders()) - { - // Set scene name and info string - s->name("Sun particle system"); - s->info("This sun particle system uses the sphere shape type for distribution.\n" - "See the properties window for the detailed settings of the particles system"); - - // Create a scene group node - SLNode* scene = new SLNode("scene node"); - s->root3D(scene); - - // Create textures and materials - SLGLTexture* texC = new SLGLTexture(am, texPath + "ParticleSmoke_08_C.png"); - SLGLTexture* texFlipbook = new SLGLTexture(am, texPath + "ParticleSmoke_03_8x8_C.png"); + // Set scene name and info string + s->name("Sun particle system"); + s->info("This sun particle system uses the sphere shape type for distribution.\n" + "See the properties window for the detailed settings of the particles system"); - // Create meshes and nodes - SLParticleSystem* ps = new SLParticleSystem(am, - 10000, - SLVec3f(0.0f, 0.0f, 0.0f), - SLVec3f(0.0f, 0.0f, 0.0f), - 4.0f, - texC, - "Sun Particle System", - texFlipbook); + // Create a scene group node + SLNode* scene = new SLNode("scene node"); + s->root3D(scene); - ps->doShape(true); - ps->shapeType(ST_Sphere); - ps->shapeRadius(3.0f); - ps->doBlendBrightness(true); - ps->color(SLCol4f(0.925f, 0.238f, 0.097f, 0.199f)); + // Create textures and materials + SLGLTexture* texC = new SLGLTexture(am, texPath + "ParticleSmoke_08_C.png"); + SLGLTexture* texFlipbook = new SLGLTexture(am, texPath + "ParticleSmoke_03_8x8_C.png"); - SLMesh* pSMesh = ps; - SLNode* pSNode = new SLNode(pSMesh, "Particle Sun node"); - scene->addChild(pSNode); + // Create meshes and nodes + SLParticleSystem* ps = new SLParticleSystem(am, + 10000, + SLVec3f(0.0f, 0.0f, 0.0f), + SLVec3f(0.0f, 0.0f, 0.0f), + 4.0f, + texC, + "Sun Particle System", + texFlipbook); + + ps->doShape(true); + ps->shapeType(ST_Sphere); + ps->shapeRadius(3.0f); + ps->doBlendBrightness(true); + ps->color(SLCol4f(0.925f, 0.238f, 0.097f, 0.199f)); + + SLMesh* pSMesh = ps; + SLNode* pSNode = new SLNode(pSMesh, "Particle Sun node"); + scene->addChild(pSNode); - // Set background color and the root scene node - sv->sceneViewCamera()->background().colors(SLCol4f(0.8f, 0.8f, 0.8f), - SLCol4f(0.2f, 0.2f, 0.2f)); - // Save energy - sv->doWaitOnIdle(false); - } + // Set background color and the root scene node + sv->sceneViewCamera()->background().colors(SLCol4f(0.8f, 0.8f, 0.8f), + SLCol4f(0.2f, 0.2f, 0.2f)); + // Save energy + sv->doWaitOnIdle(false); } else if (sceneID == SID_ParticleSystem_RingOfFire) //.......................................... { - if (stateGL->glHasGeometryShaders()) - { - // Set scene name and info string - s->name("Ring of fire particle system"); - s->info("This ring particle system uses the cone shape type for distribution.\n" - "See the properties window for the settings of the particles system"); - - // Create a scene group node - SLNode* scene = new SLNode("scene node"); - s->root3D(scene); - - // Create textures and materials - SLGLTexture* texC = new SLGLTexture(am, texPath + "ParticleSmoke_08_C.png"); - SLGLTexture* texFlipbook = new SLGLTexture(am, texPath + "ParticleSmoke_03_8x8_C.png"); + // Set scene name and info string + s->name("Ring of fire particle system"); + s->info("This ring particle system uses the cone shape type for distribution.\n" + "See the properties window for the settings of the particles system"); - // Create meshes and nodes - SLParticleSystem* ps = new SLParticleSystem(am, - 1000, - SLVec3f(0.0f, 0.0f, 0.0f), - SLVec3f(0.0f, 0.0f, 0.0f), - 4.0f, - texC, - "Ring of fire Particle System", - texFlipbook); + // Create a scene group node + SLNode* scene = new SLNode("scene node"); + s->root3D(scene); - ps->doShape(true); - ps->shapeType(ST_Cone); - ps->doShapeSpawnBase(true); - ps->doShapeSurface(true); - ps->shapeRadius(1.0f); - ps->doBlendBrightness(true); - ps->color(SLCol4f(0.925f, 0.238f, 0.097f, 0.503f)); + // Create textures and materials + SLGLTexture* texC = new SLGLTexture(am, texPath + "ParticleSmoke_08_C.png"); + SLGLTexture* texFlipbook = new SLGLTexture(am, texPath + "ParticleSmoke_03_8x8_C.png"); - SLMesh* pSMesh = ps; - SLNode* pSNode = new SLNode(pSMesh, "Particle Ring Fire node"); - pSNode->rotate(90, 1, 0, 0); - scene->addChild(pSNode); + // Create meshes and nodes + SLParticleSystem* ps = new SLParticleSystem(am, + 1000, + SLVec3f(0.0f, 0.0f, 0.0f), + SLVec3f(0.0f, 0.0f, 0.0f), + 4.0f, + texC, + "Ring of fire Particle System", + texFlipbook); + + ps->doShape(true); + ps->shapeType(ST_Cone); + ps->doShapeSpawnBase(true); + ps->doShapeSurface(true); + ps->shapeRadius(1.0f); + ps->doBlendBrightness(true); + ps->color(SLCol4f(0.925f, 0.238f, 0.097f, 0.503f)); + + SLMesh* pSMesh = ps; + SLNode* pSNode = new SLNode(pSMesh, "Particle Ring Fire node"); + pSNode->rotate(90, 1, 0, 0); + scene->addChild(pSNode); - // Set background color and the root scene node - sv->sceneViewCamera()->background().colors(SLCol4f(0.8f, 0.8f, 0.8f), - SLCol4f(0.2f, 0.2f, 0.2f)); - // Save energy - sv->doWaitOnIdle(false); - } + // Set background color and the root scene node + sv->sceneViewCamera()->background().colors(SLCol4f(0.8f, 0.8f, 0.8f), + SLCol4f(0.2f, 0.2f, 0.2f)); + // Save energy + sv->doWaitOnIdle(false); } else if (sceneID == SID_ParticleSystem_FireComplex) //......................................... { - if (stateGL->glHasGeometryShaders()) - { - // Set scene name and info string - s->name("Fire Complex particle system"); - s->info("The fire particle systems contain each multiple sub particle systems.\n" - "See the scenegraph window for the sub particles systems. " - "See the properties window for the settings of the particles systems"); + // Set scene name and info string + s->name("Fire Complex particle system"); + s->info("The fire particle systems contain each multiple sub particle systems.\n" + "See the scenegraph window for the sub particles systems. " + "See the properties window for the settings of the particles systems"); - // Create a scene group node - SLNode* scene = new SLNode("scene node"); - s->root3D(scene); + // Create a scene group node + SLNode* scene = new SLNode("scene node"); + s->root3D(scene); - // Create and add camera - SLCamera* cam1 = new SLCamera("Camera 1"); - cam1->translation(0, 1.2f, 4.0f); - cam1->lookAt(0, 1.2f, 0); - cam1->focalDist(4.5f); - cam1->setInitialState(); - scene->addChild(cam1); - sv->camera(cam1); + // Create and add camera + SLCamera* cam1 = new SLCamera("Camera 1"); + cam1->translation(0, 1.2f, 4.0f); + cam1->lookAt(0, 1.2f, 0); + cam1->focalDist(4.5f); + cam1->setInitialState(); + scene->addChild(cam1); + sv->camera(cam1); - // Create textures and materials - SLGLTexture* texFireCld = new SLGLTexture(am, texPath + "ParticleFirecloudTransparent_C.png"); - SLGLTexture* texFireFlm = new SLGLTexture(am, texPath + "ParticleFlames_06_8x8_C.png"); - SLGLTexture* texCircle = new SLGLTexture(am, texPath + "ParticleCircle_05_C.png"); - SLGLTexture* texSmokeB = new SLGLTexture(am, texPath + "ParticleCloudBlack_C.png"); - SLGLTexture* texSmokeW = new SLGLTexture(am, texPath + "ParticleCloudWhite_C.png"); - SLGLTexture* texTorchFlm = new SLGLTexture(am, texPath + "ParticleFlames_04_16x4_C.png"); - SLGLTexture* texTorchSmk = new SLGLTexture(am, texPath + "ParticleSmoke_08_C.png"); - - SLNode* complexFire = createComplexFire(am, - s, - true, - texTorchSmk, - texFireFlm, - 8, - 8, - texCircle, - texSmokeB, - texSmokeW); - scene->addChild(complexFire); - - // Room around - { - // Room parent node - SLNode* room = new SLNode("Room"); - scene->addChild(room); - - // Back wall material - SLGLTexture* texWallDIF = new SLGLTexture(am, texPath + "BrickLimestoneGray_1K_DIF.jpg", SL_ANISOTROPY_MAX, GL_LINEAR); - SLGLTexture* texWallNRM = new SLGLTexture(am, texPath + "BrickLimestoneGray_1K_NRM.jpg", SL_ANISOTROPY_MAX, GL_LINEAR); - SLMaterial* matWall = new SLMaterial(am, "mat3", texWallDIF, texWallNRM); - matWall->specular(SLCol4f::BLACK); - matWall->metalness(0); - matWall->roughness(1); - matWall->reflectionModel(RM_CookTorrance); - - // Room dimensions - SLfloat pL = -2.0f, pR = 2.0f; // left/right - SLfloat pB = -0.01f, pT = 4.0f; // bottom/top - SLfloat pN = 2.0f, pF = -2.0f; // near/far - - // bottom rectangle - SLNode* b = new SLNode(new SLRectangle(am, SLVec2f(pL, -pN), SLVec2f(pR, -pF), 10, 10, "Floor", matWall)); - b->rotate(90, -1, 0, 0); - b->translate(0, 0, pB, TS_object); - room->addChild(b); - - // far rectangle - SLNode* f = new SLNode(new SLRectangle(am, SLVec2f(pL, pB), SLVec2f(pR, pT), 10, 10, "Wall far", matWall)); - f->translate(0, 0, pF, TS_object); - room->addChild(f); - - // near rectangle - SLNode* n = new SLNode(new SLRectangle(am, SLVec2f(pL, pB), SLVec2f(pR, pT), 10, 10, "Wall near", matWall)); - n->rotate(180, 0, 1, 0); - n->translate(0, 0, pF, TS_object); - room->addChild(n); - - // left rectangle - SLNode* l = new SLNode(new SLRectangle(am, SLVec2f(-pN, pB), SLVec2f(-pF, pT), 10, 10, "Wall left", matWall)); - l->rotate(90, 0, 1, 0); - l->translate(0, 0, pL, TS_object); - room->addChild(l); - - // right rectangle - SLNode* r = new SLNode(new SLRectangle(am, SLVec2f(pF, pB), SLVec2f(pN, pT), 10, 10, "Wall right", matWall)); - r->rotate(90, 0, -1, 0); - r->translate(0, 0, -pR, TS_object); - room->addChild(r); - } + // Create textures and materials + SLGLTexture* texFireCld = new SLGLTexture(am, texPath + "ParticleFirecloudTransparent_C.png"); + SLGLTexture* texFireFlm = new SLGLTexture(am, texPath + "ParticleFlames_06_8x8_C.png"); + SLGLTexture* texCircle = new SLGLTexture(am, texPath + "ParticleCircle_05_C.png"); + SLGLTexture* texSmokeB = new SLGLTexture(am, texPath + "ParticleCloudBlack_C.png"); + SLGLTexture* texSmokeW = new SLGLTexture(am, texPath + "ParticleCloudWhite_C.png"); + SLGLTexture* texTorchFlm = new SLGLTexture(am, texPath + "ParticleFlames_04_16x4_C.png"); + SLGLTexture* texTorchSmk = new SLGLTexture(am, texPath + "ParticleSmoke_08_C.png"); + + SLNode* complexFire = createComplexFire(am, + s, + true, + texTorchSmk, + texFireFlm, + 8, + 8, + texCircle, + texSmokeB, + texSmokeW); + scene->addChild(complexFire); + + // Room around + { + // Room parent node + SLNode* room = new SLNode("Room"); + scene->addChild(room); + + // Back wall material + SLGLTexture* texWallDIF = new SLGLTexture(am, texPath + "BrickLimestoneGray_1K_DIF.jpg", SL_ANISOTROPY_MAX, GL_LINEAR); + SLGLTexture* texWallNRM = new SLGLTexture(am, texPath + "BrickLimestoneGray_1K_NRM.jpg", SL_ANISOTROPY_MAX, GL_LINEAR); + SLMaterial* matWall = new SLMaterial(am, "mat3", texWallDIF, texWallNRM); + matWall->specular(SLCol4f::BLACK); + matWall->metalness(0); + matWall->roughness(1); + matWall->reflectionModel(RM_CookTorrance); + + // Room dimensions + SLfloat pL = -2.0f, pR = 2.0f; // left/right + SLfloat pB = -0.01f, pT = 4.0f; // bottom/top + SLfloat pN = 2.0f, pF = -2.0f; // near/far + + // bottom rectangle + SLNode* b = new SLNode(new SLRectangle(am, SLVec2f(pL, -pN), SLVec2f(pR, -pF), 10, 10, "Floor", matWall)); + b->rotate(90, -1, 0, 0); + b->translate(0, 0, pB, TS_object); + room->addChild(b); + + // far rectangle + SLNode* f = new SLNode(new SLRectangle(am, SLVec2f(pL, pB), SLVec2f(pR, pT), 10, 10, "Wall far", matWall)); + f->translate(0, 0, pF, TS_object); + room->addChild(f); + + // near rectangle + SLNode* n = new SLNode(new SLRectangle(am, SLVec2f(pL, pB), SLVec2f(pR, pT), 10, 10, "Wall near", matWall)); + n->rotate(180, 0, 1, 0); + n->translate(0, 0, pF, TS_object); + room->addChild(n); + + // left rectangle + SLNode* l = new SLNode(new SLRectangle(am, SLVec2f(-pN, pB), SLVec2f(-pF, pT), 10, 10, "Wall left", matWall)); + l->rotate(90, 0, 1, 0); + l->translate(0, 0, pL, TS_object); + room->addChild(l); + + // right rectangle + SLNode* r = new SLNode(new SLRectangle(am, SLVec2f(pF, pB), SLVec2f(pN, pT), 10, 10, "Wall right", matWall)); + r->rotate(90, 0, -1, 0); + r->translate(0, 0, -pR, TS_object); + room->addChild(r); + } - // Firewood - SLAssimpImporter importer; - SLNode* firewood = importer.load(s->animManager(), - am, - modelPath + "GLTF/Firewood/Firewood1.gltf", - texPath, - nullptr, - false, - true, - nullptr, - 0.3f, - true); - firewood->scale(2); - scene->addChild(firewood); - - // Torch - SLNode* torchL = importer.load(s->animManager(), - am, - modelPath + "GLTF/Torch/Torch.gltf", - texPath, - nullptr, - false, - true, - nullptr, - 0.3f, - true); - torchL->name("Torch Left"); - SLNode* torchR = torchL->copyRec(); - torchR->name("Torch Right"); - torchL->translate(-2, 1.5f, 0); - torchL->rotate(90, 0, 1, 0); - torchL->scale(2); - scene->addChild(torchL); - torchR->translate(2, 1.5f, 0); - torchR->rotate(-90, 0, 1, 0); - torchR->scale(2); - scene->addChild(torchR); - - // Torch flame left - SLNode* torchFlameNodeL = createTorchFire(am, - s, - true, - texTorchSmk, - texTorchFlm, - 16, - 4); - torchFlameNodeL->translate(-1.6f, 2.25f, 0); - torchFlameNodeL->name("Torch Fire Left"); - scene->addChild(torchFlameNodeL); - - // Torch flame right - SLNode* torchFlameNodeR = createTorchFire(am, - s, - true, - texTorchSmk, - texTorchFlm, - 16, - 4); - torchFlameNodeR->translate(1.6f, 2.25f, 0); - torchFlameNodeR->name("Torch Fire Right"); - scene->addChild(torchFlameNodeR); + // Firewood + SLAssimpImporter importer; + SLNode* firewood = importer.load(s->animManager(), + am, + modelPath + "GLTF/Firewood/Firewood1.gltf", + texPath, + nullptr, + false, + true, + nullptr, + 0.3f, + true); + firewood->scale(2); + scene->addChild(firewood); - // Set background color and the root scene node - sv->sceneViewCamera()->background().colors(SLCol4f(0.8f, 0.8f, 0.8f), - SLCol4f(0.2f, 0.2f, 0.2f)); - // Save energy - sv->doWaitOnIdle(false); - } + // Torch + SLNode* torchL = importer.load(s->animManager(), + am, + modelPath + "GLTF/Torch/Torch.gltf", + texPath, + nullptr, + false, + true, + nullptr, + 0.3f, + true); + torchL->name("Torch Left"); + SLNode* torchR = torchL->copyRec(); + torchR->name("Torch Right"); + torchL->translate(-2, 1.5f, 0); + torchL->rotate(90, 0, 1, 0); + torchL->scale(2); + scene->addChild(torchL); + torchR->translate(2, 1.5f, 0); + torchR->rotate(-90, 0, 1, 0); + torchR->scale(2); + scene->addChild(torchR); + + // Torch flame left + SLNode* torchFlameNodeL = createTorchFire(am, + s, + true, + texTorchSmk, + texTorchFlm, + 16, + 4); + torchFlameNodeL->translate(-1.6f, 2.25f, 0); + torchFlameNodeL->name("Torch Fire Left"); + scene->addChild(torchFlameNodeL); + + // Torch flame right + SLNode* torchFlameNodeR = createTorchFire(am, + s, + true, + texTorchSmk, + texTorchFlm, + 16, + 4); + torchFlameNodeR->translate(1.6f, 2.25f, 0); + torchFlameNodeR->name("Torch Fire Right"); + scene->addChild(torchFlameNodeR); + + // Set background color and the root scene node + sv->sceneViewCamera()->background().colors(SLCol4f(0.8f, 0.8f, 0.8f), + SLCol4f(0.2f, 0.2f, 0.2f)); + // Save energy + sv->doWaitOnIdle(false); } else if (sceneID == SID_Benchmark1_LargeModel) //.............................................. diff --git a/apps/exercises/ch00_new_cpp_features/ch00_NewCppFeatures.cpp b/apps/exercises/ch00_new_cpp_features/ch00_NewCppFeatures.cpp index d5d0cfd2..db3bf7d8 100644 --- a/apps/exercises/ch00_new_cpp_features/ch00_NewCppFeatures.cpp +++ b/apps/exercises/ch00_new_cpp_features/ch00_NewCppFeatures.cpp @@ -293,7 +293,7 @@ void new_rvalue_references() const char* pb1 = b.c_str(); cout << "a before the move: " << std::hex << pa1 << ", " << pa1 << endl; cout << "b before the move: " << std::hex << pb1 << ", " << pb1 << endl; - b = move(a); + //b = move(a); const char* pa2 = a.c_str(); const char* pb2 = b.c_str(); cout << "a after the move: " << std::hex << pa2 << ", " << pa2 << endl; diff --git a/data/shaders/ch08_BlinnPhongLighting.frag b/data/shaders/ch08_BlinnPhongLighting.frag index adf344ee..4cc50e5e 100644 --- a/data/shaders/ch08_BlinnPhongLighting.frag +++ b/data/shaders/ch08_BlinnPhongLighting.frag @@ -27,7 +27,7 @@ uniform vec4 u_globalAmbi; // Global ambient scene color uniform vec4 u_matAmbi; // ambient color reflection coefficient (ka) uniform vec4 u_matDiff; // diffuse color reflection coefficient (kd) uniform vec4 u_matSpec; // specular color reflection coefficient (ks) -uniform vec4 u_matEmis; // emissive color for self-shining materials +uniform vec4 u_matEmis; // emissive color for self-shining materials (ke) uniform float u_matShin; // shininess exponent out vec4 o_fragColor; // output fragment color diff --git a/modules/cv/source/CVTrackedFeatures.cpp b/modules/cv/source/CVTrackedFeatures.cpp index 80b091ba..97b22c4b 100644 --- a/modules/cv/source/CVTrackedFeatures.cpp +++ b/modules/cv/source/CVTrackedFeatures.cpp @@ -799,7 +799,9 @@ void CVTrackedFeatures::optimizeMatches() #if DO_FEATURE_BENCHMARKING sum_reprojection_error += reprojectionError / _marker.keypoints3D.size(); +#endif +#if DO_FEATURE_BENCHMARKING CVMat prevRmat, currRmat; if (_prevFrame.foundPose) { diff --git a/modules/math/CMakeLists.txt b/modules/math/CMakeLists.txt index 0bcf811c..168fbe0e 100644 --- a/modules/math/CMakeLists.txt +++ b/modules/math/CMakeLists.txt @@ -39,6 +39,8 @@ add_library(${target} ${headers} ${sources}) +add_definitions(-w) + add_library(${META_PROJECT_NAME}::${target} ALIAS ${target} @@ -46,11 +48,6 @@ add_library(${META_PROJECT_NAME}::${target} enable_warnings(${target}) -set_target_properties(${target} - PROPERTIES - ${DEFAULT_PROJECT_OPTIONS} - ) - target_include_directories(${target} PRIVATE diff --git a/modules/math/source/SLMath.h b/modules/math/source/SLMath.h index a79a33a0..d23e413d 100644 --- a/modules/math/source/SLMath.h +++ b/modules/math/source/SLMath.h @@ -13,6 +13,7 @@ #include #include +#include //----------------------------------------------------------------------------- // Redefinition of standard types for platform independence diff --git a/modules/math/source/SLVec4.h b/modules/math/source/SLVec4.h index e34b7b70..23598c31 100644 --- a/modules/math/source/SLVec4.h +++ b/modules/math/source/SLVec4.h @@ -232,15 +232,16 @@ template SLVec4 SLVec4::YELLOW = SLVec4(1.0f, 1.0f, 0.0f, 1.0f template SLVec4 SLVec4::CYAN = SLVec4(0.0f, 1.0f, 1.0f, 1.0f); template SLVec4 SLVec4::MAGENTA= SLVec4(1.0f, 0.0f, 1.0f, 1.0f); //----------------------------------------------------------------------------- -typedef SLVec4 SLVec4f; -typedef SLVec4 SLVec4i; -typedef SLVec4 SLCol4f; +typedef SLVec4 SLVec4f; +typedef SLVec4 SLVec4i; +typedef SLVec4 SLCol4f; typedef vector SLVVec4f; +typedef vector SLVVec4i; typedef vector SLVCol4f; #ifdef SL_HAS_DOUBLE -typedef SLVec4 SLVec4d; +typedef SLVec4 SLVec4d; typedef vector SLVVec4d; #endif //----------------------------------------------------------------------------- diff --git a/modules/sl/source/SL.h b/modules/sl/source/SL.h index 8165192f..48708e1a 100644 --- a/modules/sl/source/SL.h +++ b/modules/sl/source/SL.h @@ -235,7 +235,7 @@ SL_sizeOfVector(const T& vector) //----------------------------------------------------------------------------- // Some debugging and error handling macros #define SL_LOG(...) Utils::log("SLProject", __VA_ARGS__) -#define SL_EXIT_MSG(message) Utils::log("SLProject Error", (message)) +#define SL_EXIT_MSG(message) Utils::exitMsg("SLProject", (message), __LINE__, __FILE__) #define SL_WARN_MSG(message) Utils::warnMsg("SLProject", (message), __LINE__, __FILE__) //----------------------------------------------------------------------------- #endif diff --git a/modules/sl/source/SLAssetManager.cpp b/modules/sl/source/SLAssetManager.cpp index 020a6c99..05498ce2 100644 --- a/modules/sl/source/SLAssetManager.cpp +++ b/modules/sl/source/SLAssetManager.cpp @@ -96,6 +96,21 @@ bool SLAssetManager::removeMesh(SLMesh* mesh) return false; } //----------------------------------------------------------------------------- +//! Removes the specified program from the meshes resource vector. +bool SLAssetManager::removeProgram(SLGLProgram* program) +{ + assert(program); + for (SLulong i = 0; i < _programs.size(); ++i) + { + if (_programs[i] == program) + { + _programs.erase(_programs.begin() + i); + return true; + } + } + return false; +} +//----------------------------------------------------------------------------- //! Returns the pointer to shader program if found by name SLGLProgram* SLAssetManager::getProgramByName(const string& programName) { @@ -104,6 +119,7 @@ SLGLProgram* SLAssetManager::getProgramByName(const string& programName) return sp; return nullptr; } + //----------------------------------------------------------------------------- //! merge other asset manager into this void SLAssetManager::merge(SLAssetManager& other) diff --git a/modules/sl/source/SLAssetManager.h b/modules/sl/source/SLAssetManager.h index c261a6b2..773cde87 100644 --- a/modules/sl/source/SLAssetManager.h +++ b/modules/sl/source/SLAssetManager.h @@ -52,6 +52,9 @@ class SLAssetManager //! Removes the specified mesh from the meshes resource vector. bool removeMesh(SLMesh* mesh); + //! Removes the specified mesh from the meshes resource vector. + bool removeProgram(SLGLProgram* program); + //! Returns the pointer to shader program if found by name SLGLProgram* getProgramByName(const string& programName); diff --git a/modules/sl/source/SLDrawBits.h b/modules/sl/source/SLDrawBits.h index 9bdf55eb..c6dbef7e 100644 --- a/modules/sl/source/SLDrawBits.h +++ b/modules/sl/source/SLDrawBits.h @@ -17,19 +17,20 @@ Drawing Bits control some visual states of the scene and are applied per scene view or per single node object. Not all are used from the beginning */ -#define SL_DB_HIDDEN 1 //!< Flags an object as hidden -#define SL_DB_NOTSELECTABLE 2 //!< Flags an object as selected -#define SL_DB_MESHWIRED 4 //!< Draw polygons as wired mesh -#define SL_DB_NORMALS 8 //!< Draw the vertex normals -#define SL_DB_BBOX 16 //!< Draw the bounding boxes of a node -#define SL_DB_AXIS 32 //!< Draw the coordinate axis of a node -#define SL_DB_VOXELS 64 //!< Draw the voxels of the uniform grid -#define SL_DB_SKELETON 128 //!< Draw the skeletons joints -#define SL_DB_CULLOFF 256 //!< Turn off face culling -#define SL_DB_OVERDRAW 512 //!< Draw node over all other nodes -#define SL_DB_WITHEDGES 1024 //!< Draw faces with hard edges -#define SL_DB_ONLYEDGES 2048 //!< Draw only hard edges -#define SL_DB_BRECT 4096 //!< Draw the bounding rectangle of a node +#define SL_DB_HIDDEN 1 //!< Flags an object as hidden +#define SL_DB_NOTSELECTABLE 2 //!< Flags an object as selected +#define SL_DB_MESHWIRED 4 //!< Draw polygons as wired mesh +#define SL_DB_NORMALS 8 //!< Draw the vertex normals +#define SL_DB_BBOX 16 //!< Draw the bounding boxes of a node +#define SL_DB_AXIS 32 //!< Draw the coordinate axis of a node +#define SL_DB_VOXELS 64 //!< Draw the voxels of the uniform grid +#define SL_DB_SKELETON 128 //!< Draw the skeletons joints +#define SL_DB_CULLOFF 256 //!< Turn off face culling +#define SL_DB_OVERDRAW 512 //!< Draw node over all other nodes +#define SL_DB_WITHEDGES 1024 //!< Draw faces with hard edges +#define SL_DB_ONLYEDGES 2048 //!< Draw only hard edges +#define SL_DB_BRECT 4096 //!< Draw the bounding rectangle of a node +#define SL_DB_GPU_SKINNING 8192 //!< Perform skinning on the GPU //----------------------------------------------------------------------------- //! Drawing states stored in the bits of an unsigned int diff --git a/modules/sl/source/SLMaterial.cpp b/modules/sl/source/SLMaterial.cpp index 25719855..25674510 100644 --- a/modules/sl/source/SLMaterial.cpp +++ b/modules/sl/source/SLMaterial.cpp @@ -150,7 +150,7 @@ SLMaterial::SLMaterial(SLAssetManager* am, SLGLProgram* program) : SLObject(name) { _assetManager = am; - _ambient.set(0, 0, 0); // not used in Cook-Torrance + _ambient.set(0, 0, 0); // not used in Cook-Torrance _diffuse = diffuse; _specular.set(1, 1, 1); // not used in Cook-Torrance _emissive.set(0, 0, 0, 0); // not used in Cook-Torrance @@ -372,12 +372,23 @@ SLMaterial::~SLMaterial() _errorTexture = nullptr; } } +//------------------------------------------------------------------------------ +void SLMaterial::deleteDataGpu() +{ + if (_program) + { + _assetManager->removeProgram(_program); + _program->deleteDataGpu(); + delete _program; + _program = nullptr; + } +} //----------------------------------------------------------------------------- /*! If this material has not yet a shader program assigned (SLMaterial::_program) a suitable program will be generated with an instance of SLGLProgramGenerated. */ -void SLMaterial::generateProgramPS() +void SLMaterial::generateProgramPS(bool renderInstanced) { // If no shader program is attached add a generated shader program // A 3D object can be stored without material or shader program information. @@ -389,17 +400,24 @@ void SLMaterial::generateProgramPS() // Check first the asset manager if the requested program type already exists string programNameDraw; - SLGLProgramGenerated::buildProgramNamePS(this, programNameDraw, true); + SLGLProgramGenerated::buildProgramNamePS(this, + programNameDraw, + true, + renderInstanced); _program = _assetManager->getProgramByName(programNameDraw); // If the program was not found by name generate a new one if (!_program) { + std::string geom = ""; + if (!renderInstanced) + geom = "Geom"; + _program = new SLGLProgramGenerated(_assetManager, programNameDraw, this, true, - "Geom"); + geom); } } @@ -411,7 +429,7 @@ void SLMaterial::generateProgramPS() // Check first the asset manager if the requested programTF type already exists string programNameUpdate; - SLGLProgramGenerated::buildProgramNamePS(this, programNameUpdate, false); + SLGLProgramGenerated::buildProgramNamePS(this, programNameUpdate, false, renderInstanced); _programTF = _assetManager->getProgramByName(programNameUpdate); if (!_programTF) { @@ -460,7 +478,8 @@ void SLMaterial::generateProgramPS() for (int i = 0; i < TT_numTextureType; i++) _textures[i].clear(); if (!_errorTexture && !_compileErrorTexFilePath.empty()) - _errorTexture = new SLGLTexture(nullptr, _compileErrorTexFilePath); + _errorTexture = new SLGLTexture(nullptr, + _compileErrorTexFilePath); _textures[TT_diffuse].push_back(_errorTexture); } @@ -469,7 +488,8 @@ void SLMaterial::generateProgramPS() for (int i = 0; i < TT_numTextureType; i++) _textures[i].clear(); if (!_errorTexture && !_compileErrorTexFilePath.empty()) - _errorTexture = new SLGLTexture(nullptr, _compileErrorTexFilePath); + _errorTexture = new SLGLTexture(nullptr, + _compileErrorTexFilePath); _textures[TT_diffuse].push_back(_errorTexture); } } diff --git a/modules/sl/source/SLMaterial.h b/modules/sl/source/SLMaterial.h index 60dbbd97..d1f4cf90 100644 --- a/modules/sl/source/SLMaterial.h +++ b/modules/sl/source/SLMaterial.h @@ -108,12 +108,15 @@ class SLMaterial : public SLObject SLGLProgram* program); ~SLMaterial() override; - void generateProgramPS(); + void generateProgramPS(bool renderInstanced = false); void activate(SLCamera* cam, SLVLight* lights, SLSkybox* skybox = nullptr); SLint passToUniforms(SLGLProgram* program, SLint nextTexUnit); + + void deleteDataGpu(); + //! Returns true if there is any transparency in diffuse alpha or textures SLbool hasAlpha() { @@ -198,6 +201,7 @@ class SLMaterial : public SLObject void program(SLGLProgram* sp) { _program = sp; } void programTF(SLGLProgram* sp) { _programTF = sp; } void skybox(SLSkybox* sb) { _skybox = sb; } + void supportsGPUSkinning(SLbool supportsGPUSkinning) { _supportsGPUSkinning = supportsGPUSkinning; } void ps(SLParticleSystem* ps) { _ps = ps; } // Getters @@ -220,6 +224,7 @@ class SLMaterial : public SLObject SLGLProgram* program() { return _program; } SLGLProgram* programTF() { return _programTF; } SLSkybox* skybox() { return _skybox; } + SLbool supportsGPUSkinning() { return _supportsGPUSkinning; } SLParticleSystem* ps() { return _ps; } SLVNode& nodesVisible2D() { return _nodesVisible2D; } SLVNode& nodesVisible3D() { return _nodesVisible3D; } @@ -231,36 +236,37 @@ class SLMaterial : public SLObject static SLfloat PERFECT; //!< PM: shininess/translucency limit protected: - SLAssetManager* _assetManager; //!< pointer to the asset manager (the owner) if available - SLReflectionModel _reflectionModel; //!< reflection model (RM_BlinnPhong or RM_CookTorrance) - SLCol4f _ambient; //!< ambient color (RGB reflection coefficients) - SLCol4f _diffuse; //!< diffuse color (RGB reflection coefficients) - SLCol4f _specular; //!< specular color (RGB reflection coefficients) - SLCol4f _emissive; //!< emissive color coefficients - SLfloat _shininess; //!< shininess exponent in Blinn-Phong model - SLfloat _roughness; //!< roughness property (0-1) in Cook-Torrance model - SLfloat _metalness; //!< metallic property (0-1) in Cook-Torrance model - SLCol4f _transmissive; //!< transmissive color (RGB reflection coefficients) for path tracing - SLfloat _translucency; //!< translucency exponent for light refraction for path tracing - SLfloat _kr{}; //!< reflection coefficient 0.0 - 1.0 used for ray and path tracing - SLfloat _kt{}; //!< transmission coefficient 0.0 - 1.0 used for ray and path tracing - SLfloat _kn{}; //!< refraction index - SLbool _getsShadows; //!< true if shadows are visible on this material - SLGLProgram* _program{}; //!< pointer to a GLSL shader program - SLGLProgram* _programTF{}; //!< pointer to a GLSL shader program for transformFeedback - SLint _numTextures; //!< number of textures in all _textures vectors array - SLSkybox* _skybox; //!< pointer to the skybox + SLAssetManager* _assetManager; //!< pointer to the asset manager (the owner) if available + SLReflectionModel _reflectionModel; //!< reflection model (RM_BlinnPhong or RM_CookTorrance) + SLCol4f _ambient; //!< ambient color (RGB reflection coefficients) + SLCol4f _diffuse; //!< diffuse color (RGB reflection coefficients) + SLCol4f _specular; //!< specular color (RGB reflection coefficients) + SLCol4f _emissive; //!< emissive color coefficients + SLfloat _shininess; //!< shininess exponent in Blinn-Phong model + SLfloat _roughness; //!< roughness property (0-1) in Cook-Torrance model + SLfloat _metalness; //!< metallic property (0-1) in Cook-Torrance model + SLCol4f _transmissive; //!< transmissive color (RGB reflection coefficients) for path tracing + SLfloat _translucency; //!< translucency exponent for light refraction for path tracing + SLfloat _kr{}; //!< reflection coefficient 0.0 - 1.0 used for ray and path tracing + SLfloat _kt{}; //!< transmission coefficient 0.0 - 1.0 used for ray and path tracing + SLfloat _kn{}; //!< refraction index + SLbool _getsShadows; //!< true if shadows are visible on this material + SLGLProgram* _program{}; //!< pointer to a GLSL shader program + SLGLProgram* _programTF{}; //!< pointer to a GLSL shader program for transformFeedback + SLint _numTextures; //!< number of textures in all _textures vectors array + SLSkybox* _skybox; //!< pointer to the skybox + bool _supportsGPUSkinning = false; //!< whether skinning is performed on the GPU or on the CPU // For particle system - SLParticleSystem* _ps; //!< pointer to a particle system + SLParticleSystem* _ps; //!< pointer to a particle system SLVGLTexture _textures[TT_numTextureType]; //!< array of texture vectors one for each type SLVGLTexture _textures3d; //!< texture vector for diffuse 3D textures SLGLTexture* _errorTexture = nullptr; //!< pointer to error texture that is shown if another texture fails SLstring _compileErrorTexFilePath; //!< path to the error texture - SLVNode _nodesVisible2D; //!< Vector of all visible 2D nodes of with this material - SLVNode _nodesVisible3D; //!< Vector of all visible 3D nodes of with this material + SLVNode _nodesVisible2D; //!< Vector of all visible 2D nodes of with this material + SLVNode _nodesVisible3D; //!< Vector of all visible 3D nodes of with this material }; //----------------------------------------------------------------------------- //! STL vector of material pointers diff --git a/modules/sl/source/SLScene.cpp b/modules/sl/source/SLScene.cpp index 89c239e7..53f5354b 100644 --- a/modules/sl/source/SLScene.cpp +++ b/modules/sl/source/SLScene.cpp @@ -134,7 +134,8 @@ void SLScene::unInit() @return true if really something got updated */ bool SLScene::onUpdate(bool renderTypeIsRT, - bool voxelsAreShown) + bool voxelsAreShown, + bool forceCPUSkinning) { PROFILE_FUNCTION(); @@ -176,8 +177,11 @@ bool SLScene::onUpdate(bool renderTypeIsRT, // Do software skinning on all changed skeletons. Update any out of date acceleration structure for RT or if they're being rendered. if (_root3D) { + if (renderTypeIsRT || voxelsAreShown) + forceCPUSkinning = true; + // we use a lambda to inform nodes that share a mesh that the mesh got updated (so we don't have to transfer the root node) - sceneHasChanged |= _root3D->updateMeshSkins([&](SLMesh* mesh) + sceneHasChanged |= _root3D->updateMeshSkins(forceCPUSkinning, [&](SLMesh* mesh) { SLVNode nodes = _root3D->findChildren(mesh, true); for (auto* node : nodes) diff --git a/modules/sl/source/SLScene.h b/modules/sl/source/SLScene.h index f4981adf..8a09a6c4 100644 --- a/modules/sl/source/SLScene.h +++ b/modules/sl/source/SLScene.h @@ -114,7 +114,8 @@ class SLScene : public SLObject // Misc. bool onUpdate(bool renderTypeIsRT, - bool voxelsAreShown); + bool voxelsAreShown, + bool forceCPUSkinning); void init(SLAssetManager* am); virtual void unInit(); void selectNodeMesh(SLNode* nodeToSelect, SLMesh* meshToSelect); diff --git a/modules/sl/source/SLSceneView.cpp b/modules/sl/source/SLSceneView.cpp index 57f5290a..d58af6cf 100644 --- a/modules/sl/source/SLSceneView.cpp +++ b/modules/sl/source/SLSceneView.cpp @@ -513,7 +513,8 @@ SLbool SLSceneView::onPaint() // update current scene sceneHasChanged = _s->onUpdate((_renderType == RT_rt), - drawBit(SL_DB_VOXELS)); + drawBit(SL_DB_VOXELS), + !drawBit(SL_DB_GPU_SKINNING) || drawBit(SL_DB_WITHEDGES)); } SLbool camUpdated = false; diff --git a/modules/sl/source/gl/SLGLEnums.h b/modules/sl/source/gl/SLGLEnums.h index b57ddf04..01c436d4 100644 --- a/modules/sl/source/gl/SLGLEnums.h +++ b/modules/sl/source/gl/SLGLEnums.h @@ -19,6 +19,7 @@ enum SLGLBufferType { BT_float = GL_FLOAT, //!< float vertex attributes + BT_int = GL_INT, //!< int vertex attributes BT_ubyte = GL_UNSIGNED_BYTE, //!< vertex index type (0-2^8) BT_ushort = GL_UNSIGNED_SHORT, //!< vertex index type (0-2^16) BT_uint = GL_UNSIGNED_INT //!< vertex index type (0-2^32) diff --git a/modules/sl/source/gl/SLGLProgramGenerated.cpp b/modules/sl/source/gl/SLGLProgramGenerated.cpp index bdb9aabf..2eee9230 100644 --- a/modules/sl/source/gl/SLGLProgramGenerated.cpp +++ b/modules/sl/source/gl/SLGLProgramGenerated.cpp @@ -44,12 +44,18 @@ const string vertInput_PS_a_texNum = R"( layout (location = 6) in uint a_texNum; // Particle rotation attribute)"; const string vertInput_PS_a_initP = R"( layout (location = 7) in vec3 a_initialPosition;// Particle initial position attribute)"; +const string vertInput_PS_a_positionP = R"( +layout (location = 8) in vec3 a_positionP; // Particle position attribute)"; const string vertInput_a_uv0 = R"( layout (location = 2) in vec2 a_uv0; // Vertex tex.coord. 1 for diffuse color)"; const string vertInput_a_uv1 = R"( layout (location = 3) in vec2 a_uv1; // Vertex tex.coord. 2 for AO)"; const string vertInput_a_tangent = R"( layout (location = 5) in vec4 a_tangent; // Vertex tangent attribute)"; +const string vertInput_a_skinning = R"( + +layout (location = 6) in ivec4 a_jointIds; // Vertex joint indices attributes +layout (location = 7) in vec4 a_jointWeights; // Vertex joint weights attributes)"; //----------------------------------------------------------------------------- const string vertInput_u_matrices_all = R"( @@ -59,6 +65,12 @@ uniform mat4 u_pMatrix; // Projection matrix (camera to normalize device coo const string vertInput_u_matrix_vOmv = R"( uniform mat4 u_vOmvMatrix; // view or modelview matrix)"; //----------------------------------------------------------------------------- +const string vertInput_u_skinning = R"( + +uniform mat4 u_jointMatrices[100]; // Joint matrices for skinning +uniform bool u_skinningEnabled; // Flag if the shader should perform skinning +)"; +//----------------------------------------------------------------------------- const string vertInput_u_lightNm = R"( uniform vec4 u_lightPosVS[NUM_LIGHTS]; // position of light in view space @@ -72,16 +84,19 @@ const string vertConstant_PS_pi = R"( )"; //----------------------------------------------------------------------------- const string vertInput_PS_u_time = R"( - uniform float u_time; // Simulation time uniform float u_difTime; // Simulation delta time after frustum culling uniform float u_tTL; // Time to live of a particle)"; +const string fragInput_PS_u_tTL = R"( +uniform float u_tTL; // Time to live of a particle)"; const string vertInput_PS_u_al_bernstein_alpha = R"( uniform vec4 u_al_bernstein; // Bernstein polynomial for alpha over time)"; const string vertInput_PS_u_al_bernstein_size = R"( uniform vec4 u_si_bernstein; // Bernstein polynomial for size over time)"; const string vertInput_PS_u_colorOvLF = R"( uniform float u_colorArr[256 * 3]; // Array of color value (for color over life))"; +const string fragInput_PS_u_colorOvLF = R"( +uniform float u_colorArr[256 * 3]; // Array of color value (for color over life))"; const string vertInput_PS_u_deltaTime = R"( uniform float u_deltaTime; // Elapsed time between frames)"; const string vertInput_PS_u_pgPos = R"( @@ -103,7 +118,6 @@ uniform int u_condFB; // Condition to update texNum)"; //----------------------------------------------------------------------------- const string vertOutput_v_P_VS = R"( - out vec3 v_P_VS; // Point of illumination in view space (VS))"; const string vertOutput_v_P_WS = R"( out vec3 v_P_WS; // Point of illumination in world space (WS))"; @@ -121,7 +135,14 @@ out vec3 v_lightDirTS[NUM_LIGHTS]; // Vector to the light 0 in tangent spac out vec3 v_spotDirTS[NUM_LIGHTS]; // Spot direction in tangent space)"; //----------------------------------------------------------------------------- const string vertFunction_PS_ColorOverLT = R"( +vec3 colorByAge(float age) +{ + int cachePos = int(clamp(age, 0.0, 1.0) * 255.0) * 3; + vec3 color = vec3(u_colorArr[cachePos], u_colorArr[cachePos + 1], u_colorArr[cachePos + 2]); + return color; +})"; +const string fragFunction_PS_ColorOverLT = R"( vec3 colorByAge(float age) { int cachePos = int(clamp(age, 0.0, 1.0) * 255.0) * 3; @@ -146,6 +167,43 @@ const string vertOutput_PS_struct_texNum = R"( uint texNum; // Num of texture in flipbook)"; const string vertOutput_PS_struct_End = R"( } vert; )"; + +const string vertOutput_PS_instanced_transparency = R"( +out float transparency; // transparency of a particle )"; + +const string fragInput_PS_instanced_transparency = R"( +in float transparency; // transparency of a particle )"; + +const string fragMain_PS_v_c = R"( + vec4 color = u_color; // Particle color)"; +const string fragMain_PS_v_doColorOverLT = R"( + vec4 color = vec4(vert[0].color, 1.0); // Particle color)"; +const string fragMain_PS_instanced_v_doColorOverLT = R"( + vec4 color = vec4(colorByAge(v_age/u_tTL), 1.0); // Particle color)"; +const string fragMain_PS_v_withoutColor = R"( + vec4 color = vec4( 0.0, 0.0, 0.0, 1.0); // Particle color)"; +const string fragMain_PS_instanced_c = R"( + vec4 color = u_color; // Particle color)"; +const string fragMain_PS_instanced_transparency = R"( + color.w *= transparency; // Apply transparency)"; +const string vertInput_u_matrix_p = R"( +uniform mat4 u_pMatrix; // Projection matrix)"; +const string vertInput_u_matrix_vertBillboard = R"( +uniform mat4 u_vYawPMatrix; // Projection matrix)"; +const string fragInput_PS_u_c = R"( +uniform vec4 u_color; // Particle color)"; +const string vertInput_PS_u_ScaRa = R"( +uniform float u_scale; // Particle scale +uniform float u_radiusW; // Particle width radius) +uniform float u_radiusH; // Particle height radius)"; +//----------------------------------------------------------------------------- + +const string vertOutput_PS_color = R"( +out vec4 v_particleColor; // Size of a particle )"; + +const string vertOutput_PS_texCoord = R"( +out vec2 v_texCoord; // interpolated texture coordinateA)"; + //----------------------------------------------------------------------------- const string vertOutput_PS_tf_p = R"( @@ -167,34 +225,90 @@ out vec3 tf_initialPosition; // To transform feedback)"; //----------------------------------------------------------------------------- const string vertMain_Begin = R"( - void main() {)"; + +const string vertMain_instanced_Begin = R"( +void main() +{ + vec3 position = a_positionP; +)"; + const string vertMain_v_P_VS = R"( mat4 mvMatrix = u_vMatrix * u_mMatrix; - v_P_VS = vec3(mvMatrix * a_position); // vertex position in view space)"; + v_P_VS = vec3(mvMatrix * ${localPosition}); // vertex position in view space)"; const string vertMain_v_P_WS_Sm = R"( - v_P_WS = vec3(u_mMatrix * a_position); // vertex position in world space)"; + v_P_WS = vec3(u_mMatrix * ${localPosition}); // vertex position in world space)"; const string vertMain_v_N_VS = R"( mat3 invMvMatrix = mat3(inverse(mvMatrix)); mat3 nMatrix = transpose(invMvMatrix); - v_N_VS = vec3(nMatrix * a_normal); // vertex normal in view space)"; + v_N_VS = vec3(nMatrix * ${localNormal}); // vertex normal in view space)"; const string vertMain_v_R_OS = R"( vec3 I = normalize(v_P_VS); vec3 N = normalize(v_N_VS); v_R_OS = invMvMatrix * reflect(I, N); // R = I-2.0*dot(N,I)*N;)"; const string vertMain_v_uv0 = R"( - v_uv0 = a_uv0; // pass diffuse color tex.coord. 1 for interpolation)"; const string vertMain_v_uv1 = R"( v_uv1 = a_uv1; // pass diffuse color tex.coord. 1 for interpolation)"; +const string vertMain_skinning = R"( + vec4 skinnedPosition; + vec3 skinnedNormal; + + if (u_skinningEnabled) + { + // In skinned skeleton animation, every vertex of a mesh is transformed by + // max. four joints (bones) of a skeleton identified by indices. The joint + // matrix is a weighted sum of four joint matrices and can change per frame + // to animate the mesh. + mat4 jm = u_jointMatrices[int(a_jointIds.x)] * a_jointWeights.x + + u_jointMatrices[int(a_jointIds.y)] * a_jointWeights.y + + u_jointMatrices[int(a_jointIds.z)] * a_jointWeights.z + + u_jointMatrices[int(a_jointIds.w)] * a_jointWeights.w; + + skinnedPosition = jm * a_position; + skinnedNormal = mat3(jm) * a_normal; + } + else + { + skinnedPosition = a_position; + skinnedNormal = a_normal; + } +)"; +const string vertMain_skinning_Nm = R"( + vec4 skinnedPosition; + vec3 skinnedNormal; + vec4 skinnedTangent; + + if (u_skinningEnabled) + { + // In skinned skeleton animation, every vertex of a mesh is transformed by + // max. four joints (bones) of a skeleton identified by indices. The joint + // matrix is a weighted sum of four joint matrices and can change per frame + // to animate the mesh. + mat4 jm = u_jointMatrices[int(a_jointIds.x)] * a_jointWeights.x + + u_jointMatrices[int(a_jointIds.y)] * a_jointWeights.y + + u_jointMatrices[int(a_jointIds.z)] * a_jointWeights.z + + u_jointMatrices[int(a_jointIds.w)] * a_jointWeights.w; + + skinnedPosition = jm * a_position; + skinnedNormal = mat3(jm) * a_normal; + skinnedTangent = vec4(mat3(jm) * a_tangent.xyz, a_tangent.w); + } + else + { + skinnedPosition = a_position; + skinnedNormal = a_normal; + skinnedTangent = a_tangent; + } +)"; const string vertMain_TBN_Nm = R"( // Building the matrix Eye Space -> Tangent Space // See the math behind at: http://www.terathon.com/code/tangent.html - vec3 n = normalize(nMatrix * a_normal); - vec3 t = normalize(nMatrix * a_tangent.xyz); - vec3 b = cross(n, t) * a_tangent.w; // bitangent w. corrected handedness + vec3 n = normalize(nMatrix * ${localNormal}); + vec3 t = normalize(nMatrix * ${localTangent}.xyz); + vec3 b = cross(n, t) * ${localTangent}.w; // bitangent w. corrected handedness mat3 TBN = mat3(t,b,n); // Transform vector to the eye into tangent space @@ -213,8 +327,9 @@ const string vertMain_TBN_Nm = R"( v_lightDirTS[i] *= TBN; } )"; -const string vertMain_PS_v_a = R"( - float age = u_time - a_startTime; // Get the age of the particle)"; + +//----------------------------------------------------------------------------- +// Things that goes directly to geometry shader const string vertMain_PS_v_t_default = R"( if(age < 0.0) vert.transparency = 0.0; // To be discard, because the particle is to be born @@ -244,10 +359,101 @@ const string vertMain_PS_v_s_curve = R"( pow(vert.size,2.0) * u_si_bernstein.y + vert.size * u_si_bernstein.z + u_si_bernstein.w; // Get transparency by bezier curve)"; + +const string vertMain_PS_instanced_v_r = R"( + float rotation = a_rotation;)"; + +const string vertMain_PS_instanced_v_s = R"( + float size = age / u_tTL;)"; +const string vertMain_PS_instanced_v_s_curve = R"( + size = pow(size,3.0) * u_si_bernstein.x + + pow(size,2.0) * u_si_bernstein.y + + size * u_si_bernstein.z + + u_si_bernstein.w; // Get transparency by bezier curve)"; + +const string vertMain_PS_instanced_v_sS = R"( + position = size * position; + )"; + const string vertMain_PS_v_doColorOverLT = R"( vert.color = colorByAge(age/u_tTL);)"; + +const string vertOutput_PS_age = R"( +out float v_age; // Age of a particle)"; + +const string fragInput_PS_age = R"( +in float v_age; // Age of a particle)"; + +const string vertMain_PS_v_color = R"( + vert.color = u_color.rgb;)"; + const string vertMain_PS_v_texNum = R"( vert.texNum = a_texNum;)"; + +const string vertMain_PS_v_a = R"( + float age = u_time - a_startTime; // Get the age of the particle)"; + +const string vertMain_PS_v_tC = R"( + v_texCoord = 0.5 * (a_positionP.xy + vec2(1.0));)"; + +const string vertMain_PS_v_age = R"( + v_age = age;)"; + +const string vertMain_PS_v_tC_flipbook = R"( + uint actCI = uint(mod(float(a_texNum), float(u_col))); + uint actRI = (a_texNum - actCI) / u_col; + float actC = float(actCI); + float actR = float(actRI); + + vec2 p = 0.5 * (a_positionP.xy + vec2(1.0)); + v_texCoord = vec2((actC + p.x)/float(u_col), 1.0 - (actR - p.y)/float(u_row)); +)"; + +const string vertMain_PS_instanced_v_t_default = R"( + if(age < 0.0) + transparency = 0.0; // To be discard, because the particle is to be born + else + transparency = 1.0;)"; +const string vertMain_PS_instanced_v_t_begin = R"( + if(age < 0.0) + transparency = 0.0; // To be discard, because the particle is to be born + else + { + transparency = age / u_tTL; // Get by the ratio age:lifetime)"; +const string vertMain_PS_instanced_v_t_linear = R"( + transparency = 1.0 - transparency; // Linear)"; +const string vertMain_PS_instanced_v_t_curve = R"( + transparency = pow(transparency,3.0) * u_al_bernstein.x + + pow(transparency,2.0) * u_al_bernstein.y + + transparency * u_al_bernstein.z + + u_al_bernstein.w; // Get transparency by bezier curve)"; + +const string vertMain_PS_instanced_scale = R"( + position = vec3(u_radiusW * position.x, u_radiusH * position.y, position.z); + position = u_scale * position; + )"; + +const string vertMain_PS_instanced_rotate = R"( + mat2 rot = mat2(cos(a_rotation),-sin(a_rotation), + sin(a_rotation), cos(a_rotation)); // Matrix of rotation + position = vec3(rot * position.xy, position.z); + )"; + +const string vertMain_PS_instanced_EndAll = R"( + + // Modelview matrix multiplication with (particle position + particle generator position) + // Calculate position in view space + gl_Position = u_pMatrix * (u_vOmvMatrix * vec4(a_position, 1) + vec4(position, 0.0)); +} +)"; + +const string vertMain_PS_instanced_EndAll_VertBillboard = R"( + //gl_Position = vec4(a_position + a_positionP, 1); + gl_Position = u_pMatrix * u_vYawPMatrix * vec4(a_position + position, 1.0); +} +)"; + + const string vertMain_PS_EndAll = R"( // Modelview matrix multiplication with (particle position + particle generator position) @@ -257,14 +463,14 @@ const string vertMain_PS_EndAll = R"( )"; const string vertMain_PS_EndAll_VertBillboard = R"( - gl_Position = vec4(a_position, 1); + gl_Position = vec4(a_position, 1); } )"; const string vertMain_EndAll = R"( // pass the vertex w. the fix-function transform - gl_Position = u_pMatrix * mvMatrix * a_position; + gl_Position = u_pMatrix * mvMatrix * ${localPosition}; } )"; //----------------------------------------------------------------------------- @@ -336,6 +542,9 @@ const string vertMain_PS_U_EndAll = R"( } } })"; + + + //----------------------------------------------------------------------------- const string geomConfig_PS = R"( layout (points) in; // Primitives that we received from vertex shader @@ -361,7 +570,6 @@ uniform mat4 u_pMatrix; // Projection matrix)"; const string geomInput_u_matrix_vertBillboard = R"( uniform mat4 u_vYawPMatrix; // Projection matrix)"; const string geomInput_PS_u_ScaRa = R"( - uniform float u_scale; // Particle scale uniform float u_radiusW; // Particle width radius) uniform float u_radiusH; // Particle height radius)"; @@ -373,10 +581,12 @@ const string geomInput_PS_u_row = R"( uniform int u_row; // Number of row of flipbook texture)"; //----------------------------------------------------------------------------- const string geomOutput_PS_v_pC = R"( - out vec4 v_particleColor; // The resulting color per vertex)"; const string geomOutput_PS_v_tC = R"( out vec2 v_texCoord; // Texture coordinate at vertex)"; + +const string vertOutput_PS_v_tC = R"( +out vec2 v_texCoord; // Texture coordinate at vertex)"; //----------------------------------------------------------------------------- const string geomMain_PS_Begin = R"( @@ -633,7 +843,6 @@ const string fragInput_PS_u_wireFrame = R"( uniform bool u_doWireFrame; // Boolean for wireFrame)"; //----------------------------------------------------------------------------- const string fragMain_PS_TF = R"( - out vec4 o_fragColor; // output fragment color void main() @@ -643,9 +852,6 @@ void main() )"; //----------------------------------------------------------------------------- const string fragMain_PS = R"( - -void main() -{ // Just set the interpolated color from the vertex shader o_fragColor = v_particleColor; @@ -655,23 +861,50 @@ void main() if(o_fragColor.a < 0.001) discard; +)"; +const string fragMain_PS_begin = R"( +void main() +{ )"; + const string fragMain_PS_withoutColor = R"( -void main() -{ // componentwise multiply w. texture color if(!u_doWireFrame) o_fragColor = texture(u_matTextureDiffuse0, v_texCoord); else o_fragColor = vec4(0,0,0,1.0); - o_fragColor.a *= v_particleColor.a; + o_fragColor.a *= v_particleColor.a; + + if(o_fragColor.a < 0.001) + discard; +)"; + +const string fragMain_PS_instanced_withoutColor = R"( + // componentwise multiply w. texture color + if(!u_doWireFrame) + o_fragColor = texture(u_matTextureDiffuse0, v_texCoord); + else + o_fragColor = vec4(0,0,0,1.0); + + o_fragColor.a *= transparency; if(o_fragColor.a < 0.001) discard; +)"; + +const string fragMain_instanced_PS_end = R"( + // Just set the interpolated color from the vertex shader + o_fragColor = color; + // componentwise multiply w. texture color + if(!u_doWireFrame) + o_fragColor *= texture(u_matTextureDiffuse0, v_texCoord); + if(o_fragColor.a < 0.001) + discard; )"; + const string fragMain_PS_endAll = R"( //Same color for each wireframe if(u_doWireFrame) @@ -1444,7 +1677,6 @@ const string fragMainCook_2_LightLoopNmSm = R"( } )"; const string fragMainCook_3_FragColor = R"( - // ambient lighting (note that the next IBL tutorial will replace // this ambient lighting with environment lighting). vec3 ambient = vec3(0.03) * matDiff.rgb * matOccl; @@ -1459,7 +1691,6 @@ const string fragMainCook_3_FragColor = R"( o_fragColor.a = matDiff.a; )"; const string fragMainCook_3_FragColorSky = R"( - // Build diffuse reflection from environment light map vec3 F = fresnelSchlickRoughness(max(dot(N, E), 0.0), F0, matRough); vec3 kS = F; @@ -1542,8 +1773,9 @@ void main() The shader program gets a unique name with the following pattern:
- genCook-D00-N00-E00-O01-RM00-Sky-C4s
-    |    |   |   |   |   |    |   |
+ genCook-D00-N00-E00-O01-RM00-Sky-C4s-S
+    |    |   |   |   |   |    |   |   |
+    |    |   |   |   |   |    |   |   + Support for GPU skinning
     |    |   |   |   |   |    |   + Directional light w. 4 shadow cascades
     |    |   |   |   |   |    + Ambient light from skybox
     |    |   |   |   |   + Roughness-metallic map with index 0 and uv 0
@@ -1582,7 +1814,7 @@ void SLGLProgramGenerated::buildProgramName(SLMaterial* mat,
             if (light->doCascadedShadows())
                 programName += "C" + std::to_string(light->shadowMap()->numCascades()); // Directional light with cascaded shadowmap
             else
-                programName += "D";                                                     // Directional light
+                programName += "D"; // Directional light
         }
         else if (light->spotCutOffDEG() < 180.0f)
             programName += "S"; // Spot light
@@ -1591,6 +1823,9 @@ void SLGLProgramGenerated::buildProgramName(SLMaterial* mat,
         if (light->createsShadows())
             programName += "s"; // Creates shadows
     }
+
+    if (mat->supportsGPUSkinning())
+        programName += "-S";
 }
 //-----------------------------------------------------------------------------
 /*! See the class information for more insights of the generated name. This
@@ -1616,7 +1851,8 @@ void SLGLProgramGenerated::buildProgramName(SLMaterial* mat,
  */
 void SLGLProgramGenerated::buildProgramNamePS(SLMaterial* mat,
                                               string&     programName,
-                                              bool        isDrawProg)
+                                              bool        isDrawProg,
+                                              bool        renderInstanced)
 {
     assert(mat && "No material pointer passed!");
     programName = "gen";
@@ -1630,6 +1866,10 @@ void SLGLProgramGenerated::buildProgramNamePS(SLMaterial* mat,
     if (isDrawProg) // Drawing program
     {
         programName += "-Draw";
+
+        if (renderInstanced)
+            programName += "-Inst";
+
         programName += mat->texturesString();
         GLint billboardType = mat->ps()->billboardType();      // Billboard type (0 -> default; 1 -> vertical billboard, 2 -> horizontal billboard)
         bool  AlOvLi        = mat->ps()->doAlphaOverLT();      // Alpha over life
@@ -1643,7 +1883,6 @@ void SLGLProgramGenerated::buildProgramNamePS(SLMaterial* mat,
         bool  rot           = mat->ps()->doRotation();         // Rotation
         programName += "-B" + std::to_string(billboardType);
         if (rot) programName += "-RT";
-
         if (AlOvLi) programName += "-AL";
         if (AlOvLi && AlOvLiCu) programName += "cu";
         if (SiOvLi) programName += "-SL";
@@ -1653,7 +1892,7 @@ void SLGLProgramGenerated::buildProgramNamePS(SLMaterial* mat,
         if (FlBoTex) programName += "-FB";
         if (WS) programName += "-WS";
     }
-    else                                                  // Updating program
+    else // Updating program
     {
         bool counterGap = mat->ps()->doCounterGap();      // Counter gap/lag
         bool acc        = mat->ps()->doAcc();             // Acceleration
@@ -1733,7 +1972,7 @@ void SLGLProgramGenerated::buildProgramCode(SLMaterial* mat,
  * @param mat Parent material pointer
  * @param isDrawProg Flag if program is for drawing instead of update
  */
-void SLGLProgramGenerated::buildProgramCodePS(SLMaterial* mat, bool isDrawProg)
+void SLGLProgramGenerated::buildProgramCodePS(SLMaterial* mat, bool isDrawProg, bool renderInstanced)
 {
     if (mat->name() == "IBLMat")
     {
@@ -1745,7 +1984,12 @@ void SLGLProgramGenerated::buildProgramCodePS(SLMaterial* mat, bool isDrawProg)
            _shaders[1]->type() == ST_fragment);
 
     if (isDrawProg)
-        buildPerPixParticle(mat);
+    {
+        if (renderInstanced)
+            buildPerPixParticleInstanced(mat);
+        else
+            buildPerPixParticle(mat);
+    }
     else
         buildPerPixParticleUpdate(mat);
 }
@@ -1775,6 +2019,9 @@ void SLGLProgramGenerated::buildPerPixCook(SLMaterial* mat, SLVLight* lights)
     bool uv1  = mat->usesUVIndex(1);
     bool sky  = mat->skybox() != nullptr;
 
+    // Check if the shader has to support skinning
+    bool skinning = mat->supportsGPUSkinning();
+
     // Assemble vertex shader code
     string vertCode;
     vertCode += shaderHeader((int)lights->size());
@@ -1783,9 +2030,11 @@ void SLGLProgramGenerated::buildPerPixCook(SLMaterial* mat, SLVLight* lights)
     vertCode += vertInput_a_pn;
     if (uv0) vertCode += vertInput_a_uv0;
     if (Nm) vertCode += vertInput_a_tangent;
+    if (skinning) vertCode += vertInput_a_skinning;
     vertCode += vertInput_u_matrices_all;
     // if (sky) vertCode += vertInput_u_matrix_invMv;
     if (Nm) vertCode += vertInput_u_lightNm;
+    if (skinning) vertCode += vertInput_u_skinning;
 
     // Vertex shader outputs
     vertCode += vertOutput_v_P_VS;
@@ -1797,6 +2046,7 @@ void SLGLProgramGenerated::buildPerPixCook(SLMaterial* mat, SLVLight* lights)
 
     // Vertex shader main loop
     vertCode += vertMain_Begin;
+    if (skinning) vertCode += Nm ? vertMain_skinning_Nm : vertMain_skinning;
     vertCode += vertMain_v_P_VS;
     if (Sm) vertCode += vertMain_v_P_WS_Sm;
     vertCode += vertMain_v_N_VS;
@@ -1805,6 +2055,11 @@ void SLGLProgramGenerated::buildPerPixCook(SLMaterial* mat, SLVLight* lights)
     if (Nm) vertCode += vertMain_TBN_Nm;
     vertCode += vertMain_EndAll;
 
+    // Vertex shader variables
+    setVariable(vertCode, "localPosition", skinning ? "skinnedPosition" : "a_position");
+    setVariable(vertCode, "localNormal", skinning ? "skinnedNormal" : "a_normal");
+    if (Nm) setVariable(vertCode, "localTangent", skinning ? "skinnedTangent" : "a_tangent");
+
     addCodeToShader(_shaders[0], vertCode, _name + ".vert");
 
     // Assemble fragment shader code
@@ -1889,6 +2144,9 @@ void SLGLProgramGenerated::buildPerPixBlinn(SLMaterial* mat, SLVLight* lights)
     bool uv0 = mat->usesUVIndex(0);
     bool uv1 = mat->usesUVIndex(1);
 
+    // Check if the shader has to support skinning
+    bool skinning = mat->supportsGPUSkinning();
+
     // Assemble vertex shader code
     string vertCode;
     vertCode += shaderHeader((int)lights->size());
@@ -1898,8 +2156,10 @@ void SLGLProgramGenerated::buildPerPixBlinn(SLMaterial* mat, SLVLight* lights)
     if (uv0) vertCode += vertInput_a_uv0;
     if (uv1) vertCode += vertInput_a_uv1;
     if (Nm) vertCode += vertInput_a_tangent;
+    if (skinning) vertCode += vertInput_a_skinning;
     vertCode += vertInput_u_matrices_all;
     if (Nm) vertCode += vertInput_u_lightNm;
+    if (skinning) vertCode += vertInput_u_skinning;
 
     // Vertex shader outputs
     vertCode += vertOutput_v_P_VS;
@@ -1911,6 +2171,7 @@ void SLGLProgramGenerated::buildPerPixBlinn(SLMaterial* mat, SLVLight* lights)
 
     // Vertex shader main loop
     vertCode += vertMain_Begin;
+    if (skinning) vertCode += Nm ? vertMain_skinning_Nm : vertMain_skinning;
     vertCode += vertMain_v_P_VS;
     if (Sm) vertCode += vertMain_v_P_WS_Sm;
     vertCode += vertMain_v_N_VS;
@@ -1919,6 +2180,11 @@ void SLGLProgramGenerated::buildPerPixBlinn(SLMaterial* mat, SLVLight* lights)
     if (Nm) vertCode += vertMain_TBN_Nm;
     vertCode += vertMain_EndAll;
 
+    // Vertex shader variables
+    setVariable(vertCode, "localPosition", skinning ? "skinnedPosition" : "a_position");
+    setVariable(vertCode, "localNormal", skinning ? "skinnedNormal" : "a_normal");
+    if (Nm) setVariable(vertCode, "localTangent", skinning ? "skinnedTangent" : "a_tangent");
+
     addCodeToShader(_shaders[0], vertCode, _name + ".vert");
 
     // Assemble fragment shader code
@@ -1974,6 +2240,138 @@ void SLGLProgramGenerated::buildPerPixBlinn(SLMaterial* mat, SLVLight* lights)
     addCodeToShader(_shaders[1], fragCode, _name + ".frag");
 }
 //-----------------------------------------------------------------------------
+void SLGLProgramGenerated::buildPerPixParticleInstanced(SLMaterial* mat)
+{
+    assert(_shaders.size() == 2 &&
+           _shaders[0]->type() == ST_vertex &&
+           _shaders[1]->type() == ST_fragment);
+
+    // Check what textures the material has
+    bool  Dm            = mat->hasTextureType(TT_diffuse);
+    GLint billboardType = mat->ps()->billboardType();      // Billboard type (0 -> default; 1 -> vertical billboard, 2 -> horizontal billboard)
+    bool  rot           = mat->ps()->doRotation();         // Rotation
+    bool  AlOvLi        = mat->ps()->doAlphaOverLT();      // Alpha over life
+    bool  Co            = mat->ps()->doColor();            // Color over life
+    bool  CoOvLi        = mat->ps()->doColorOverLT();      // Color over life
+    bool  AlOvLiCu      = mat->ps()->doAlphaOverLTCurve(); // Alpha over life curve
+    bool  SiOvLi        = mat->ps()->doSizeOverLT();       // Size over life
+    bool  SiOvLiCu      = mat->ps()->doSizeOverLTCurve();  // Size over life curve
+    bool  FlBoTex       = mat->ps()->doFlipBookTexture();  // Flipbook texture
+
+    //////////////////////////////
+    // Assemble vertex shader code
+    //////////////////////////////
+
+    string vertCode;
+    vertCode += shaderHeader();
+
+    // Vertex shader inputs
+    vertCode += vertInput_PS_a_positionP;
+    vertCode += vertInput_PS_a_p; //position
+    vertCode += vertInput_PS_a_st; // start time
+    if (rot) vertCode += vertInput_PS_a_r; //rotation as float
+    if (FlBoTex)
+    {
+        vertCode += vertInput_PS_u_col;
+        vertCode += vertInput_PS_u_row;
+        vertCode += vertInput_PS_a_texNum; // per particle texture number
+    }
+
+    // Vertex shader uniforms
+    vertCode += vertInput_PS_u_ScaRa;
+    vertCode += vertInput_u_matrix_p;
+    vertCode += vertInput_PS_u_time;
+
+    if (billboardType == BT_Vertical)
+        vertCode += vertInput_u_matrix_vertBillboard;
+
+    vertCode += vertInput_u_matrix_vOmv;
+
+    if (AlOvLi && AlOvLiCu) vertCode += vertInput_PS_u_al_bernstein_alpha;
+    if (SiOvLi && SiOvLiCu) vertCode += vertInput_PS_u_al_bernstein_size;
+
+    // Vertex shader outputs
+    vertCode += vertOutput_PS_instanced_transparency;
+    vertCode += vertOutput_PS_v_tC;
+
+    if (CoOvLi) vertCode += vertOutput_PS_age;
+
+    // Vertex shader main loop
+    vertCode += vertMain_instanced_Begin;
+    vertCode += vertMain_PS_v_a;
+    if (FlBoTex)
+        vertCode += vertMain_PS_v_tC_flipbook;
+    else
+        vertCode += vertMain_PS_v_tC;
+
+    if (AlOvLi)
+        vertCode += vertMain_PS_instanced_v_t_begin;
+    else
+        vertCode += vertMain_PS_instanced_v_t_default;
+    if (AlOvLi) vertCode += AlOvLiCu ? vertMain_PS_instanced_v_t_curve : vertMain_PS_instanced_v_t_linear;
+    if (AlOvLi) vertCode += vertMain_PS_v_t_end;
+    vertCode += vertMain_PS_instanced_scale;
+    if (SiOvLi) vertCode += vertMain_PS_instanced_v_s;
+    if (SiOvLi && SiOvLiCu) vertCode += vertMain_PS_instanced_v_s_curve;
+    if (SiOvLi) vertCode += vertMain_PS_instanced_v_sS;
+    if (rot) vertCode += vertMain_PS_instanced_rotate;
+    if (CoOvLi) vertCode += vertMain_PS_v_age;
+
+    if (billboardType == BT_Vertical || billboardType == BT_Horizontal)
+        vertCode += vertMain_PS_instanced_EndAll_VertBillboard;
+    else
+        vertCode += vertMain_PS_instanced_EndAll;
+
+    addCodeToShader(_shaders[0], vertCode, _name + ".vert");
+
+    ////////////////////////////////
+    // Assemble fragment shader code
+    ////////////////////////////////
+
+    string fragCode;
+    fragCode += shaderHeader();
+
+    // Fragment shader inputs
+    fragCode += fragInput_PS_v_tC;
+    if (Co && !CoOvLi) fragCode += fragInput_PS_u_c;
+    if (CoOvLi)
+    {   fragCode += fragInput_PS_u_tTL;
+        fragCode += fragInput_PS_age;
+        fragCode += fragInput_PS_u_colorOvLF;
+    }
+    fragCode += fragInput_PS_instanced_transparency;
+
+    // Fragment shader uniforms
+    if (Dm) fragCode += fragInput_u_matTexDm;
+    fragCode += fragInput_PS_u_overG;
+    fragCode += fragInput_PS_u_wireFrame;
+
+    // Fragment shader outputs
+    fragCode += fragOutputs_o_fragColor;
+
+    if (CoOvLi) fragCode += fragFunction_PS_ColorOverLT;
+
+    // Fragment shader main loop
+    fragCode += fragMain_PS_begin;
+
+
+    if (Co && !CoOvLi) fragCode += fragMain_PS_instanced_c;
+
+    if (CoOvLi) fragCode += fragMain_PS_instanced_v_doColorOverLT;
+
+    if (Co || CoOvLi)
+    {
+        fragCode += fragMain_PS_instanced_transparency;
+        fragCode += fragMain_instanced_PS_end;
+    }
+    else
+        fragCode += fragMain_PS_instanced_withoutColor;
+
+    fragCode += fragMain_PS_endAll;
+
+    addCodeToShader(_shaders[1], fragCode, _name + ".frag");
+}
+//-----------------------------------------------------------------------------
 void SLGLProgramGenerated::buildPerPixParticle(SLMaterial* mat)
 {
     assert(_shaders.size() > 2 &&
@@ -2032,6 +2430,7 @@ void SLGLProgramGenerated::buildPerPixParticle(SLMaterial* mat)
         vertCode += vertMain_PS_v_t_begin;
     else
         vertCode += vertMain_PS_v_t_default;
+
     if (AlOvLi) vertCode += AlOvLiCu ? vertMain_PS_v_t_curve : vertMain_PS_v_t_linear;
     if (AlOvLi) vertCode += vertMain_PS_v_t_end;
     if (rot) vertCode += vertMain_PS_v_r;
@@ -2120,6 +2519,7 @@ void SLGLProgramGenerated::buildPerPixParticle(SLMaterial* mat)
     fragCode += fragOutputs_o_fragColor;
 
     // Fragment shader main loop
+    fragCode += fragMain_PS_begin;
     fragCode += Co ? fragMain_PS : fragMain_PS_withoutColor;
     fragCode += fragMain_PS_endAll;
 
@@ -2229,6 +2629,11 @@ void SLGLProgramGenerated::buildPerPixVideoBkgdSm(SLVLight* lights)
     vertCode += vertMain_v_P_WS_Sm;
     vertCode += vertMain_v_N_VS;
     vertCode += vertMain_EndAll;
+
+    // Vertex shader variables
+    setVariable(vertCode, "localPosition", "a_position");
+    setVariable(vertCode, "localNormal", "a_normal");
+
     addCodeToShader(_shaders[0], vertCode, _name + ".vert");
 
     // Assemble fragment shader code
@@ -2618,6 +3023,23 @@ void SLGLProgramGenerated::addCodeToShader(SLGLShader*   shader,
     shader->file(generatedShaderPath + name);
 }
 //-----------------------------------------------------------------------------
+//! Sets a variable in the shader code.
+/*! A variable is specified in templates like this: ${variableName}.
+ */
+void SLGLProgramGenerated::setVariable(std::string&       code,
+                                       const std::string& name,
+                                       const std::string& value)
+{
+    std::string placeholder = "${" + name + "}";
+
+    std::string::size_type pos = 0;
+    while ((pos = code.find(placeholder)) != std::string::npos)
+    {
+        code.replace(pos, placeholder.size(), value);
+        pos += value.size();
+    }
+}
+//-----------------------------------------------------------------------------
 //! Adds shader header code
 string SLGLProgramGenerated::shaderHeader(int numLights)
 
diff --git a/modules/sl/source/gl/SLGLProgramGenerated.h b/modules/sl/source/gl/SLGLProgramGenerated.h
index 93ebc1da..39738407 100644
--- a/modules/sl/source/gl/SLGLProgramGenerated.h
+++ b/modules/sl/source/gl/SLGLProgramGenerated.h
@@ -81,7 +81,15 @@ class SLGLProgramGenerated : public SLGLProgram
                     geomShader,
                     programName)
     {
-        buildProgramCodePS(mat, isDrawProg);
+
+        if (geomShader != "")
+        {
+            buildProgramCodePS(mat, isDrawProg, false);
+        }
+        else
+        {
+            buildProgramCodePS(mat, isDrawProg, true);
+        }
     }
 
     static bool lightsDoShadowMapping(SLVLight* lights);
@@ -90,9 +98,10 @@ class SLGLProgramGenerated : public SLGLProgram
                                  string&     programName);
     static void buildProgramNamePS(SLMaterial* mat,
                                    string&     programName,
-                                   bool        isDrawProg);
+                                   bool        isDrawProg,
+                                   bool        renderInstanced);
 
-    void buildProgramCodePS(SLMaterial* mat, bool isDrawProg);
+    void buildProgramCodePS(SLMaterial* mat, bool isDrawProg, bool renderInstanced = false);
     void buildProgramCode(SLMaterial* mat,
                           SLVLight*   lights);
     void beginShader(SLCamera*   cam,
@@ -104,6 +113,7 @@ class SLGLProgramGenerated : public SLGLProgram
     void buildPerPixCook(SLMaterial* mat, SLVLight* lights);
     void buildPerPixBlinn(SLMaterial* mat, SLVLight* lights);
     void buildPerPixParticle(SLMaterial* mat);
+    void buildPerPixParticleInstanced(SLMaterial* mat);
     void buildPerPixParticleUpdate(SLMaterial* mat);
 
     // Video background shader builder functions
@@ -118,6 +128,10 @@ class SLGLProgramGenerated : public SLGLProgram
     static void   addCodeToShader(SLGLShader*   shader,
                                   const string& code,
                                   const string& name);
+    static void   setVariable(std::string&       code,
+                              const std::string& name,
+                              const std::string& value);
+
     static string generatedShaderPath; //! Path to write out generated shaders
 };
 //-----------------------------------------------------------------------------
diff --git a/modules/sl/source/gl/SLGLShader.cpp b/modules/sl/source/gl/SLGLShader.cpp
index eaa87438..7602a311 100644
--- a/modules/sl/source/gl/SLGLShader.cpp
+++ b/modules/sl/source/gl/SLGLShader.cpp
@@ -149,6 +149,7 @@ SLbool SLGLShader::createAndCompile(SLVLight* lights)
         SLint     lineNum = 1;
         for (string& line : lines)
             SL_LOG("%4d: %s", lineNum++, line.c_str());
+        SL_LOG("\n");
         return false;
     }
 
diff --git a/modules/sl/source/gl/SLGLState.cpp b/modules/sl/source/gl/SLGLState.cpp
index 724bcdcd..d50cb31f 100644
--- a/modules/sl/source/gl/SLGLState.cpp
+++ b/modules/sl/source/gl/SLGLState.cpp
@@ -409,10 +409,10 @@ void SLGLState::useProgram(SLuint progID)
  */
 void SLGLState::bindTexture(SLenum target, SLuint textureID)
 {
-    // (luc) If there we call glActiveTexture and glBindTexture from outside,
+    // (luc) If we call glActiveTexture and glBindTexture from outside,
     // This will lead to problems as the global state in SLGLState will not be
     // equivalent to the OpenGL state.
-    // We should solve this by querying opengl for the last binded texture.
+    // We should solve this by querying opengl for the last bound texture.
     // glGetIntegeriv(GL_ACTIVE_TEXTURE, active_texture)
     // glGetIntegeriv(GL_TEXTURE_BINDING_2D, textureID)
 
diff --git a/modules/sl/source/gl/SLGLTexture.h b/modules/sl/source/gl/SLGLTexture.h
index a9542069..546af1e6 100644
--- a/modules/sl/source/gl/SLGLTexture.h
+++ b/modules/sl/source/gl/SLGLTexture.h
@@ -220,8 +220,8 @@ class SLGLTexture : public SLObject
     SLuint        depth() { return _depth; }
     SLbyte        uvIndex() { return _uvIndex; }
     SLint         bytesPerPixel() { return _bytesPerPixel; }
-    SLint         bytesOnGPU() { return _bytesOnGPU; }
-    SLint         bytesInFile() { return _bytesInFile; }
+    SLint         bytesOnGPU() { return (SLint)_bytesOnGPU; }
+    SLint         bytesInFile() { return (SLint)_bytesInFile; }
     CVVImage&     images() { return _images; }
     SLenum        target() const { return _target; }
     SLuint        texID() const { return _texID; }
diff --git a/modules/sl/source/gl/SLGLVertexArray.cpp b/modules/sl/source/gl/SLGLVertexArray.cpp
index 9738308f..79e4d8c2 100644
--- a/modules/sl/source/gl/SLGLVertexArray.cpp
+++ b/modules/sl/source/gl/SLGLVertexArray.cpp
@@ -29,6 +29,7 @@ SLGLVertexArray::SLGLVertexArray()
     _numVertices        = 0;
     _indexDataElements  = nullptr;
     _indexDataEdges     = nullptr;
+    _externalVBOf       = nullptr;
 }
 //-----------------------------------------------------------------------------
 /*! Deletes the OpenGL objects for the vertex array and the vertex buffer.
@@ -86,6 +87,14 @@ void SLGLVertexArray::setAttrib(SLGLAttributeType type,
 
     _VBOf.attribs().push_back(va);
 }
+
+//-----------------------------------------------------------------------------
+ void SLGLVertexArray::setExternalVBO(SLGLVertexBuffer* vbo, SLuint divisor)
+ {
+    _externalVBOf = vbo;
+    _externalDivisor = divisor;
+ }
+
 //-----------------------------------------------------------------------------
 /*! Defines the vertex indices for the element drawing. Without indices vertex
 array can only be drawn with SLGLVertexArray::drawArrayAs.
@@ -140,6 +149,7 @@ void SLGLVertexArray::updateAttrib(SLGLAttributeType type,
     glBindVertexArray(0);
     GET_GL_ERROR;
 }
+
 //-----------------------------------------------------------------------------
 /*! Generates the OpenGL objects for the vertex array and the vertex buffer
  object. If the input data is an interleaved array (all attribute data pointer
@@ -174,7 +184,8 @@ void SLGLVertexArray::updateAttrib(SLGLAttributeType type,
 */
 void SLGLVertexArray::generate(SLuint          numVertices,
                                SLGLBufferUsage usage,
-                               SLbool          outputInterleaved)
+                               SLbool          outputInterleaved,
+                               SLuint          divisor)
 {
     assert(numVertices);
 
@@ -193,7 +204,15 @@ void SLGLVertexArray::generate(SLuint          numVertices,
 
     // Generate the vertex buffer object for float attributes
     if (_VBOf.attribs().size())
+    {
         _VBOf.generate(numVertices, usage, outputInterleaved);
+        _VBOf.bindAndEnableAttrib(divisor);
+    }
+
+    if (_externalVBOf != nullptr)
+    {
+        _externalVBOf->bindAndEnableAttrib(_externalDivisor);
+    }
 
     /////////////////////////////////////////////////////////////////
     // Create Element Array Buffer for Indices for elements and edges
@@ -240,7 +259,8 @@ void SLGLVertexArray::generate(SLuint          numVertices,
 /*! Same as generate but with transform feedback */
 void SLGLVertexArray::generateTF(SLuint          numVertices,
                                  SLGLBufferUsage usage,
-                                 SLbool          outputInterleaved)
+                                 SLbool          outputInterleaved,
+                                 SLuint          divisor)
 {
     assert(numVertices);
 
@@ -262,7 +282,15 @@ void SLGLVertexArray::generateTF(SLuint          numVertices,
 
     // Generate the vertex buffer object for float attributes
     if (_VBOf.attribs().size())
+    {
         _VBOf.generate(numVertices, usage, outputInterleaved);
+        _VBOf.bindAndEnableAttrib(divisor);
+    }
+
+    if (_externalVBOf != nullptr)
+    {
+        _externalVBOf->bindAndEnableAttrib(_externalDivisor);
+    }
 
     ///////////////////////////////
     // Bind transform feedback
@@ -430,6 +458,52 @@ void SLGLVertexArray::drawArrayAs(SLGLPrimitiveType primitiveType,
 
     GET_GL_ERROR;
 }
+
+void SLGLVertexArray::drawElementsInstanced(SLGLPrimitiveType primitiveType,
+                                            SLsizei           countInstance,
+                                            SLuint            numIndexes,
+                                            SLuint            indexOffset
+                                            )
+{
+    assert(_numIndicesElements && _idVBOIndices && "No index VBO generated for VAO");
+
+    // From OpenGL 3.0 on we have the OpenGL Vertex Arrays
+    // Binding the VAO saves all the commands after the else (per draw call!)
+    glBindVertexArray(_vaoID);
+    GET_GL_ERROR;
+
+    // Do the draw call with indices
+    if (numIndexes == 0)
+        numIndexes = _numIndicesElements;
+
+    SLuint indexTypeSize = SLGLVertexBuffer::sizeOfType(_indexDataType);
+
+    ////////////////////////////////////////////////////////
+    glDrawElementsInstanced(primitiveType, (SLsizei)numIndexes ,_indexDataType, (void*)(size_t)(indexOffset * (SLuint)indexTypeSize), countInstance);
+    //glDrawElements(primitiveType, (SLsizei)numIndexes ,_indexDataType, (void*)(size_t)(indexOffset * (SLuint)indexTypeSize));
+    ////////////////////////////////////////////////////////
+
+    GET_GL_ERROR;
+    // Update statistics
+    totalDrawCalls++;
+    switch (primitiveType)
+    {
+        case PT_triangles:
+            totalPrimitivesRendered += (numIndexes / 3);
+            break;
+        case PT_lines:
+            totalPrimitivesRendered += (numIndexes / 2);
+            break;
+        case PT_points:
+            totalPrimitivesRendered += numIndexes;
+            break;
+        default: break;
+    }
+    glBindVertexArray(0);
+
+    GET_GL_ERROR;
+}
+
 //-----------------------------------------------------------------------------
 /*! Draws the hard edges with the specified color.
  The VAO has no or one active index buffer. For drawArrayAs no indices are needed.
diff --git a/modules/sl/source/gl/SLGLVertexArray.h b/modules/sl/source/gl/SLGLVertexArray.h
index 4b902f54..852e9017 100644
--- a/modules/sl/source/gl/SLGLVertexArray.h
+++ b/modules/sl/source/gl/SLGLVertexArray.h
@@ -90,6 +90,11 @@ class SLGLVertexArray
                    SLint             location,
                    SLVVec4f*         data) { setAttrib(type, 4, location, &data->operator[](0)); }
 
+    //! Adds a vertex attribute with vector of SLVec4i
+    void setAttrib(SLGLAttributeType type,
+                   SLint             location,
+                   SLVVec4i*         data) { setAttrib(type, 4, location, &data->operator[](0), BT_int); }
+
     //! Adds the index array for indexed element drawing
     void setIndices(SLuint         numIndicesElements,
                     SLGLBufferType indexDataType,
@@ -130,6 +135,9 @@ class SLGLVertexArray
                    indicesEdges && indicesEdges->size() ? (void*)&indicesEdges->operator[](0) : nullptr);
     }
 
+    //! Attach a VBO that has been created outside of this VAO
+    void setExternalVBO(SLGLVertexBuffer *vbo, SLuint divisor = 0);
+
     //! Updates a specific vertex attribute in the VBO
     void updateAttrib(SLGLAttributeType type,
                       SLint             elementSize,
@@ -158,12 +166,14 @@ class SLGLVertexArray
     //! Generates the VA & VB objects for a NO. of vertices
     void generate(SLuint          numVertices,
                   SLGLBufferUsage usage             = BU_static,
-                  SLbool          outputInterleaved = true);
+                  SLbool          outputInterleaved = true,
+                  SLuint          divisor           = 0);
 
     //! Generates the VA & VB & TF objects
     void generateTF(SLuint          numVertices,
                     SLGLBufferUsage usage             = BU_static,
-                    SLbool          outputInterleaved = true);
+                    SLbool          outputInterleaved = true,
+                    SLuint          divisor           = 0);
 
     //! Begin transform feedback
     void beginTF(SLuint tfoID);
@@ -181,6 +191,12 @@ class SLGLVertexArray
                      SLint             firstVertex   = 0,
                      SLsizei           countVertices = 0);
 
+    //! Draws the VAO as an array with instance primitive type
+    void drawElementsInstanced(SLGLPrimitiveType primitiveType,
+                               SLsizei           countInstance = 0,
+                               SLuint            numIndexes = 0,
+                               SLuint            indexOffset = 0);
+
     //! Draws the hard edges of the VAO with the edge indices
     void drawEdges(SLCol4f color, SLfloat lineWidth = 1.0f);
 
@@ -188,22 +204,26 @@ class SLGLVertexArray
     SLuint numVertices() const { return _numVertices; }
     SLuint numIndicesElements() const { return _numIndicesElements; }
     SLuint numIndicesEdges() const { return _numIndicesEdges; }
+    SLGLVertexBuffer* vbo() { return &_VBOf; }
 
     // Some statistics
     static SLuint totalDrawCalls;          //! static total no. of draw calls
     static SLuint totalPrimitivesRendered; //! static total no. of primitives rendered
 
 protected:
-    SLuint           _vaoID;              //! OpenGL id of vertex array object
-    SLuint           _tfoID;              //! OpenGL id of transform feedback object
-    SLuint           _numVertices;        //! NO. of vertices in array
-    SLGLVertexBuffer _VBOf;               //! Vertex buffer object for float attributes
-    SLuint           _idVBOIndices;       //! OpenGL id of index vbo
-    SLuint           _numIndicesElements; //! NO. of vertex indices in array for triangles, lines or points
-    void*            _indexDataElements;  //! Pointer to index data for elements
-    SLuint           _numIndicesEdges;    //! NO. of vertex indices in array for hard edges
-    void*            _indexDataEdges;     //! Pointer to index data for hard edges
-    SLGLBufferType   _indexDataType;      //! index data type (ubyte, ushort, uint)
+    SLuint            _instances;          //! Number of instances of drawing
+    SLuint            _vaoID;              //! OpenGL id of vertex array object
+    SLuint            _tfoID;              //! OpenGL id of transform feedback object
+    SLuint            _numVertices;        //! NO. of vertices in array
+    SLGLVertexBuffer  _VBOf;               //! Vertex buffer object for float attributes
+    SLGLVertexBuffer* _externalVBOf;       //! Vertex buffer object that has beed created outside of this VAO
+    SLuint            _externalDivisor;    //! VBO attrib divisor for the external VBO
+    SLuint            _idVBOIndices;       //! OpenGL id of index vbo
+    SLuint            _numIndicesElements; //! NO. of vertex indices in array for triangles, lines or points
+    void*             _indexDataElements;  //! Pointer to index data for elements
+    SLuint            _numIndicesEdges;    //! NO. of vertex indices in array for hard edges
+    void*             _indexDataEdges;     //! Pointer to index data for hard edges
+    SLGLBufferType    _indexDataType;      //! index data type (ubyte, ushort, uint)
 };
 //-----------------------------------------------------------------------------
 
diff --git a/modules/sl/source/gl/SLGLVertexBuffer.cpp b/modules/sl/source/gl/SLGLVertexBuffer.cpp
index 935f776d..7b0fc4e3 100644
--- a/modules/sl/source/gl/SLGLVertexBuffer.cpp
+++ b/modules/sl/source/gl/SLGLVertexBuffer.cpp
@@ -21,7 +21,7 @@ SLGLVertexBuffer::SLGLVertexBuffer()
     _id                = 0;
     _numVertices       = 0;
     _sizeBytes         = 0;
-    _outputInterleaved = false;
+    _outputIsInterleaved = false;
     _usage             = BU_stream;
 }
 //-----------------------------------------------------------------------------
@@ -70,7 +70,7 @@ void SLGLVertexBuffer::updateAttrib(SLGLAttributeType type,
         SL_EXIT_MSG("Attribute type does not exist in VBO.");
     if (_attribs[(SLuint)index].elementSize != elementSize)
         SL_EXIT_MSG("Attribute element size differs.");
-    if (_outputInterleaved)
+    if (_outputIsInterleaved)
         SL_EXIT_MSG("Interleaved buffers can't be updated.");
 
     // Generate the vertex buffer object if there is none
@@ -119,6 +119,7 @@ sequential vertex buffer.\n\n
 \n           |<---------- strideBytes=32 ----------->|
 
*/ +/* void SLGLVertexBuffer::generate(SLuint numVertices, SLGLBufferUsage usage, SLbool outputInterleaved) @@ -195,43 +196,143 @@ void SLGLVertexBuffer::generate(SLuint numVertices, if (inputIsInterleaved) { - for (auto a : _attribs) + // generate the interleaved VBO buffer on the GPU + glBufferData(GL_ARRAY_BUFFER, _sizeBytes, _attribs[0].dataPointer, _usage); + } + else // input is in separate attribute data block + { + if (_outputInterleaved) // Copy attribute data interleaved { - if (a.location > -1) - { // Sets the vertex attribute data pointer to its corresponding GLSL variable - if (a.dataType == BT_uint) + SLVuchar data; + data.resize(_sizeBytes); + + for (auto a : _attribs) + { + SLuint elementSizeBytes = (SLuint)a.elementSize * sizeOfType(a.dataType); + + // Copy attributes interleaved + for (SLuint v = 0; v < _numVertices; ++v) { - glVertexAttribIPointer((SLuint)a.location, - a.elementSize, - a.dataType, - (SLint)_strideBytes, - (void*)(size_t)a.offsetBytes); + SLuint iDst = v * _strideBytes + a.offsetBytes; + SLuint iSrc = v * elementSizeBytes; + for (SLuint b = 0; b < elementSizeBytes; ++b) + data[iDst + b] = ((SLuchar*)a.dataPointer)[iSrc + b]; } - else + } + + // generate the interleaved VBO buffer on the GPU + glBufferData(GL_ARRAY_BUFFER, _sizeBytes, &data[0], _usage); + } + else // copy attributes buffers sequentially + { + // allocate the VBO buffer on the GPU + glBufferData(GL_ARRAY_BUFFER, _sizeBytes, nullptr, _usage); + + for (auto a : _attribs) + { + if (a.location > -1) { - glVertexAttribPointer((SLuint)a.location, - a.elementSize, - a.dataType, - GL_FALSE, - (SLint)_strideBytes, - (void*)(size_t)a.offsetBytes); + // Copies the attributes data at the right offset into the VBO + glBufferSubData(GL_ARRAY_BUFFER, + a.offsetBytes, + a.bufferSizeBytes, + a.dataPointer); } + } + } + } - // Tell the attribute to be an array attribute instead of a state variable - glEnableVertexAttribArray((SLuint)a.location); + totalBufferCount++; + totalBufferSize += _sizeBytes; + GET_GL_ERROR; +} +*/ + +void SLGLVertexBuffer::generate(SLuint numVertices, + SLGLBufferUsage usage, + SLbool outputInterleaved) +{ + assert(numVertices); + + // if buffers exist delete them first + deleteGL(); + + _numVertices = numVertices; + _usage = usage; + _outputIsInterleaved = outputInterleaved; + + // Generate the vertex buffer object + if (_attribs.size()) + { + glGenBuffers(1, &_id); + glBindBuffer(GL_ARRAY_BUFFER, _id); + } + + // Check first if all attribute data pointer point to the same interleaved data + _inputIsInterleaved = false; + if (_attribs.size() > 1) + { + _inputIsInterleaved = true; + for (auto a : _attribs) + { + if (a.dataPointer != _attribs[0].dataPointer) + { + _inputIsInterleaved = false; + break; } } + } + + /////////////////////////////////////////////////////// + // Calculate total VBO size & attribute stride & offset + /////////////////////////////////////////////////////// + + _sizeBytes = 0; + _strideBytes = 0; + + if (_inputIsInterleaved) + { + _outputIsInterleaved = true; + for (SLuint i = 0; i < _attribs.size(); ++i) + { + SLuint elementSizeBytes = (SLuint)_attribs[i].elementSize * sizeOfType(_attribs[i].dataType); + _attribs[i].offsetBytes = _strideBytes; + _attribs[i].bufferSizeBytes = elementSizeBytes * _numVertices; + _sizeBytes += _attribs[i].bufferSizeBytes; + _strideBytes += elementSizeBytes; + } + } + else // input is in separate attribute data blocks + { + for (SLuint i = 0; i < _attribs.size(); ++i) + { + SLuint elementSizeBytes = (SLuint)_attribs[i].elementSize * sizeOfType(_attribs[i].dataType); + if (_outputIsInterleaved) + _attribs[i].offsetBytes = _strideBytes; + else + _attribs[i].offsetBytes = _sizeBytes; + _attribs[i].bufferSizeBytes = elementSizeBytes * _numVertices; + _sizeBytes += _attribs[i].bufferSizeBytes; + if (_outputIsInterleaved) _strideBytes += elementSizeBytes; + } + } + + ////////////////////////////// + // Generate VBO for Attributes + ////////////////////////////// + + if (_inputIsInterleaved) + { // generate the interleaved VBO buffer on the GPU glBufferData(GL_ARRAY_BUFFER, _sizeBytes, _attribs[0].dataPointer, _usage); } else // input is in separate attribute data block { - if (_outputInterleaved) // Copy attribute data interleaved + if (_outputIsInterleaved) // Copy attribute data interleaved { SLVuchar data; data.resize(_sizeBytes); - for (auto a : _attribs) { SLuint elementSizeBytes = (SLuint)a.elementSize * sizeOfType(a.dataType); @@ -245,33 +346,9 @@ void SLGLVertexBuffer::generate(SLuint numVertices, data[iDst + b] = ((SLuchar*)a.dataPointer)[iSrc + b]; } - if (a.location > -1) - { // Sets the vertex attribute data pointer to its corresponding GLSL variable - if (a.dataType == BT_uint) - { - glVertexAttribIPointer((SLuint)a.location, - a.elementSize, - a.dataType, - (SLint)_strideBytes, - (void*)(size_t)a.offsetBytes); - } - else - { - glVertexAttribPointer((SLuint)a.location, - a.elementSize, - a.dataType, - GL_FALSE, - (SLint)_strideBytes, - (void*)(size_t)a.offsetBytes); - } - - // Tell the attribute to be an array attribute instead of a state variable - glEnableVertexAttribArray((SLuint)a.location); - } + // generate the interleaved VBO buffer on the GPU + glBufferData(GL_ARRAY_BUFFER, _sizeBytes, &data[0], _usage); } - - // generate the interleaved VBO buffer on the GPU - glBufferData(GL_ARRAY_BUFFER, _sizeBytes, &data[0], _usage); } else // copy attributes buffers sequentially { @@ -287,28 +364,6 @@ void SLGLVertexBuffer::generate(SLuint numVertices, a.offsetBytes, a.bufferSizeBytes, a.dataPointer); - - // Sets the vertex attribute data pointer to its corresponding GLSL variable - if (a.dataType == BT_uint) - { - glVertexAttribIPointer((SLuint)a.location, - a.elementSize, - a.dataType, - 0, - (void*)(size_t)a.offsetBytes); - } - else - { - glVertexAttribPointer((SLuint)a.location, - a.elementSize, - a.dataType, - GL_FALSE, - 0, - (void*)(size_t)a.offsetBytes); - } - - // Tell the attribute to be an array attribute instead of a state variable - glEnableVertexAttribArray((SLuint)a.location); } } } @@ -318,31 +373,79 @@ void SLGLVertexBuffer::generate(SLuint numVertices, totalBufferSize += _sizeBytes; GET_GL_ERROR; } + + //----------------------------------------------------------------------------- /*! This method is only used by SLGLVertexArray drawing methods for OpenGL contexts prior to 3.0 where vertex array objects did not exist. This is the additional overhead that had to be done per draw call. */ -void SLGLVertexBuffer::bindAndEnableAttrib() +void SLGLVertexBuffer::bindAndEnableAttrib(SLuint divisor) const { - if (_attribs.size()) + ////////////////////////////////////// + // Associate VBO to Attribute location + ////////////////////////////////////// + glBindBuffer(GL_ARRAY_BUFFER, _id); + if (_outputIsInterleaved) // Copy attribute data interleaved { - glBindBuffer(GL_ARRAY_BUFFER, _id); + for (auto a : _attribs) + { + if (a.location > -1) + { // Sets the vertex attribute data pointer to its corresponding GLSL variable + if (a.dataType == BT_int || a.dataType == BT_uint) + { + glVertexAttribIPointer((SLuint)a.location, + a.elementSize, + a.dataType, + (SLint)_strideBytes, + (void*)(size_t)a.offsetBytes); + } + else + { + glVertexAttribPointer((SLuint)a.location, + a.elementSize, + a.dataType, + GL_FALSE, + (SLint)_strideBytes, + (void*)(size_t)a.offsetBytes); + } + // Tell the attribute to be an array attribute instead of a state variable + glEnableVertexAttribArray((SLuint)a.location); + if (divisor > 0) + glVertexAttribDivisor((SLuint)a.location, divisor); + } + } + } + else // copy attributes buffers sequentially + { for (auto a : _attribs) { if (a.location > -1) { // Sets the vertex attribute data pointer to its corresponding GLSL variable - glVertexAttribPointer((SLuint)a.location, - a.elementSize, - a.dataType, - GL_FALSE, - (SLsizei)_strideBytes, - (void*)(size_t)a.offsetBytes); + if (a.dataType == BT_int || a.dataType == BT_uint) + { + glVertexAttribIPointer((SLuint)a.location, + a.elementSize, + a.dataType, + 0, + (void*)(size_t)a.offsetBytes); + } + else + { + glVertexAttribPointer((SLuint)a.location, + a.elementSize, + a.dataType, + GL_FALSE, + 0, + (void*)(size_t)a.offsetBytes); + } // Tell the attribute to be an array attribute instead of a state variable glEnableVertexAttribArray((SLuint)a.location); + if (divisor > 0) + glVertexAttribDivisor((SLuint)a.location, divisor); } } } @@ -367,10 +470,11 @@ SLuint SLGLVertexBuffer::sizeOfType(SLGLBufferType type) { switch (type) { - case BT_float: return sizeof(float); - case BT_ubyte: return sizeof(unsigned char); - case BT_ushort: return sizeof(unsigned short); - case BT_uint: return sizeof(unsigned int); + case BT_float: return sizeof(SLfloat); + case BT_int: return sizeof(SLint); + case BT_ubyte: return sizeof(SLuchar); + case BT_ushort: return sizeof(SLushort); + case BT_uint: return sizeof(SLint); default: SL_EXIT_MSG("Invalid buffer data type"); } return 0; diff --git a/modules/sl/source/gl/SLGLVertexBuffer.h b/modules/sl/source/gl/SLGLVertexBuffer.h index 7d6ec776..d2ae1292 100644 --- a/modules/sl/source/gl/SLGLVertexBuffer.h +++ b/modules/sl/source/gl/SLGLVertexBuffer.h @@ -87,7 +87,7 @@ class SLGLVertexBuffer SLbool outputInterleaved = true); //! Binds & enables the vertex attribute for OpenGL < 3.0 - void bindAndEnableAttrib(); + void bindAndEnableAttrib(SLuint divisor = 0) const; //! disables the vertex attribute for OpenGL < 3.0 void disableAttrib(); @@ -96,7 +96,7 @@ class SLGLVertexBuffer SLuint id() const { return _id; } SLuint size() const { return _id; } SLVVertexAttrib& attribs() { return _attribs; } - SLbool outputInterleaved() const { return _outputInterleaved; } + SLbool outputIsInterleaved() const { return _outputIsInterleaved; } // Setters @@ -111,7 +111,8 @@ class SLGLVertexBuffer SLuint _id; //! OpenGL id of vertex buffer object SLuint _numVertices; //! NO. of vertices in array SLVVertexAttrib _attribs; //! Vector of vertex attributes - SLbool _outputInterleaved; //! Flag if VBO should be generated interleaved + SLbool _outputIsInterleaved; //! Flag if VBO should be generated interleaved + SLbool _inputIsInterleaved; //! Flag if VBO should be generated interleaved SLuint _strideBytes; //! Distance for interleaved attributes in bytes SLuint _sizeBytes; //! Total size of float VBO in bytes SLGLBufferUsage _usage; //! buffer usage (static, dynamic or stream) diff --git a/modules/sl/source/input/SLAssimpImporter.cpp b/modules/sl/source/input/SLAssimpImporter.cpp index 80527a28..a05de729 100644 --- a/modules/sl/source/input/SLAssimpImporter.cpp +++ b/modules/sl/source/input/SLAssimpImporter.cpp @@ -691,6 +691,7 @@ SLMaterial* SLAssimpImporter::loadMaterial(SLAssetManager* am, // Create SLMaterial instance. It is also added to the SLScene::_materials vector SLMaterial* slMat = new SLMaterial(am, name.c_str()); + slMat->supportsGPUSkinning(true); // load all the textures for this aiMat and add it to the aiMat vector for (int tt = aiTextureType_NONE; tt <= aiTextureType_UNKNOWN; ++tt) @@ -916,7 +917,7 @@ SLGLTexture* SLAssimpImporter::loadTexture(SLAssetManager* assetMgr, minificationFilter, GL_LINEAR, texType); - texture->uvIndex(uvIndex); + texture->uvIndex((SLbyte)uvIndex); // if texture images get deleted after build you can't do ray tracing if (deleteTexImgAfterBuild) @@ -1273,7 +1274,7 @@ SLAnimation* SLAssimpImporter::loadAnimation(SLAnimManager& animManager, aiAnima // exit if we didn't load a skeleton but have animations for one if (!_skinnedMeshes.empty()) - assert(_skeleton != nullptr && "The skeleton wasn't impoted correctly."); + assert(_skeleton != nullptr && "The skeleton wasn't imported correctly."); // create the animation SLAnimation* result; diff --git a/modules/sl/source/mesh/SLMesh.cpp b/modules/sl/source/mesh/SLMesh.cpp index e2d0be05..7ebd30b7 100644 --- a/modules/sl/source/mesh/SLMesh.cpp +++ b/modules/sl/source/mesh/SLMesh.cpp @@ -379,7 +379,7 @@ Optionally you can draw the normals and/or the uniform grid voxels.

Please view also the full process of rendering one frame */ -void SLMesh::draw(SLSceneView* sv, SLNode* node) +void SLMesh::draw(SLSceneView* sv, SLNode* node, SLuint instances) { SLGLState* stateGL = SLGLState::instance(); @@ -446,6 +446,17 @@ void SLMesh::draw(SLSceneView* sv, SLNode* node) sp->uniformMatrix4fv("u_vMatrix", 1, (SLfloat*)&stateGL->viewMatrix); sp->uniformMatrix4fv("u_pMatrix", 1, (SLfloat*)&stateGL->projectionMatrix); + // Pass skeleton joint matrices to the shader program + if (_mat->supportsGPUSkinning()) + { + // Only perform skinning in the shader if we haven't performed CPU skinning and if there are joint IDs + SLbool skinningEnabled = !_isCPUSkinned && !Ji.empty(); + sp->uniform1i("u_skinningEnabled", skinningEnabled); + + if (skinningEnabled && !_jointMatrices.empty()) + sp->uniformMatrix4fv("u_jointMatrices", _jointMatrices.size(), (SLfloat*)&_jointMatrices[0]); + } + SLint locTM = sp->getUniformLocation("u_tMatrix"); if (locTM >= 0) { @@ -469,7 +480,10 @@ void SLMesh::draw(SLSceneView* sv, SLNode* node) _vao.drawArrayAs(PT_points); else { - _vao.drawElementsAs(primitiveType); + if (instances > 1) + _vao.drawElementsInstanced(primitiveType, instances); + else + _vao.drawElementsAs(primitiveType); if ((sv->drawBit(SL_DB_WITHEDGES) || node->drawBit(SL_DB_WITHEDGES)) && (!IE32.empty() || !IE16.empty())) @@ -601,7 +615,7 @@ void SLMesh::handleRectangleSelection(SLSceneView* sv, (SLfloat)vp.width, (SLfloat)vp.height); SLMat4f v_mvp = v * mvp; - set tempIselected; // Temp. vector for selected vertex indices + set tempIselected; // Temp. vector for selected vertex indices if (!cam->selectRect().isEmpty()) // Do rectangle Selection { @@ -739,6 +753,44 @@ void SLMesh::generateVAO(SLGLVertexArray& vao) } } + SLVVec4i jointIndicesData; // indices are passed to the shader as ivec4s + SLVVec4f jointWeightsData; // weights are passed to the shader as vec4s + + if (!Ji.empty() && _mat->supportsGPUSkinning()) + { + assert(Ji.size() == P.size()); + assert(Jw.size() == P.size()); + + jointIndicesData = SLVVec4i(P.size(), SLVec4i(0, 0, 0, 0)); + jointWeightsData = SLVVec4f(P.size(), SLVec4f(0.0f, 0.0f, 0.0f, 0.0f)); + + // create the vec4 of indices for all points + for (unsigned i = 0; i < P.size(); i++) + { + const SLVuchar& curIndices = Ji[i]; + assert(!curIndices.empty()); + + jointIndicesData[i] = SLVec4i(curIndices.size() >= 1 ? curIndices[0] : 0, + curIndices.size() >= 2 ? curIndices[1] : 0, + curIndices.size() >= 3 ? curIndices[2] : 0, + curIndices.size() >= 4 ? curIndices[3] : 0); + } + + // create the vec4 of weights for all points + for (unsigned i = 0; i < P.size(); i++) + { + const SLVfloat& curWeights = Jw[i]; + assert(curWeights.size() == Ji[i].size()); + + jointWeightsData[i] = SLVec4f(curWeights.size() >= 1 ? curWeights[0] : 0.0f, + curWeights.size() >= 2 ? curWeights[1] : 0.0f, + curWeights.size() >= 3 ? curWeights[2] : 0.0f, + curWeights.size() >= 4 ? curWeights[3] : 0.0f); + } + vao.setAttrib(AT_jointIndex, AT_jointIndex, &jointIndicesData); + vao.setAttrib(AT_jointWeight, AT_jointWeight, &jointWeightsData); + } + vao.generate((SLuint)P.size(), !Ji.empty() ? BU_stream : BU_static, Ji.empty()); @@ -855,8 +907,18 @@ SLbool SLMesh::hit(SLRay* ray, SLNode* node) return true; } + // Force the mesh to be skinned in software even if it would be normally skinned on the GPU. + // We need the results from the skinning on the CPU to perform the ray-triangle intersection. + if (_skeleton && _mat && _mat->supportsGPUSkinning() && !_isCPUSkinned) + transformSkin(true, [](SLMesh*) {}); + if (_accelStruct) + { + if (_accelStructIsOutOfDate) + updateAccelStruct(); + return _accelStruct->intersect(ray, node); + } else { // intersect against all faces SLbool wasHit = false; @@ -1495,8 +1557,12 @@ a weight and an index. After the transform the VBO have to be updated. This skinning process can also be done (a lot faster) on the GPU. This software skinning is also needed for ray or path tracing. */ -void SLMesh::transformSkin(const std::function& cbInformNodes) +void SLMesh::transformSkin(bool forceCPUSkinning, + const std::function& cbInformNodes) { + if (_isSelected) + forceCPUSkinning = true; + // create the secondary buffers for P and N once if (skinnedP.empty()) { @@ -1521,42 +1587,54 @@ void SLMesh::transformSkin(const std::function& cbInformNodes) // update the joint matrix array _skeleton->getJointMatrices(_jointMatrices); - // notify Parent Nodes to update AABB + // notify parent nodes to update AABB cbInformNodes(this); - // temporarily set finalP and finalN - _finalP = &skinnedP; - _finalN = &skinnedN; - // flag acceleration structure to be rebuilt _accelStructIsOutOfDate = true; - // iterate over all vertices and write to new buffers - for (SLulong i = 0; i < P.size(); ++i) + // remember if this node has been skinned on the CPU + _isCPUSkinned = forceCPUSkinning; + + // Perform software skinning if the material doesn't support CPU skinning or + // if the results of the skinning process are required somewehere else. + if (!_mat->supportsGPUSkinning() || forceCPUSkinning) { - skinnedP[i] = SLVec3f::ZERO; - if (!N.empty()) skinnedN[i] = SLVec3f::ZERO; + _finalP = &skinnedP; + _finalN = &skinnedN; - // accumulate final normal and positions - for (SLulong j = 0; j < Ji[i].size(); ++j) + // iterate over all vertices and write to new buffers + for (SLulong i = 0; i < P.size(); ++i) { - const SLMat4f& jm = _jointMatrices[Ji[i][j]]; - SLVec4f tempPos = SLVec4f(jm * P[i]); - skinnedP[i].x += tempPos.x * Jw[i][j]; - skinnedP[i].y += tempPos.y * Jw[i][j]; - skinnedP[i].z += tempPos.z * Jw[i][j]; + skinnedP[i] = SLVec3f::ZERO; + if (!N.empty()) skinnedN[i] = SLVec3f::ZERO; - if (!N.empty()) + // accumulate final normal and positions + for (SLulong j = 0; j < Ji[i].size(); ++j) { - // Build the 3x3 submatrix in GLSL 110 (= mat3 jt3 = mat3(jt)) - // for the normal transform that is the normally the inverse transpose. - // The inverse transpose can be ignored as long as we only have - // rotation and uniform scaling in the 3x3 submatrix. - SLMat3f jnm = jm.mat3(); - skinnedN[i] += jnm * N[i] * Jw[i][j]; + const SLMat4f& jm = _jointMatrices[Ji[i][j]]; + SLVec4f tempPos = SLVec4f(jm * P[i]); + skinnedP[i].x += tempPos.x * Jw[i][j]; + skinnedP[i].y += tempPos.y * Jw[i][j]; + skinnedP[i].z += tempPos.z * Jw[i][j]; + + if (!N.empty()) + { + // Build the 3x3 submatrix in GLSL 110 (= mat3 jt3 = mat3(jt)) + // for the normal transform that is the normally the inverse transpose. + // The inverse transpose can be ignored as long as we only have + // rotation and uniform scaling in the 3x3 submatrix. + SLMat3f jnm = jm.mat3(); + skinnedN[i] += jnm * N[i] * Jw[i][j]; + } } } } + else + { + _finalP = &P; + _finalN = &N; + } // update or create buffers if (_vao.vaoID()) diff --git a/modules/sl/source/mesh/SLMesh.h b/modules/sl/source/mesh/SLMesh.h index ff475e3f..635860cf 100644 --- a/modules/sl/source/mesh/SLMesh.h +++ b/modules/sl/source/mesh/SLMesh.h @@ -130,7 +130,7 @@ class SLMesh : public SLObject ~SLMesh() override; virtual void init(SLNode* node); - virtual void draw(SLSceneView* sv, SLNode* node); + virtual void draw(SLSceneView* sv, SLNode* node, SLuint intances = 0); void drawIntoDepthBuffer(SLSceneView* sv, SLNode* node, SLMaterial* depthMat); @@ -151,7 +151,8 @@ class SLMesh : public SLObject SLbool hitTriangleOS(SLRay* ray, SLNode* node, SLuint iT); virtual void generateVAO(SLGLVertexArray& vao); void computeHardEdgesIndices(float angleRAD, float epsilon); - void transformSkin(const std::function& cbInformNodes); + void transformSkin(bool forceCPUSkinning, + const std::function& cbInformNodes); void deselectPartialSelection(); #ifdef SL_HAS_OPTIX @@ -208,8 +209,8 @@ class SLMesh : public SLObject SLVushort IE16; //!< Vector of hard edges vertex indices 16 bit (see computeHardEdgesIndices) SLVuint IE32; //!< Vector of hard edges vertex indices 32 bit (see computeHardEdgesIndices) - SLVec3f minP; //!< min. vertex in OS - SLVec3f maxP; //!< max. vertex in OS + SLVec3f minP; //!< min. vertex in OS + SLVec3f maxP; //!< max. vertex in OS private: void calcTangents(); @@ -243,9 +244,10 @@ class SLMesh : public SLObject SLbool _isVolume; //!< Flag for RT if mesh is a closed volume SLAccelStruct* _accelStruct; //!< KD-tree or uniform grid - SLbool _accelStructIsOutOfDate; //!< Flag id accel.struct needs update + SLbool _accelStructIsOutOfDate; //!< Flag if accel. struct needs update SLAnimSkeleton* _skeleton; //!< The skeleton this mesh is bound to SLVMat4f _jointMatrices; //!< Joint matrix vector for this mesh + SLbool _isCPUSkinned; //!< Flag if mesh has been skinned on CPU during update SLVVec3f* _finalP; //!< Pointer to final vertex position vector SLVVec3f* _finalN; //!< pointer to final vertex normal vector }; diff --git a/modules/sl/source/mesh/SLParticleSystem.cpp b/modules/sl/source/mesh/SLParticleSystem.cpp index c69dcefe..07bcb9a5 100644 --- a/modules/sl/source/mesh/SLParticleSystem.cpp +++ b/modules/sl/source/mesh/SLParticleSystem.cpp @@ -29,12 +29,28 @@ SLParticleSystem::SLParticleSystem(SLAssetManager* assetMgr, const SLfloat& timeToLive, SLGLTexture* texC, const SLstring& name, - SLGLTexture* texFlipbook) : SLMesh(assetMgr, name) + SLGLTexture* texFlipbook, + const bool renderInstanced) : SLMesh(assetMgr, name) { assert(!name.empty()); + // To be added to constructor + + _assetManager = assetMgr; + + if (SLGLState::instance()->glHasGeometryShaders()) + { + _renderInstanced = renderInstanced; + } + else + { + _renderInstanced = true; + } _primitive = PT_points; + P.push_back(SLVec3f(0, 0, 0)); // Trick SL project because it want mesh to have vertex + I32.push_back(0); + if (amount > UINT_MAX) // Need to change for number of floats SL_EXIT_MSG("SLParticleSystem supports max. 2^32 vertices."); @@ -43,17 +59,12 @@ SLParticleSystem::SLParticleSystem(SLAssetManager* assetMgr, _velocityRndMin = velocityRandomStart; _velocityRndMax = velocityRandomEnd; - P.resize(1); // To trick parent class - _textureFirst = texC; _textureFlipbook = texFlipbook; - // Initialize the drawing: - SLMaterial* mDraw = new SLMaterial(assetMgr, "Drawing-Material", this, texC); - mat(mDraw); - _updateTime.init(60, 0.0f); _drawTime.init(60, 0.0f); + } //----------------------------------------------------------------------------- //! Function which return a position in a sphere @@ -308,6 +319,25 @@ void SLParticleSystem::generate() SLVuint tempTexNum; SLVVec3f tempInitP; + if (_renderInstanced) + _primitive = PT_triangles; + else + _primitive = PT_points; + + deleteDataGpu(); + + // Initialize the drawing: + if (_doFlipBookTexture) + { + SLMaterial* mDraw = new SLMaterial(_assetManager, "Drawing-Material", this, _textureFlipbook); + mat(mDraw); + } + else + { + SLMaterial* mDraw = new SLMaterial(_assetManager, "Drawing-Material", this, _textureFirst); + mat(mDraw); + } + tempP.resize(_amount); tempV.resize(_amount); tempST.resize(_amount); @@ -447,6 +477,39 @@ void SLParticleSystem::generate() _vao2.setAttrib(AT_initialPosition, AT_initialPosition, &tempInitP); _vao2.generateTF((SLuint)tempP.size()); + if (_renderInstanced) + { + P.clear(); + I32.clear(); + /* Generate for billboard (for drawing without geometry shader)*/ + P.push_back(SLVec3f(-1, -1, 0)); + P.push_back(SLVec3f(1, -1, 0)); + P.push_back(SLVec3f(1, 1, 0)); + P.push_back(SLVec3f(-1, 1, 0)); + + I32.push_back(0); + I32.push_back(1); + I32.push_back(2); + I32.push_back(2); + I32.push_back(3); + I32.push_back(0); + + + _renderVao1.deleteGL(); + _renderVao2.deleteGL(); + /* Generate vao for rendering with draw instanced */ + _renderVao1.setAttrib(AT_custom0, AT_custom0, &P); + _renderVao1.setIndices(&I32); + _renderVao1.setExternalVBO(_vao1.vbo(), 2); + + _renderVao2.setAttrib(AT_custom0, AT_custom0, &P); + _renderVao2.setIndices(&I32); + _renderVao2.setExternalVBO(_vao2.vbo(), 2); + + _renderVao1.generate((SLuint)P.size()); + _renderVao2.generate((SLuint)P.size()); + } + _isGenerated = true; } //----------------------------------------------------------------------------- @@ -489,24 +552,7 @@ void SLParticleSystem::generateBernsteinPSize() // 1 _bernsteinPYSize.w = StaEnd[1]; } -//----------------------------------------------------------------------------- -/*! -Change the current use texture, this will switch between the normal texture and -the flipbook texture (and vice versa) -*/ -void SLParticleSystem::changeTexture() -{ - if (_doFlipBookTexture) - { - mat()->removeTextureType(TT_diffuse); - mat()->addTexture(_textureFlipbook); - } - else - { - mat()->removeTextureType(TT_diffuse); - mat()->addTexture(_textureFirst); - } -} + //----------------------------------------------------------------------------- /*! Function called inside SLNode cull3DRec(..) which flags the particle system to be not visible in the view frustum. This is needed to correct later the @@ -542,21 +588,21 @@ void SLParticleSystem::pauseOrResume() * user. After I update the particle in the update pass, then and finally I * draw them. */ -void SLParticleSystem::draw(SLSceneView* sv, SLNode* node) +void SLParticleSystem::draw(SLSceneView* sv, SLNode* node, SLuint instances) { ///////////////////////////////////// // Init particles vector and init VAO ///////////////////////////////////// - if (!_isGenerated) generate(); //////////////////// // Generate programs //////////////////// - if (!_mat->program() || !_mat->programTF()) - _mat->generateProgramPS(); + { + _mat->generateProgramPS(_renderInstanced); + } //////////////////////////////////////////////// // Calculate time and paused and frustum culling @@ -680,14 +726,20 @@ void SLParticleSystem::draw(SLSceneView* sv, SLNode* node) _vao1.beginTF(_vao2.tfoID()); _vao1.drawArrayAs(PT_points); _vao1.endTF(); - _vao = _vao2; + if (_renderInstanced) + _vao = _renderVao1; + else + _vao = _vao2; } else { _vao2.beginTF(_vao1.tfoID()); _vao2.drawArrayAs(PT_points); _vao2.endTF(); - _vao = _vao1; + if (_renderInstanced) + _vao = _renderVao2; + else + _vao = _vao1; } _updateTime.set(GlobalTimer::timeMS() - _startUpdateTimeMS); } @@ -709,7 +761,6 @@ void SLParticleSystem::draw(SLSceneView* sv, SLNode* node) // World space if (_doWorldSpace) { - if (_billboardType == BT_Vertical) { SLMat4f vMat = stateGL->viewMatrix; // Just view matrix because world space is enabled @@ -728,7 +779,22 @@ void SLParticleSystem::draw(SLSceneView* sv, SLNode* node) } else { - spD->uniformMatrix4fv("u_vOmvMatrix", 1, (SLfloat*)&stateGL->viewMatrix); + + SLMat4f vMat = stateGL->viewMatrix; // Just view matrix because world space is enabled + std::cout << "vMat" << std::endl; + vMat.m(0, 1.0f); + vMat.m(1, 0.0f); + vMat.m(2, 0.0f); + + vMat.m(3, 1.0f); + vMat.m(4, 0.0f); + vMat.m(5, 0.0f); + + vMat.m(6, 0.0f); + vMat.m(7, 0.0f); + vMat.m(8, 1.0f); + spD->uniformMatrix4fv("u_vOmvMatrix", 1, (SLfloat*)&vMat); + //spD->uniformMatrix4fv("u_vOmvMatrix", 1, (SLfloat*)&stateGL->viewMatrix); } } else @@ -822,7 +888,10 @@ void SLParticleSystem::draw(SLSceneView* sv, SLNode* node) stateGL->blendFunc(GL_SRC_ALPHA, GL_ONE); /////////////////////// - SLMesh::draw(sv, node); + if (_renderInstanced) + SLMesh::draw(sv, node, 2*_amount); //2 triangles per particle + else + SLMesh::draw(sv, node); /////////////////////// if (_doColor && _doBlendBrightness) @@ -834,20 +903,37 @@ void SLParticleSystem::draw(SLSceneView* sv, SLNode* node) // Swap buffer _drawBuf = 1 - _drawBuf; } + + +/*! +Change the current use texture, this will switch between the normal texture and +the flipbook texture (and vice versa) +*/ +void SLParticleSystem::changeTexture() +{ + if (_doFlipBookTexture) + { + mat()->removeTextureType(TT_diffuse); + mat()->addTexture(_textureFlipbook); + } + else + { + mat()->removeTextureType(TT_diffuse); + mat()->addTexture(_textureFirst); + } +} + + //----------------------------------------------------------------------------- //! deleteData deletes all mesh data and VAOs void SLParticleSystem::deleteData() { - _vao1.deleteGL(); - _vao2.deleteGL(); - SLMesh::deleteData(); + return; } //----------------------------------------------------------------------------- //! deleteData deletes all mesh data and VAOs void SLParticleSystem::deleteDataGpu() { - _vao1.deleteGL(); - _vao2.deleteGL(); SLMesh::deleteDataGpu(); } //----------------------------------------------------------------------------- diff --git a/modules/sl/source/mesh/SLParticleSystem.h b/modules/sl/source/mesh/SLParticleSystem.h index fd1b5b9d..17fcb340 100644 --- a/modules/sl/source/mesh/SLParticleSystem.h +++ b/modules/sl/source/mesh/SLParticleSystem.h @@ -40,9 +40,10 @@ class SLParticleSystem : public SLMesh const SLfloat& timeToLive, SLGLTexture* texC, const SLstring& name = "Particle system", - SLGLTexture* texFlipbook = nullptr); + SLGLTexture* texFlipbook = nullptr, + const bool renderInstanced = false); - void draw(SLSceneView* sv, SLNode* node); + void draw(SLSceneView* sv, SLNode* node, SLuint instances = 1); void deleteData(); void deleteDataGpu(); void buildAABB(SLAABBox& aabb, const SLMat4f& wmNode); @@ -52,8 +53,12 @@ class SLParticleSystem : public SLMesh void changeTexture(); void setNotVisibleInFrustum(); void pauseOrResume(); + void calcNormals() { N.push_back(SLVec3f(0, 1, 0)); }; + // Getters + + SLbool renderInstanced() { return _renderInstanced; } SLVec3f acceleration() { return _acceleration; } SLfloat accelerationConst() { return _accelerationConst; } SLint amount() { return _amount; } @@ -117,6 +122,7 @@ class SLParticleSystem : public SLMesh SLVec3f velocityRndMax() { return _velocityRndMax; } // Setters + void drawInstanced(bool instanced) {_renderInstanced = instanced; } void amount(SLint i) { _amount = i; } void accConst(SLfloat f) { _accelerationConst = f; } void acceleration(SLVec3f v) { _acceleration = v; } @@ -267,6 +273,9 @@ class SLParticleSystem : public SLMesh SLVec3f getPointOnPyramid(); SLVec3f getDirectionPyramid(SLVec3f position); + // Use to recreate material (the shader change depending if the PS is instanced or not) + SLAssetManager* _assetManager; //!< pointer to the asset manager (the owner) if available + // Core values SLint _amount; //!< Amount of a particle SLVec3f _emitterPos; //!< Position of the particle emitter @@ -350,6 +359,9 @@ class SLParticleSystem : public SLMesh SLGLVertexArray _vao1; //!< First OpenGL Vertex Array Object for swapping between updating/drawing SLGLVertexArray _vao2; //!< Second OpenGL Vertex Array Object for swapping between updating/drawing + SLGLVertexArray _renderVao1; //!< First OpenGL Vertex Array Object for swapping between updating/drawing + SLGLVertexArray _renderVao2; //!< Second OpenGL Vertex Array Object for swapping between updating/drawing + // Boolean for generation/resume SLbool _isVisibleInFrustum = true; //!< Boolean to set time since node not visible SLbool _isPaused = false; //!< Boolean to stop updating @@ -377,6 +389,7 @@ class SLParticleSystem : public SLMesh SLbool _doSizeOverLT = true; //!< Boolean for size over life time SLbool _doSizeOverLTCurve = false; //!< Boolean for size over life time curve SLbool _doFlipBookTexture = false; //!< Boolean for flipbook texture + SLbool _renderInstanced = false; //!< Boolean for instanced rendering }; //----------------------------------------------------------------------------- #endif diff --git a/modules/sl/source/node/SLNode.cpp b/modules/sl/source/node/SLNode.cpp index ee1e83a0..cee233cf 100644 --- a/modules/sl/source/node/SLNode.cpp +++ b/modules/sl/source/node/SLNode.cpp @@ -1106,19 +1106,23 @@ void SLNode::updateRec() /*! Do software skinning on all changed skeletons && updateRec any out of date acceleration structure for RT or if they're being rendered. */ -bool SLNode::updateMeshSkins(const std::function& cbInformNodes) +bool SLNode::updateMeshSkins(bool forceCPUSkinning, + const std::function& cbInformNodes) { + if (drawBit(SL_DB_WITHEDGES) || drawBit(SL_DB_ONLYEDGES) || drawBit(SL_DB_VOXELS)) + forceCPUSkinning = true; + bool hasChanges = false; // Do software skinning on changed skeleton if (_mesh && _mesh->skeleton() && _mesh->skeleton()->changed()) { - _mesh->transformSkin(cbInformNodes); + _mesh->transformSkin(forceCPUSkinning, cbInformNodes); hasChanges = true; } for (auto* child : _children) - hasChanges |= child->updateMeshSkins(cbInformNodes); + hasChanges |= child->updateMeshSkins(forceCPUSkinning, cbInformNodes); return hasChanges; } diff --git a/modules/sl/source/node/SLNode.h b/modules/sl/source/node/SLNode.h index 74e12451..c4e6ac1f 100644 --- a/modules/sl/source/node/SLNode.h +++ b/modules/sl/source/node/SLNode.h @@ -301,7 +301,8 @@ class SLNode const SLAnimSkeleton* skeleton(); void updateRec(); virtual void doUpdate() {} - bool updateMeshSkins(const std::function& cbInformNodes); + bool updateMeshSkins(bool forceCPUSkinning, + const std::function& cbInformNodes); void updateMeshAccelStructs(); void updateMeshMat(std::function setMat, bool recursive); @@ -310,7 +311,7 @@ class SLNode SLfloat minLodCoverage() { return _minLodCoverage; } SLubyte levelForSM() { return _levelForSM; } - static SLuint numWMUpdates; //!< NO. of calls to updateWMRec per frame + static SLuint numWMUpdates; //!< NO. of calls to updateWMRec per frame static unsigned int instanceIndex; //!< ??? @@ -335,9 +336,9 @@ class SLNode SLbool findRecursive); protected: - SLNode* _parent; //!< pointer to the parent node - SLVNode _children; //!< vector of children nodes - SLMesh* _mesh; //!< pointer to a single mesh + SLNode* _parent; //!< pointer to the parent node + SLVNode _children; //!< vector of children nodes + SLMesh* _mesh; //!< pointer to a single mesh SLint _depth; //!< depth of the node in a scene tree SLint _entityID; //!< ID in the SLVEntity graph for Data Oriented Design diff --git a/modules/utils/source/Utils.cpp b/modules/utils/source/Utils.cpp index 80889202..2f06388b 100644 --- a/modules/utils/source/Utils.cpp +++ b/modules/utils/source/Utils.cpp @@ -201,10 +201,10 @@ vector getStringLines(const string& multiLineString) { std::string line; std::getline(stream, line); - if (!stream.good()) - break; line = Utils::trimString(line, "\r"); res.push_back(line); + if (!stream.good()) + break; } return res; } @@ -1391,7 +1391,7 @@ std::string ComputerInfos::get() // model = model; # endif -#elif defined(ANDROID) //................................................ +#elif defined(ANDROID) //................................................ os = "Android";