From 9fab556232522fbafb0935ec27c076b3e7f53ce2 Mon Sep 17 00:00:00 2001 From: Marino von Wattenwyl Date: Fri, 16 Jun 2023 14:05:53 +0200 Subject: [PATCH 001/108] [SL] Add support for GPU skinning GPU skinning can be enabled for a material, which causes the shader generator to include code for applying the joint transforms to the vertices. GPU skinning is currently enabled for models loaded using assimp. --- modules/sl/source/SLMaterial.h | 47 ++++++++++--------- modules/sl/source/gl/SLGLProgramGenerated.cpp | 45 ++++++++++++++++-- modules/sl/source/input/SLAssimpImporter.cpp | 1 + modules/sl/source/mesh/SLMesh.cpp | 47 +++++++++++++++++-- 4 files changed, 112 insertions(+), 28 deletions(-) diff --git a/modules/sl/source/SLMaterial.h b/modules/sl/source/SLMaterial.h index 60dbbd97..2a4d6f2f 100644 --- a/modules/sl/source/SLMaterial.h +++ b/modules/sl/source/SLMaterial.h @@ -198,6 +198,7 @@ class SLMaterial : public SLObject void program(SLGLProgram* sp) { _program = sp; } void programTF(SLGLProgram* sp) { _programTF = sp; } void skybox(SLSkybox* sb) { _skybox = sb; } + void useGPUSkinning(SLbool useGPUSkinning) { _useGPUSkinning = useGPUSkinning; } void ps(SLParticleSystem* ps) { _ps = ps; } // Getters @@ -220,6 +221,7 @@ class SLMaterial : public SLObject SLGLProgram* program() { return _program; } SLGLProgram* programTF() { return _programTF; } SLSkybox* skybox() { return _skybox; } + SLbool useGPUSkinning() { return _useGPUSkinning; } SLParticleSystem* ps() { return _ps; } SLVNode& nodesVisible2D() { return _nodesVisible2D; } SLVNode& nodesVisible3D() { return _nodesVisible3D; } @@ -231,36 +233,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 _useGPUSkinning = 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/gl/SLGLProgramGenerated.cpp b/modules/sl/source/gl/SLGLProgramGenerated.cpp index bdb9aabf..0423ee6c 100644 --- a/modules/sl/source/gl/SLGLProgramGenerated.cpp +++ b/modules/sl/source/gl/SLGLProgramGenerated.cpp @@ -50,6 +50,10 @@ 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 vec4 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 +63,10 @@ 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_jointMatrices = R"( + +uniform mat4 u_jointMatrices[100]; // Joint matrices for skinning)"; +//----------------------------------------------------------------------------- const string vertInput_u_lightNm = R"( uniform vec4 u_lightPosVS[NUM_LIGHTS]; // position of light in view space @@ -188,6 +196,20 @@ 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 totalPosition = vec4(0.0); + vec3 totalNormal = vec3(0.0); + + for (int i = 0; i < 4; i++) + { + mat4 jointMatrix = u_jointMatrices[int(a_jointIds[i])]; + totalPosition += a_jointWeights[i] * jointMatrix * a_position; + totalNormal += a_jointWeights[i] * mat3(jointMatrix) * a_normal; + } + + v_P_VS = vec3(mvMatrix * totalPosition); + v_N_VS = normalize(vec3(nMatrix * totalNormal));)"; const string vertMain_TBN_Nm = R"( // Building the matrix Eye Space -> Tangent Space @@ -267,6 +289,14 @@ const string vertMain_EndAll = R"( gl_Position = u_pMatrix * mvMatrix * a_position; } )"; + +const string vertMain_EndSkinned = R"( + + // pass the vertex w. the fix-function transform + gl_Position = u_pMatrix * mvMatrix * totalPosition; +} +)"; + //----------------------------------------------------------------------------- const string vertMain_PS_U_Begin = R"( @@ -1582,7 +1612,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 +1621,9 @@ void SLGLProgramGenerated::buildProgramName(SLMaterial* mat, if (light->createsShadows()) programName += "s"; // Creates shadows } + + if (mat->useGPUSkinning()) + programName += "-S"; } //----------------------------------------------------------------------------- /*! See the class information for more insights of the generated name. This @@ -1653,7 +1686,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 @@ -1889,6 +1922,9 @@ void SLGLProgramGenerated::buildPerPixBlinn(SLMaterial* mat, SLVLight* lights) bool uv0 = mat->usesUVIndex(0); bool uv1 = mat->usesUVIndex(1); + // Check if we use GPU skinning + bool skinning = mat->useGPUSkinning(); + // Assemble vertex shader code string vertCode; vertCode += shaderHeader((int)lights->size()); @@ -1898,7 +1934,9 @@ 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 (skinning) vertCode += vertInput_u_jointMatrices; if (Nm) vertCode += vertInput_u_lightNm; // Vertex shader outputs @@ -1916,8 +1954,9 @@ void SLGLProgramGenerated::buildPerPixBlinn(SLMaterial* mat, SLVLight* lights) vertCode += vertMain_v_N_VS; if (uv0) vertCode += vertMain_v_uv0; if (uv1) vertCode += vertMain_v_uv1; + if (skinning) vertCode += vertMain_skinning; if (Nm) vertCode += vertMain_TBN_Nm; - vertCode += vertMain_EndAll; + vertCode += skinning ? vertMain_EndSkinned : vertMain_EndAll; addCodeToShader(_shaders[0], vertCode, _name + ".vert"); diff --git a/modules/sl/source/input/SLAssimpImporter.cpp b/modules/sl/source/input/SLAssimpImporter.cpp index 4d89bb61..823e7bca 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->useGPUSkinning(true); // load all the textures for this aiMat and add it to the aiMat vector for (int tt = aiTextureType_NONE; tt <= aiTextureType_UNKNOWN; ++tt) diff --git a/modules/sl/source/mesh/SLMesh.cpp b/modules/sl/source/mesh/SLMesh.cpp index 99c21015..c46b5227 100644 --- a/modules/sl/source/mesh/SLMesh.cpp +++ b/modules/sl/source/mesh/SLMesh.cpp @@ -442,6 +442,9 @@ void SLMesh::draw(SLSceneView* sv, SLNode* node) sp->uniformMatrix4fv("u_vMatrix", 1, (SLfloat*)&stateGL->viewMatrix); sp->uniformMatrix4fv("u_pMatrix", 1, (SLfloat*)&stateGL->projectionMatrix); + if (!_jointMatrices.empty()) + sp->uniformMatrix4fv("u_jointMatrices", 100, (SLfloat*)&_jointMatrices[0]); + SLint locTM = sp->getUniformLocation("u_tMatrix"); if (locTM >= 0) { @@ -597,7 +600,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 { @@ -735,8 +738,44 @@ void SLMesh::generateVAO(SLGLVertexArray& vao) } } + bool useCPUSkinning = !Ji.empty() && !_mat->useGPUSkinning(); + bool useGPUSkinning = _mat->useGPUSkinning(); + + SLVVec4f gpuIndices(P.size(), SLVec4f(0, 0, 0, 0)); + SLVVec4f gpuWeights(P.size(), SLVec4f(1.0f, 0.0f, 0.0f, 0.0f)); + + if (!Ji.empty()) + { + for (unsigned i = 0; i < P.size(); i++) + { + const SLVuchar& indices = Ji[i]; + + SLint i0 = indices.size() >= 1 ? indices[0] : 0; + SLint i1 = indices.size() >= 2 ? indices[1] : 0; + SLint i2 = indices.size() >= 3 ? indices[2] : 0; + SLint i3 = indices.size() >= 4 ? indices[3] : 0; + + gpuIndices[i] = SLVec4f((SLfloat)i0, (SLfloat)i1, (SLfloat)i2, (SLfloat)i3); + } + + for (unsigned i = 0; i < P.size(); i++) + { + const SLVfloat& weights = Jw[i]; + + SLfloat w0 = weights.size() >= 1 ? weights[0] : 0.0f; + SLfloat w1 = weights.size() >= 2 ? weights[1] : 0.0f; + SLfloat w2 = weights.size() >= 3 ? weights[2] : 0.0f; + SLfloat w3 = weights.size() >= 4 ? weights[3] : 0.0f; + + gpuWeights[i] = SLVec4f(w0, w1, w2, w3); + } + } + + vao.setAttrib(AT_jointIndex, AT_jointIndex, &gpuIndices); + vao.setAttrib(AT_jointWeight, AT_jointWeight, &gpuWeights); + vao.generate((SLuint)P.size(), - !Ji.empty() ? BU_stream : BU_static, + useCPUSkinning ? BU_stream : BU_static, Ji.empty()); } //----------------------------------------------------------------------------- @@ -1511,7 +1550,7 @@ void SLMesh::transformSkin(const std::function& cbInformNodes) if (_jointMatrices.empty()) { _jointMatrices.clear(); - _jointMatrices.resize((SLuint)_skeleton->numJoints()); + _jointMatrices.resize(100); } // update the joint matrix array @@ -1527,6 +1566,8 @@ void SLMesh::transformSkin(const std::function& cbInformNodes) // flag acceleration structure to be rebuilt _accelStructIsOutOfDate = true; + if (_mat->useGPUSkinning()) return; + // iterate over all vertices and write to new buffers for (SLulong i = 0; i < P.size(); ++i) { From 27033f672ad0ae15121b5fa34db5161be2bcfa49 Mon Sep 17 00:00:00 2001 From: Marino von Wattenwyl Date: Fri, 16 Jun 2023 14:43:24 +0200 Subject: [PATCH 002/108] [SL] Use integer joint IDs for GPU skinning The a_jointIds attribute now has the type ivec4 instead of vec4. This removes the cost of converting the IDs to floats on the CPU and back to int on the GPU. --- modules/math/source/SLVec4.h | 9 +++++---- modules/sl/source/gl/SLGLEnums.h | 1 + modules/sl/source/gl/SLGLProgramGenerated.cpp | 4 ++-- modules/sl/source/gl/SLGLVertexArray.h | 5 +++++ modules/sl/source/gl/SLGLVertexBuffer.cpp | 15 ++++++++------- modules/sl/source/mesh/SLMesh.cpp | 4 ++-- 6 files changed, 23 insertions(+), 15 deletions(-) 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/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 0423ee6c..479a4395 100644 --- a/modules/sl/source/gl/SLGLProgramGenerated.cpp +++ b/modules/sl/source/gl/SLGLProgramGenerated.cpp @@ -52,7 +52,7 @@ 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 vec4 a_jointIds; // Vertex joint indices attributes +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"( @@ -203,7 +203,7 @@ const string vertMain_skinning = R"( for (int i = 0; i < 4; i++) { - mat4 jointMatrix = u_jointMatrices[int(a_jointIds[i])]; + mat4 jointMatrix = u_jointMatrices[a_jointIds[i]]; totalPosition += a_jointWeights[i] * jointMatrix * a_position; totalNormal += a_jointWeights[i] * mat3(jointMatrix) * a_normal; } diff --git a/modules/sl/source/gl/SLGLVertexArray.h b/modules/sl/source/gl/SLGLVertexArray.h index 4b902f54..72561127 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, diff --git a/modules/sl/source/gl/SLGLVertexBuffer.cpp b/modules/sl/source/gl/SLGLVertexBuffer.cpp index 935f776d..70b4c525 100644 --- a/modules/sl/source/gl/SLGLVertexBuffer.cpp +++ b/modules/sl/source/gl/SLGLVertexBuffer.cpp @@ -199,7 +199,7 @@ void SLGLVertexBuffer::generate(SLuint numVertices, { if (a.location > -1) { // Sets the vertex attribute data pointer to its corresponding GLSL variable - if (a.dataType == BT_uint) + if (a.dataType == BT_int || a.dataType == BT_uint) { glVertexAttribIPointer((SLuint)a.location, a.elementSize, @@ -247,7 +247,7 @@ void SLGLVertexBuffer::generate(SLuint numVertices, if (a.location > -1) { // Sets the vertex attribute data pointer to its corresponding GLSL variable - if (a.dataType == BT_uint) + if (a.dataType == BT_int || a.dataType == BT_uint) { glVertexAttribIPointer((SLuint)a.location, a.elementSize, @@ -289,7 +289,7 @@ void SLGLVertexBuffer::generate(SLuint numVertices, a.dataPointer); // Sets the vertex attribute data pointer to its corresponding GLSL variable - if (a.dataType == BT_uint) + if (a.dataType == BT_int || a.dataType == BT_uint) { glVertexAttribIPointer((SLuint)a.location, a.elementSize, @@ -367,10 +367,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/mesh/SLMesh.cpp b/modules/sl/source/mesh/SLMesh.cpp index c46b5227..251e3d99 100644 --- a/modules/sl/source/mesh/SLMesh.cpp +++ b/modules/sl/source/mesh/SLMesh.cpp @@ -741,7 +741,7 @@ void SLMesh::generateVAO(SLGLVertexArray& vao) bool useCPUSkinning = !Ji.empty() && !_mat->useGPUSkinning(); bool useGPUSkinning = _mat->useGPUSkinning(); - SLVVec4f gpuIndices(P.size(), SLVec4f(0, 0, 0, 0)); + SLVVec4i gpuIndices(P.size(), SLVec4i(0, 0, 0, 0)); SLVVec4f gpuWeights(P.size(), SLVec4f(1.0f, 0.0f, 0.0f, 0.0f)); if (!Ji.empty()) @@ -755,7 +755,7 @@ void SLMesh::generateVAO(SLGLVertexArray& vao) SLint i2 = indices.size() >= 3 ? indices[2] : 0; SLint i3 = indices.size() >= 4 ? indices[3] : 0; - gpuIndices[i] = SLVec4f((SLfloat)i0, (SLfloat)i1, (SLfloat)i2, (SLfloat)i3); + gpuIndices[i] = SLVec4i(i0, i1, i2, i3); } for (unsigned i = 0; i < P.size(); i++) From 6d8dc1ab3aec7bef22b945d306fbc28404595c8d Mon Sep 17 00:00:00 2001 From: Marino von Wattenwyl Date: Mon, 19 Jun 2023 14:03:13 +0200 Subject: [PATCH 003/108] [SL] Force CPU skinning when required --- modules/sl/source/SLScene.cpp | 8 +- modules/sl/source/SLScene.h | 3 +- modules/sl/source/SLSceneView.cpp | 3 +- modules/sl/source/gl/SLGLProgramGenerated.cpp | 33 ++++--- modules/sl/source/mesh/SLMesh.cpp | 88 +++++++++++++------ modules/sl/source/mesh/SLMesh.h | 10 ++- modules/sl/source/node/SLNode.cpp | 10 ++- modules/sl/source/node/SLNode.h | 11 +-- 8 files changed, 113 insertions(+), 53 deletions(-) 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..fdf264b6 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_WITHEDGES)); } SLbool camUpdated = false; diff --git a/modules/sl/source/gl/SLGLProgramGenerated.cpp b/modules/sl/source/gl/SLGLProgramGenerated.cpp index 479a4395..6142ffc2 100644 --- a/modules/sl/source/gl/SLGLProgramGenerated.cpp +++ b/modules/sl/source/gl/SLGLProgramGenerated.cpp @@ -65,7 +65,9 @@ uniform mat4 u_vOmvMatrix; // view or modelview matrix)"; //----------------------------------------------------------------------------- const string vertInput_u_jointMatrices = R"( -uniform mat4 u_jointMatrices[100]; // Joint matrices for skinning)"; +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"( @@ -198,18 +200,29 @@ const string vertMain_v_uv1 = R"( v_uv1 = a_uv1; // pass diffuse color tex.coord. 1 for interpolation)"; const string vertMain_skinning = R"( - vec4 totalPosition = vec4(0.0); - vec3 totalNormal = vec3(0.0); + vec4 totalPosition; + vec3 totalNormal; - for (int i = 0; i < 4; i++) + if (u_skinningEnabled) { - mat4 jointMatrix = u_jointMatrices[a_jointIds[i]]; - totalPosition += a_jointWeights[i] * jointMatrix * a_position; - totalNormal += a_jointWeights[i] * mat3(jointMatrix) * a_normal; - } + totalPosition = vec4(0.0); + totalNormal = vec3(0.0); + + for (int i = 0; i < 4; i++) + { + mat4 jointMatrix = u_jointMatrices[a_jointIds[i]]; + totalPosition += a_jointWeights[i] * jointMatrix * a_position; + totalNormal += a_jointWeights[i] * mat3(jointMatrix) * a_normal; + } - v_P_VS = vec3(mvMatrix * totalPosition); - v_N_VS = normalize(vec3(nMatrix * totalNormal));)"; + v_P_VS = vec3(mvMatrix * totalPosition); + v_N_VS = normalize(vec3(nMatrix * totalNormal)); + } + else + { + totalPosition = a_position; + totalNormal = a_normal; + })"; const string vertMain_TBN_Nm = R"( // Building the matrix Eye Space -> Tangent Space diff --git a/modules/sl/source/mesh/SLMesh.cpp b/modules/sl/source/mesh/SLMesh.cpp index 251e3d99..6bc4d7d4 100644 --- a/modules/sl/source/mesh/SLMesh.cpp +++ b/modules/sl/source/mesh/SLMesh.cpp @@ -442,8 +442,16 @@ void SLMesh::draw(SLSceneView* sv, SLNode* node) sp->uniformMatrix4fv("u_vMatrix", 1, (SLfloat*)&stateGL->viewMatrix); sp->uniformMatrix4fv("u_pMatrix", 1, (SLfloat*)&stateGL->projectionMatrix); - if (!_jointMatrices.empty()) - sp->uniformMatrix4fv("u_jointMatrices", 100, (SLfloat*)&_jointMatrices[0]); + // Pass skeleton joint matrices to the shader program + if (_mat->useGPUSkinning()) + { + // 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) + sp->uniformMatrix4fv("u_jointMatrices", 100, (SLfloat*)&_jointMatrices[0]); + } SLint locTM = sp->getUniformLocation("u_tMatrix"); if (locTM >= 0) @@ -890,8 +898,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->useGPUSkinning() && !_isCPUSkinned) + transformSkin(true, [](SLMesh*) {}); + if (_accelStruct) + { + if (_accelStructIsOutOfDate) + updateAccelStruct(); + return _accelStruct->intersect(ray, node); + } else { // intersect against all faces SLbool wasHit = false; @@ -1092,6 +1110,8 @@ flag is set. This can happen for mesh animations. */ void SLMesh::updateAccelStruct() { + SL_LOG("."); + calcMinMax(); // Add half a percent in each direction to avoid zero size dimensions @@ -1530,8 +1550,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()) { @@ -1556,44 +1580,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; - if (_mat->useGPUSkinning()) return; + // remember if this node has been skinned on the CPU + _isCPUSkinned = forceCPUSkinning; - // iterate over all vertices and write to new buffers - for (SLulong i = 0; i < P.size(); ++i) + // 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->useGPUSkinning() || 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..6873db2a 100644 --- a/modules/sl/source/mesh/SLMesh.h +++ b/modules/sl/source/mesh/SLMesh.h @@ -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/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 From 7427bcc31ed6c3ea75b28675be2b820d37bbe01d Mon Sep 17 00:00:00 2001 From: Marino von Wattenwyl Date: Mon, 19 Jun 2023 15:03:11 +0200 Subject: [PATCH 004/108] [SL] Add draw bit for GPU skinning --- apps/app_demo_slproject/source/AppDemoGui.cpp | 4 +++ .../app_demo_slproject/source/AppDemoLoad.cpp | 4 +++ modules/sl/source/SLDrawBits.h | 27 ++++++++++--------- modules/sl/source/SLSceneView.cpp | 2 +- 4 files changed, 23 insertions(+), 14 deletions(-) diff --git a/apps/app_demo_slproject/source/AppDemoGui.cpp b/apps/app_demo_slproject/source/AppDemoGui.cpp index e4d20e0d..20459536 100644 --- a/apps/app_demo_slproject/source/AppDemoGui.cpp +++ b/apps/app_demo_slproject/source/AppDemoGui.cpp @@ -2393,6 +2393,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", "G", sv->drawBits()->get(SL_DB_GPU_SKINNING))) + sv->drawBits()->toggle(SL_DB_GPU_SKINNING); + if (ImGui::MenuItem("All off")) sv->drawBits()->allOff(); @@ -2407,6 +2410,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); diff --git a/apps/app_demo_slproject/source/AppDemoLoad.cpp b/apps/app_demo_slproject/source/AppDemoLoad.cpp index d8f0a7ec..66184142 100644 --- a/apps/app_demo_slproject/source/AppDemoLoad.cpp +++ b/apps/app_demo_slproject/source/AppDemoLoad.cpp @@ -3594,6 +3594,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) @@ -3607,6 +3610,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); } } 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/SLSceneView.cpp b/modules/sl/source/SLSceneView.cpp index fdf264b6..d58af6cf 100644 --- a/modules/sl/source/SLSceneView.cpp +++ b/modules/sl/source/SLSceneView.cpp @@ -514,7 +514,7 @@ SLbool SLSceneView::onPaint() // update current scene sceneHasChanged = _s->onUpdate((_renderType == RT_rt), drawBit(SL_DB_VOXELS), - drawBit(SL_DB_WITHEDGES)); + !drawBit(SL_DB_GPU_SKINNING) || drawBit(SL_DB_WITHEDGES)); } SLbool camUpdated = false; From e2731d738ce0a82c5df10153e5fd5e1266e761c9 Mon Sep 17 00:00:00 2001 From: Marino von Wattenwyl Date: Mon, 19 Jun 2023 15:06:23 +0200 Subject: [PATCH 005/108] [SL] Remove updateAccelStruct debug printing --- modules/sl/source/mesh/SLMesh.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/modules/sl/source/mesh/SLMesh.cpp b/modules/sl/source/mesh/SLMesh.cpp index 6bc4d7d4..bc64ad6a 100644 --- a/modules/sl/source/mesh/SLMesh.cpp +++ b/modules/sl/source/mesh/SLMesh.cpp @@ -1110,8 +1110,6 @@ flag is set. This can happen for mesh animations. */ void SLMesh::updateAccelStruct() { - SL_LOG("."); - calcMinMax(); // Add half a percent in each direction to avoid zero size dimensions From 3575143985d5707ad10bdafd1e3689f19d5a15bb Mon Sep 17 00:00:00 2001 From: Marcus Hudritsch Date: Mon, 19 Jun 2023 16:36:58 +0200 Subject: [PATCH 006/108] Update SLMesh.cpp --- modules/sl/source/mesh/SLMesh.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/sl/source/mesh/SLMesh.cpp b/modules/sl/source/mesh/SLMesh.cpp index bc64ad6a..48ebb2aa 100644 --- a/modules/sl/source/mesh/SLMesh.cpp +++ b/modules/sl/source/mesh/SLMesh.cpp @@ -449,7 +449,7 @@ void SLMesh::draw(SLSceneView* sv, SLNode* node) SLbool skinningEnabled = !_isCPUSkinned && !Ji.empty(); sp->uniform1i("u_skinningEnabled", skinningEnabled); - if (skinningEnabled) + if (skinningEnabled && !_jointMatrices.empty()) sp->uniformMatrix4fv("u_jointMatrices", 100, (SLfloat*)&_jointMatrices[0]); } From 851d931476d2c5e9b08b27e94d0fc1e7faa050ba Mon Sep 17 00:00:00 2001 From: Marino von Wattenwyl Date: Mon, 19 Jun 2023 16:43:02 +0200 Subject: [PATCH 007/108] [SL] Rename useGPUSkinning to supportsGPUSkinning --- apps/app_demo_slproject/source/AppDemoGui.cpp | 2 +- modules/sl/source/SLMaterial.h | 44 +++++++++---------- modules/sl/source/gl/SLGLProgramGenerated.cpp | 4 +- modules/sl/source/input/SLAssimpImporter.cpp | 2 +- modules/sl/source/mesh/SLMesh.cpp | 23 +++++----- 5 files changed, 36 insertions(+), 39 deletions(-) diff --git a/apps/app_demo_slproject/source/AppDemoGui.cpp b/apps/app_demo_slproject/source/AppDemoGui.cpp index 20459536..30b69b5b 100644 --- a/apps/app_demo_slproject/source/AppDemoGui.cpp +++ b/apps/app_demo_slproject/source/AppDemoGui.cpp @@ -2393,7 +2393,7 @@ 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", "G", sv->drawBits()->get(SL_DB_GPU_SKINNING))) + 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")) diff --git a/modules/sl/source/SLMaterial.h b/modules/sl/source/SLMaterial.h index 2a4d6f2f..e1ea9bde 100644 --- a/modules/sl/source/SLMaterial.h +++ b/modules/sl/source/SLMaterial.h @@ -198,7 +198,7 @@ class SLMaterial : public SLObject void program(SLGLProgram* sp) { _program = sp; } void programTF(SLGLProgram* sp) { _programTF = sp; } void skybox(SLSkybox* sb) { _skybox = sb; } - void useGPUSkinning(SLbool useGPUSkinning) { _useGPUSkinning = useGPUSkinning; } + void supportsGPUSkinning(SLbool supportsGPUSkinning) { _supportsGPUSkinning = supportsGPUSkinning; } void ps(SLParticleSystem* ps) { _ps = ps; } // Getters @@ -221,7 +221,7 @@ class SLMaterial : public SLObject SLGLProgram* program() { return _program; } SLGLProgram* programTF() { return _programTF; } SLSkybox* skybox() { return _skybox; } - SLbool useGPUSkinning() { return _useGPUSkinning; } + SLbool supportsGPUSkinning() { return _supportsGPUSkinning; } SLParticleSystem* ps() { return _ps; } SLVNode& nodesVisible2D() { return _nodesVisible2D; } SLVNode& nodesVisible3D() { return _nodesVisible3D; } @@ -233,26 +233,26 @@ 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 - bool _useGPUSkinning = false; //!< whether skinning is performed on the GPU or on the CPU + 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 diff --git a/modules/sl/source/gl/SLGLProgramGenerated.cpp b/modules/sl/source/gl/SLGLProgramGenerated.cpp index 6142ffc2..465c9670 100644 --- a/modules/sl/source/gl/SLGLProgramGenerated.cpp +++ b/modules/sl/source/gl/SLGLProgramGenerated.cpp @@ -1635,7 +1635,7 @@ void SLGLProgramGenerated::buildProgramName(SLMaterial* mat, programName += "s"; // Creates shadows } - if (mat->useGPUSkinning()) + if (mat->supportsGPUSkinning()) programName += "-S"; } //----------------------------------------------------------------------------- @@ -1936,7 +1936,7 @@ void SLGLProgramGenerated::buildPerPixBlinn(SLMaterial* mat, SLVLight* lights) bool uv1 = mat->usesUVIndex(1); // Check if we use GPU skinning - bool skinning = mat->useGPUSkinning(); + bool skinning = mat->supportsGPUSkinning(); // Assemble vertex shader code string vertCode; diff --git a/modules/sl/source/input/SLAssimpImporter.cpp b/modules/sl/source/input/SLAssimpImporter.cpp index 823e7bca..12930a22 100644 --- a/modules/sl/source/input/SLAssimpImporter.cpp +++ b/modules/sl/source/input/SLAssimpImporter.cpp @@ -691,7 +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->useGPUSkinning(true); + 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) diff --git a/modules/sl/source/mesh/SLMesh.cpp b/modules/sl/source/mesh/SLMesh.cpp index 48ebb2aa..8de969d2 100644 --- a/modules/sl/source/mesh/SLMesh.cpp +++ b/modules/sl/source/mesh/SLMesh.cpp @@ -443,7 +443,7 @@ void SLMesh::draw(SLSceneView* sv, SLNode* node) sp->uniformMatrix4fv("u_pMatrix", 1, (SLfloat*)&stateGL->projectionMatrix); // Pass skeleton joint matrices to the shader program - if (_mat->useGPUSkinning()) + 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(); @@ -746,14 +746,11 @@ void SLMesh::generateVAO(SLGLVertexArray& vao) } } - bool useCPUSkinning = !Ji.empty() && !_mat->useGPUSkinning(); - bool useGPUSkinning = _mat->useGPUSkinning(); - - SLVVec4i gpuIndices(P.size(), SLVec4i(0, 0, 0, 0)); - SLVVec4f gpuWeights(P.size(), SLVec4f(1.0f, 0.0f, 0.0f, 0.0f)); - if (!Ji.empty()) { + SLVVec4i gpuIndices(P.size(), SLVec4i(0, 0, 0, 0)); + SLVVec4f gpuWeights(P.size(), SLVec4f(1.0f, 0.0f, 0.0f, 0.0f)); + for (unsigned i = 0; i < P.size(); i++) { const SLVuchar& indices = Ji[i]; @@ -777,13 +774,13 @@ void SLMesh::generateVAO(SLGLVertexArray& vao) gpuWeights[i] = SLVec4f(w0, w1, w2, w3); } - } - vao.setAttrib(AT_jointIndex, AT_jointIndex, &gpuIndices); - vao.setAttrib(AT_jointWeight, AT_jointWeight, &gpuWeights); + vao.setAttrib(AT_jointIndex, AT_jointIndex, &gpuIndices); + vao.setAttrib(AT_jointWeight, AT_jointWeight, &gpuWeights); + } vao.generate((SLuint)P.size(), - useCPUSkinning ? BU_stream : BU_static, + !Ji.empty() && !_mat->supportsGPUSkinning() ? BU_stream : BU_static, Ji.empty()); } //----------------------------------------------------------------------------- @@ -900,7 +897,7 @@ SLbool SLMesh::hit(SLRay* ray, SLNode* node) // 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->useGPUSkinning() && !_isCPUSkinned) + if (_skeleton && _mat && _mat->supportsGPUSkinning() && !_isCPUSkinned) transformSkin(true, [](SLMesh*) {}); if (_accelStruct) @@ -1589,7 +1586,7 @@ void SLMesh::transformSkin(bool 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->useGPUSkinning() || forceCPUSkinning) + if (!_mat->supportsGPUSkinning() || forceCPUSkinning) { _finalP = &skinnedP; _finalN = &skinnedN; From d518217916d6eb67d4464204b7107367c510ab6c Mon Sep 17 00:00:00 2001 From: Marino von Wattenwyl Date: Mon, 19 Jun 2023 16:56:14 +0200 Subject: [PATCH 008/108] [SL] Add GUI text for GPU skinning The text shows whether a material shader has support for GPU skinning. --- apps/app_demo_slproject/source/AppDemoGui.cpp | 4 ++++ modules/sl/source/mesh/SLMesh.cpp | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/app_demo_slproject/source/AppDemoGui.cpp b/apps/app_demo_slproject/source/AppDemoGui.cpp index 30b69b5b..d2724752 100644 --- a/apps/app_demo_slproject/source/AppDemoGui.cpp +++ b/apps/app_demo_slproject/source/AppDemoGui.cpp @@ -4337,6 +4337,7 @@ void AppDemoGui::buildProperties(SLScene* s, SLSceneView* sv) ImGui::TreePop(); } + if (m->program() != nullptr) { for (auto* shd : m->program()->shaders()) @@ -4371,6 +4372,9 @@ void AppDemoGui::buildProperties(SLScene* s, SLSceneView* sv) } } } + + ImGui::Text("Supports GPU skinning: %s", m->supportsGPUSkinning() ? "Yes" : "No"); + ImGui::TreePop(); } } diff --git a/modules/sl/source/mesh/SLMesh.cpp b/modules/sl/source/mesh/SLMesh.cpp index 8de969d2..2ecef129 100644 --- a/modules/sl/source/mesh/SLMesh.cpp +++ b/modules/sl/source/mesh/SLMesh.cpp @@ -749,7 +749,7 @@ void SLMesh::generateVAO(SLGLVertexArray& vao) if (!Ji.empty()) { SLVVec4i gpuIndices(P.size(), SLVec4i(0, 0, 0, 0)); - SLVVec4f gpuWeights(P.size(), SLVec4f(1.0f, 0.0f, 0.0f, 0.0f)); + SLVVec4f gpuWeights(P.size(), SLVec4f(0.0f, 0.0f, 0.0f, 0.0f)); for (unsigned i = 0; i < P.size(); i++) { From 5291a44a50978879eecc1031fa99d8e900374850 Mon Sep 17 00:00:00 2001 From: Marino von Wattenwyl Date: Mon, 19 Jun 2023 17:08:15 +0200 Subject: [PATCH 009/108] [SL] Only create as many joint matrices as required --- modules/sl/source/mesh/SLMesh.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/sl/source/mesh/SLMesh.cpp b/modules/sl/source/mesh/SLMesh.cpp index 2ecef129..6791934e 100644 --- a/modules/sl/source/mesh/SLMesh.cpp +++ b/modules/sl/source/mesh/SLMesh.cpp @@ -450,7 +450,7 @@ void SLMesh::draw(SLSceneView* sv, SLNode* node) sp->uniform1i("u_skinningEnabled", skinningEnabled); if (skinningEnabled && !_jointMatrices.empty()) - sp->uniformMatrix4fv("u_jointMatrices", 100, (SLfloat*)&_jointMatrices[0]); + sp->uniformMatrix4fv("u_jointMatrices", _jointMatrices.size(), (SLfloat*)&_jointMatrices[0]); } SLint locTM = sp->getUniformLocation("u_tMatrix"); @@ -1569,7 +1569,7 @@ void SLMesh::transformSkin(bool forceCPUSkinning, if (_jointMatrices.empty()) { _jointMatrices.clear(); - _jointMatrices.resize(100); + _jointMatrices.resize((SLuint)_skeleton->numJoints()); } // update the joint matrix array From 29adf5b785386ea306b9b6fbd2279b6ec292cf8a Mon Sep 17 00:00:00 2001 From: Marino von Wattenwyl Date: Wed, 21 Jun 2023 09:04:26 +0200 Subject: [PATCH 010/108] [SL] Add some comments for GPU skinning --- modules/sl/source/gl/SLGLProgramGenerated.cpp | 10 +++++ modules/sl/source/mesh/SLMesh.cpp | 39 ++++++++++--------- 2 files changed, 30 insertions(+), 19 deletions(-) diff --git a/modules/sl/source/gl/SLGLProgramGenerated.cpp b/modules/sl/source/gl/SLGLProgramGenerated.cpp index 465c9670..43185814 100644 --- a/modules/sl/source/gl/SLGLProgramGenerated.cpp +++ b/modules/sl/source/gl/SLGLProgramGenerated.cpp @@ -205,12 +205,22 @@ const string vertMain_skinning = R"( 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 matrices represent this transformation and can change per frame + // to animate the mesh. The effect that a joint has on a vertex is + // specified by the four weights passed along with the joint indices. + totalPosition = vec4(0.0); totalNormal = vec3(0.0); + // iterate over all joints that are transforming this vertex. for (int i = 0; i < 4; i++) { + // get the transformation of this joint. mat4 jointMatrix = u_jointMatrices[a_jointIds[i]]; + + // apply the weighted transformation to the position and normal of the vertex. totalPosition += a_jointWeights[i] * jointMatrix * a_position; totalNormal += a_jointWeights[i] * mat3(jointMatrix) * a_normal; } diff --git a/modules/sl/source/mesh/SLMesh.cpp b/modules/sl/source/mesh/SLMesh.cpp index 6791934e..16d193a3 100644 --- a/modules/sl/source/mesh/SLMesh.cpp +++ b/modules/sl/source/mesh/SLMesh.cpp @@ -748,35 +748,36 @@ void SLMesh::generateVAO(SLGLVertexArray& vao) if (!Ji.empty()) { - SLVVec4i gpuIndices(P.size(), SLVec4i(0, 0, 0, 0)); - SLVVec4f gpuWeights(P.size(), SLVec4f(0.0f, 0.0f, 0.0f, 0.0f)); + // indices are passed to the shader as ivec4s + SLVVec4i indicesData(P.size(), SLVec4i(0, 0, 0, 0)); + // weights are passed to the shader as vec4s + SLVVec4f indicesWeights(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& indices = Ji[i]; - - SLint i0 = indices.size() >= 1 ? indices[0] : 0; - SLint i1 = indices.size() >= 2 ? indices[1] : 0; - SLint i2 = indices.size() >= 3 ? indices[2] : 0; - SLint i3 = indices.size() >= 4 ? indices[3] : 0; + const SLVuchar& curIndices = Ji[i]; - gpuIndices[i] = SLVec4i(i0, i1, i2, i3); + indicesData[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& weights = Jw[i]; - - SLfloat w0 = weights.size() >= 1 ? weights[0] : 0.0f; - SLfloat w1 = weights.size() >= 2 ? weights[1] : 0.0f; - SLfloat w2 = weights.size() >= 3 ? weights[2] : 0.0f; - SLfloat w3 = weights.size() >= 4 ? weights[3] : 0.0f; + const SLVfloat& curWeights = Jw[i]; - gpuWeights[i] = SLVec4f(w0, w1, w2, w3); + indicesWeights[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, &gpuIndices); - vao.setAttrib(AT_jointWeight, AT_jointWeight, &gpuWeights); + vao.setAttrib(AT_jointIndex, AT_jointIndex, &indicesData); + vao.setAttrib(AT_jointWeight, AT_jointWeight, &indicesWeights); } vao.generate((SLuint)P.size(), @@ -1621,7 +1622,7 @@ void SLMesh::transformSkin(bool forceCPUSkinning, else { _finalP = &P; - _finalN = &N; + _finalN = &N; } // update or create buffers From 8cf8955579a9c08b6d7380d1dd455ae937a88703 Mon Sep 17 00:00:00 2001 From: Marino von Wattenwyl Date: Wed, 21 Jun 2023 15:24:03 +0200 Subject: [PATCH 011/108] [SL] Fix GPU skinning UB in generateVAO --- modules/sl/source/mesh/SLMesh.cpp | 32 +++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/modules/sl/source/mesh/SLMesh.cpp b/modules/sl/source/mesh/SLMesh.cpp index 16d193a3..d3398093 100644 --- a/modules/sl/source/mesh/SLMesh.cpp +++ b/modules/sl/source/mesh/SLMesh.cpp @@ -746,20 +746,24 @@ void SLMesh::generateVAO(SLGLVertexArray& vao) } } - if (!Ji.empty()) + 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()) { - // indices are passed to the shader as ivec4s - SLVVec4i indicesData(P.size(), SLVec4i(0, 0, 0, 0)); + assert(Ji.size() == P.size()); + assert(Jw.size() == P.size()); - // weights are passed to the shader as vec4s - SLVVec4f indicesWeights(P.size(), SLVec4f(0.0f, 0.0f, 0.0f, 0.0f)); + 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()); - indicesData[i] = SLVec4i(curIndices.size() >= 1 ? curIndices[0] : 0, + 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); @@ -769,19 +773,19 @@ void SLMesh::generateVAO(SLGLVertexArray& vao) for (unsigned i = 0; i < P.size(); i++) { const SLVfloat& curWeights = Jw[i]; + assert(curWeights.size() == Ji[i].size()); - indicesWeights[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); + 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, &indicesData); - vao.setAttrib(AT_jointWeight, AT_jointWeight, &indicesWeights); + vao.setAttrib(AT_jointIndex, AT_jointIndex, &jointIndicesData); + vao.setAttrib(AT_jointWeight, AT_jointWeight, &jointWeightsData); } vao.generate((SLuint)P.size(), - !Ji.empty() && !_mat->supportsGPUSkinning() ? BU_stream : BU_static, + !Ji.empty() ? BU_stream : BU_static, Ji.empty()); } //----------------------------------------------------------------------------- From a49729fa5db46de3dd0529c43d3bb193588c81b8 Mon Sep 17 00:00:00 2001 From: Marino von Wattenwyl Date: Wed, 28 Jun 2023 10:17:55 +0200 Subject: [PATCH 012/108] [SL] Add support for variables to shader generator --- modules/sl/source/gl/SLGLProgramGenerated.cpp | 59 ++++++++++++------- modules/sl/source/gl/SLGLProgramGenerated.h | 4 ++ 2 files changed, 43 insertions(+), 20 deletions(-) diff --git a/modules/sl/source/gl/SLGLProgramGenerated.cpp b/modules/sl/source/gl/SLGLProgramGenerated.cpp index 43185814..6d630fe4 100644 --- a/modules/sl/source/gl/SLGLProgramGenerated.cpp +++ b/modules/sl/source/gl/SLGLProgramGenerated.cpp @@ -200,8 +200,8 @@ const string vertMain_v_uv1 = R"( v_uv1 = a_uv1; // pass diffuse color tex.coord. 1 for interpolation)"; const string vertMain_skinning = R"( - vec4 totalPosition; - vec3 totalNormal; + vec4 skinnedPosition; + vec3 skinnedNormal; if (u_skinningEnabled) { @@ -211,8 +211,8 @@ const string vertMain_skinning = R"( // to animate the mesh. The effect that a joint has on a vertex is // specified by the four weights passed along with the joint indices. - totalPosition = vec4(0.0); - totalNormal = vec3(0.0); + skinnedPosition = vec4(0.0); + skinnedNormal = vec3(0.0); // iterate over all joints that are transforming this vertex. for (int i = 0; i < 4; i++) @@ -221,17 +221,17 @@ const string vertMain_skinning = R"( mat4 jointMatrix = u_jointMatrices[a_jointIds[i]]; // apply the weighted transformation to the position and normal of the vertex. - totalPosition += a_jointWeights[i] * jointMatrix * a_position; - totalNormal += a_jointWeights[i] * mat3(jointMatrix) * a_normal; + skinnedPosition += a_jointWeights[i] * jointMatrix * a_position; + skinnedNormal += a_jointWeights[i] * mat3(jointMatrix) * a_normal; } - v_P_VS = vec3(mvMatrix * totalPosition); - v_N_VS = normalize(vec3(nMatrix * totalNormal)); + v_P_VS = vec3(mvMatrix * skinnedPosition); + v_N_VS = normalize(vec3(nMatrix * skinnedNormal)); } else { - totalPosition = a_position; - totalNormal = a_normal; + skinnedPosition = a_position; + skinnedNormal = a_normal; })"; const string vertMain_TBN_Nm = R"( @@ -309,17 +309,9 @@ const string vertMain_PS_EndAll_VertBillboard = R"( 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}; } )"; - -const string vertMain_EndSkinned = R"( - - // pass the vertex w. the fix-function transform - gl_Position = u_pMatrix * mvMatrix * totalPosition; -} -)"; - //----------------------------------------------------------------------------- const string vertMain_PS_U_Begin = R"( @@ -1861,6 +1853,9 @@ void SLGLProgramGenerated::buildPerPixCook(SLMaterial* mat, SLVLight* lights) if (Nm) vertCode += vertMain_TBN_Nm; vertCode += vertMain_EndAll; + // Vertex shader variables + setVariable(vertCode, "localPosition", "a_position"); + addCodeToShader(_shaders[0], vertCode, _name + ".vert"); // Assemble fragment shader code @@ -1979,7 +1974,10 @@ void SLGLProgramGenerated::buildPerPixBlinn(SLMaterial* mat, SLVLight* lights) if (uv1) vertCode += vertMain_v_uv1; if (skinning) vertCode += vertMain_skinning; if (Nm) vertCode += vertMain_TBN_Nm; - vertCode += skinning ? vertMain_EndSkinned : vertMain_EndAll; + vertCode += vertMain_EndAll; + + // Vertex shader variables + setVariable(vertCode, "localPosition", skinning ? "skinnedPosition" : "a_position"); addCodeToShader(_shaders[0], vertCode, _name + ".vert"); @@ -2291,6 +2289,10 @@ 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"); + addCodeToShader(_shaders[0], vertCode, _name + ".vert"); // Assemble fragment shader code @@ -2680,6 +2682,23 @@ void SLGLProgramGenerated::addCodeToShader(SLGLShader* shader, shader->file(generatedShaderPath + name); } //----------------------------------------------------------------------------- +//! Builds unique program name that identifies shader program +/*! 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..3ce56bd1 100644 --- a/modules/sl/source/gl/SLGLProgramGenerated.h +++ b/modules/sl/source/gl/SLGLProgramGenerated.h @@ -118,6 +118,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 }; //----------------------------------------------------------------------------- From d4854bedac88a2279a64540952aaa0055da4dabc Mon Sep 17 00:00:00 2001 From: Marino von Wattenwyl Date: Wed, 28 Jun 2023 11:26:26 +0200 Subject: [PATCH 013/108] [SL] GPU skinning in more complex shaders GPU skinning should now work with the Cook-Torrance reflection model and with normal mapping. --- modules/sl/source/gl/SLGLProgramGenerated.cpp | 100 +++++++++++------- 1 file changed, 64 insertions(+), 36 deletions(-) diff --git a/modules/sl/source/gl/SLGLProgramGenerated.cpp b/modules/sl/source/gl/SLGLProgramGenerated.cpp index 6d630fe4..ae0cafed 100644 --- a/modules/sl/source/gl/SLGLProgramGenerated.cpp +++ b/modules/sl/source/gl/SLGLProgramGenerated.cpp @@ -63,7 +63,7 @@ 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_jointMatrices = R"( +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 @@ -182,13 +182,13 @@ void main() {)"; 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); @@ -199,47 +199,63 @@ const string vertMain_v_uv0 = R"( 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 matrices represent this transformation and can change per frame - // to animate the mesh. The effect that a joint has on a vertex is - // specified by the four weights passed along with the joint indices. - - skinnedPosition = vec4(0.0); - skinnedNormal = vec3(0.0); - - // iterate over all joints that are transforming this vertex. - for (int i = 0; i < 4; i++) - { - // get the transformation of this joint. - mat4 jointMatrix = u_jointMatrices[a_jointIds[i]]; - - // apply the weighted transformation to the position and normal of the vertex. - skinnedPosition += a_jointWeights[i] * jointMatrix * a_position; - skinnedNormal += a_jointWeights[i] * mat3(jointMatrix) * a_normal; - } + // 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; - v_P_VS = vec3(mvMatrix * skinnedPosition); - v_N_VS = normalize(vec3(nMatrix * 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; + 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 @@ -1587,8 +1603,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
@@ -1823,6 +1840,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());
@@ -1831,9 +1851,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;
@@ -1845,6 +1867,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;
@@ -1854,7 +1877,9 @@ void SLGLProgramGenerated::buildPerPixCook(SLMaterial* mat, SLVLight* lights)
     vertCode += vertMain_EndAll;
 
     // Vertex shader variables
-    setVariable(vertCode, "localPosition", "a_position");
+    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");
 
@@ -1940,7 +1965,7 @@ void SLGLProgramGenerated::buildPerPixBlinn(SLMaterial* mat, SLVLight* lights)
     bool uv0 = mat->usesUVIndex(0);
     bool uv1 = mat->usesUVIndex(1);
 
-    // Check if we use GPU skinning
+    // Check if the shader has to support skinning
     bool skinning = mat->supportsGPUSkinning();
 
     // Assemble vertex shader code
@@ -1954,8 +1979,8 @@ void SLGLProgramGenerated::buildPerPixBlinn(SLMaterial* mat, SLVLight* lights)
     if (Nm) vertCode += vertInput_a_tangent;
     if (skinning) vertCode += vertInput_a_skinning;
     vertCode += vertInput_u_matrices_all;
-    if (skinning) vertCode += vertInput_u_jointMatrices;
     if (Nm) vertCode += vertInput_u_lightNm;
+    if (skinning) vertCode += vertInput_u_skinning;
 
     // Vertex shader outputs
     vertCode += vertOutput_v_P_VS;
@@ -1967,17 +1992,19 @@ 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;
     if (uv0) vertCode += vertMain_v_uv0;
     if (uv1) vertCode += vertMain_v_uv1;
-    if (skinning) vertCode += vertMain_skinning;
     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");
 
@@ -2292,6 +2319,7 @@ void SLGLProgramGenerated::buildPerPixVideoBkgdSm(SLVLight* lights)
 
     // Vertex shader variables
     setVariable(vertCode, "localPosition", "a_position");
+    setVariable(vertCode, "localNormal", "a_normal");
 
     addCodeToShader(_shaders[0], vertCode, _name + ".vert");
 
@@ -2682,7 +2710,7 @@ void SLGLProgramGenerated::addCodeToShader(SLGLShader*   shader,
     shader->file(generatedShaderPath + name);
 }
 //-----------------------------------------------------------------------------
-//! Builds unique program name that identifies shader program
+//! Sets a variable in the shader code.
 /*! A variable is specified in templates like this: ${variableName}.
  */
 void SLGLProgramGenerated::setVariable(std::string&       code,

From d890b770445be6892d9a045a9ff77ebbfe0740bc Mon Sep 17 00:00:00 2001
From: Marcus Hudritsch 
Date: Tue, 11 Jul 2023 14:42:35 +0200
Subject: [PATCH 014/108] Silenced some Warnings and typos

---
 .../ch00_new_cpp_features/ch00_NewCppFeatures.cpp          | 2 +-
 modules/cv/source/CVTrackedFeatures.cpp                    | 2 +-
 modules/math/CMakeLists.txt                                | 7 ++-----
 modules/math/externals/Shoemake/EulerAngles.cpp            | 6 ------
 modules/sl/source/gl/SLGLTexture.h                         | 4 ++--
 modules/sl/source/input/SLAssimpImporter.cpp               | 4 ++--
 6 files changed, 8 insertions(+), 17 deletions(-)

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/modules/cv/source/CVTrackedFeatures.cpp b/modules/cv/source/CVTrackedFeatures.cpp
index 329a9de1..6fb7ab1c 100644
--- a/modules/cv/source/CVTrackedFeatures.cpp
+++ b/modules/cv/source/CVTrackedFeatures.cpp
@@ -792,9 +792,9 @@ void CVTrackedFeatures::optimizeMatches()
 #endif
     }
 
-#if DO_FEATURE_BENCHMARKING
     sum_reprojection_error += reprojectionError / _marker.keypoints3D.size();
 
+#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/externals/Shoemake/EulerAngles.cpp b/modules/math/externals/Shoemake/EulerAngles.cpp
index bde45d00..7ef1f215 100644
--- a/modules/math/externals/Shoemake/EulerAngles.cpp
+++ b/modules/math/externals/Shoemake/EulerAngles.cpp
@@ -12,8 +12,6 @@ EulerAngles Eul_(float ai, float aj, float ah, int order)
     return (ea);
 }
 /* Construct quaternion from Euler angles (in radians). */
-#pragma warning(disable:4552)
-#pragma warning(disable:4244)
 Quat Eul_ToQuat(EulerAngles ea)
 {
     Quat qu;
@@ -43,8 +41,6 @@ Quat Eul_ToQuat(EulerAngles ea)
 }
 
 /* Construct matrix from Euler angles (in radians). */
-#pragma warning(disable:4552)
-#pragma warning( disable:4244)
 void Eul_ToHMatrix(EulerAngles ea, HMatrix M)
 {  double ti, tj, th, ci, cj, ch, si, sj, sh, cc, cs, sc, ss;
    int i,j,k,h,n,s,f;
@@ -68,8 +64,6 @@ void Eul_ToHMatrix(EulerAngles ea, HMatrix M)
 }
 
 /* Convert matrix to Euler angles (in radians). */
-#pragma warning(disable:4552)
-#pragma warning( disable:4244)
 EulerAngles Eul_FromHMatrix(HMatrix M, int order)
 {
     EulerAngles ea;
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/input/SLAssimpImporter.cpp b/modules/sl/source/input/SLAssimpImporter.cpp
index a031840e..a05de729 100644
--- a/modules/sl/source/input/SLAssimpImporter.cpp
+++ b/modules/sl/source/input/SLAssimpImporter.cpp
@@ -917,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)
@@ -1274,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;

From be175660b2261a281d0ee3f5452df5fd3cdd756f Mon Sep 17 00:00:00 2001
From: Marcus Hudritsch 
Date: Wed, 20 Sep 2023 08:16:32 +0200
Subject: [PATCH 015/108] Update README.md

---
 README.md | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

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

From af8fd9293c218e4b13cd65a05010b0c9b454319c Mon Sep 17 00:00:00 2001
From: Marcus Hudritsch 
Date: Fri, 29 Sep 2023 12:44:09 +0200
Subject: [PATCH 016/108] Update ch08_BlinnPhongLighting.frag

---
 data/shaders/ch08_BlinnPhongLighting.frag | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

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

From 406e36972ce0a6b2011b91df8f86bfac1c1e91aa Mon Sep 17 00:00:00 2001
From: luc 
Date: Thu, 26 Oct 2023 18:29:01 +0200
Subject: [PATCH 017/108] Add support for draw instanced and use of a shared
 VBO in multiples VAO

---
 modules/sl/source/gl/SLGLVertexArray.cpp  |  78 +++++++++++-
 modules/sl/source/gl/SLGLVertexArray.h    |  39 ++++--
 modules/sl/source/gl/SLGLVertexBuffer.cpp | 141 +++++++++-------------
 modules/sl/source/gl/SLGLVertexBuffer.h   |   2 +-
 modules/sl/source/mesh/SLMesh.cpp         |   7 +-
 modules/sl/source/mesh/SLMesh.h           |   2 +-
 6 files changed, 169 insertions(+), 100 deletions(-)

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..b0c0459d 100644
--- a/modules/sl/source/gl/SLGLVertexArray.h
+++ b/modules/sl/source/gl/SLGLVertexArray.h
@@ -130,6 +130,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 +161,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 +186,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 +199,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..d5bae373 100644
--- a/modules/sl/source/gl/SLGLVertexBuffer.cpp
+++ b/modules/sl/source/gl/SLGLVertexBuffer.cpp
@@ -195,33 +195,6 @@ void SLGLVertexBuffer::generate(SLuint          numVertices,
 
     if (inputIsInterleaved)
     {
-        for (auto a : _attribs)
-        {
-            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, _attribs[0].dataPointer, _usage);
     }
@@ -244,30 +217,6 @@ void SLGLVertexBuffer::generate(SLuint          numVertices,
                     for (SLuint b = 0; b < elementSizeBytes; ++b)
                         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
@@ -287,28 +236,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,34 +245,84 @@ 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 (_outputInterleaved) // 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_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_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);
             }
         }
     }
+    GET_GL_ERROR;
 }
 //-----------------------------------------------------------------------------
 /*! This method is only used by SLGLVertexArray drawing methods for OpenGL
diff --git a/modules/sl/source/gl/SLGLVertexBuffer.h b/modules/sl/source/gl/SLGLVertexBuffer.h
index 7d6ec776..b6425fa3 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();
diff --git a/modules/sl/source/mesh/SLMesh.cpp b/modules/sl/source/mesh/SLMesh.cpp
index e2d0be05..c9c0b0ce 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(); @@ -469,7 +469,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())) diff --git a/modules/sl/source/mesh/SLMesh.h b/modules/sl/source/mesh/SLMesh.h index ff475e3f..957f2ad4 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); From dd1e89b441b6ec9863c6c85306302d47e38aba22 Mon Sep 17 00:00:00 2001 From: Marino von Wattenwyl Date: Mon, 30 Oct 2023 16:51:39 +0100 Subject: [PATCH 018/108] [WebGPU] Set up CMake to download wgpu --- apps/CMakeLists.txt | 6 ++++- apps/webgpu/CMakeLists.txt | 50 ++++++++++++++++++++++++++++++++++++++ apps/webgpu/main.cpp | 18 ++++++++++++++ 3 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 apps/webgpu/CMakeLists.txt create mode 100644 apps/webgpu/main.cpp diff --git a/apps/CMakeLists.txt b/apps/CMakeLists.txt index 73765b36..d4889f7a 100644 --- a/apps/CMakeLists.txt +++ b/apps/CMakeLists.txt @@ -9,4 +9,8 @@ else() add_subdirectory(app_demo_imgui) add_subdirectory(app_demo_node) add_subdirectory(app_demo_slproject) -endif() + + if (SL_BUILD_WEBGPU_DEMO) + add_subdirectory(webgpu) + endif () +endif() \ No newline at end of file diff --git a/apps/webgpu/CMakeLists.txt b/apps/webgpu/CMakeLists.txt new file mode 100644 index 00000000..9b74fd34 --- /dev/null +++ b/apps/webgpu/CMakeLists.txt @@ -0,0 +1,50 @@ +if (SYSTEM_NAME_UPPER MATCHES "WINDOWS") + set(WGPU_OS "windows") +elseif (SYSTEM_NAME_UPPER MATCHES "DARWIN") + set(WGPU_OS "macos") +elseif (SYSTEM_NAME_UPPER MATCHES "LINUX") + set(WGPU_OS "linux") +else () + message(FATAL_ERROR "[WebGPU] System '${SYSTEM_NAME_UPPER}' is not supported") +endif () + +if (CMAKE_SYSTEM_PROCESSOR MATCHES "AMD64" OR CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64") + set(WGPU_ARCH "x86_64") +elseif (CMAKE_SYSTEM_PROCESSOR MATCHES "arm64") + set(WGPU_ARCH "arm64") +else () + message(FATAL_ERROR "[WebGPU] Architecture '${CMAKE_SYSTEM_PROCESSOR}' is not supported") +endif () + +set(WGPU_VERSION "v0.18.0.1") +set(WGPU_DIR "${SL_PROJECT_ROOT}/externals/prebuilt/wgpu_${WGPU_VERSION}") +set(WGPU_ZIP "wgpu-${WGPU_OS}-${WGPU_ARCH}-release.zip") + +if (NOT EXISTS "${WGPU_DIR}") + set(BASE_URL "https://github.com/gfx-rs/wgpu-native/releases/download") + set(DOWNLOAD_URL "${BASE_URL}/${WGPU_VERSION}/${WGPU_ZIP}") + + message(STATUS "[WebGPU] Downloading ${WGPU_ZIP}...") + file(DOWNLOAD "${DOWNLOAD_URL}" "${WGPU_DIR}/${WGPU_ZIP}") + message(STATUS "[WebGPU] Download complete") + + execute_process(COMMAND ${CMAKE_COMMAND} -E tar xzf "${WGPU_ZIP}" WORKING_DIRECTORY "${WGPU_DIR}") + message(STATUS "[WebGPU] Files extracted") + + file(REMOVE "${WGPU_DIR}/commit-sha" "${WGPU_DIR}/${WGPU_ZIP}") + message(STATUS "[WebGPU] Directory cleaned up") +else () + message(STATUS "[WebGPU] Directory existing") +endif () + +add_library(wgpu STATIC IMPORTED) +set_target_properties(wgpu PROPERTIES + IMPORTED_LOCATION "${WGPU_DIR}/wgpu_native.lib" + INTERFACE_INCLUDE_DIRECTORIES "${WGPU_DIR}") + +if (SYSTEM_NAME_UPPER MATCHES "WINDOWS") + target_link_libraries(wgpu INTERFACE "ws2_32" "userenv" "advapi32" "bcrypt" "d3dcompiler" "ntdll" "opengl32") +endif () + +add_executable(webgpu-demo main.cpp) +target_link_libraries(webgpu-demo wgpu) diff --git a/apps/webgpu/main.cpp b/apps/webgpu/main.cpp new file mode 100644 index 00000000..c014c1dc --- /dev/null +++ b/apps/webgpu/main.cpp @@ -0,0 +1,18 @@ +#include + +#include + +int main(int argc, const char* argv[]) { + WGPUInstance instance = wgpuCreateInstance(nullptr); + if (instance) { + std::cout << "[WebGPU] Instance created" << std::endl; + } else { + std::cerr << "[WebGPU] Failed to create instance" << std::endl; + return 1; + } + + wgpuInstanceRelease(instance); + std::cerr << "[WebGPU] Resources released" << std::endl; + + return 0; +} \ No newline at end of file From 50b2f28340db2020c6cb1013132a817bb0d6f6f3 Mon Sep 17 00:00:00 2001 From: Marino von Wattenwyl Date: Thu, 2 Nov 2023 17:16:02 +0100 Subject: [PATCH 019/108] [WebGPU] Add triangle demo for Windows --- CMakeLists.txt | 2 + apps/webgpu/CMakeLists.txt | 2 +- apps/webgpu/main.cpp | 318 ++++++++++++++++++++++++++++++++++++- 3 files changed, 314 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2018324f..f03e78da 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -49,6 +49,7 @@ option(SL_BUILD_WAI "Specifies if the WAI library should be built" ON) option(SL_BUILD_APPS "Specifies if sample apps should be built" ON) option(SL_BUILD_EXERCISES "Specifies if exercise apps should be built" ON) option(SL_BUILD_VULKAN_APPS "Specifies if vulkan apps should be built" OFF) +option(SL_BUILD_WEBGPU_DEMO "Specifies if WebGPU demo should be built" ON) option(SL_BUILD_WITH_OPTIX "Specifies if Optix renderer should be built" OFF) option(SL_BUILD_WITH_KTX "Specifies if Kronos Texture library (ktx) should be used" ON) option(SL_BUILD_WITH_OPENSSL "Specifies if OpenSSL should be used" ON) @@ -71,6 +72,7 @@ message(STATUS "SL_BUILD_WAI: ${SL_BUILD_WAI}") message(STATUS "SL_BUILD_APPS: ${SL_BUILD_APPS}") message(STATUS "SL_BUILD_EXERCISES: ${SL_BUILD_EXERCISES}") message(STATUS "SL_BUILD_VULKAN_APPS: ${SL_BUILD_VULKAN_APPS}") +message(STATUS "SL_BUILD_WEBGPU_DEMO: ${SL_BUILD_WEBGPU_DEMO}") message(STATUS "SL_BUILD_WITH_OPTIX: ${SL_BUILD_WITH_OPTIX}") message(STATUS "SL_BUILD_WITH_KTX: ${SL_BUILD_WITH_KTX}") message(STATUS "SL_BUILD_WITH_OPENSSL: ${SL_BUILD_WITH_OPENSSL}") diff --git a/apps/webgpu/CMakeLists.txt b/apps/webgpu/CMakeLists.txt index 9b74fd34..97bfde63 100644 --- a/apps/webgpu/CMakeLists.txt +++ b/apps/webgpu/CMakeLists.txt @@ -47,4 +47,4 @@ if (SYSTEM_NAME_UPPER MATCHES "WINDOWS") endif () add_executable(webgpu-demo main.cpp) -target_link_libraries(webgpu-demo wgpu) +target_link_libraries(webgpu-demo PRIVATE wgpu glfw3dll) diff --git a/apps/webgpu/main.cpp b/apps/webgpu/main.cpp index c014c1dc..b23d48cb 100644 --- a/apps/webgpu/main.cpp +++ b/apps/webgpu/main.cpp @@ -1,18 +1,322 @@ +#if defined(_WIN32) +# define SYSTEM_WINDOWS +# define GLFW_EXPOSE_NATIVE_WIN32 +# define WIN32_LEAN_AND_MEAN +# include +#elif defined(__linux__) +# define SYSTEM_LINUX +# define GLFW_EXPOSE_NATIVE_X11 +#elif defined(__APPLE__) +# define SYSTEM_DARWIN +# define GLFW_EXPOSE_NATIVE_COCOA +#endif + #include +#include +#include #include +#include + +#define WEBGPU_DEMO_LOG(msg) std::cout << (msg) << std::endl + +#define WEBGPU_DEMO_CHECK(condition, errorMsg) \ + if (!(condition)) \ + { \ + std::cerr << (errorMsg) << std::endl; \ + std::exit(1); \ + } + +void handleAdapterRequest(WGPURequestAdapterStatus status, + WGPUAdapter adapter, + char const* message, + void* userdata) +{ + WEBGPU_DEMO_CHECK(status == WGPURequestAdapterStatus_Success, + "[WebGPU] Failed to acquire adapter: " + std::string(message)); + WEBGPU_DEMO_LOG("[WebGPU] Adapter acquired"); + + WGPUAdapter* outAdapter = (WGPUAdapter*)userdata; + *outAdapter = adapter; +} + +void handleDeviceRequest(WGPURequestDeviceStatus status, + WGPUDevice device, + char const* message, + void* userdata) +{ + WEBGPU_DEMO_CHECK(status == WGPURequestDeviceStatus_Success, + "[WebGPU] Failed to acquire device: " + std::string(message)); + WEBGPU_DEMO_LOG("[WebGPU] Device acquired"); + + WGPUDevice* outDevice = (WGPUDevice*)userdata; + *outDevice = device; +} + +int main(int argc, const char* argv[]) +{ + // === Initialize GLFW === + + WEBGPU_DEMO_CHECK(glfwInit(), "[GLFW] Failed to initialize"); + WEBGPU_DEMO_LOG("[GLFW] Initialized"); + + // === Create the GLFW window === + + // Prevent GLFW from creating an OpenGL context as the underlying graphics API probably won't be OpenGL. + glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + + // Create a non-resizable window as we currently don't recreate surfaces when the window size changes. + glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); + + GLFWwindow* window = glfwCreateWindow(1280, 720, "WebGPU Demo", nullptr, nullptr); + WEBGPU_DEMO_CHECK(window, "[GLFW] Window created"); + + // === Create a WebGPU instance === + // The instance is the root interface to WebGPU through which we create all other WebGPU resources. -int main(int argc, const char* argv[]) { WGPUInstance instance = wgpuCreateInstance(nullptr); - if (instance) { - std::cout << "[WebGPU] Instance created" << std::endl; - } else { - std::cerr << "[WebGPU] Failed to create instance" << std::endl; - return 1; + WEBGPU_DEMO_CHECK(instance, "[WebGPU] Failed to create instance"); + WEBGPU_DEMO_LOG("[WebGPU] Instance created"); + + // === Acquire a WebGPU adapter === + // An adapter provides information about the capabilities of the GPU. + + WGPURequestAdapterOptions adapterOptions = {}; + + WGPUAdapter adapter; + wgpuInstanceRequestAdapter(instance, &adapterOptions, handleAdapterRequest, &adapter); + + // === Acquire a WebGPU device === + // A device provides access to a GPU and is created from an adapter. + + WGPUDeviceDescriptor deviceDesc = {}; + deviceDesc.label = "Demo Device"; + deviceDesc.defaultQueue.label = "Demo Queue"; + + WGPUDevice device; + wgpuAdapterRequestDevice(adapter, &deviceDesc, handleDeviceRequest, &device); + + // === Acquire a WebGPU queue === + // The queue is where the commands for the GPU are submitted to. + + WGPUQueue queue = wgpuDeviceGetQueue(device); + WEBGPU_DEMO_CHECK(queue, "[WebGPU] Failed to acquire queue"); + WEBGPU_DEMO_LOG("[WebGPU] Queue acquired"); + + // === Create a WebGPU surface === + // The surface is where our rendered images will be presented to. + // It is created from window handles on most platforms and from a canvas in the browser. + +#ifdef SYSTEM_WINDOWS + WGPUSurfaceDescriptorFromWindowsHWND nativeSurfaceDesc = {}; + nativeSurfaceDesc.chain.sType = WGPUSType_SurfaceDescriptorFromWindowsHWND; + nativeSurfaceDesc.hinstance = GetModuleHandle(nullptr); + nativeSurfaceDesc.hwnd = glfwGetWin32Window(window); +#endif + + WGPUSurfaceDescriptor surfaceDesc = {}; + surfaceDesc.label = "Demo Surface"; + surfaceDesc.nextInChain = (const WGPUChainedStruct*)&nativeSurfaceDesc; + + WGPUSurface surface = wgpuInstanceCreateSurface(instance, &surfaceDesc); + WEBGPU_DEMO_CHECK(surface, "[WebGPU] Failed to create surface"); + WEBGPU_DEMO_LOG("[WebGPU] Surface created"); + + // === Configure the surface === + // The surface needs to be configured before images can be presented. + + // Query the surface capabilities from the adapter. + WGPUSurfaceCapabilities surfaceCapabilities; + wgpuSurfaceGetCapabilities(surface, adapter, &surfaceCapabilities); + + // Get the window size from the GLFW window. + int surfaceWidth; + int surfaceHeight; + glfwGetWindowSize(window, &surfaceWidth, &surfaceHeight); + + WGPUSurfaceConfiguration surfaceConfig = {}; + surfaceConfig.device = device; + surfaceConfig.usage = WGPUTextureUsage_RenderAttachment; + surfaceConfig.format = surfaceCapabilities.formats[0]; + surfaceConfig.presentMode = WGPUPresentMode_Fifo; + surfaceConfig.alphaMode = surfaceCapabilities.alphaModes[0]; + surfaceConfig.width = surfaceWidth; + surfaceConfig.height = surfaceHeight; + wgpuSurfaceConfigure(surface, &surfaceConfig); + WEBGPU_DEMO_LOG("[WebGPU] Surface configured"); + + // === Create the shader module === + // Create a shader module for use in our render pipeline. + // Shaders are written in a WebGPU-specific language called WGSL (WebGPU shader language). + // Shader modules can be used in multiple pipeline stages by specifying different entry points. + + const char* shaderSource = R"( + @vertex + fn vs_main(@builtin(vertex_index) in_vertex_index: u32) -> @builtin(position) vec4f { + if (in_vertex_index == 0u) { + return vec4f(-0.5, -0.5, 0.0, 1.0); + } else if (in_vertex_index == 1u) { + return vec4f(0.5, -0.5, 0.0, 1.0); + } else { + return vec4f(0.0, 0.5, 0.0, 1.0); + } + } + + @fragment + fn fs_main() -> @location(0) vec4f { + return vec4f(0.2, 0.2, 1.0, 1.0); + } + )"; + + WGPUShaderModuleWGSLDescriptor shaderModuleWGSLDesc = {}; + shaderModuleWGSLDesc.chain.sType = WGPUSType_ShaderModuleWGSLDescriptor; + shaderModuleWGSLDesc.code = shaderSource; + + WGPUShaderModuleDescriptor shaderModuleDesc = {}; + shaderModuleDesc.label = "Hello Triangle Shader"; + shaderModuleDesc.nextInChain = (const WGPUChainedStruct*)&shaderModuleWGSLDesc; + + WGPUShaderModule shaderModule = wgpuDeviceCreateShaderModule(device, &shaderModuleDesc); + WEBGPU_DEMO_CHECK(shaderModule, "[WebGPU] Failed to create shader module"); + WEBGPU_DEMO_LOG("[WebGPU] Shader module created"); + + // === Create the WebGPU render pipeline === + // The render pipeline specifies the configuration for the fixed-function stages as well as + // the shaders for the programmable stages of the hardware pipeline. + + // Configuration for the vertex shader stage + WGPUVertexState vertexState = {}; + vertexState.module = shaderModule; + vertexState.entryPoint = "vs_main"; + + // Configuration for the primitive assembly and rasterization stages + WGPUPrimitiveState primitiveState = {}; + primitiveState.topology = WGPUPrimitiveTopology_TriangleList; + primitiveState.stripIndexFormat = WGPUIndexFormat_Undefined; + primitiveState.frontFace = WGPUFrontFace_CCW; + primitiveState.cullMode = WGPUCullMode_None; + + // Configuration for multisampling + WGPUMultisampleState multisampleState = {}; + multisampleState.count = 1; + multisampleState.mask = WGPUColorWriteMask_All; + + // Configuration for the fragment shader stage + WGPUColorTargetState colorTargetState = {}; + colorTargetState.format = surfaceCapabilities.formats[0]; + colorTargetState.writeMask = WGPUColorWriteMask_All; + WGPUFragmentState fragmentState = {}; + fragmentState.module = shaderModule; + fragmentState.entryPoint = "fs_main"; + fragmentState.targetCount = 1; + fragmentState.targets = &colorTargetState; + + // Configuration for the entire pipeline + WGPURenderPipelineDescriptor pipelineDesc = {}; + pipelineDesc.label = "Hello Triangle Pipeline"; + pipelineDesc.vertex = vertexState; + pipelineDesc.primitive = primitiveState; + pipelineDesc.multisample = multisampleState; + pipelineDesc.fragment = &fragmentState; + + WGPURenderPipeline pipeline = wgpuDeviceCreateRenderPipeline(device, &pipelineDesc); + WEBGPU_DEMO_CHECK(pipeline, "[WebGPU] Failed to create render pipeline"); + WEBGPU_DEMO_LOG("[WebGPU] Render pipeline created"); + + // === Render loop === + + while (!glfwWindowShouldClose(window)) + { + // === Create a WebGPU texture view === + // The texture view is where we render our image into. + + // Get a texture from the surface to render into. + WGPUSurfaceTexture surfaceTexture; + wgpuSurfaceGetCurrentTexture(surface, &surfaceTexture); + WEBGPU_DEMO_CHECK(surfaceTexture.status == WGPUSurfaceGetCurrentTextureStatus_Success, "[WebGPU] Failed to acquire current surface texture"); + + // Create a view into the texture to specify where and how to modify the texture. + WGPUTextureView view = wgpuTextureCreateView(surfaceTexture.texture, nullptr); + + // === Create a WebGPU command encoder === + // The encoder encodes the commands for the GPU into a command buffer. + + WGPUCommandEncoderDescriptor cmdEncoderDesc = {}; + cmdEncoderDesc.label = " Hello Triangle Command Encoder"; + + WGPUCommandEncoder cmdEncoder = wgpuDeviceCreateCommandEncoder(device, &cmdEncoderDesc); + WEBGPU_DEMO_CHECK(cmdEncoder, "[WebGPU] Failed to create command encoder"); + + // === Create a WebGPU render pass === + // The render pass specifies what attachments to use while rendering. + // A color attachment specifies what view to render into and what to do with the texture before and after + // rendering. We clear the texture before rendering and store the results after rendering. + + WGPURenderPassColorAttachment colorAttachment = {}; + colorAttachment.view = view; + colorAttachment.loadOp = WGPULoadOp_Clear; + colorAttachment.storeOp = WGPUStoreOp_Store; + colorAttachment.clearValue.r = 0.3; + colorAttachment.clearValue.g = 0.0; + colorAttachment.clearValue.b = 0.2; + colorAttachment.clearValue.a = 1.0; + + WGPURenderPassDescriptor renderPassDesc = {}; + renderPassDesc.label = "Hello Triangle Render Pass"; + renderPassDesc.colorAttachmentCount = 1; + renderPassDesc.colorAttachments = &colorAttachment; + + // === Encode the commands === + // The commands to begin a render pass, bind a pipeline, draw the triangle and end the render pass + // are encoded into a buffer. + + WGPURenderPassEncoder renderPassEncoder = wgpuCommandEncoderBeginRenderPass(cmdEncoder, &renderPassDesc); + wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipeline); + wgpuRenderPassEncoderDraw(renderPassEncoder, 3, 1, 0, 0); + wgpuRenderPassEncoderEnd(renderPassEncoder); + + // === Get the command buffer === + // The command encoder is finished to get the commands for the GPU to execute in a command buffer. + + WGPUCommandBufferDescriptor cmdBufferDesc = {}; + cmdBufferDesc.label = "Hello Triangle Command Buffer"; + + WGPUCommandBuffer cmdBuffer = wgpuCommandEncoderFinish(cmdEncoder, &cmdBufferDesc); + + // === Submit the command buffer to the GPU === + // The work for the GPU is submitted through the queue and executed. + wgpuQueueSubmit(queue, 1, &cmdBuffer); + + // === Present the surface === + // This presents our rendered texture to the screen. + wgpuSurfacePresent(surface); + + // === Clean up resources === + wgpuCommandBufferRelease(cmdBuffer); + wgpuRenderPassEncoderRelease(renderPassEncoder); + wgpuCommandEncoderRelease(cmdEncoder); + wgpuTextureViewRelease(view); + wgpuTextureRelease(surfaceTexture.texture); + + glfwPollEvents(); } + // === Release all WebGPU resources === + + wgpuRenderPipelineRelease(pipeline); + wgpuShaderModuleRelease(shaderModule); + wgpuSurfaceRelease(surface); + wgpuQueueRelease(queue); + wgpuDeviceRelease(device); + wgpuAdapterRelease(adapter); wgpuInstanceRelease(instance); - std::cerr << "[WebGPU] Resources released" << std::endl; + WEBGPU_DEMO_LOG("[WebGPU] Resources released"); + + // === Destroy the window and terminate GLFW === + + glfwDestroyWindow(window); + glfwTerminate(); + WEBGPU_DEMO_LOG("[GLFW] Window closed and terminated"); return 0; } \ No newline at end of file From 7618a51eb6b31906f245bb98927900eef2414be6 Mon Sep 17 00:00:00 2001 From: luc Date: Thu, 2 Nov 2023 17:22:42 +0100 Subject: [PATCH 020/108] working version without finition --- modules/sl/source/SLMaterial.cpp | 8 +- modules/sl/source/SLMaterial.h | 2 +- modules/sl/source/gl/SLGLProgramGenerated.cpp | 285 ++++++++++++++++-- modules/sl/source/gl/SLGLProgramGenerated.h | 13 +- modules/sl/source/mesh/SLParticleSystem.cpp | 80 ++++- modules/sl/source/mesh/SLParticleSystem.h | 6 +- 6 files changed, 360 insertions(+), 34 deletions(-) diff --git a/modules/sl/source/SLMaterial.cpp b/modules/sl/source/SLMaterial.cpp index 25719855..d26365a3 100644 --- a/modules/sl/source/SLMaterial.cpp +++ b/modules/sl/source/SLMaterial.cpp @@ -377,7 +377,7 @@ SLMaterial::~SLMaterial() 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. @@ -395,11 +395,15 @@ void SLMaterial::generateProgramPS() // 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); } } diff --git a/modules/sl/source/SLMaterial.h b/modules/sl/source/SLMaterial.h index 60dbbd97..983f2ff5 100644 --- a/modules/sl/source/SLMaterial.h +++ b/modules/sl/source/SLMaterial.h @@ -108,7 +108,7 @@ class SLMaterial : public SLObject SLGLProgram* program); ~SLMaterial() override; - void generateProgramPS(); + void generateProgramPS(bool renderInstanced = false); void activate(SLCamera* cam, SLVLight* lights, SLSkybox* skybox = nullptr); diff --git a/modules/sl/source/gl/SLGLProgramGenerated.cpp b/modules/sl/source/gl/SLGLProgramGenerated.cpp index bdb9aabf..9aee3ca3 100644 --- a/modules/sl/source/gl/SLGLProgramGenerated.cpp +++ b/modules/sl/source/gl/SLGLProgramGenerated.cpp @@ -44,6 +44,8 @@ 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"( @@ -103,7 +105,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))"; @@ -146,6 +147,46 @@ 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_texNum = R"( +out uint texNum; // Num of texture in flipbook)"; +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 fragInput_PS_instanced_texNum = R"( +in uint texNum; // Num of texture in flipbook)"; + +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_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 + 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,9 +208,15 @@ 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)"; @@ -213,8 +260,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 +292,82 @@ 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 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_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(position + a_positionP, 1); +} +)"; + + + const string vertMain_PS_EndAll = R"( // Modelview matrix multiplication with (particle position + particle generator position) @@ -265,8 +385,7 @@ const string vertMain_EndAll = R"( // pass the vertex w. the fix-function transform gl_Position = u_pMatrix * mvMatrix * a_position; -} -)"; +})"; //----------------------------------------------------------------------------- const string vertMain_PS_U_Begin = R"( @@ -336,6 +455,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 +483,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 +494,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 +756,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,7 +765,6 @@ void main() )"; //----------------------------------------------------------------------------- const string fragMain_PS = R"( - void main() { // Just set the interpolated color from the vertex shader @@ -657,21 +778,36 @@ void main() discard; )"; -const string fragMain_PS_withoutColor = R"( + +const string fragMain_instanced_PS_begin = R"( void main() -{ +{ +)"; + +const string fragMain_PS_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 *= v_particleColor.a; + 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 +1580,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 +1594,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; @@ -1643,7 +1777,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"; @@ -1733,7 +1866,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 isRenderInstanced) { if (mat->name() == "IBLMat") { @@ -1745,7 +1878,12 @@ void SLGLProgramGenerated::buildProgramCodePS(SLMaterial* mat, bool isDrawProg) _shaders[1]->type() == ST_fragment); if (isDrawProg) - buildPerPixParticle(mat); + { + if (isRenderInstanced) + buildPerPixParticleInstanced(mat); + else + buildPerPixParticle(mat); + } else buildPerPixParticleUpdate(mat); } @@ -1974,6 +2112,117 @@ 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_a_texNum; // per particle texture number + + // Vertex shader uniforms + vertCode += vertInput_PS_u_ScaRa; + vertCode += vertInput_u_matrix_p; + vertCode += vertInput_PS_u_time; + 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; + if (Co && CoOvLi) vertCode += vertInput_PS_u_colorOvLF; + + // Vertex shader outputs + if (FlBoTex) vertCode += vertOutput_PS_instanced_texNum; + vertCode += vertOutput_PS_instanced_transparency; + vertCode += vertOutput_PS_v_tC; + + // Vertex shader functions + if (Co && CoOvLi) vertCode += vertFunction_PS_ColorOverLT; + + // Vertex shader main loop + vertCode += vertMain_instanced_Begin; + vertCode += vertMain_PS_v_a; + 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; + //if (rot) vertCode += vertMain_PS_v_r; + 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 (Co && CoOvLi) vertCode += vertMain_PS_v_doColorOverLT; + if (FlBoTex) vertCode += vertMain_PS_v_texNum; + if (billboardType == BT_Vertical || billboardType == BT_Horizontal) + vertCode += vertMain_PS_EndAll_VertBillboard; + else + vertCode += vertMain_PS_instanced_EndAll; + + addCodeToShader(_shaders[0], vertCode, _name + ".vert"); + + //////////////////////////////// + // Assemble fragment shader code + //////////////////////////////// + + string fragCode; + fragCode += shaderHeader(); + + if (Co && !CoOvLi) vertCode += fragInput_PS_u_c; + // Fragment shader inputs + if (FlBoTex) fragCode += fragInput_PS_instanced_texNum; + fragCode += fragInput_PS_v_tC; + if (Co && !CoOvLi) fragCode += fragInput_PS_u_c; + 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; + + // Fragment shader main loop + fragCode += fragMain_instanced_PS_begin; + if (Co || CoOvLi) + { + fragCode += fragMain_PS_instanced_c; + fragCode += fragMain_instanced_PS_end; + } + else + fragCode += fragMain_PS_withoutColor; + + fragCode += fragMain_PS_endAll; + + addCodeToShader(_shaders[1], fragCode, _name + ".frag"); +} +//----------------------------------------------------------------------------- void SLGLProgramGenerated::buildPerPixParticle(SLMaterial* mat) { assert(_shaders.size() > 2 && diff --git a/modules/sl/source/gl/SLGLProgramGenerated.h b/modules/sl/source/gl/SLGLProgramGenerated.h index 93ebc1da..22865155 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); @@ -92,7 +100,7 @@ class SLGLProgramGenerated : public SLGLProgram string& programName, bool isDrawProg); - void buildProgramCodePS(SLMaterial* mat, bool isDrawProg); + void buildProgramCodePS(SLMaterial* mat, bool isDrawProg, bool isRenderInstanced = false); void buildProgramCode(SLMaterial* mat, SLVLight* lights); void beginShader(SLCamera* cam, @@ -104,6 +112,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 diff --git a/modules/sl/source/mesh/SLParticleSystem.cpp b/modules/sl/source/mesh/SLParticleSystem.cpp index c69dcefe..c5751e4c 100644 --- a/modules/sl/source/mesh/SLParticleSystem.cpp +++ b/modules/sl/source/mesh/SLParticleSystem.cpp @@ -33,7 +33,13 @@ SLParticleSystem::SLParticleSystem(SLAssetManager* assetMgr, { assert(!name.empty()); - _primitive = PT_points; + // To be added to constructor + _renderInstanced = true; + + if (_renderInstanced) + _primitive = PT_triangles; + else + _primitive = PT_points; if (amount > UINT_MAX) // Need to change for number of floats SL_EXIT_MSG("SLParticleSystem supports max. 2^32 vertices."); @@ -43,8 +49,6 @@ SLParticleSystem::SLParticleSystem(SLAssetManager* assetMgr, _velocityRndMin = velocityRandomStart; _velocityRndMax = velocityRandomEnd; - P.resize(1); // To trick parent class - _textureFirst = texC; _textureFlipbook = texFlipbook; @@ -447,6 +451,39 @@ void SLParticleSystem::generate() _vao2.setAttrib(AT_initialPosition, AT_initialPosition, &tempInitP); _vao2.generateTF((SLuint)tempP.size()); + if (_renderInstanced) + { + /* 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); + + /* 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()); + } + else + { + P.push_back(SLVec3f(1, 1, 0)); + I32.push_back(0); + } + _isGenerated = true; } //----------------------------------------------------------------------------- @@ -542,7 +579,7 @@ 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 @@ -556,7 +593,7 @@ void SLParticleSystem::draw(SLSceneView* sv, SLNode* node) //////////////////// if (!_mat->program() || !_mat->programTF()) - _mat->generateProgramPS(); + _mat->generateProgramPS(_renderInstanced); //////////////////////////////////////////////// // Calculate time and paused and frustum culling @@ -680,14 +717,20 @@ void SLParticleSystem::draw(SLSceneView* sv, SLNode* node) _vao1.beginTF(_vao2.tfoID()); _vao1.drawArrayAs(PT_points); _vao1.endTF(); - _vao = _vao2; + if (_renderInstanced) + _vao = _renderVao2; + 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 +752,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 +770,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 +879,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, _amount*2); + else + SLMesh::draw(sv, node); /////////////////////// if (_doColor && _doBlendBrightness) diff --git a/modules/sl/source/mesh/SLParticleSystem.h b/modules/sl/source/mesh/SLParticleSystem.h index fd1b5b9d..c3c1ec62 100644 --- a/modules/sl/source/mesh/SLParticleSystem.h +++ b/modules/sl/source/mesh/SLParticleSystem.h @@ -42,7 +42,7 @@ class SLParticleSystem : public SLMesh const SLstring& name = "Particle system", SLGLTexture* texFlipbook = nullptr); - 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); @@ -350,6 +350,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 +380,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 From 0f20c06451244c5d5c5f1bcd4084370cb5e57204 Mon Sep 17 00:00:00 2001 From: Marino von Wattenwyl Date: Fri, 3 Nov 2023 10:40:30 +0100 Subject: [PATCH 021/108] [Externals] Add Python build script for GLFW --- externals/prebuild_scripts/build_glfw.py | 112 +++++++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 externals/prebuild_scripts/build_glfw.py diff --git a/externals/prebuild_scripts/build_glfw.py b/externals/prebuild_scripts/build_glfw.py new file mode 100644 index 00000000..4ec469f4 --- /dev/null +++ b/externals/prebuild_scripts/build_glfw.py @@ -0,0 +1,112 @@ +#!/usr/bin/python3 + +import platform +import subprocess +import os +import multiprocessing +import shutil +from pathlib import Path + + +BUILD_DIR = Path("build") +INSTALL_DIR = Path("install") + +SYSTEM_WIN64 = "win64" +SYSTEM_LINUX = "linux" +SYSTEM_MAC64 = "mac64" +SYSTEM_MACARM64 = "macArm64" + + +is_windows = platform.system() == "Windows" +is_linux = platform.system() == "Linux" +is_darwin = platform.system() == "Darwin" + + +def build(build_type): + build_dir = BUILD_DIR / build_type + install_dir = INSTALL_DIR / build_type + + # Convert the first letter to uppercase for CMake. + cmake_build_type = build_type[0].upper() + build_type[1:] + + config_command = [ + "cmake", + "-B" + str(build_dir), + "-DCMAKE_INSTALL_PREFIX=" + str(install_dir), + "-DGLFW_BUILD_EXAMPLES=OFF", + "-DGLFW_BUILD_TESTS=OFF", + "-DGLFW_BUILD_DOCS=OFF" + ] + + if not is_windows: + config_command.append("-DCMAKE_BUILD_TYPE=" + cmake_build_type) + + build_command = [ + "cmake", + "--build", build_dir, + "--target", "install", + "-j" + str(multiprocessing.cpu_count()) + ] + + if is_windows: + build_command.extend(["--config", cmake_build_type]) + + subprocess.run(config_command) + subprocess.run(build_command) + + destination = output_dir / build_type + destination.mkdir() + + if is_windows: + shutil.copy(install_dir / "lib" / "glfw3.lib", destination) + else: + shutil.copy(install_dir / "lib" / "libglfw3.a", destination) + + +print(f"\n=== Building GLFW ===\n") +version = input("Version: ") + +if is_windows: + system = SYSTEM_WIN64 + print("System: " + system) +elif is_linux: + system = SYSTEM_LINUX + print("System: " + system) +elif is_darwin: + system = input(f"System ({SYSTEM_MAC64}/{SYSTEM_MACARM64}): ") + assert system in (SYSTEM_MAC64, SYSTEM_MACARM64) + +name = system + "_glfw_" + version + +prebuilt_dir = Path(os.curdir).absolute().parent / "prebuilt" +output_dir = Path(prebuilt_dir, name) +print("Output directory: " + str(output_dir)) + +zip_filename = name + ".zip" +print("Zip file: " + zip_filename) + +if not os.path.isdir("glfw"): + print("\n=== Cloning GLFW ===\n") + subprocess.run(["git", "clone", "https://github.com/glfw/glfw.git"]) + +os.chdir("glfw") + +print(f"\n=== Checking out version {version} ===\n") +subprocess.run(["git", "checkout", version]) +subprocess.run(["git", "pull", "origin", version]) + +print(f"\n=== Preparing output directory ===\n") + +shutil.rmtree(output_dir, ignore_errors=True) +output_dir.mkdir() + +print("Directory created") + +print(f"\n=== Building debug version ===\n") +build("debug") +print(f"\n=== Building release version ===\n") +build("release") + +shutil.copytree(INSTALL_DIR / "debug" / "include", output_dir / "include") +shutil.copy("LICENSE.md", output_dir) +shutil.copy("README.md", output_dir) \ No newline at end of file From 3b5ec616362aff4e71444d9c20ce3e260284aa0d Mon Sep 17 00:00:00 2001 From: Marino von Wattenwyl Date: Fri, 3 Nov 2023 10:53:37 +0100 Subject: [PATCH 022/108] [Externals] Add Windows support for build_glfw.py --- externals/prebuild_scripts/build_glfw.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/externals/prebuild_scripts/build_glfw.py b/externals/prebuild_scripts/build_glfw.py index 4ec469f4..e3036d0e 100644 --- a/externals/prebuild_scripts/build_glfw.py +++ b/externals/prebuild_scripts/build_glfw.py @@ -82,9 +82,6 @@ def build(build_type): output_dir = Path(prebuilt_dir, name) print("Output directory: " + str(output_dir)) -zip_filename = name + ".zip" -print("Zip file: " + zip_filename) - if not os.path.isdir("glfw"): print("\n=== Cloning GLFW ===\n") subprocess.run(["git", "clone", "https://github.com/glfw/glfw.git"]) @@ -109,4 +106,13 @@ def build(build_type): shutil.copytree(INSTALL_DIR / "debug" / "include", output_dir / "include") shutil.copy("LICENSE.md", output_dir) -shutil.copy("README.md", output_dir) \ No newline at end of file +shutil.copy("README.md", output_dir) + +print("\n=== Creating archive === \n") + +os.chdir(os.pardir) +shutil.make_archive(name, "zip", output_dir.parent, output_dir.name) +print("Archive created") +print("Path: " + str(Path(name + ".zip").absolute())) + +print("\n") \ No newline at end of file From 03c0999ba27b285ed6da91f17fc843e73b5fa43e Mon Sep 17 00:00:00 2001 From: Marino von Wattenwyl Date: Fri, 3 Nov 2023 10:59:37 +0100 Subject: [PATCH 023/108] [CMake] Upgrade GLFW to 3.3.8 on Windows and macOS --- cmake/DownloadPrebuilts.cmake | 47 ++++++++++++++++------------------- 1 file changed, 21 insertions(+), 26 deletions(-) diff --git a/cmake/DownloadPrebuilts.cmake b/cmake/DownloadPrebuilts.cmake index 8858fcb8..9e8d3dfc 100644 --- a/cmake/DownloadPrebuilts.cmake +++ b/cmake/DownloadPrebuilts.cmake @@ -446,22 +446,19 @@ elseif ("${SYSTEM_NAME_UPPER}" STREQUAL "WINDOWS") #---------------------------- # GLFW for Windows # #################### - set(glfw_VERSION "3.3.2") + set(glfw_VERSION "3.3.8") set(glfw_PREBUILT_DIR "win64_glfw_${glfw_VERSION}") set(glfw_DIR "${PREBUILT_PATH}/${glfw_PREBUILT_DIR}") - set(glfw_INCLUDE_DIR "${glfw_DIR}/include") - set(glfw_LINK_DIR "${glfw_DIR}/lib-vc2019") - add_library(glfw3dll SHARED IMPORTED) - set_target_properties(glfw3dll PROPERTIES - IMPORTED_IMPLIB "${glfw_LINK_DIR}/glfw3dll.lib" - IMPORTED_LOCATION "${glfw_LINK_DIR}/glfw3.dll" - INTERFACE_INCLUDE_DIRECTORIES "${glfw_INCLUDE_DIR}" + add_library(glfw3 STATIC IMPORTED) + set_target_properties(glfw3 PROPERTIES + IMPORTED_LOCATION "${glfw_DIR}/release/glfw3.lib" + IMPORTED_LOCATION_DEBUG "${glfw_DIR}/debug/glfw3.lib" + INTERFACE_INCLUDE_DIRECTORIES "${glfw_DIR}/include" ) - set(glfw_LIBS glfw3dll) + set(glfw_LIBS glfw3) download_lib("${glfw_PREBUILT_DIR}") - copy_dlls("${glfw_LIBS}") ################### # KTX for Windows # @@ -643,19 +640,18 @@ elseif ("${SYSTEM_NAME_UPPER}" STREQUAL "DARWIN" AND # GLFW for MacOS-x86_64 # ######################### - set(glfw_VERSION "3.3.2") + set(glfw_VERSION "3.3.8") set(glfw_PREBUILT_DIR "mac64_glfw_${glfw_VERSION}") set(glfw_DIR "${PREBUILT_PATH}/${glfw_PREBUILT_DIR}") - set(glfw_INCLUDE_DIR "${glfw_DIR}/include") - add_library(glfw SHARED IMPORTED) - set_target_properties(glfw PROPERTIES - IMPORTED_LOCATION "${glfw_DIR}/Release/libglfw.3.dylib" - INTERFACE_INCLUDE_DIRECTORIES "${glfw_INCLUDE_DIR}") - set(glfw_LIBS glfw) + add_library(glfw3 STATIC IMPORTED) + set_target_properties(glfw3 PROPERTIES + IMPORTED_LOCATION "${glfw_DIR}/release/libglfw3.a" + IMPORTED_LOCATION_DEBUG "${glfw_DIR}/debug/libglfw3.a" + INTERFACE_INCLUDE_DIRECTORIES "${glfw_DIR}/include") + set(glfw_LIBS glfw3) download_lib("${glfw_PREBUILT_DIR}") - copy_dylibs("${glfw_LIBS}") ######################## # KTX for MacOS-x86_64 # @@ -824,19 +820,18 @@ elseif ("${SYSTEM_NAME_UPPER}" STREQUAL "DARWIN" AND # GLFW for MacOS-arm64 # ######################## - set(glfw_VERSION "3.3.2") + set(glfw_VERSION "3.3.8") set(glfw_PREBUILT_DIR "macArm64_glfw_${glfw_VERSION}") set(glfw_DIR "${PREBUILT_PATH}/${glfw_PREBUILT_DIR}") - set(glfw_INCLUDE_DIR "${glfw_DIR}/include") - add_library(glfw SHARED IMPORTED) - set_target_properties(glfw PROPERTIES - IMPORTED_LOCATION "${glfw_DIR}/Release/libglfw.3.3.dylib" - INTERFACE_INCLUDE_DIRECTORIES "${glfw_INCLUDE_DIR}") - set(glfw_LIBS glfw) + add_library(glfw3 STATIC IMPORTED) + set_target_properties(glfw3 PROPERTIES + IMPORTED_LOCATION "${glfw_DIR}/release/libglfw3.a" + IMPORTED_LOCATION_DEBUG "${glfw_DIR}/debug/libglfw3.a" + INTERFACE_INCLUDE_DIRECTORIES "${glfw_DIR}/include") + set(glfw_LIBS glfw3) download_lib("${glfw_PREBUILT_DIR}") - copy_dylibs("${glfw_LIBS}") ####################### # KTX for MacOS-arm64 # From ba333c3da4a0bda9f772f6f3b730ca8a4d7265bd Mon Sep 17 00:00:00 2001 From: Marino von Wattenwyl Date: Fri, 3 Nov 2023 11:00:53 +0100 Subject: [PATCH 024/108] [WebGPU] Fix GLFW linking --- apps/webgpu/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/webgpu/CMakeLists.txt b/apps/webgpu/CMakeLists.txt index 97bfde63..40b48567 100644 --- a/apps/webgpu/CMakeLists.txt +++ b/apps/webgpu/CMakeLists.txt @@ -47,4 +47,4 @@ if (SYSTEM_NAME_UPPER MATCHES "WINDOWS") endif () add_executable(webgpu-demo main.cpp) -target_link_libraries(webgpu-demo PRIVATE wgpu glfw3dll) +target_link_libraries(webgpu-demo PRIVATE wgpu ${glfw_LIBS}) From 3e91462dad04e440f38159b7e8e97c83bdd67196 Mon Sep 17 00:00:00 2001 From: Marino von Wattenwyl Date: Fri, 3 Nov 2023 11:04:19 +0100 Subject: [PATCH 025/108] [Externals] Add support for macOS cross-compilation to build_glfw.py --- externals/prebuild_scripts/build_glfw.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/externals/prebuild_scripts/build_glfw.py b/externals/prebuild_scripts/build_glfw.py index e3036d0e..1f66519a 100644 --- a/externals/prebuild_scripts/build_glfw.py +++ b/externals/prebuild_scripts/build_glfw.py @@ -40,6 +40,11 @@ def build(build_type): if not is_windows: config_command.append("-DCMAKE_BUILD_TYPE=" + cmake_build_type) + + if system == SYSTEM_MAC64: + config_command.append("-DCMAKE_OSX_ARCHITECTURES=x86_64") + elif system == SYSTEM_MACARM64: + config_command.append("-DCMAKE_OSX_ARCHITECTURES=arm64") build_command = [ "cmake", From 29c1a76bd393901c0b6b16bf46e38c1a0759df79 Mon Sep 17 00:00:00 2001 From: Marino von Wattenwyl Date: Fri, 3 Nov 2023 11:12:21 +0100 Subject: [PATCH 026/108] [WebGPU] Add support for Metal surfaces --- apps/webgpu/CMakeLists.txt | 17 +++++++++++++---- apps/webgpu/main.cpp | 24 ++++++++++++++++++++++-- 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/apps/webgpu/CMakeLists.txt b/apps/webgpu/CMakeLists.txt index 40b48567..7eb61dd6 100644 --- a/apps/webgpu/CMakeLists.txt +++ b/apps/webgpu/CMakeLists.txt @@ -38,13 +38,22 @@ else () endif () add_library(wgpu STATIC IMPORTED) -set_target_properties(wgpu PROPERTIES - IMPORTED_LOCATION "${WGPU_DIR}/wgpu_native.lib" - INTERFACE_INCLUDE_DIRECTORIES "${WGPU_DIR}") if (SYSTEM_NAME_UPPER MATCHES "WINDOWS") - target_link_libraries(wgpu INTERFACE "ws2_32" "userenv" "advapi32" "bcrypt" "d3dcompiler" "ntdll" "opengl32") + target_link_libraries(wgpu INTERFACE "ws2_32" "userenv" "bcrypt" "d3dcompiler" "ntdll" "opengl32") + set_target_properties(wgpu PROPERTIES + IMPORTED_LOCATION "${WGPU_DIR}/libwgpu_native.lib" + INTERFACE_INCLUDE_DIRECTORIES "${WGPU_DIR}") +elseif (SYSTEM_NAME_UPPER MATCHES "DARWIN") + target_link_libraries(wgpu INTERFACE "-framework CoreFoundation" "-framework QuartzCore" "-framework Metal") + set_target_properties(wgpu PROPERTIES + IMPORTED_LOCATION "${WGPU_DIR}/libwgpu_native.a" + INTERFACE_INCLUDE_DIRECTORIES "${WGPU_DIR}") endif () add_executable(webgpu-demo main.cpp) target_link_libraries(webgpu-demo PRIVATE wgpu ${glfw_LIBS}) + +if (SYSTEM_NAME_UPPER MATCHES "DARWIN") + target_link_libraries(webgpu-demo PRIVATE "-framework Cocoa" "-framework IOKit") +endif () \ No newline at end of file diff --git a/apps/webgpu/main.cpp b/apps/webgpu/main.cpp index b23d48cb..b835cf79 100644 --- a/apps/webgpu/main.cpp +++ b/apps/webgpu/main.cpp @@ -9,10 +9,17 @@ #elif defined(__APPLE__) # define SYSTEM_DARWIN # define GLFW_EXPOSE_NATIVE_COCOA +# define OBJC_OLD_DISPATCH_PROTOTYPES 1 +# include +# include +# include +# include #endif #include + #include +#define GLFW_NATIVE_INCLUDE_NONE #include #include @@ -107,11 +114,24 @@ int main(int argc, const char* argv[]) // The surface is where our rendered images will be presented to. // It is created from window handles on most platforms and from a canvas in the browser. -#ifdef SYSTEM_WINDOWS +#if defined(SYSTEM_WINDOWS) WGPUSurfaceDescriptorFromWindowsHWND nativeSurfaceDesc = {}; nativeSurfaceDesc.chain.sType = WGPUSType_SurfaceDescriptorFromWindowsHWND; nativeSurfaceDesc.hinstance = GetModuleHandle(nullptr); nativeSurfaceDesc.hwnd = glfwGetWin32Window(window); +#elif defined(SYSTEM_DARWIN) + id cocoaWindow = glfwGetCocoaWindow(window); + + id contentView = objc_msgSend(cocoaWindow, sel_registerName("contentView")); + objc_msgSend(contentView, sel_getUid("setWantsLayer:"), 1); + + objc_class *metalLayerClass = objc_getClass("CAMetalLayer"); + id metalLayer = objc_msgSend((id)metalLayerClass, sel_getUid("layer")); + objc_msgSend(contentView, sel_registerName("setLayer:"), metalLayer); + + WGPUSurfaceDescriptorFromMetalLayer nativeSurfaceDesc = {}; + nativeSurfaceDesc.chain.sType = WGPUSType_SurfaceDescriptorFromMetalLayer; + nativeSurfaceDesc.layer = metalLayer; #endif WGPUSurfaceDescriptor surfaceDesc = {}; @@ -286,7 +306,7 @@ int main(int argc, const char* argv[]) // === Submit the command buffer to the GPU === // The work for the GPU is submitted through the queue and executed. wgpuQueueSubmit(queue, 1, &cmdBuffer); - + // === Present the surface === // This presents our rendered texture to the screen. wgpuSurfacePresent(surface); From a81d07795739a7d28f8bada8af21290e03ff7c6f Mon Sep 17 00:00:00 2001 From: Marino von Wattenwyl Date: Fri, 3 Nov 2023 11:17:03 +0100 Subject: [PATCH 027/108] [WebGPU] Add support for Xlib surfaces --- apps/webgpu/main.cpp | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/apps/webgpu/main.cpp b/apps/webgpu/main.cpp index b835cf79..7d48d42e 100644 --- a/apps/webgpu/main.cpp +++ b/apps/webgpu/main.cpp @@ -9,6 +9,8 @@ #elif defined(__APPLE__) # define SYSTEM_DARWIN # define GLFW_EXPOSE_NATIVE_COCOA +# define GLFW_NATIVE_INCLUDE_NONE + # define OBJC_OLD_DISPATCH_PROTOTYPES 1 # include # include @@ -17,9 +19,7 @@ #endif #include - #include -#define GLFW_NATIVE_INCLUDE_NONE #include #include @@ -119,19 +119,24 @@ int main(int argc, const char* argv[]) nativeSurfaceDesc.chain.sType = WGPUSType_SurfaceDescriptorFromWindowsHWND; nativeSurfaceDesc.hinstance = GetModuleHandle(nullptr); nativeSurfaceDesc.hwnd = glfwGetWin32Window(window); +#elif defined(SYSTEM_LINUX) + WGPUSurfaceDescriptorFromXlibWindow nativeSurfaceDesc = {}; + nativeSurfaceDesc.chain.sType = WGPUSType_SurfaceDescriptorFromXlibWindow; + nativeSurfaceDesc.display = glfwGetX11Display(); + nativeSurfaceDesc.window = glfwGetX11Window(window); #elif defined(SYSTEM_DARWIN) id cocoaWindow = glfwGetCocoaWindow(window); id contentView = objc_msgSend(cocoaWindow, sel_registerName("contentView")); objc_msgSend(contentView, sel_getUid("setWantsLayer:"), 1); - objc_class *metalLayerClass = objc_getClass("CAMetalLayer"); - id metalLayer = objc_msgSend((id)metalLayerClass, sel_getUid("layer")); + objc_class* metalLayerClass = objc_getClass("CAMetalLayer"); + id metalLayer = objc_msgSend((id)metalLayerClass, sel_getUid("layer")); objc_msgSend(contentView, sel_registerName("setLayer:"), metalLayer); WGPUSurfaceDescriptorFromMetalLayer nativeSurfaceDesc = {}; - nativeSurfaceDesc.chain.sType = WGPUSType_SurfaceDescriptorFromMetalLayer; - nativeSurfaceDesc.layer = metalLayer; + nativeSurfaceDesc.chain.sType = WGPUSType_SurfaceDescriptorFromMetalLayer; + nativeSurfaceDesc.layer = metalLayer; #endif WGPUSurfaceDescriptor surfaceDesc = {}; From fef422b6521a71477a7d3bea5efb102409de73ed Mon Sep 17 00:00:00 2001 From: Marino von Wattenwyl Date: Fri, 3 Nov 2023 11:18:27 +0100 Subject: [PATCH 028/108] [WebGPU] Add Linux libraries to CMake --- apps/webgpu/CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/webgpu/CMakeLists.txt b/apps/webgpu/CMakeLists.txt index 7eb61dd6..55b477a3 100644 --- a/apps/webgpu/CMakeLists.txt +++ b/apps/webgpu/CMakeLists.txt @@ -44,6 +44,10 @@ if (SYSTEM_NAME_UPPER MATCHES "WINDOWS") set_target_properties(wgpu PROPERTIES IMPORTED_LOCATION "${WGPU_DIR}/libwgpu_native.lib" INTERFACE_INCLUDE_DIRECTORIES "${WGPU_DIR}") +elseif (SYSTEM_NAME_UPPER MATCHES "LINUX") + set_target_properties(wgpu PROPERTIES + IMPORTED_LOCATION "${WGPU_DIR}/libwgpu_native.a" + INTERFACE_INCLUDE_DIRECTORIES "${WGPU_DIR}") elseif (SYSTEM_NAME_UPPER MATCHES "DARWIN") target_link_libraries(wgpu INTERFACE "-framework CoreFoundation" "-framework QuartzCore" "-framework Metal") set_target_properties(wgpu PROPERTIES From c7370487a25a28a7525eb0fa5cf8437fd987c3b1 Mon Sep 17 00:00:00 2001 From: Marino von Wattenwyl Date: Fri, 3 Nov 2023 11:31:54 +0100 Subject: [PATCH 029/108] [WebGPU] Link X11 on Linux --- apps/webgpu/CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/webgpu/CMakeLists.txt b/apps/webgpu/CMakeLists.txt index 55b477a3..4bc31d6b 100644 --- a/apps/webgpu/CMakeLists.txt +++ b/apps/webgpu/CMakeLists.txt @@ -58,6 +58,8 @@ endif () add_executable(webgpu-demo main.cpp) target_link_libraries(webgpu-demo PRIVATE wgpu ${glfw_LIBS}) -if (SYSTEM_NAME_UPPER MATCHES "DARWIN") +if (SYSTEM_NAME_UPPER MATCHES "LINUX") + target_link_libraries(webgpu-demo PRIVATE "X11") +elseif (SYSTEM_NAME_UPPER MATCHES "DARWIN") target_link_libraries(webgpu-demo PRIVATE "-framework Cocoa" "-framework IOKit") endif () \ No newline at end of file From 9c340b69d909b8ef410193a9f94b5631d642a13f Mon Sep 17 00:00:00 2001 From: Marino von Wattenwyl Date: Fri, 3 Nov 2023 13:33:34 +0100 Subject: [PATCH 030/108] [WebGPU] Render demo using vertex and index buffers --- apps/webgpu/CMakeLists.txt | 2 +- apps/webgpu/main.cpp | 111 ++++++++++++++++++++++++++++++++----- 2 files changed, 98 insertions(+), 15 deletions(-) diff --git a/apps/webgpu/CMakeLists.txt b/apps/webgpu/CMakeLists.txt index 4bc31d6b..cf777bb2 100644 --- a/apps/webgpu/CMakeLists.txt +++ b/apps/webgpu/CMakeLists.txt @@ -42,7 +42,7 @@ add_library(wgpu STATIC IMPORTED) if (SYSTEM_NAME_UPPER MATCHES "WINDOWS") target_link_libraries(wgpu INTERFACE "ws2_32" "userenv" "bcrypt" "d3dcompiler" "ntdll" "opengl32") set_target_properties(wgpu PROPERTIES - IMPORTED_LOCATION "${WGPU_DIR}/libwgpu_native.lib" + IMPORTED_LOCATION "${WGPU_DIR}/wgpu_native.lib" INTERFACE_INCLUDE_DIRECTORIES "${WGPU_DIR}") elseif (SYSTEM_NAME_UPPER MATCHES "LINUX") set_target_properties(wgpu PROPERTIES diff --git a/apps/webgpu/main.cpp b/apps/webgpu/main.cpp index 7d48d42e..5bf45c94 100644 --- a/apps/webgpu/main.cpp +++ b/apps/webgpu/main.cpp @@ -23,7 +23,9 @@ #include #include +#include #include +#include #define WEBGPU_DEMO_LOG(msg) std::cout << (msg) << std::endl @@ -93,11 +95,26 @@ int main(int argc, const char* argv[]) WGPUAdapter adapter; wgpuInstanceRequestAdapter(instance, &adapterOptions, handleAdapterRequest, &adapter); + WGPUSupportedLimits adapterLimits; + wgpuAdapterGetLimits(adapter, &adapterLimits); + // === Acquire a WebGPU device === // A device provides access to a GPU and is created from an adapter. + // We specify the capabilites that we require our device to have in requiredLimits. + // We cannot access more resources than specified in the required limits, + // which is how WebGPU prevents code from working on one machine and not on another. + + WGPURequiredLimits requiredLimits = {}; + requiredLimits.limits.maxVertexAttributes = 1; + requiredLimits.limits.maxVertexBuffers = 1; + requiredLimits.limits.maxBufferSize = 4ull * 2ull * sizeof(float); + requiredLimits.limits.maxVertexBufferArrayStride = 2ull * sizeof(float); + requiredLimits.limits.minStorageBufferOffsetAlignment = adapterLimits.limits.minStorageBufferOffsetAlignment; + requiredLimits.limits.minUniformBufferOffsetAlignment = adapterLimits.limits.minUniformBufferOffsetAlignment; WGPUDeviceDescriptor deviceDesc = {}; deviceDesc.label = "Demo Device"; + deviceDesc.requiredLimits = &requiredLimits; deviceDesc.defaultQueue.label = "Demo Queue"; WGPUDevice device; @@ -170,6 +187,58 @@ int main(int argc, const char* argv[]) wgpuSurfaceConfigure(surface, &surfaceConfig); WEBGPU_DEMO_LOG("[WebGPU] Surface configured"); + // === Create the WebGPU vertex buffer === + // The vertex buffer contains the input data for the shader. + + // clang-format off + std::vector vertexData = + { + -0.5, 0.5, // top-left corner + -0.5, -0.5, // bottom-left corner + 0.5, 0.5, // top-right corner + 0.5, -0.5, // bottom-right corner + }; + // clang-format on + + unsigned vertexCount = vertexData.size() / 2; + unsigned vertexDataSize = vertexData.size() * sizeof(float); + + WGPUBufferDescriptor vertexBufferDesc = {}; + vertexBufferDesc.label = "Demo Vertex Buffer"; + vertexBufferDesc.size = vertexDataSize; + vertexBufferDesc.usage = WGPUBufferUsage_CopyDst | WGPUBufferUsage_Vertex; + + WGPUBuffer vertexBuffer = wgpuDeviceCreateBuffer(device, &vertexBufferDesc); + WEBGPU_DEMO_CHECK(vertexBuffer, "[WebGPU] Failed to create vertex buffer"); + WEBGPU_DEMO_LOG("[WebGPU] Vertex buffer created"); + + // Upload the data to the GPU. + wgpuQueueWriteBuffer(queue, vertexBuffer, 0, vertexData.data(), vertexDataSize); + + // === Create the WebGPU index buffer === + + // clang-format off + std::vector indexData = + { + 0, 1, 2, + 2, 1, 3, + }; + // clang-format on + + unsigned indexCount = indexData.size(); + unsigned indexDataSize = indexData.size() * sizeof(std::uint16_t); + + WGPUBufferDescriptor indexBufferDesc = {}; + indexBufferDesc.label = "Demo Index Buffer"; + indexBufferDesc.size = indexDataSize; + indexBufferDesc.usage = WGPUBufferUsage_CopyDst | WGPUBufferUsage_Index; + + WGPUBuffer indexBuffer = wgpuDeviceCreateBuffer(device, &indexBufferDesc); + WEBGPU_DEMO_CHECK(indexBuffer, "[WebGPU] Failed to create index buffer"); + WEBGPU_DEMO_LOG("[WebGPU] Index buffer created"); + + wgpuQueueWriteBuffer(queue, indexBuffer, 0, indexData.data(), indexDataSize); + // === Create the shader module === // Create a shader module for use in our render pipeline. // Shaders are written in a WebGPU-specific language called WGSL (WebGPU shader language). @@ -177,14 +246,8 @@ int main(int argc, const char* argv[]) const char* shaderSource = R"( @vertex - fn vs_main(@builtin(vertex_index) in_vertex_index: u32) -> @builtin(position) vec4f { - if (in_vertex_index == 0u) { - return vec4f(-0.5, -0.5, 0.0, 1.0); - } else if (in_vertex_index == 1u) { - return vec4f(0.5, -0.5, 0.0, 1.0); - } else { - return vec4f(0.0, 0.5, 0.0, 1.0); - } + fn vs_main(@location(0) in_vertex_position: vec2f) -> @builtin(position) vec4f { + return vec4f(in_vertex_position, 0.0, 1.0); } @fragment @@ -198,7 +261,7 @@ int main(int argc, const char* argv[]) shaderModuleWGSLDesc.code = shaderSource; WGPUShaderModuleDescriptor shaderModuleDesc = {}; - shaderModuleDesc.label = "Hello Triangle Shader"; + shaderModuleDesc.label = "Demo Shader"; shaderModuleDesc.nextInChain = (const WGPUChainedStruct*)&shaderModuleWGSLDesc; WGPUShaderModule shaderModule = wgpuDeviceCreateShaderModule(device, &shaderModuleDesc); @@ -209,10 +272,25 @@ int main(int argc, const char* argv[]) // The render pipeline specifies the configuration for the fixed-function stages as well as // the shaders for the programmable stages of the hardware pipeline. + // Description of the vertex attribute for the vertex buffer layout + WGPUVertexAttribute vertexAttribute = {}; + vertexAttribute.format = WGPUVertexFormat_Float32x2; + vertexAttribute.offset = 0; + vertexAttribute.shaderLocation = 0; + + // Description of the vertex buffer layout for the vertex shader stage + WGPUVertexBufferLayout vertexBufferLayout = {}; + vertexBufferLayout.arrayStride = 2ull * sizeof(float); + vertexBufferLayout.stepMode = WGPUVertexStepMode_Vertex; + vertexBufferLayout.attributeCount = 1; + vertexBufferLayout.attributes = &vertexAttribute; + // Configuration for the vertex shader stage WGPUVertexState vertexState = {}; vertexState.module = shaderModule; vertexState.entryPoint = "vs_main"; + vertexState.bufferCount = 1; + vertexState.buffers = &vertexBufferLayout; // Configuration for the primitive assembly and rasterization stages WGPUPrimitiveState primitiveState = {}; @@ -238,7 +316,7 @@ int main(int argc, const char* argv[]) // Configuration for the entire pipeline WGPURenderPipelineDescriptor pipelineDesc = {}; - pipelineDesc.label = "Hello Triangle Pipeline"; + pipelineDesc.label = "Demo Pipeline"; pipelineDesc.vertex = vertexState; pipelineDesc.primitive = primitiveState; pipelineDesc.multisample = multisampleState; @@ -252,6 +330,7 @@ int main(int argc, const char* argv[]) while (!glfwWindowShouldClose(window)) { + // === Create a WebGPU texture view === // The texture view is where we render our image into. @@ -267,7 +346,7 @@ int main(int argc, const char* argv[]) // The encoder encodes the commands for the GPU into a command buffer. WGPUCommandEncoderDescriptor cmdEncoderDesc = {}; - cmdEncoderDesc.label = " Hello Triangle Command Encoder"; + cmdEncoderDesc.label = "Demo Command Encoder"; WGPUCommandEncoder cmdEncoder = wgpuDeviceCreateCommandEncoder(device, &cmdEncoderDesc); WEBGPU_DEMO_CHECK(cmdEncoder, "[WebGPU] Failed to create command encoder"); @@ -287,7 +366,7 @@ int main(int argc, const char* argv[]) colorAttachment.clearValue.a = 1.0; WGPURenderPassDescriptor renderPassDesc = {}; - renderPassDesc.label = "Hello Triangle Render Pass"; + renderPassDesc.label = "Demo Render Pass"; renderPassDesc.colorAttachmentCount = 1; renderPassDesc.colorAttachments = &colorAttachment; @@ -297,14 +376,16 @@ int main(int argc, const char* argv[]) WGPURenderPassEncoder renderPassEncoder = wgpuCommandEncoderBeginRenderPass(cmdEncoder, &renderPassDesc); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipeline); - wgpuRenderPassEncoderDraw(renderPassEncoder, 3, 1, 0, 0); + wgpuRenderPassEncoderSetVertexBuffer(renderPassEncoder, 0, vertexBuffer, 0, vertexDataSize); + wgpuRenderPassEncoderSetIndexBuffer(renderPassEncoder, indexBuffer, WGPUIndexFormat_Uint16, 0, indexDataSize); + wgpuRenderPassEncoderDrawIndexed(renderPassEncoder, indexCount, 1, 0, 0, 0); wgpuRenderPassEncoderEnd(renderPassEncoder); // === Get the command buffer === // The command encoder is finished to get the commands for the GPU to execute in a command buffer. WGPUCommandBufferDescriptor cmdBufferDesc = {}; - cmdBufferDesc.label = "Hello Triangle Command Buffer"; + cmdBufferDesc.label = "Demo Command Buffer"; WGPUCommandBuffer cmdBuffer = wgpuCommandEncoderFinish(cmdEncoder, &cmdBufferDesc); @@ -330,6 +411,8 @@ int main(int argc, const char* argv[]) wgpuRenderPipelineRelease(pipeline); wgpuShaderModuleRelease(shaderModule); + wgpuBufferDestroy(vertexBuffer); + wgpuBufferRelease(vertexBuffer); wgpuSurfaceRelease(surface); wgpuQueueRelease(queue); wgpuDeviceRelease(device); From 0794f766ec7eaa9b73a7a47d3658613b3c77efc9 Mon Sep 17 00:00:00 2001 From: Marino von Wattenwyl Date: Fri, 3 Nov 2023 13:43:49 +0100 Subject: [PATCH 031/108] [WebGPU] Add surface re-configuring to demo --- apps/webgpu/main.cpp | 65 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 51 insertions(+), 14 deletions(-) diff --git a/apps/webgpu/main.cpp b/apps/webgpu/main.cpp index 5bf45c94..96a672d3 100644 --- a/apps/webgpu/main.cpp +++ b/apps/webgpu/main.cpp @@ -74,9 +74,6 @@ int main(int argc, const char* argv[]) // Prevent GLFW from creating an OpenGL context as the underlying graphics API probably won't be OpenGL. glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); - // Create a non-resizable window as we currently don't recreate surfaces when the window size changes. - glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); - GLFWwindow* window = glfwCreateWindow(1280, 720, "WebGPU Demo", nullptr, nullptr); WEBGPU_DEMO_CHECK(window, "[GLFW] Window created"); @@ -200,7 +197,7 @@ int main(int argc, const char* argv[]) }; // clang-format on - unsigned vertexCount = vertexData.size() / 2; + unsigned vertexCount = vertexData.size() / 2; unsigned vertexDataSize = vertexData.size() * sizeof(float); WGPUBufferDescriptor vertexBufferDesc = {}; @@ -216,7 +213,7 @@ int main(int argc, const char* argv[]) wgpuQueueWriteBuffer(queue, vertexBuffer, 0, vertexData.data(), vertexDataSize); // === Create the WebGPU index buffer === - + // clang-format off std::vector indexData = { @@ -225,7 +222,7 @@ int main(int argc, const char* argv[]) }; // clang-format on - unsigned indexCount = indexData.size(); + unsigned indexCount = indexData.size(); unsigned indexDataSize = indexData.size() * sizeof(std::uint16_t); WGPUBufferDescriptor indexBufferDesc = {}; @@ -274,16 +271,16 @@ int main(int argc, const char* argv[]) // Description of the vertex attribute for the vertex buffer layout WGPUVertexAttribute vertexAttribute = {}; - vertexAttribute.format = WGPUVertexFormat_Float32x2; - vertexAttribute.offset = 0; - vertexAttribute.shaderLocation = 0; + vertexAttribute.format = WGPUVertexFormat_Float32x2; + vertexAttribute.offset = 0; + vertexAttribute.shaderLocation = 0; // Description of the vertex buffer layout for the vertex shader stage WGPUVertexBufferLayout vertexBufferLayout = {}; - vertexBufferLayout.arrayStride = 2ull * sizeof(float); - vertexBufferLayout.stepMode = WGPUVertexStepMode_Vertex; - vertexBufferLayout.attributeCount = 1; - vertexBufferLayout.attributes = &vertexAttribute; + vertexBufferLayout.arrayStride = 2ull * sizeof(float); + vertexBufferLayout.stepMode = WGPUVertexStepMode_Vertex; + vertexBufferLayout.attributeCount = 1; + vertexBufferLayout.attributes = &vertexAttribute; // Configuration for the vertex shader stage WGPUVertexState vertexState = {}; @@ -337,7 +334,47 @@ int main(int argc, const char* argv[]) // Get a texture from the surface to render into. WGPUSurfaceTexture surfaceTexture; wgpuSurfaceGetCurrentTexture(surface, &surfaceTexture); - WEBGPU_DEMO_CHECK(surfaceTexture.status == WGPUSurfaceGetCurrentTextureStatus_Success, "[WebGPU] Failed to acquire current surface texture"); + + // The surface might change over time. + // For example, the window might be resized or minimized. + // We have to check the status and adapt to it. + switch (surfaceTexture.status) + { + case WGPUSurfaceGetCurrentTextureStatus_Success: + // Everything is ok. + // TODO: check for a suboptimal texture and re-configure it if needed. + break; + + case WGPUSurfaceGetCurrentTextureStatus_Timeout: + case WGPUSurfaceGetCurrentTextureStatus_Outdated: + case WGPUSurfaceGetCurrentTextureStatus_Lost: + // The surface needs to be re-configured. + + // Get the window size from the GLFW window. + glfwGetWindowSize(window, &surfaceWidth, &surfaceHeight); + + // The surface size might be zero if the window is minimized. + if (surfaceWidth != 0 && surfaceHeight != 0) + { + WEBGPU_DEMO_LOG("[WebGPU] Re-configuring surface"); + surfaceConfig.width = surfaceWidth; + surfaceConfig.height = surfaceHeight; + wgpuSurfaceConfigure(surface, &surfaceConfig); + } + + // Skip this frame. + glfwPollEvents(); + continue; + + case WGPUSurfaceGetCurrentTextureStatus_OutOfMemory: + case WGPUSurfaceGetCurrentTextureStatus_DeviceLost: + // An error occured. + WEBGPU_DEMO_CHECK(false, "[WebGPU] Failed to acquire current surface texture"); + break; + + case WGPUSurfaceGetCurrentTextureStatus_Force32: + break; + } // Create a view into the texture to specify where and how to modify the texture. WGPUTextureView view = wgpuTextureCreateView(surfaceTexture.texture, nullptr); From 1cf61b7d645de46b1aa79d91e0fdf1cee0d20271 Mon Sep 17 00:00:00 2001 From: Marino von Wattenwyl Date: Fri, 3 Nov 2023 14:29:57 +0100 Subject: [PATCH 032/108] [WebGPU] Add uniforms to demo --- apps/webgpu/main.cpp | 109 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 98 insertions(+), 11 deletions(-) diff --git a/apps/webgpu/main.cpp b/apps/webgpu/main.cpp index 96a672d3..c433a0f0 100644 --- a/apps/webgpu/main.cpp +++ b/apps/webgpu/main.cpp @@ -102,10 +102,14 @@ int main(int argc, const char* argv[]) // which is how WebGPU prevents code from working on one machine and not on another. WGPURequiredLimits requiredLimits = {}; - requiredLimits.limits.maxVertexAttributes = 1; - requiredLimits.limits.maxVertexBuffers = 1; + requiredLimits.limits.maxVertexAttributes = 1u; + requiredLimits.limits.maxVertexBuffers = 1u; requiredLimits.limits.maxBufferSize = 4ull * 2ull * sizeof(float); - requiredLimits.limits.maxVertexBufferArrayStride = 2ull * sizeof(float); + requiredLimits.limits.maxVertexBufferArrayStride = 2u * sizeof(float); + requiredLimits.limits.maxInterStageShaderComponents = 2u; + requiredLimits.limits.maxBindGroups = 1u; + requiredLimits.limits.maxUniformBuffersPerShaderStage = 1u; + requiredLimits.limits.maxUniformBufferBindingSize = 16ull * 4ull; requiredLimits.limits.minStorageBufferOffsetAlignment = adapterLimits.limits.minStorageBufferOffsetAlignment; requiredLimits.limits.minUniformBufferOffsetAlignment = adapterLimits.limits.minUniformBufferOffsetAlignment; @@ -184,7 +188,7 @@ int main(int argc, const char* argv[]) wgpuSurfaceConfigure(surface, &surfaceConfig); WEBGPU_DEMO_LOG("[WebGPU] Surface configured"); - // === Create the WebGPU vertex buffer === + // === Create the vertex buffer === // The vertex buffer contains the input data for the shader. // clang-format off @@ -212,7 +216,7 @@ int main(int argc, const char* argv[]) // Upload the data to the GPU. wgpuQueueWriteBuffer(queue, vertexBuffer, 0, vertexData.data(), vertexDataSize); - // === Create the WebGPU index buffer === + // === Create the index buffer === // clang-format off std::vector indexData = @@ -236,20 +240,46 @@ int main(int argc, const char* argv[]) wgpuQueueWriteBuffer(queue, indexBuffer, 0, indexData.data(), indexDataSize); + // === Create the uniform buffer === + + WGPUBufferDescriptor uniformBufferDesc = {}; + uniformBufferDesc.label = "Demo Uniform Buffer"; + uniformBufferDesc.size = sizeof(float); + uniformBufferDesc.usage = WGPUBufferUsage_CopyDst | WGPUBufferUsage_Uniform; + + WGPUBuffer uniformBuffer = wgpuDeviceCreateBuffer(device, &uniformBufferDesc); + WEBGPU_DEMO_CHECK(indexBuffer, "[WebGPU] Failed to create uniform buffer"); + WEBGPU_DEMO_LOG("[WebGPU] Uniform buffer created"); + + float uniformBufferData = 0.0f; + wgpuQueueWriteBuffer(queue, uniformBuffer, 0, &uniformBufferData, sizeof(float)); + // === Create the shader module === // Create a shader module for use in our render pipeline. // Shaders are written in a WebGPU-specific language called WGSL (WebGPU shader language). // Shader modules can be used in multiple pipeline stages by specifying different entry points. const char* shaderSource = R"( + @group(0) @binding(0) var u_time: f32; + + struct VertexOutput { + @builtin(position) position: vec4f, + @location(0) texture_coords: vec2f, + } + @vertex - fn vs_main(@location(0) in_vertex_position: vec2f) -> @builtin(position) vec4f { - return vec4f(in_vertex_position, 0.0, 1.0); + fn vs_main(@location(0) in_vertex_position: vec2f) -> VertexOutput { + var offset = vec2(0.5 * sin(u_time), 0.5 * cos(u_time)); + + var out: VertexOutput; + out.position = vec4f(in_vertex_position + offset, 0.0, 1.0); + out.texture_coords = in_vertex_position + vec2(0.5, 0.5); + return out; } @fragment - fn fs_main() -> @location(0) vec4f { - return vec4f(0.2, 0.2, 1.0, 1.0); + fn fs_main(in: VertexOutput) -> @location(0) vec4f { + return vec4f(in.texture_coords, 1.0, 1.0); } )"; @@ -265,7 +295,52 @@ int main(int argc, const char* argv[]) WEBGPU_DEMO_CHECK(shaderModule, "[WebGPU] Failed to create shader module"); WEBGPU_DEMO_LOG("[WebGPU] Shader module created"); - // === Create the WebGPU render pipeline === + // === Create the bind group layout === + // Bind groups contain binding layouts that describe how uniforms are passed to shaders. + + WGPUBindGroupLayoutEntry bindingLayout = {}; + bindingLayout.binding = 0; + bindingLayout.visibility = WGPUShaderStage_Vertex; + bindingLayout.buffer.type = WGPUBufferBindingType_Uniform; + bindingLayout.buffer.minBindingSize = sizeof(float); + + WGPUBindGroupLayoutDescriptor bindGroupLayoutDesc = {}; + bindGroupLayoutDesc.label = "Demo Bind Group Layout"; + bindGroupLayoutDesc.entryCount = 1; + bindGroupLayoutDesc.entries = &bindingLayout; + WGPUBindGroupLayout bindGroupLayout = wgpuDeviceCreateBindGroupLayout(device, &bindGroupLayoutDesc); + WEBGPU_DEMO_CHECK(bindGroupLayout, "[WebGPU] Failed to create bind group layout"); + WEBGPU_DEMO_LOG("[WebGPU] Bind group layout created"); + + // === Create the bind group === + // The bind group actually binds the buffer to the uniform. + + WGPUBindGroupEntry binding = {}; + binding.binding = 0; + binding.buffer = uniformBuffer; + binding.offset = 0; + binding.size = sizeof(float); + + WGPUBindGroupDescriptor bindGroupDesc = {}; + bindGroupDesc.layout = bindGroupLayout; + bindGroupDesc.entryCount = bindGroupLayoutDesc.entryCount; + bindGroupDesc.entries = &binding; + WGPUBindGroup bindGroup = wgpuDeviceCreateBindGroup(device, &bindGroupDesc); + WEBGPU_DEMO_CHECK(bindGroup, "[WebGPU] Failed to create bind group"); + WEBGPU_DEMO_LOG("[WebGPU] Bind group created"); + + // === Create the pipeline layout === + // The pipeline layout specifies the bind groups the pipeline uses. + + WGPUPipelineLayoutDescriptor pipelineLayoutDesc = {}; + pipelineLayoutDesc.label = "Demo Pipeline Layout"; + pipelineLayoutDesc.bindGroupLayoutCount = 1; + pipelineLayoutDesc.bindGroupLayouts = &bindGroupLayout; + WGPUPipelineLayout pipelineLayout = wgpuDeviceCreatePipelineLayout(device, &pipelineLayoutDesc); + WEBGPU_DEMO_CHECK(pipelineLayout, "[WebGPU] Failed to create pipeline layout"); + WEBGPU_DEMO_LOG("[WebGPU] Pipeline layout created"); + + // === Create the render pipeline === // The render pipeline specifies the configuration for the fixed-function stages as well as // the shaders for the programmable stages of the hardware pipeline. @@ -314,6 +389,7 @@ int main(int argc, const char* argv[]) // Configuration for the entire pipeline WGPURenderPipelineDescriptor pipelineDesc = {}; pipelineDesc.label = "Demo Pipeline"; + pipelineDesc.layout = pipelineLayout; pipelineDesc.vertex = vertexState; pipelineDesc.primitive = primitiveState; pipelineDesc.multisample = multisampleState; @@ -327,7 +403,6 @@ int main(int argc, const char* argv[]) while (!glfwWindowShouldClose(window)) { - // === Create a WebGPU texture view === // The texture view is where we render our image into. @@ -379,6 +454,10 @@ int main(int argc, const char* argv[]) // Create a view into the texture to specify where and how to modify the texture. WGPUTextureView view = wgpuTextureCreateView(surfaceTexture.texture, nullptr); + // === Update the uniform === + float time = static_cast(glfwGetTime()); + wgpuQueueWriteBuffer(queue, uniformBuffer, 0, &time, sizeof(float)); + // === Create a WebGPU command encoder === // The encoder encodes the commands for the GPU into a command buffer. @@ -415,6 +494,7 @@ int main(int argc, const char* argv[]) wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipeline); wgpuRenderPassEncoderSetVertexBuffer(renderPassEncoder, 0, vertexBuffer, 0, vertexDataSize); wgpuRenderPassEncoderSetIndexBuffer(renderPassEncoder, indexBuffer, WGPUIndexFormat_Uint16, 0, indexDataSize); + wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroup, 0, nullptr); wgpuRenderPassEncoderDrawIndexed(renderPassEncoder, indexCount, 1, 0, 0, 0); wgpuRenderPassEncoderEnd(renderPassEncoder); @@ -447,7 +527,14 @@ int main(int argc, const char* argv[]) // === Release all WebGPU resources === wgpuRenderPipelineRelease(pipeline); + wgpuPipelineLayoutRelease(pipelineLayout); + wgpuBindGroupRelease(bindGroup); + wgpuBindGroupLayoutRelease(bindGroupLayout); wgpuShaderModuleRelease(shaderModule); + wgpuBufferDestroy(uniformBuffer); + wgpuBufferRelease(uniformBuffer); + wgpuBufferDestroy(indexBuffer); + wgpuBufferRelease(indexBuffer); wgpuBufferDestroy(vertexBuffer); wgpuBufferRelease(vertexBuffer); wgpuSurfaceRelease(surface); From 7f7bd944f2bf3a0569d6896addfe62f97df3a7a5 Mon Sep 17 00:00:00 2001 From: Marino von Wattenwyl Date: Fri, 3 Nov 2023 16:13:41 +0100 Subject: [PATCH 033/108] [WebGPU] Add texture to demo --- apps/webgpu/CMakeLists.txt | 2 +- apps/webgpu/main.cpp | 142 +++++++++++++++++++++++++++++-------- 2 files changed, 115 insertions(+), 29 deletions(-) diff --git a/apps/webgpu/CMakeLists.txt b/apps/webgpu/CMakeLists.txt index cf777bb2..373f3bec 100644 --- a/apps/webgpu/CMakeLists.txt +++ b/apps/webgpu/CMakeLists.txt @@ -56,7 +56,7 @@ elseif (SYSTEM_NAME_UPPER MATCHES "DARWIN") endif () add_executable(webgpu-demo main.cpp) -target_link_libraries(webgpu-demo PRIVATE wgpu ${glfw_LIBS}) +target_link_libraries(webgpu-demo PRIVATE wgpu sl_cv ${glfw_LIBS}) if (SYSTEM_NAME_UPPER MATCHES "LINUX") target_link_libraries(webgpu-demo PRIVATE "X11") diff --git a/apps/webgpu/main.cpp b/apps/webgpu/main.cpp index c433a0f0..5a8f2286 100644 --- a/apps/webgpu/main.cpp +++ b/apps/webgpu/main.cpp @@ -21,6 +21,8 @@ #include #include #include +#include +#include #include #include @@ -101,17 +103,22 @@ int main(int argc, const char* argv[]) // We cannot access more resources than specified in the required limits, // which is how WebGPU prevents code from working on one machine and not on another. - WGPURequiredLimits requiredLimits = {}; - requiredLimits.limits.maxVertexAttributes = 1u; - requiredLimits.limits.maxVertexBuffers = 1u; - requiredLimits.limits.maxBufferSize = 4ull * 2ull * sizeof(float); - requiredLimits.limits.maxVertexBufferArrayStride = 2u * sizeof(float); - requiredLimits.limits.maxInterStageShaderComponents = 2u; - requiredLimits.limits.maxBindGroups = 1u; - requiredLimits.limits.maxUniformBuffersPerShaderStage = 1u; - requiredLimits.limits.maxUniformBufferBindingSize = 16ull * 4ull; - requiredLimits.limits.minStorageBufferOffsetAlignment = adapterLimits.limits.minStorageBufferOffsetAlignment; - requiredLimits.limits.minUniformBufferOffsetAlignment = adapterLimits.limits.minUniformBufferOffsetAlignment; + WGPURequiredLimits requiredLimits = {}; + requiredLimits.limits.maxVertexAttributes = 1u; + requiredLimits.limits.maxVertexBuffers = 1u; + requiredLimits.limits.maxBufferSize = 4ull * 2ull * sizeof(float); + requiredLimits.limits.maxVertexBufferArrayStride = 2u * sizeof(float); + requiredLimits.limits.maxInterStageShaderComponents = 2u; + requiredLimits.limits.maxBindGroups = 2u; + requiredLimits.limits.maxBindingsPerBindGroup = 2u; + requiredLimits.limits.maxUniformBuffersPerShaderStage = 1u; + requiredLimits.limits.maxUniformBufferBindingSize = 16ull * 4ull; + requiredLimits.limits.maxSampledTexturesPerShaderStage = 1u; + requiredLimits.limits.maxTextureDimension1D = 2048; + requiredLimits.limits.maxTextureDimension2D = 2048; + requiredLimits.limits.maxTextureArrayLayers = 1; + requiredLimits.limits.minStorageBufferOffsetAlignment = adapterLimits.limits.minStorageBufferOffsetAlignment; + requiredLimits.limits.minUniformBufferOffsetAlignment = adapterLimits.limits.minUniformBufferOffsetAlignment; WGPUDeviceDescriptor deviceDesc = {}; deviceDesc.label = "Demo Device"; @@ -254,6 +261,65 @@ int main(int argc, const char* argv[]) float uniformBufferData = 0.0f; wgpuQueueWriteBuffer(queue, uniformBuffer, 0, &uniformBufferData, sizeof(float)); + // === Create the texture === + + // unsigned pixelDataSize = 256 * 256 * 4; + // std::vector pixelData(pixelDataSize, 100); + + cv::Mat image = cv::imread(std::string(SL_PROJECT_ROOT) + "/data/images/textures/brickwall0512_C.jpg"); + cv::cvtColor(image, image, cv::COLOR_BGR2RGBA); + + unsigned imageWidth = image.cols; + unsigned imageHeight = image.rows; + unsigned pixelDataSize = 4 * imageWidth * imageHeight; + + WGPUTextureDescriptor textureDesc = {}; + textureDesc.label = "Demo Texture"; + textureDesc.usage = WGPUTextureUsage_CopyDst | WGPUTextureUsage_TextureBinding; + textureDesc.dimension = WGPUTextureDimension_2D; + textureDesc.size.width = imageWidth; + textureDesc.size.height = imageHeight; + textureDesc.size.depthOrArrayLayers = 1; + textureDesc.format = WGPUTextureFormat_RGBA8UnormSrgb; + textureDesc.mipLevelCount = 1; + textureDesc.sampleCount = 1; + + WGPUTexture texture = wgpuDeviceCreateTexture(device, &textureDesc); + WEBGPU_DEMO_CHECK(texture, "[WebGPU] Failed to create texture"); + WEBGPU_DEMO_LOG("[WebGPU] Texture created"); + + // Where do we copyu the pixel data to? + WGPUImageCopyTexture destination = {}; + destination.texture = texture; + destination.mipLevel = 0; + destination.origin.x = 0; + destination.origin.y = 0; + destination.origin.z = 0; + destination.aspect = WGPUTextureAspect_All; + + // Where do we copy the pixel data from? + WGPUTextureDataLayout pixelDataLayout = {}; + pixelDataLayout.offset = 0; + pixelDataLayout.bytesPerRow = 4 * textureDesc.size.width; + pixelDataLayout.rowsPerImage = textureDesc.size.height; + + // Upload the data to the GPU. + wgpuQueueWriteTexture(queue, &destination, image.data, pixelDataSize, &pixelDataLayout, &textureDesc.size); + + // === Create a texture view into the texture === + WGPUTextureViewDescriptor textureViewDesc = {}; + textureViewDesc.aspect = WGPUTextureAspect_All; + textureViewDesc.baseArrayLayer = 0; + textureViewDesc.arrayLayerCount = 1; + textureViewDesc.baseMipLevel = 0; + textureViewDesc.mipLevelCount = 1; + textureViewDesc.dimension = WGPUTextureViewDimension_2D; + textureViewDesc.format = textureDesc.format; + + WGPUTextureView textureView = wgpuTextureCreateView(texture, &textureViewDesc); + WEBGPU_DEMO_CHECK(textureView, "[WebGPU] Failed to create texture view"); + WEBGPU_DEMO_LOG("[WebGPU] Texture view created"); + // === Create the shader module === // Create a shader module for use in our render pipeline. // Shaders are written in a WebGPU-specific language called WGSL (WebGPU shader language). @@ -261,6 +327,7 @@ int main(int argc, const char* argv[]) const char* shaderSource = R"( @group(0) @binding(0) var u_time: f32; + @group(0) @binding(1) var texture: texture_2d; struct VertexOutput { @builtin(position) position: vec4f, @@ -279,7 +346,7 @@ int main(int argc, const char* argv[]) @fragment fn fs_main(in: VertexOutput) -> @location(0) vec4f { - return vec4f(in.texture_coords, 1.0, 1.0); + return textureLoad(texture, vec2i(i32(512.0 * in.texture_coords.x), i32(512.0 * in.texture_coords.y)), 0).rgba; } )"; @@ -296,35 +363,51 @@ int main(int argc, const char* argv[]) WEBGPU_DEMO_LOG("[WebGPU] Shader module created"); // === Create the bind group layout === - // Bind groups contain binding layouts that describe how uniforms are passed to shaders. + // Bind groups contain binding layouts that describe how uniforms and textures are passed to shaders. + + // Entry for the uniform + WGPUBindGroupLayoutEntry uniformLayout = {}; + uniformLayout.binding = 0; + uniformLayout.visibility = WGPUShaderStage_Vertex; + uniformLayout.buffer.type = WGPUBufferBindingType_Uniform; + uniformLayout.buffer.minBindingSize = sizeof(float); - WGPUBindGroupLayoutEntry bindingLayout = {}; - bindingLayout.binding = 0; - bindingLayout.visibility = WGPUShaderStage_Vertex; - bindingLayout.buffer.type = WGPUBufferBindingType_Uniform; - bindingLayout.buffer.minBindingSize = sizeof(float); + // Entry for the texture + WGPUBindGroupLayoutEntry textureLayout = {}; + textureLayout.binding = 1; + textureLayout.visibility = WGPUShaderStage_Fragment; + textureLayout.texture.sampleType = WGPUTextureSampleType_Float; + textureLayout.texture.viewDimension = WGPUTextureViewDimension_2D; + + std::vector bindGroupLayoutEntries = {uniformLayout, textureLayout}; WGPUBindGroupLayoutDescriptor bindGroupLayoutDesc = {}; bindGroupLayoutDesc.label = "Demo Bind Group Layout"; - bindGroupLayoutDesc.entryCount = 1; - bindGroupLayoutDesc.entries = &bindingLayout; + bindGroupLayoutDesc.entryCount = bindGroupLayoutEntries.size(); + bindGroupLayoutDesc.entries = bindGroupLayoutEntries.data(); WGPUBindGroupLayout bindGroupLayout = wgpuDeviceCreateBindGroupLayout(device, &bindGroupLayoutDesc); WEBGPU_DEMO_CHECK(bindGroupLayout, "[WebGPU] Failed to create bind group layout"); WEBGPU_DEMO_LOG("[WebGPU] Bind group layout created"); // === Create the bind group === - // The bind group actually binds the buffer to the uniform. + // The bind group actually binds the buffer to uniforms and textures. + + WGPUBindGroupEntry uniformBinding = {}; + uniformBinding.binding = 0; + uniformBinding.buffer = uniformBuffer; + uniformBinding.offset = 0; + uniformBinding.size = sizeof(float); + + WGPUBindGroupEntry textureBinding = {}; + textureBinding.binding = 1; + textureBinding.textureView = textureView; - WGPUBindGroupEntry binding = {}; - binding.binding = 0; - binding.buffer = uniformBuffer; - binding.offset = 0; - binding.size = sizeof(float); + std::vector bindGroupEntries = {uniformBinding, textureBinding}; WGPUBindGroupDescriptor bindGroupDesc = {}; bindGroupDesc.layout = bindGroupLayout; - bindGroupDesc.entryCount = bindGroupLayoutDesc.entryCount; - bindGroupDesc.entries = &binding; + bindGroupDesc.entryCount = bindGroupEntries.size(); + bindGroupDesc.entries = bindGroupEntries.data(); WGPUBindGroup bindGroup = wgpuDeviceCreateBindGroup(device, &bindGroupDesc); WEBGPU_DEMO_CHECK(bindGroup, "[WebGPU] Failed to create bind group"); WEBGPU_DEMO_LOG("[WebGPU] Bind group created"); @@ -531,6 +614,9 @@ int main(int argc, const char* argv[]) wgpuBindGroupRelease(bindGroup); wgpuBindGroupLayoutRelease(bindGroupLayout); wgpuShaderModuleRelease(shaderModule); + wgpuTextureViewRelease(textureView); + wgpuTextureDestroy(texture); + wgpuTextureRelease(texture); wgpuBufferDestroy(uniformBuffer); wgpuBufferRelease(uniformBuffer); wgpuBufferDestroy(indexBuffer); From 7339609d7410f7a1baa7efb4ec5d1898e52a8f34 Mon Sep 17 00:00:00 2001 From: Marino von Wattenwyl Date: Fri, 3 Nov 2023 16:15:12 +0100 Subject: [PATCH 034/108] [WebGPU] Use mailbox present mode in demo --- apps/webgpu/main.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/apps/webgpu/main.cpp b/apps/webgpu/main.cpp index 5a8f2286..ccad5570 100644 --- a/apps/webgpu/main.cpp +++ b/apps/webgpu/main.cpp @@ -188,7 +188,7 @@ int main(int argc, const char* argv[]) surfaceConfig.device = device; surfaceConfig.usage = WGPUTextureUsage_RenderAttachment; surfaceConfig.format = surfaceCapabilities.formats[0]; - surfaceConfig.presentMode = WGPUPresentMode_Fifo; + surfaceConfig.presentMode = WGPUPresentMode_Mailbox; surfaceConfig.alphaMode = surfaceCapabilities.alphaModes[0]; surfaceConfig.width = surfaceWidth; surfaceConfig.height = surfaceHeight; @@ -263,9 +263,6 @@ int main(int argc, const char* argv[]) // === Create the texture === - // unsigned pixelDataSize = 256 * 256 * 4; - // std::vector pixelData(pixelDataSize, 100); - cv::Mat image = cv::imread(std::string(SL_PROJECT_ROOT) + "/data/images/textures/brickwall0512_C.jpg"); cv::cvtColor(image, image, cv::COLOR_BGR2RGBA); From 68a6d9ffe70b54245c8a59df33cb3a06e6f732db Mon Sep 17 00:00:00 2001 From: luc Date: Fri, 3 Nov 2023 18:32:16 +0100 Subject: [PATCH 035/108] Add option to swith particle rendering method in the gui and fix some particles instanced rendering shaders --- apps/app_demo_slproject/source/AppDemoGui.cpp | 8 ++ modules/sl/source/SLAssetManager.cpp | 16 +++ modules/sl/source/SLAssetManager.h | 3 + modules/sl/source/SLMaterial.cpp | 18 ++- modules/sl/source/SLMaterial.h | 3 + modules/sl/source/gl/SLGLProgramGenerated.cpp | 133 +++++++++++++----- modules/sl/source/gl/SLGLProgramGenerated.h | 3 +- modules/sl/source/mesh/SLParticleSystem.cpp | 41 +++--- modules/sl/source/mesh/SLParticleSystem.h | 8 +- 9 files changed, 180 insertions(+), 53 deletions(-) diff --git a/apps/app_demo_slproject/source/AppDemoGui.cpp b/apps/app_demo_slproject/source/AppDemoGui.cpp index e4d20e0d..d943894d 100644 --- a/apps/app_demo_slproject/source/AppDemoGui.cpp +++ b/apps/app_demo_slproject/source/AppDemoGui.cpp @@ -2393,6 +2393,7 @@ 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("All off")) sv->drawBits()->allOff(); @@ -3770,6 +3771,13 @@ void AppDemoGui::buildProperties(SLScene* s, SLSceneView* sv) ps->isGenerated(false); } + bool instanced = ps->renderInstanced(); + if (ImGui::Checkbox("Instanced draw", &instanced)) + { + ps->drawInstanced(instanced); + ps->isGenerated(false); + } + // TTL (Time to live) if (ImGui::CollapsingHeader("Time to live")) { diff --git a/modules/sl/source/SLAssetManager.cpp b/modules/sl/source/SLAssetManager.cpp index 020a6c99..1fedf31f 100644 --- a/modules/sl/source/SLAssetManager.cpp +++ b/modules/sl/source/SLAssetManager.cpp @@ -95,6 +95,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/SLMaterial.cpp b/modules/sl/source/SLMaterial.cpp index d26365a3..3f8c0bab 100644 --- a/modules/sl/source/SLMaterial.cpp +++ b/modules/sl/source/SLMaterial.cpp @@ -372,6 +372,20 @@ 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) @@ -389,7 +403,7 @@ void SLMaterial::generateProgramPS(bool renderInstanced) // 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 @@ -415,7 +429,7 @@ void SLMaterial::generateProgramPS(bool renderInstanced) // 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) { diff --git a/modules/sl/source/SLMaterial.h b/modules/sl/source/SLMaterial.h index 983f2ff5..9ae29fcb 100644 --- a/modules/sl/source/SLMaterial.h +++ b/modules/sl/source/SLMaterial.h @@ -114,6 +114,9 @@ class SLMaterial : public SLObject 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() { diff --git a/modules/sl/source/gl/SLGLProgramGenerated.cpp b/modules/sl/source/gl/SLGLProgramGenerated.cpp index 9aee3ca3..81cec78c 100644 --- a/modules/sl/source/gl/SLGLProgramGenerated.cpp +++ b/modules/sl/source/gl/SLGLProgramGenerated.cpp @@ -74,16 +74,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"( @@ -122,7 +125,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; @@ -162,13 +172,14 @@ 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 - color.w *= transparency; // Apply transparency - )"; - + 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"( @@ -231,7 +242,6 @@ const string vertMain_v_R_OS = R"( 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)"; @@ -310,18 +320,38 @@ const string vertMain_PS_instanced_v_sS = R"( 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_instanced_v_texNum = R"( + 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_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 @@ -341,7 +371,6 @@ const string vertMain_PS_instanced_v_t_curve = R"( 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; @@ -362,12 +391,12 @@ const string vertMain_PS_instanced_EndAll = R"( )"; const string vertMain_PS_instanced_EndAll_VertBillboard = R"( - gl_Position = vec4(position + a_positionP, 1); + //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) @@ -377,7 +406,7 @@ 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); } )"; @@ -765,8 +794,6 @@ void main() )"; //----------------------------------------------------------------------------- const string fragMain_PS = R"( -void main() -{ // Just set the interpolated color from the vertex shader o_fragColor = v_particleColor; @@ -776,10 +803,9 @@ void main() if(o_fragColor.a < 0.001) discard; - )"; -const string fragMain_instanced_PS_begin = R"( +const string fragMain_PS_begin = R"( void main() { )"; @@ -791,7 +817,20 @@ const string fragMain_PS_withoutColor = R"( else o_fragColor = vec4(0,0,0,1.0); - o_fragColor.a *= transparency; + 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; @@ -1750,7 +1789,8 @@ void SLGLProgramGenerated::buildProgramName(SLMaterial* mat, */ void SLGLProgramGenerated::buildProgramNamePS(SLMaterial* mat, string& programName, - bool isDrawProg) + bool isDrawProg, + bool instancedRendering) { assert(mat && "No material pointer passed!"); programName = "gen"; @@ -1764,6 +1804,10 @@ void SLGLProgramGenerated::buildProgramNamePS(SLMaterial* mat, if (isDrawProg) // Drawing program { programName += "-Draw"; + + if (instancedRendering) + 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 @@ -2114,7 +2158,7 @@ void SLGLProgramGenerated::buildPerPixBlinn(SLMaterial* mat, SLVLight* lights) //----------------------------------------------------------------------------- void SLGLProgramGenerated::buildPerPixParticleInstanced(SLMaterial* mat) { - assert(_shaders.size() > 2 && + assert(_shaders.size() == 2 && _shaders[0]->type() == ST_vertex && _shaders[1]->type() == ST_fragment); @@ -2142,29 +2186,41 @@ void SLGLProgramGenerated::buildPerPixParticleInstanced(SLMaterial* mat) 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_a_texNum; // per particle texture number + 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; - if (Co && CoOvLi) vertCode += vertInput_PS_u_colorOvLF; // Vertex shader outputs - if (FlBoTex) vertCode += vertOutput_PS_instanced_texNum; vertCode += vertOutput_PS_instanced_transparency; vertCode += vertOutput_PS_v_tC; - // Vertex shader functions - if (Co && CoOvLi) vertCode += vertFunction_PS_ColorOverLT; + if (CoOvLi) vertCode += vertOutput_PS_age; + // Vertex shader functions // Vertex shader main loop vertCode += vertMain_instanced_Begin; vertCode += vertMain_PS_v_a; - vertCode += vertMain_PS_v_tC; + if (FlBoTex) + vertCode += vertMain_PS_v_tC_flipbook; + else + vertCode += vertMain_PS_v_tC; + if (AlOvLi) vertCode += vertMain_PS_instanced_v_t_begin; else @@ -2177,10 +2233,9 @@ void SLGLProgramGenerated::buildPerPixParticleInstanced(SLMaterial* mat) 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 (Co && CoOvLi) vertCode += vertMain_PS_v_doColorOverLT; - if (FlBoTex) vertCode += vertMain_PS_v_texNum; + //if (Co && CoOvLi) vertCode += vertMain_PS_v_doColorOverLT; if (billboardType == BT_Vertical || billboardType == BT_Horizontal) - vertCode += vertMain_PS_EndAll_VertBillboard; + vertCode += vertMain_PS_instanced_EndAll_VertBillboard; else vertCode += vertMain_PS_instanced_EndAll; @@ -2193,11 +2248,15 @@ void SLGLProgramGenerated::buildPerPixParticleInstanced(SLMaterial* mat) string fragCode; fragCode += shaderHeader(); - if (Co && !CoOvLi) vertCode += fragInput_PS_u_c; // Fragment shader inputs if (FlBoTex) fragCode += fragInput_PS_instanced_texNum; 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 @@ -2208,15 +2267,23 @@ void SLGLProgramGenerated::buildPerPixParticleInstanced(SLMaterial* mat) // Fragment shader outputs fragCode += fragOutputs_o_fragColor; + if (CoOvLi) fragCode += fragFunction_PS_ColorOverLT; + // Fragment shader main loop - fragCode += fragMain_instanced_PS_begin; + 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_c; + fragCode += fragMain_PS_instanced_transparency; fragCode += fragMain_instanced_PS_end; } else - fragCode += fragMain_PS_withoutColor; + fragCode += fragMain_PS_instanced_withoutColor; fragCode += fragMain_PS_endAll; @@ -2281,6 +2348,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; @@ -2292,7 +2360,7 @@ void SLGLProgramGenerated::buildPerPixParticle(SLMaterial* mat) vertCode += vertMain_PS_EndAll_VertBillboard; else vertCode += vertMain_PS_EndAll; - + addCodeToShader(_shaders[0], vertCode, _name + ".vert"); //////////////////////////////// @@ -2369,9 +2437,10 @@ 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; - + addCodeToShader(_shaders[1], fragCode, _name + ".frag"); } //----------------------------------------------------------------------------- diff --git a/modules/sl/source/gl/SLGLProgramGenerated.h b/modules/sl/source/gl/SLGLProgramGenerated.h index 22865155..b410bbc8 100644 --- a/modules/sl/source/gl/SLGLProgramGenerated.h +++ b/modules/sl/source/gl/SLGLProgramGenerated.h @@ -98,7 +98,8 @@ class SLGLProgramGenerated : public SLGLProgram string& programName); static void buildProgramNamePS(SLMaterial* mat, string& programName, - bool isDrawProg); + bool isDrawProg, + bool instancedRendering); void buildProgramCodePS(SLMaterial* mat, bool isDrawProg, bool isRenderInstanced = false); void buildProgramCode(SLMaterial* mat, diff --git a/modules/sl/source/mesh/SLParticleSystem.cpp b/modules/sl/source/mesh/SLParticleSystem.cpp index c5751e4c..52c9bb13 100644 --- a/modules/sl/source/mesh/SLParticleSystem.cpp +++ b/modules/sl/source/mesh/SLParticleSystem.cpp @@ -29,17 +29,17 @@ 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 - _renderInstanced = true; + _renderInstanced = renderInstanced; + _primitive = PT_points; - if (_renderInstanced) - _primitive = PT_triangles; - else - _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."); @@ -58,6 +58,7 @@ SLParticleSystem::SLParticleSystem(SLAssetManager* assetMgr, _updateTime.init(60, 0.0f); _drawTime.init(60, 0.0f); + } //----------------------------------------------------------------------------- //! Function which return a position in a sphere @@ -312,6 +313,13 @@ void SLParticleSystem::generate() SLVuint tempTexNum; SLVVec3f tempInitP; + if (_renderInstanced) + _primitive = PT_triangles; + else + _primitive = PT_points; + + deleteDataGpu(); + tempP.resize(_amount); tempV.resize(_amount); tempST.resize(_amount); @@ -453,6 +461,8 @@ void SLParticleSystem::generate() 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)); @@ -478,11 +488,6 @@ void SLParticleSystem::generate() _renderVao1.generate((SLuint)P.size()); _renderVao2.generate((SLuint)P.size()); } - else - { - P.push_back(SLVec3f(1, 1, 0)); - I32.push_back(0); - } _isGenerated = true; } @@ -591,9 +596,10 @@ void SLParticleSystem::draw(SLSceneView* sv, SLNode* node, SLuint instances) //////////////////// // Generate programs //////////////////// - if (!_mat->program() || !_mat->programTF()) + { _mat->generateProgramPS(_renderInstanced); + } //////////////////////////////////////////////// // Calculate time and paused and frustum culling @@ -718,7 +724,7 @@ void SLParticleSystem::draw(SLSceneView* sv, SLNode* node, SLuint instances) _vao1.drawArrayAs(PT_points); _vao1.endTF(); if (_renderInstanced) - _vao = _renderVao2; + _vao = _renderVao1; else _vao = _vao2; } @@ -880,7 +886,7 @@ void SLParticleSystem::draw(SLSceneView* sv, SLNode* node, SLuint instances) /////////////////////// if (_renderInstanced) - SLMesh::draw(sv, node, _amount*2); + SLMesh::draw(sv, node, _amount); else SLMesh::draw(sv, node); /////////////////////// @@ -898,9 +904,7 @@ void SLParticleSystem::draw(SLSceneView* sv, SLNode* node, SLuint instances) //! deleteData deletes all mesh data and VAOs void SLParticleSystem::deleteData() { - _vao1.deleteGL(); - _vao2.deleteGL(); - SLMesh::deleteData(); + return; } //----------------------------------------------------------------------------- //! deleteData deletes all mesh data and VAOs @@ -908,7 +912,10 @@ void SLParticleSystem::deleteDataGpu() { _vao1.deleteGL(); _vao2.deleteGL(); + _renderVao1.deleteGL(); + _renderVao2.deleteGL(); SLMesh::deleteDataGpu(); + _mat->deleteDataGpu(); } //----------------------------------------------------------------------------- /*! SLParticleSystem::buildAABB builds the passed axis-aligned bounding box in diff --git a/modules/sl/source/mesh/SLParticleSystem.h b/modules/sl/source/mesh/SLParticleSystem.h index c3c1ec62..d856e62f 100644 --- a/modules/sl/source/mesh/SLParticleSystem.h +++ b/modules/sl/source/mesh/SLParticleSystem.h @@ -40,7 +40,8 @@ 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, SLuint instances = 1); void deleteData(); @@ -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; } From 7ffe7273215358b4ad9f059a01adb8c5e5da557e Mon Sep 17 00:00:00 2001 From: luc Date: Fri, 3 Nov 2023 19:38:14 +0100 Subject: [PATCH 036/108] Fix color over life in instanced particle shader --- modules/sl/source/gl/SLGLProgramGenerated.cpp | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/modules/sl/source/gl/SLGLProgramGenerated.cpp b/modules/sl/source/gl/SLGLProgramGenerated.cpp index 81cec78c..1cd9a6ac 100644 --- a/modules/sl/source/gl/SLGLProgramGenerated.cpp +++ b/modules/sl/source/gl/SLGLProgramGenerated.cpp @@ -322,10 +322,10 @@ 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)"; +out float v_age; // Age of a particle)"; const string fragInput_PS_age = R"( - in float v_age; // Age of a particle)"; +in float v_age; // Age of a particle)"; const string vertMain_PS_v_color = R"( vert.color = u_color.rgb;)"; @@ -333,15 +333,15 @@ const string vertMain_PS_v_color = R"( const string vertMain_PS_v_texNum = R"( vert.texNum = a_texNum;)"; -const string vertMain_PS_instanced_v_texNum = R"( - 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; @@ -2212,7 +2212,6 @@ void SLGLProgramGenerated::buildPerPixParticleInstanced(SLMaterial* mat) if (CoOvLi) vertCode += vertOutput_PS_age; - // Vertex shader functions // Vertex shader main loop vertCode += vertMain_instanced_Begin; vertCode += vertMain_PS_v_a; @@ -2227,13 +2226,13 @@ void SLGLProgramGenerated::buildPerPixParticleInstanced(SLMaterial* mat) 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; - //if (rot) vertCode += vertMain_PS_v_r; 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 (Co && CoOvLi) vertCode += vertMain_PS_v_doColorOverLT; + if (CoOvLi) vertCode += vertMain_PS_v_age; + if (billboardType == BT_Vertical || billboardType == BT_Horizontal) vertCode += vertMain_PS_instanced_EndAll_VertBillboard; else @@ -2360,7 +2359,7 @@ void SLGLProgramGenerated::buildPerPixParticle(SLMaterial* mat) vertCode += vertMain_PS_EndAll_VertBillboard; else vertCode += vertMain_PS_EndAll; - + addCodeToShader(_shaders[0], vertCode, _name + ".vert"); //////////////////////////////// @@ -2440,7 +2439,7 @@ void SLGLProgramGenerated::buildPerPixParticle(SLMaterial* mat) fragCode += fragMain_PS_begin; fragCode += Co ? fragMain_PS : fragMain_PS_withoutColor; fragCode += fragMain_PS_endAll; - + addCodeToShader(_shaders[1], fragCode, _name + ".frag"); } //----------------------------------------------------------------------------- From 77d7c2b077333751ef184a43d51230b096488d1f Mon Sep 17 00:00:00 2001 From: luc Date: Mon, 6 Nov 2023 11:14:49 +0100 Subject: [PATCH 037/108] comment and small modif --- modules/sl/source/mesh/SLParticleSystem.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/sl/source/mesh/SLParticleSystem.cpp b/modules/sl/source/mesh/SLParticleSystem.cpp index 52c9bb13..bef75869 100644 --- a/modules/sl/source/mesh/SLParticleSystem.cpp +++ b/modules/sl/source/mesh/SLParticleSystem.cpp @@ -886,7 +886,7 @@ void SLParticleSystem::draw(SLSceneView* sv, SLNode* node, SLuint instances) /////////////////////// if (_renderInstanced) - SLMesh::draw(sv, node, _amount); + SLMesh::draw(sv, node, 2*_amount); //2 triangles per particle else SLMesh::draw(sv, node); /////////////////////// From 6fb69e03e7f748f121b6dd492986a4ebea145847 Mon Sep 17 00:00:00 2001 From: Marino von Wattenwyl Date: Mon, 6 Nov 2023 11:36:16 +0100 Subject: [PATCH 038/108] [WebGPU] Add 3d cube to demo --- apps/webgpu/CMakeLists.txt | 2 +- apps/webgpu/main.cpp | 335 ++++++++++++++++++++++++++++++------- 2 files changed, 272 insertions(+), 65 deletions(-) diff --git a/apps/webgpu/CMakeLists.txt b/apps/webgpu/CMakeLists.txt index 373f3bec..0c747f93 100644 --- a/apps/webgpu/CMakeLists.txt +++ b/apps/webgpu/CMakeLists.txt @@ -56,7 +56,7 @@ elseif (SYSTEM_NAME_UPPER MATCHES "DARWIN") endif () add_executable(webgpu-demo main.cpp) -target_link_libraries(webgpu-demo PRIVATE wgpu sl_cv ${glfw_LIBS}) +target_link_libraries(webgpu-demo PRIVATE wgpu sl_cv sl_math ${glfw_LIBS}) if (SYSTEM_NAME_UPPER MATCHES "LINUX") target_link_libraries(webgpu-demo PRIVATE "X11") diff --git a/apps/webgpu/main.cpp b/apps/webgpu/main.cpp index ccad5570..0a57aef4 100644 --- a/apps/webgpu/main.cpp +++ b/apps/webgpu/main.cpp @@ -23,11 +23,13 @@ #include #include #include +#include #include #include #include #include +#include #define WEBGPU_DEMO_LOG(msg) std::cout << (msg) << std::endl @@ -38,6 +40,15 @@ std::exit(1); \ } +struct alignas(16) ShaderUniformData +{ + float projectionMatrix[16]; + float viewMatrix[16]; + float modelMatrix[16]; +}; + +static_assert(sizeof(ShaderUniformData) % 16 == 0, "uniform data size must be a multiple of 16"); + void handleAdapterRequest(WGPURequestAdapterStatus status, WGPUAdapter adapter, char const* message, @@ -104,18 +115,19 @@ int main(int argc, const char* argv[]) // which is how WebGPU prevents code from working on one machine and not on another. WGPURequiredLimits requiredLimits = {}; - requiredLimits.limits.maxVertexAttributes = 1u; + requiredLimits.limits.maxVertexAttributes = 2u; requiredLimits.limits.maxVertexBuffers = 1u; - requiredLimits.limits.maxBufferSize = 4ull * 2ull * sizeof(float); - requiredLimits.limits.maxVertexBufferArrayStride = 2u * sizeof(float); + requiredLimits.limits.maxBufferSize = 1024ull; + requiredLimits.limits.maxVertexBufferArrayStride = 5u * sizeof(float); requiredLimits.limits.maxInterStageShaderComponents = 2u; - requiredLimits.limits.maxBindGroups = 2u; - requiredLimits.limits.maxBindingsPerBindGroup = 2u; + requiredLimits.limits.maxBindGroups = 1u; + requiredLimits.limits.maxBindingsPerBindGroup = 3u; requiredLimits.limits.maxUniformBuffersPerShaderStage = 1u; - requiredLimits.limits.maxUniformBufferBindingSize = 16ull * 4ull; + requiredLimits.limits.maxUniformBufferBindingSize = 512ull; requiredLimits.limits.maxSampledTexturesPerShaderStage = 1u; - requiredLimits.limits.maxTextureDimension1D = 2048; - requiredLimits.limits.maxTextureDimension2D = 2048; + requiredLimits.limits.maxSamplersPerShaderStage = 1u; + requiredLimits.limits.maxTextureDimension1D = 4096; + requiredLimits.limits.maxTextureDimension2D = 4096; requiredLimits.limits.maxTextureArrayLayers = 1; requiredLimits.limits.minStorageBufferOffsetAlignment = adapterLimits.limits.minStorageBufferOffsetAlignment; requiredLimits.limits.minUniformBufferOffsetAlignment = adapterLimits.limits.minUniformBufferOffsetAlignment; @@ -201,14 +213,57 @@ int main(int argc, const char* argv[]) // clang-format off std::vector vertexData = { - -0.5, 0.5, // top-left corner - -0.5, -0.5, // bottom-left corner - 0.5, 0.5, // top-right corner - 0.5, -0.5, // bottom-right corner + // left + -0.5, 0.5, -0.5, 0.0f, 0.0f, + -0.5, -0.5, -0.5, 0.0f, 1.0f, + -0.5, 0.5, 0.5, 1.0f, 0.0f, + -0.5, 0.5, 0.5, 1.0f, 0.0f, + -0.5, -0.5, -0.5, 0.0f, 1.0f, + -0.5, -0.5, 0.5, 1.0f, 1.0f, + + // right + 0.5, 0.5, 0.5, 0.0f, 0.0f, + 0.5, -0.5, 0.5, 0.0f, 1.0f, + 0.5, 0.5, -0.5, 1.0f, 0.0f, + 0.5, 0.5, -0.5, 1.0f, 0.0f, + 0.5, -0.5, 0.5, 0.0f, 1.0f, + 0.5, -0.5, -0.5, 1.0f, 1.0f, + + // bottom + -0.5, -0.5, 0.5, 0.0f, 0.0f, + -0.5, -0.5, -0.5, 0.0f, 1.0f, + 0.5, -0.5, 0.5, 1.0f, 0.0f, + 0.5, -0.5, 0.5, 1.0f, 0.0f, + -0.5, -0.5, -0.5, 0.0f, 1.0f, + 0.5, -0.5, -0.5, 1.0f, 1.0f, + + // top + -0.5, 0.5, -0.5, 0.0f, 0.0f, + -0.5, 0.5, 0.5, 0.0f, 1.0f, + 0.5, 0.5, -0.5, 1.0f, 0.0f, + 0.5, 0.5, -0.5, 1.0f, 0.0f, + -0.5, 0.5, 0.5, 0.0f, 1.0f, + 0.5, 0.5, 0.5, 1.0f, 1.0f, + + // back + 0.5, 0.5, -0.5, 0.0f, 0.0f, + 0.5, -0.5, -0.5, 0.0f, 1.0f, + -0.5, 0.5, -0.5, 1.0f, 0.0f, + -0.5, 0.5, -0.5, 1.0f, 0.0f, + 0.5, -0.5, -0.5, 0.0f, 1.0f, + -0.5, -0.5, -0.5, 1.0f, 1.0f, + + // front + -0.5, 0.5, 0.5, 0.0f, 0.0f, + -0.5, -0.5, 0.5, 0.0f, 1.0f, + 0.5, 0.5, 0.5, 1.0f, 0.0f, + 0.5, 0.5, 0.5, 1.0f, 0.0f, + -0.5, -0.5, 0.5, 0.0f, 1.0f, + 0.5, -0.5, 0.5, 1.0f, 1.0f, }; // clang-format on - unsigned vertexCount = vertexData.size() / 2; + unsigned vertexCount = vertexData.size() / 5; unsigned vertexDataSize = vertexData.size() * sizeof(float); WGPUBufferDescriptor vertexBufferDesc = {}; @@ -225,13 +280,9 @@ int main(int argc, const char* argv[]) // === Create the index buffer === - // clang-format off - std::vector indexData = - { - 0, 1, 2, - 2, 1, 3, - }; - // clang-format on + std::vector indexData = {}; + for (std::uint16_t index = 0; index < 36; index++) + indexData.push_back(index); unsigned indexCount = indexData.size(); unsigned indexDataSize = indexData.size() * sizeof(std::uint16_t); @@ -251,23 +302,56 @@ int main(int argc, const char* argv[]) WGPUBufferDescriptor uniformBufferDesc = {}; uniformBufferDesc.label = "Demo Uniform Buffer"; - uniformBufferDesc.size = sizeof(float); + uniformBufferDesc.size = sizeof(ShaderUniformData); uniformBufferDesc.usage = WGPUBufferUsage_CopyDst | WGPUBufferUsage_Uniform; WGPUBuffer uniformBuffer = wgpuDeviceCreateBuffer(device, &uniformBufferDesc); WEBGPU_DEMO_CHECK(indexBuffer, "[WebGPU] Failed to create uniform buffer"); WEBGPU_DEMO_LOG("[WebGPU] Uniform buffer created"); - float uniformBufferData = 0.0f; - wgpuQueueWriteBuffer(queue, uniformBuffer, 0, &uniformBufferData, sizeof(float)); + ShaderUniformData uniformData = {}; + wgpuQueueWriteBuffer(queue, uniformBuffer, 0, &uniformData, sizeof(ShaderUniformData)); + + // === Create the depth texture === + + WGPUTextureFormat depthTextureFormat = WGPUTextureFormat_Depth24Plus; + + WGPUTextureDescriptor depthTextureDesc = {}; + depthTextureDesc.dimension = WGPUTextureDimension_2D; + depthTextureDesc.format = depthTextureFormat; + depthTextureDesc.mipLevelCount = 1; + depthTextureDesc.sampleCount = 1; + depthTextureDesc.size.width = surfaceWidth; + depthTextureDesc.size.height = surfaceHeight; + depthTextureDesc.size.depthOrArrayLayers = 1; + depthTextureDesc.usage = WGPUTextureUsage_RenderAttachment; + depthTextureDesc.viewFormatCount = 1; + depthTextureDesc.viewFormats = &depthTextureFormat; + + WGPUTexture depthTexture = wgpuDeviceCreateTexture(device, &depthTextureDesc); + WEBGPU_DEMO_CHECK(depthTexture, "[WebGPU] Failed to create depth texture"); + WEBGPU_DEMO_LOG("[WebGPU] Depth texture created"); + + WGPUTextureViewDescriptor depthTextureViewDesc = {}; + depthTextureViewDesc.aspect = WGPUTextureAspect_DepthOnly; + depthTextureViewDesc.baseArrayLayer = 0; + depthTextureViewDesc.arrayLayerCount = 1; + depthTextureViewDesc.baseMipLevel = 0; + depthTextureViewDesc.mipLevelCount = 1; + depthTextureViewDesc.dimension = WGPUTextureViewDimension_2D; + depthTextureViewDesc.format = depthTextureFormat; + + WGPUTextureView depthTextureView = wgpuTextureCreateView(depthTexture, &depthTextureViewDesc); + WEBGPU_DEMO_CHECK(depthTextureView, "[WebGPU] Failed to create depth texture view"); + WEBGPU_DEMO_LOG("[WebGPU] Depth texture view created"); // === Create the texture === cv::Mat image = cv::imread(std::string(SL_PROJECT_ROOT) + "/data/images/textures/brickwall0512_C.jpg"); cv::cvtColor(image, image, cv::COLOR_BGR2RGBA); - unsigned imageWidth = image.cols; - unsigned imageHeight = image.rows; + unsigned imageWidth = image.cols; + unsigned imageHeight = image.rows; unsigned pixelDataSize = 4 * imageWidth * imageHeight; WGPUTextureDescriptor textureDesc = {}; @@ -317,33 +401,67 @@ int main(int argc, const char* argv[]) WEBGPU_DEMO_CHECK(textureView, "[WebGPU] Failed to create texture view"); WEBGPU_DEMO_LOG("[WebGPU] Texture view created"); + // === Create the texture sampler === + // The sampler is used to look up values of the texture in the shader. + // We could look up values directly without a sampler, but with a sampler we + // get access to features like interpolation and mipmapping. + + WGPUSamplerDescriptor samplerDesc = {}; + samplerDesc.addressModeU = WGPUAddressMode_ClampToEdge; + samplerDesc.addressModeV = WGPUAddressMode_ClampToEdge; + samplerDesc.addressModeW = WGPUAddressMode_ClampToEdge; + samplerDesc.magFilter = WGPUFilterMode_Linear; + samplerDesc.minFilter = WGPUFilterMode_Linear; + samplerDesc.mipmapFilter = WGPUMipmapFilterMode_Linear; + samplerDesc.lodMinClamp = 0.0f; + samplerDesc.lodMaxClamp = 1.0f; + samplerDesc.compare = WGPUCompareFunction_Undefined; + samplerDesc.maxAnisotropy = 1; + + WGPUSampler sampler = wgpuDeviceCreateSampler(device, &samplerDesc); + WEBGPU_DEMO_CHECK(sampler, "[WebGPU] Failed to create sampler"); + WEBGPU_DEMO_LOG("[WebGPU] Sampler created"); + // === Create the shader module === // Create a shader module for use in our render pipeline. // Shaders are written in a WebGPU-specific language called WGSL (WebGPU shader language). // Shader modules can be used in multiple pipeline stages by specifying different entry points. const char* shaderSource = R"( - @group(0) @binding(0) var u_time: f32; - @group(0) @binding(1) var texture: texture_2d; + struct VertexInput { + @location(0) position: vec3f, + @location(1) uvs: vec2f, + }; + + struct Uniforms { + projectionMatrix: mat4x4f, + viewMatrix: mat4x4f, + modelMatrix: mat4x4f, + }; struct VertexOutput { @builtin(position) position: vec4f, - @location(0) texture_coords: vec2f, + @location(0) uvs: vec2f, } + + @group(0) @binding(0) var uniforms: Uniforms; + @group(0) @binding(1) var texture: texture_2d; + @group(0) @binding(2) var textureSampler: sampler; @vertex - fn vs_main(@location(0) in_vertex_position: vec2f) -> VertexOutput { - var offset = vec2(0.5 * sin(u_time), 0.5 * cos(u_time)); - + fn vs_main(in: VertexInput) -> VertexOutput { + var localPos = vec4f(in.position, 1.0); + var worldPos = uniforms.modelMatrix * localPos; + var out: VertexOutput; - out.position = vec4f(in_vertex_position + offset, 0.0, 1.0); - out.texture_coords = in_vertex_position + vec2(0.5, 0.5); + out.position = uniforms.projectionMatrix * uniforms.viewMatrix * worldPos; + out.uvs = in.uvs; return out; } @fragment fn fs_main(in: VertexOutput) -> @location(0) vec4f { - return textureLoad(texture, vec2i(i32(512.0 * in.texture_coords.x), i32(512.0 * in.texture_coords.y)), 0).rgba; + return textureSample(texture, textureSampler, in.uvs).rgba; } )"; @@ -363,20 +481,28 @@ int main(int argc, const char* argv[]) // Bind groups contain binding layouts that describe how uniforms and textures are passed to shaders. // Entry for the uniform - WGPUBindGroupLayoutEntry uniformLayout = {}; - uniformLayout.binding = 0; - uniformLayout.visibility = WGPUShaderStage_Vertex; - uniformLayout.buffer.type = WGPUBufferBindingType_Uniform; - uniformLayout.buffer.minBindingSize = sizeof(float); + WGPUBindGroupLayoutEntry uniformBindLayout = {}; + uniformBindLayout.binding = 0; + uniformBindLayout.visibility = WGPUShaderStage_Vertex; + uniformBindLayout.buffer.type = WGPUBufferBindingType_Uniform; + uniformBindLayout.buffer.minBindingSize = sizeof(ShaderUniformData); // Entry for the texture - WGPUBindGroupLayoutEntry textureLayout = {}; - textureLayout.binding = 1; - textureLayout.visibility = WGPUShaderStage_Fragment; - textureLayout.texture.sampleType = WGPUTextureSampleType_Float; - textureLayout.texture.viewDimension = WGPUTextureViewDimension_2D; - - std::vector bindGroupLayoutEntries = {uniformLayout, textureLayout}; + WGPUBindGroupLayoutEntry textureBindLayout = {}; + textureBindLayout.binding = 1; + textureBindLayout.visibility = WGPUShaderStage_Fragment; + textureBindLayout.texture.sampleType = WGPUTextureSampleType_Float; + textureBindLayout.texture.viewDimension = WGPUTextureViewDimension_2D; + + // Entry for the sampler + WGPUBindGroupLayoutEntry samplerBindLayout = {}; + samplerBindLayout.binding = 2; + samplerBindLayout.visibility = WGPUShaderStage_Fragment; + samplerBindLayout.sampler.type = WGPUSamplerBindingType_Filtering; + + std::vector bindGroupLayoutEntries = {uniformBindLayout, + textureBindLayout, + samplerBindLayout}; WGPUBindGroupLayoutDescriptor bindGroupLayoutDesc = {}; bindGroupLayoutDesc.label = "Demo Bind Group Layout"; @@ -390,16 +516,22 @@ int main(int argc, const char* argv[]) // The bind group actually binds the buffer to uniforms and textures. WGPUBindGroupEntry uniformBinding = {}; - uniformBinding.binding = 0; - uniformBinding.buffer = uniformBuffer; - uniformBinding.offset = 0; - uniformBinding.size = sizeof(float); + uniformBinding.binding = 0; + uniformBinding.buffer = uniformBuffer; + uniformBinding.offset = 0; + uniformBinding.size = sizeof(ShaderUniformData); WGPUBindGroupEntry textureBinding = {}; - textureBinding.binding = 1; - textureBinding.textureView = textureView; + textureBinding.binding = 1; + textureBinding.textureView = textureView; + + WGPUBindGroupEntry samplerBinding = {}; + samplerBinding.binding = 2; + samplerBinding.sampler = sampler; - std::vector bindGroupEntries = {uniformBinding, textureBinding}; + std::vector bindGroupEntries = {uniformBinding, + textureBinding, + samplerBinding}; WGPUBindGroupDescriptor bindGroupDesc = {}; bindGroupDesc.layout = bindGroupLayout; @@ -424,18 +556,25 @@ int main(int argc, const char* argv[]) // The render pipeline specifies the configuration for the fixed-function stages as well as // the shaders for the programmable stages of the hardware pipeline. - // Description of the vertex attribute for the vertex buffer layout - WGPUVertexAttribute vertexAttribute = {}; - vertexAttribute.format = WGPUVertexFormat_Float32x2; - vertexAttribute.offset = 0; - vertexAttribute.shaderLocation = 0; + // Description of the vertex attributes for the vertex buffer layout + WGPUVertexAttribute positionAttribute = {}; + positionAttribute.format = WGPUVertexFormat_Float32x3; + positionAttribute.offset = 0; + positionAttribute.shaderLocation = 0; + + WGPUVertexAttribute uvsAttribute = {}; + uvsAttribute.format = WGPUVertexFormat_Float32x2; + uvsAttribute.offset = 3 * sizeof(float); + uvsAttribute.shaderLocation = 1; + + std::vector vertexAttributes = {positionAttribute, uvsAttribute}; // Description of the vertex buffer layout for the vertex shader stage WGPUVertexBufferLayout vertexBufferLayout = {}; - vertexBufferLayout.arrayStride = 2ull * sizeof(float); + vertexBufferLayout.arrayStride = 5ull * sizeof(float); vertexBufferLayout.stepMode = WGPUVertexStepMode_Vertex; - vertexBufferLayout.attributeCount = 1; - vertexBufferLayout.attributes = &vertexAttribute; + vertexBufferLayout.attributeCount = vertexAttributes.size(); + vertexBufferLayout.attributes = vertexAttributes.data(); // Configuration for the vertex shader stage WGPUVertexState vertexState = {}; @@ -451,6 +590,22 @@ int main(int argc, const char* argv[]) primitiveState.frontFace = WGPUFrontFace_CCW; primitiveState.cullMode = WGPUCullMode_None; + // Configuration for depth testing and stencil buffer + WGPUDepthStencilState depthStencilState = {}; + depthStencilState.format = depthTextureFormat; + depthStencilState.depthWriteEnabled = true; + depthStencilState.depthCompare = WGPUCompareFunction_Less; + depthStencilState.stencilReadMask = 0; + depthStencilState.stencilWriteMask = 0; + depthStencilState.stencilFront.compare = WGPUCompareFunction_Always; + depthStencilState.stencilFront.failOp = WGPUStencilOperation_Keep; + depthStencilState.stencilFront.depthFailOp = WGPUStencilOperation_Keep; + depthStencilState.stencilFront.passOp = WGPUStencilOperation_Keep; + depthStencilState.stencilBack.compare = WGPUCompareFunction_Always; + depthStencilState.stencilBack.failOp = WGPUStencilOperation_Keep; + depthStencilState.stencilBack.depthFailOp = WGPUStencilOperation_Keep; + depthStencilState.stencilBack.passOp = WGPUStencilOperation_Keep; + // Configuration for multisampling WGPUMultisampleState multisampleState = {}; multisampleState.count = 1; @@ -472,6 +627,7 @@ int main(int argc, const char* argv[]) pipelineDesc.layout = pipelineLayout; pipelineDesc.vertex = vertexState; pipelineDesc.primitive = primitiveState; + pipelineDesc.depthStencil = &depthStencilState; pipelineDesc.multisample = multisampleState; pipelineDesc.fragment = &fragmentState; @@ -479,6 +635,8 @@ int main(int argc, const char* argv[]) WEBGPU_DEMO_CHECK(pipeline, "[WebGPU] Failed to create render pipeline"); WEBGPU_DEMO_LOG("[WebGPU] Render pipeline created"); + float rotation = 0.0f; + // === Render loop === while (!glfwWindowShouldClose(window)) @@ -515,6 +673,23 @@ int main(int argc, const char* argv[]) surfaceConfig.width = surfaceWidth; surfaceConfig.height = surfaceHeight; wgpuSurfaceConfigure(surface, &surfaceConfig); + + // Recreate the depth texture. + + wgpuTextureViewRelease(textureView); + wgpuTextureDestroy(depthTexture); + wgpuTextureRelease(depthTexture); + + depthTextureDesc.size.width = surfaceWidth; + depthTextureDesc.size.height = surfaceHeight; + + depthTexture = wgpuDeviceCreateTexture(device, &depthTextureDesc); + WEBGPU_DEMO_CHECK(depthTexture, "[WebGPU] Failed to re-create depth texture"); + WEBGPU_DEMO_LOG("[WebGPU] Depth texture re-created"); + + depthTextureView = wgpuTextureCreateView(depthTexture, &depthTextureViewDesc); + WEBGPU_DEMO_CHECK(depthTextureView, "[WebGPU] Failed to re-create depth texture view"); + WEBGPU_DEMO_LOG("[WebGPU] Depth texture view re-created"); } // Skip this frame. @@ -534,9 +709,24 @@ int main(int argc, const char* argv[]) // Create a view into the texture to specify where and how to modify the texture. WGPUTextureView view = wgpuTextureCreateView(surfaceTexture.texture, nullptr); - // === Update the uniform === - float time = static_cast(glfwGetTime()); - wgpuQueueWriteBuffer(queue, uniformBuffer, 0, &time, sizeof(float)); + // === Prepare uniform data === + float aspectRatio = static_cast(surfaceWidth) / static_cast(surfaceHeight); + + SLMat4f projectionMatrix; + projectionMatrix.perspective(70.0f, aspectRatio, 0.1, 1000.0f); + + SLMat4f viewMatrix; + viewMatrix.translate(0.0f, 0.0f, -2.0f); + + SLMat4f modelMatrix; + modelMatrix.rotation(90.0f * static_cast(glfwGetTime()), SLVec3f::AXISY); + + // === Update uniforms === + ShaderUniformData uniformData = {}; + std::memcpy(uniformData.projectionMatrix, projectionMatrix.m(), sizeof(uniformData.projectionMatrix)); + std::memcpy(uniformData.viewMatrix, viewMatrix.m(), sizeof(uniformData.viewMatrix)); + std::memcpy(uniformData.modelMatrix, modelMatrix.m(), sizeof(uniformData.modelMatrix)); + wgpuQueueWriteBuffer(queue, uniformBuffer, 0, &uniformData, sizeof(ShaderUniformData)); // === Create a WebGPU command encoder === // The encoder encodes the commands for the GPU into a command buffer. @@ -551,6 +741,7 @@ int main(int argc, const char* argv[]) // The render pass specifies what attachments to use while rendering. // A color attachment specifies what view to render into and what to do with the texture before and after // rendering. We clear the texture before rendering and store the results after rendering. + // The depth attachment specifies what depth texture to use. WGPURenderPassColorAttachment colorAttachment = {}; colorAttachment.view = view; @@ -561,10 +752,22 @@ int main(int argc, const char* argv[]) colorAttachment.clearValue.b = 0.2; colorAttachment.clearValue.a = 1.0; + WGPURenderPassDepthStencilAttachment depthStencilAttachment = {}; + depthStencilAttachment.view = depthTextureView; + depthStencilAttachment.depthLoadOp = WGPULoadOp_Clear; + depthStencilAttachment.depthStoreOp = WGPUStoreOp_Store; + depthStencilAttachment.depthClearValue = 1.0f; + depthStencilAttachment.depthReadOnly = false; + depthStencilAttachment.stencilLoadOp = WGPULoadOp_Clear; + depthStencilAttachment.stencilStoreOp = WGPUStoreOp_Store; + depthStencilAttachment.stencilClearValue = 0.0f; + depthStencilAttachment.stencilReadOnly = true; + WGPURenderPassDescriptor renderPassDesc = {}; renderPassDesc.label = "Demo Render Pass"; renderPassDesc.colorAttachmentCount = 1; renderPassDesc.colorAttachments = &colorAttachment; + renderPassDesc.depthStencilAttachment = &depthStencilAttachment; // === Encode the commands === // The commands to begin a render pass, bind a pipeline, draw the triangle and end the render pass @@ -611,9 +814,13 @@ int main(int argc, const char* argv[]) wgpuBindGroupRelease(bindGroup); wgpuBindGroupLayoutRelease(bindGroupLayout); wgpuShaderModuleRelease(shaderModule); + wgpuSamplerRelease(sampler); wgpuTextureViewRelease(textureView); wgpuTextureDestroy(texture); wgpuTextureRelease(texture); + wgpuTextureViewRelease(depthTextureView); + wgpuTextureDestroy(depthTexture); + wgpuTextureRelease(depthTexture); wgpuBufferDestroy(uniformBuffer); wgpuBufferRelease(uniformBuffer); wgpuBufferDestroy(indexBuffer); From 7aa30d574dcbff1bd35524c4154ba340b0e18128 Mon Sep 17 00:00:00 2001 From: Marino von Wattenwyl Date: Mon, 6 Nov 2023 13:11:40 +0100 Subject: [PATCH 039/108] [WebGPU] Add mipmapping to demo --- apps/webgpu/main.cpp | 87 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 77 insertions(+), 10 deletions(-) diff --git a/apps/webgpu/main.cpp b/apps/webgpu/main.cpp index 0a57aef4..3173eeeb 100644 --- a/apps/webgpu/main.cpp +++ b/apps/webgpu/main.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -362,7 +363,7 @@ int main(int argc, const char* argv[]) textureDesc.size.height = imageHeight; textureDesc.size.depthOrArrayLayers = 1; textureDesc.format = WGPUTextureFormat_RGBA8UnormSrgb; - textureDesc.mipLevelCount = 1; + textureDesc.mipLevelCount = 4; textureDesc.sampleCount = 1; WGPUTexture texture = wgpuDeviceCreateTexture(device, &textureDesc); @@ -384,8 +385,51 @@ int main(int argc, const char* argv[]) pixelDataLayout.bytesPerRow = 4 * textureDesc.size.width; pixelDataLayout.rowsPerImage = textureDesc.size.height; - // Upload the data to the GPU. - wgpuQueueWriteTexture(queue, &destination, image.data, pixelDataSize, &pixelDataLayout, &textureDesc.size); + // Generate mip levels. + + WGPUExtent3D mipLevelSize; + mipLevelSize.width = textureDesc.size.width; + mipLevelSize.height = textureDesc.size.height; + mipLevelSize.depthOrArrayLayers = 1; + + for (unsigned mipLevel = 0; mipLevel < textureDesc.mipLevelCount; mipLevel++) + { + // === Test colors === + // + // std::uint64_t mipLevelColors[] = + // { + // 0xFF0000FF, + // 0xFFFF00FF, + // 0x00FF00FF, + // 0x0000FFFF + // }; + // + // for (unsigned y = 0; y < mipLevelSize.height; y++) + // { + // for (unsigned x = 0; x < mipLevelSize.width; x++) + // { + // unsigned pixelIndex = x + y * mipLevelSize.width; + // std::memcpy(&mipLevelData[4ull * pixelIndex], &mipLevelColors[mipLevel], 4); + // } + // } + + cv::Mat mipLevelImage; + cv::Size cvSize(static_cast(mipLevelSize.width), static_cast(mipLevelSize.height)); + cv::resize(image, mipLevelImage, cvSize); + + std::size_t mipLevelBytes = 4ull * mipLevelSize.width * mipLevelSize.height; + + destination.mipLevel = mipLevel; + pixelDataLayout.bytesPerRow = 4 * mipLevelSize.width; + pixelDataLayout.rowsPerImage = mipLevelSize.height; + + // Upload the data to the GPU. + wgpuQueueWriteTexture(queue, &destination, mipLevelImage.data, mipLevelBytes, &pixelDataLayout, &mipLevelSize); + + // Scale the image down for the next mip level. + mipLevelSize.width /= 2; + mipLevelSize.height /= 2; + } // === Create a texture view into the texture === WGPUTextureViewDescriptor textureViewDesc = {}; @@ -393,7 +437,7 @@ int main(int argc, const char* argv[]) textureViewDesc.baseArrayLayer = 0; textureViewDesc.arrayLayerCount = 1; textureViewDesc.baseMipLevel = 0; - textureViewDesc.mipLevelCount = 1; + textureViewDesc.mipLevelCount = textureDesc.mipLevelCount; textureViewDesc.dimension = WGPUTextureViewDimension_2D; textureViewDesc.format = textureDesc.format; @@ -414,9 +458,9 @@ int main(int argc, const char* argv[]) samplerDesc.minFilter = WGPUFilterMode_Linear; samplerDesc.mipmapFilter = WGPUMipmapFilterMode_Linear; samplerDesc.lodMinClamp = 0.0f; - samplerDesc.lodMaxClamp = 1.0f; + samplerDesc.lodMaxClamp = static_cast(textureDesc.mipLevelCount); samplerDesc.compare = WGPUCompareFunction_Undefined; - samplerDesc.maxAnisotropy = 1; + samplerDesc.maxAnisotropy = 1.0f; WGPUSampler sampler = wgpuDeviceCreateSampler(device, &samplerDesc); WEBGPU_DEMO_CHECK(sampler, "[WebGPU] Failed to create sampler"); @@ -635,12 +679,38 @@ int main(int argc, const char* argv[]) WEBGPU_DEMO_CHECK(pipeline, "[WebGPU] Failed to create render pipeline"); WEBGPU_DEMO_LOG("[WebGPU] Render pipeline created"); - float rotation = 0.0f; + SLMat4f modelMatrix; + + double lastCursorX = 0.0f; + double lastCursorY = 0.0f; // === Render loop === while (!glfwWindowShouldClose(window)) { + // === Update cube model matrix === + + double cursorX; + double cursorY; + glfwGetCursorPos(window, &cursorX, &cursorY); + + SLMat4f rotationX; + SLMat4f rotationY; + + if (glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_1) == GLFW_PRESS) + { + float deltaRotX = 0.5f * static_cast(cursorY - lastCursorY); + float deltaRotY = 0.5f * static_cast(cursorX - lastCursorX); + + rotationX.rotate(deltaRotX, SLVec3f::AXISX); + rotationY.rotate(deltaRotY, SLVec3f::AXISY); + } + + modelMatrix = rotationY * rotationX * modelMatrix; + + lastCursorX = cursorX; + lastCursorY = cursorY; + // === Create a WebGPU texture view === // The texture view is where we render our image into. @@ -718,9 +788,6 @@ int main(int argc, const char* argv[]) SLMat4f viewMatrix; viewMatrix.translate(0.0f, 0.0f, -2.0f); - SLMat4f modelMatrix; - modelMatrix.rotation(90.0f * static_cast(glfwGetTime()), SLVec3f::AXISY); - // === Update uniforms === ShaderUniformData uniformData = {}; std::memcpy(uniformData.projectionMatrix, projectionMatrix.m(), sizeof(uniformData.projectionMatrix)); From e6ad617ba7abce5a73c27bdbee96e91e017a378c Mon Sep 17 00:00:00 2001 From: Marino von Wattenwyl Date: Mon, 6 Nov 2023 13:14:05 +0100 Subject: [PATCH 040/108] [WebGPU] Re-configure suboptimal surfaces in demo --- apps/webgpu/main.cpp | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/apps/webgpu/main.cpp b/apps/webgpu/main.cpp index 3173eeeb..4939f53e 100644 --- a/apps/webgpu/main.cpp +++ b/apps/webgpu/main.cpp @@ -725,7 +725,20 @@ int main(int argc, const char* argv[]) { case WGPUSurfaceGetCurrentTextureStatus_Success: // Everything is ok. - // TODO: check for a suboptimal texture and re-configure it if needed. + + // Check for a suboptimal texture and re-configure it if needed. + if (surfaceTexture.suboptimal) + { + WEBGPU_DEMO_LOG("[WebGPU] Re-configuring currently suboptimal surface"); + surfaceConfig.width = surfaceWidth; + surfaceConfig.height = surfaceHeight; + wgpuSurfaceConfigure(surface, &surfaceConfig); + + // Skip this frame. + glfwPollEvents(); + continue; + } + break; case WGPUSurfaceGetCurrentTextureStatus_Timeout: From 2f3e6c40b9d462b362f68e19671c69cd55fcbfca Mon Sep 17 00:00:00 2001 From: Marino von Wattenwyl Date: Mon, 6 Nov 2023 15:14:33 +0100 Subject: [PATCH 041/108] [WebGPU] Use new Objective-C runtime declarations in demo --- apps/webgpu/main.cpp | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/apps/webgpu/main.cpp b/apps/webgpu/main.cpp index 4939f53e..2b63ec6b 100644 --- a/apps/webgpu/main.cpp +++ b/apps/webgpu/main.cpp @@ -11,7 +11,6 @@ # define GLFW_EXPOSE_NATIVE_COCOA # define GLFW_NATIVE_INCLUDE_NONE -# define OBJC_OLD_DISPATCH_PROTOTYPES 1 # include # include # include @@ -165,12 +164,18 @@ int main(int argc, const char* argv[]) #elif defined(SYSTEM_DARWIN) id cocoaWindow = glfwGetCocoaWindow(window); - id contentView = objc_msgSend(cocoaWindow, sel_registerName("contentView")); - objc_msgSend(contentView, sel_getUid("setWantsLayer:"), 1); + // We call the Objective-C runtime directly to get the Metal surface. + + // We need to cast 'objc_msgSend' to appropiate function pointer types before calling them. + auto* sendMessageReturnId = (id(*)(id, ...))objc_msgSend; + auto* sendMessageReturnNothing = (void (*)(id, ...))objc_msgSend; + + id contentView = sendMessageReturnId(cocoaWindow, sel_getUid("contentView")); + sendMessageReturnNothing(contentView, sel_getUid("setWantsLayer:"), 1); objc_class* metalLayerClass = objc_getClass("CAMetalLayer"); - id metalLayer = objc_msgSend((id)metalLayerClass, sel_getUid("layer")); - objc_msgSend(contentView, sel_registerName("setLayer:"), metalLayer); + id metalLayer = sendMessageReturnId((id)metalLayerClass, sel_getUid("layer")); + sendMessageReturnNothing(contentView, sel_registerName("setLayer:"), metalLayer); WGPUSurfaceDescriptorFromMetalLayer nativeSurfaceDesc = {}; nativeSurfaceDesc.chain.sType = WGPUSType_SurfaceDescriptorFromMetalLayer; @@ -388,8 +393,8 @@ int main(int argc, const char* argv[]) // Generate mip levels. WGPUExtent3D mipLevelSize; - mipLevelSize.width = textureDesc.size.width; - mipLevelSize.height = textureDesc.size.height; + mipLevelSize.width = textureDesc.size.width; + mipLevelSize.height = textureDesc.size.height; mipLevelSize.depthOrArrayLayers = 1; for (unsigned mipLevel = 0; mipLevel < textureDesc.mipLevelCount; mipLevel++) @@ -403,17 +408,17 @@ int main(int argc, const char* argv[]) // 0x00FF00FF, // 0x0000FFFF // }; - // + // // for (unsigned y = 0; y < mipLevelSize.height; y++) // { // for (unsigned x = 0; x < mipLevelSize.width; x++) // { // unsigned pixelIndex = x + y * mipLevelSize.width; // std::memcpy(&mipLevelData[4ull * pixelIndex], &mipLevelColors[mipLevel], 4); - // } + // } // } - cv::Mat mipLevelImage; + cv::Mat mipLevelImage; cv::Size cvSize(static_cast(mipLevelSize.width), static_cast(mipLevelSize.height)); cv::resize(image, mipLevelImage, cvSize); @@ -725,7 +730,7 @@ int main(int argc, const char* argv[]) { case WGPUSurfaceGetCurrentTextureStatus_Success: // Everything is ok. - + // Check for a suboptimal texture and re-configure it if needed. if (surfaceTexture.suboptimal) { From f35025373c1d8dad65fc386854d65070b368daf1 Mon Sep 17 00:00:00 2001 From: Marino von Wattenwyl Date: Mon, 6 Nov 2023 15:17:47 +0100 Subject: [PATCH 042/108] [WebGPU] Use FIFO present mode in demo --- apps/webgpu/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/webgpu/main.cpp b/apps/webgpu/main.cpp index 2b63ec6b..efc71100 100644 --- a/apps/webgpu/main.cpp +++ b/apps/webgpu/main.cpp @@ -206,7 +206,7 @@ int main(int argc, const char* argv[]) surfaceConfig.device = device; surfaceConfig.usage = WGPUTextureUsage_RenderAttachment; surfaceConfig.format = surfaceCapabilities.formats[0]; - surfaceConfig.presentMode = WGPUPresentMode_Mailbox; + surfaceConfig.presentMode = WGPUPresentMode_Fifo; surfaceConfig.alphaMode = surfaceCapabilities.alphaModes[0]; surfaceConfig.width = surfaceWidth; surfaceConfig.height = surfaceHeight; From f981ed4a78190c2413655a6c9ae29f5f7b311295 Mon Sep 17 00:00:00 2001 From: Marino von Wattenwyl Date: Mon, 6 Nov 2023 16:33:40 +0100 Subject: [PATCH 043/108] [WebGPU] Move Metal layer creation into Objective-C file --- apps/webgpu/CMakeLists.txt | 7 ++++++- apps/webgpu/main.cpp | 27 +++++---------------------- apps/webgpu/metal_layer.mm | 11 +++++++++++ 3 files changed, 22 insertions(+), 23 deletions(-) create mode 100644 apps/webgpu/metal_layer.mm diff --git a/apps/webgpu/CMakeLists.txt b/apps/webgpu/CMakeLists.txt index 0c747f93..fff48076 100644 --- a/apps/webgpu/CMakeLists.txt +++ b/apps/webgpu/CMakeLists.txt @@ -55,7 +55,12 @@ elseif (SYSTEM_NAME_UPPER MATCHES "DARWIN") INTERFACE_INCLUDE_DIRECTORIES "${WGPU_DIR}") endif () -add_executable(webgpu-demo main.cpp) +set(TARGET_SPECIFIC_SOURCES) +if (SYSTEM_NAME_UPPER MATCHES "DARWIN") + set(TARGET_SPECIFIC_SOURCES metal_layer.mm) +endif () + +add_executable(webgpu-demo main.cpp ${TARGET_SPECIFIC_SOURCES}) target_link_libraries(webgpu-demo PRIVATE wgpu sl_cv sl_math ${glfw_LIBS}) if (SYSTEM_NAME_UPPER MATCHES "LINUX") diff --git a/apps/webgpu/main.cpp b/apps/webgpu/main.cpp index efc71100..062c56e6 100644 --- a/apps/webgpu/main.cpp +++ b/apps/webgpu/main.cpp @@ -9,12 +9,6 @@ #elif defined(__APPLE__) # define SYSTEM_DARWIN # define GLFW_EXPOSE_NATIVE_COCOA -# define GLFW_NATIVE_INCLUDE_NONE - -# include -# include -# include -# include #endif #include @@ -49,6 +43,10 @@ struct alignas(16) ShaderUniformData static_assert(sizeof(ShaderUniformData) % 16 == 0, "uniform data size must be a multiple of 16"); +#ifdef SYSTEM_DARWIN +extern "C" void* createMetalLayer(void* window); +#endif + void handleAdapterRequest(WGPURequestAdapterStatus status, WGPUAdapter adapter, char const* message, @@ -162,24 +160,9 @@ int main(int argc, const char* argv[]) nativeSurfaceDesc.display = glfwGetX11Display(); nativeSurfaceDesc.window = glfwGetX11Window(window); #elif defined(SYSTEM_DARWIN) - id cocoaWindow = glfwGetCocoaWindow(window); - - // We call the Objective-C runtime directly to get the Metal surface. - - // We need to cast 'objc_msgSend' to appropiate function pointer types before calling them. - auto* sendMessageReturnId = (id(*)(id, ...))objc_msgSend; - auto* sendMessageReturnNothing = (void (*)(id, ...))objc_msgSend; - - id contentView = sendMessageReturnId(cocoaWindow, sel_getUid("contentView")); - sendMessageReturnNothing(contentView, sel_getUid("setWantsLayer:"), 1); - - objc_class* metalLayerClass = objc_getClass("CAMetalLayer"); - id metalLayer = sendMessageReturnId((id)metalLayerClass, sel_getUid("layer")); - sendMessageReturnNothing(contentView, sel_registerName("setLayer:"), metalLayer); - WGPUSurfaceDescriptorFromMetalLayer nativeSurfaceDesc = {}; nativeSurfaceDesc.chain.sType = WGPUSType_SurfaceDescriptorFromMetalLayer; - nativeSurfaceDesc.layer = metalLayer; + nativeSurfaceDesc.layer = createMetalLayer(glfwGetCocoaWindow(window)); #endif WGPUSurfaceDescriptor surfaceDesc = {}; diff --git a/apps/webgpu/metal_layer.mm b/apps/webgpu/metal_layer.mm new file mode 100644 index 00000000..a7a832ca --- /dev/null +++ b/apps/webgpu/metal_layer.mm @@ -0,0 +1,11 @@ +#include +#include + +extern "C" id createMetalLayer(NSWindow* window) +{ + [window.contentView setWantsLayer:YES]; + id metalLayer = [CAMetalLayer layer]; + [window.contentView setLayer:metalLayer]; + + return metalLayer; +} \ No newline at end of file From 2e2c6775e4c15f7d0b73e5dff62a5790ac33bfce Mon Sep 17 00:00:00 2001 From: Marcus Hudritsch Date: Mon, 6 Nov 2023 18:36:51 +0100 Subject: [PATCH 044/108] Cosmetics --- apps/webgpu/main.cpp | 92 ++++++++++++++++++++++---------------------- 1 file changed, 46 insertions(+), 46 deletions(-) diff --git a/apps/webgpu/main.cpp b/apps/webgpu/main.cpp index 062c56e6..f66e72ce 100644 --- a/apps/webgpu/main.cpp +++ b/apps/webgpu/main.cpp @@ -202,53 +202,53 @@ int main(int argc, const char* argv[]) // clang-format off std::vector vertexData = { - // left - -0.5, 0.5, -0.5, 0.0f, 0.0f, - -0.5, -0.5, -0.5, 0.0f, 1.0f, - -0.5, 0.5, 0.5, 1.0f, 0.0f, - -0.5, 0.5, 0.5, 1.0f, 0.0f, - -0.5, -0.5, -0.5, 0.0f, 1.0f, - -0.5, -0.5, 0.5, 1.0f, 1.0f, - - // right - 0.5, 0.5, 0.5, 0.0f, 0.0f, - 0.5, -0.5, 0.5, 0.0f, 1.0f, - 0.5, 0.5, -0.5, 1.0f, 0.0f, - 0.5, 0.5, -0.5, 1.0f, 0.0f, - 0.5, -0.5, 0.5, 0.0f, 1.0f, - 0.5, -0.5, -0.5, 1.0f, 1.0f, - - // bottom - -0.5, -0.5, 0.5, 0.0f, 0.0f, - -0.5, -0.5, -0.5, 0.0f, 1.0f, - 0.5, -0.5, 0.5, 1.0f, 0.0f, - 0.5, -0.5, 0.5, 1.0f, 0.0f, - -0.5, -0.5, -0.5, 0.0f, 1.0f, - 0.5, -0.5, -0.5, 1.0f, 1.0f, - - // top + // left -0.5, 0.5, -0.5, 0.0f, 0.0f, - -0.5, 0.5, 0.5, 0.0f, 1.0f, - 0.5, 0.5, -0.5, 1.0f, 0.0f, - 0.5, 0.5, -0.5, 1.0f, 0.0f, - -0.5, 0.5, 0.5, 0.0f, 1.0f, - 0.5, 0.5, 0.5, 1.0f, 1.0f, - - // back - 0.5, 0.5, -0.5, 0.0f, 0.0f, - 0.5, -0.5, -0.5, 0.0f, 1.0f, - -0.5, 0.5, -0.5, 1.0f, 0.0f, - -0.5, 0.5, -0.5, 1.0f, 0.0f, - 0.5, -0.5, -0.5, 0.0f, 1.0f, - -0.5, -0.5, -0.5, 1.0f, 1.0f, - - // front - -0.5, 0.5, 0.5, 0.0f, 0.0f, - -0.5, -0.5, 0.5, 0.0f, 1.0f, - 0.5, 0.5, 0.5, 1.0f, 0.0f, - 0.5, 0.5, 0.5, 1.0f, 0.0f, - -0.5, -0.5, 0.5, 0.0f, 1.0f, - 0.5, -0.5, 0.5, 1.0f, 1.0f, + -0.5, -0.5, -0.5, 0.0f, 1.0f, + -0.5, 0.5, 0.5, 1.0f, 0.0f, + -0.5, 0.5, 0.5, 1.0f, 0.0f, + -0.5, -0.5, -0.5, 0.0f, 1.0f, + -0.5, -0.5, 0.5, 1.0f, 1.0f, + + // right + 0.5, 0.5, 0.5, 0.0f, 0.0f, + 0.5, -0.5, 0.5, 0.0f, 1.0f, + 0.5, 0.5, -0.5, 1.0f, 0.0f, + 0.5, 0.5, -0.5, 1.0f, 0.0f, + 0.5, -0.5, 0.5, 0.0f, 1.0f, + 0.5, -0.5, -0.5, 1.0f, 1.0f, + + // bottom + -0.5, -0.5, 0.5, 0.0f, 0.0f, + -0.5, -0.5, -0.5, 0.0f, 1.0f, + 0.5, -0.5, 0.5, 1.0f, 0.0f, + 0.5, -0.5, 0.5, 1.0f, 0.0f, + -0.5, -0.5, -0.5, 0.0f, 1.0f, + 0.5, -0.5, -0.5, 1.0f, 1.0f, + + // top + -0.5, 0.5, -0.5, 0.0f, 0.0f, + -0.5, 0.5, 0.5, 0.0f, 1.0f, + 0.5, 0.5, -0.5, 1.0f, 0.0f, + 0.5, 0.5, -0.5, 1.0f, 0.0f, + -0.5, 0.5, 0.5, 0.0f, 1.0f, + 0.5, 0.5, 0.5, 1.0f, 1.0f, + + // back + 0.5, 0.5, -0.5, 0.0f, 0.0f, + 0.5, -0.5, -0.5, 0.0f, 1.0f, + -0.5, 0.5, -0.5, 1.0f, 0.0f, + -0.5, 0.5, -0.5, 1.0f, 0.0f, + 0.5, -0.5, -0.5, 0.0f, 1.0f, + -0.5, -0.5, -0.5, 1.0f, 1.0f, + + // front + -0.5, 0.5, 0.5, 0.0f, 0.0f, + -0.5, -0.5, 0.5, 0.0f, 1.0f, + 0.5, 0.5, 0.5, 1.0f, 0.0f, + 0.5, 0.5, 0.5, 1.0f, 0.0f, + -0.5, -0.5, 0.5, 0.0f, 1.0f, + 0.5, -0.5, 0.5, 1.0f, 1.0f, }; // clang-format on From cfa20a95ffedad9a58e26acb7760620492421cb3 Mon Sep 17 00:00:00 2001 From: Marcus Hudritsch Date: Mon, 6 Nov 2023 18:43:54 +0100 Subject: [PATCH 045/108] Fixed Warning --- apps/webgpu/main.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/webgpu/main.cpp b/apps/webgpu/main.cpp index f66e72ce..e87ddc83 100644 --- a/apps/webgpu/main.cpp +++ b/apps/webgpu/main.cpp @@ -7,7 +7,9 @@ # define SYSTEM_LINUX # define GLFW_EXPOSE_NATIVE_X11 #elif defined(__APPLE__) -# define SYSTEM_DARWIN +# ifndef SYSTEM_DARWIN +# define SYSTEM_DARWIN +# endif # define GLFW_EXPOSE_NATIVE_COCOA #endif From 61583d619de36d06785e3e39185c3206a808e616 Mon Sep 17 00:00:00 2001 From: Marino von Wattenwyl Date: Thu, 9 Nov 2023 09:48:14 +0100 Subject: [PATCH 046/108] [WebGPU] Update demo --- apps/webgpu/main.cpp | 173 ++++++++++++++++++++++++------------------- 1 file changed, 95 insertions(+), 78 deletions(-) diff --git a/apps/webgpu/main.cpp b/apps/webgpu/main.cpp index 062c56e6..eb231299 100644 --- a/apps/webgpu/main.cpp +++ b/apps/webgpu/main.cpp @@ -34,6 +34,14 @@ std::exit(1); \ } +struct AppData +{ + WGPUDevice device = nullptr; + WGPUSurface surface = nullptr; + + bool requireSurfaceReconfiguration = false; +}; + struct alignas(16) ShaderUniformData { float projectionMatrix[16]; @@ -47,6 +55,12 @@ static_assert(sizeof(ShaderUniformData) % 16 == 0, "uniform data size must be a extern "C" void* createMetalLayer(void* window); #endif +void onWindowResized(GLFWwindow* window, int width, int height) +{ + AppData& app = *((AppData*)glfwGetWindowUserPointer(window)); + app.requireSurfaceReconfiguration = true; +} + void handleAdapterRequest(WGPURequestAdapterStatus status, WGPUAdapter adapter, char const* message, @@ -75,6 +89,8 @@ void handleDeviceRequest(WGPURequestDeviceStatus status, int main(int argc, const char* argv[]) { + AppData app; + // === Initialize GLFW === WEBGPU_DEMO_CHECK(glfwInit(), "[GLFW] Failed to initialize"); @@ -86,7 +102,11 @@ int main(int argc, const char* argv[]) glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); GLFWwindow* window = glfwCreateWindow(1280, 720, "WebGPU Demo", nullptr, nullptr); - WEBGPU_DEMO_CHECK(window, "[GLFW] Window created"); + WEBGPU_DEMO_CHECK(window, "[GLFW] Failed to create window"); + WEBGPU_DEMO_LOG("[GLFW] Window created"); + + glfwSetWindowUserPointer(window, &app); + glfwSetWindowSizeCallback(window, onWindowResized); // === Create a WebGPU instance === // The instance is the root interface to WebGPU through which we create all other WebGPU resources. @@ -135,13 +155,12 @@ int main(int argc, const char* argv[]) deviceDesc.requiredLimits = &requiredLimits; deviceDesc.defaultQueue.label = "Demo Queue"; - WGPUDevice device; - wgpuAdapterRequestDevice(adapter, &deviceDesc, handleDeviceRequest, &device); + wgpuAdapterRequestDevice(adapter, &deviceDesc, handleDeviceRequest, &app.device); // === Acquire a WebGPU queue === // The queue is where the commands for the GPU are submitted to. - WGPUQueue queue = wgpuDeviceGetQueue(device); + WGPUQueue queue = wgpuDeviceGetQueue(app.device); WEBGPU_DEMO_CHECK(queue, "[WebGPU] Failed to acquire queue"); WEBGPU_DEMO_LOG("[WebGPU] Queue acquired"); @@ -169,8 +188,8 @@ int main(int argc, const char* argv[]) surfaceDesc.label = "Demo Surface"; surfaceDesc.nextInChain = (const WGPUChainedStruct*)&nativeSurfaceDesc; - WGPUSurface surface = wgpuInstanceCreateSurface(instance, &surfaceDesc); - WEBGPU_DEMO_CHECK(surface, "[WebGPU] Failed to create surface"); + app.surface = wgpuInstanceCreateSurface(instance, &surfaceDesc); + WEBGPU_DEMO_CHECK(app.surface, "[WebGPU] Failed to create surface"); WEBGPU_DEMO_LOG("[WebGPU] Surface created"); // === Configure the surface === @@ -178,7 +197,7 @@ int main(int argc, const char* argv[]) // Query the surface capabilities from the adapter. WGPUSurfaceCapabilities surfaceCapabilities; - wgpuSurfaceGetCapabilities(surface, adapter, &surfaceCapabilities); + wgpuSurfaceGetCapabilities(app.surface, adapter, &surfaceCapabilities); // Get the window size from the GLFW window. int surfaceWidth; @@ -186,14 +205,14 @@ int main(int argc, const char* argv[]) glfwGetWindowSize(window, &surfaceWidth, &surfaceHeight); WGPUSurfaceConfiguration surfaceConfig = {}; - surfaceConfig.device = device; + surfaceConfig.device = app.device; surfaceConfig.usage = WGPUTextureUsage_RenderAttachment; surfaceConfig.format = surfaceCapabilities.formats[0]; surfaceConfig.presentMode = WGPUPresentMode_Fifo; surfaceConfig.alphaMode = surfaceCapabilities.alphaModes[0]; surfaceConfig.width = surfaceWidth; surfaceConfig.height = surfaceHeight; - wgpuSurfaceConfigure(surface, &surfaceConfig); + wgpuSurfaceConfigure(app.surface, &surfaceConfig); WEBGPU_DEMO_LOG("[WebGPU] Surface configured"); // === Create the vertex buffer === @@ -260,7 +279,7 @@ int main(int argc, const char* argv[]) vertexBufferDesc.size = vertexDataSize; vertexBufferDesc.usage = WGPUBufferUsage_CopyDst | WGPUBufferUsage_Vertex; - WGPUBuffer vertexBuffer = wgpuDeviceCreateBuffer(device, &vertexBufferDesc); + WGPUBuffer vertexBuffer = wgpuDeviceCreateBuffer(app.device, &vertexBufferDesc); WEBGPU_DEMO_CHECK(vertexBuffer, "[WebGPU] Failed to create vertex buffer"); WEBGPU_DEMO_LOG("[WebGPU] Vertex buffer created"); @@ -281,7 +300,7 @@ int main(int argc, const char* argv[]) indexBufferDesc.size = indexDataSize; indexBufferDesc.usage = WGPUBufferUsage_CopyDst | WGPUBufferUsage_Index; - WGPUBuffer indexBuffer = wgpuDeviceCreateBuffer(device, &indexBufferDesc); + WGPUBuffer indexBuffer = wgpuDeviceCreateBuffer(app.device, &indexBufferDesc); WEBGPU_DEMO_CHECK(indexBuffer, "[WebGPU] Failed to create index buffer"); WEBGPU_DEMO_LOG("[WebGPU] Index buffer created"); @@ -294,7 +313,7 @@ int main(int argc, const char* argv[]) uniformBufferDesc.size = sizeof(ShaderUniformData); uniformBufferDesc.usage = WGPUBufferUsage_CopyDst | WGPUBufferUsage_Uniform; - WGPUBuffer uniformBuffer = wgpuDeviceCreateBuffer(device, &uniformBufferDesc); + WGPUBuffer uniformBuffer = wgpuDeviceCreateBuffer(app.device, &uniformBufferDesc); WEBGPU_DEMO_CHECK(indexBuffer, "[WebGPU] Failed to create uniform buffer"); WEBGPU_DEMO_LOG("[WebGPU] Uniform buffer created"); @@ -317,7 +336,7 @@ int main(int argc, const char* argv[]) depthTextureDesc.viewFormatCount = 1; depthTextureDesc.viewFormats = &depthTextureFormat; - WGPUTexture depthTexture = wgpuDeviceCreateTexture(device, &depthTextureDesc); + WGPUTexture depthTexture = wgpuDeviceCreateTexture(app.device, &depthTextureDesc); WEBGPU_DEMO_CHECK(depthTexture, "[WebGPU] Failed to create depth texture"); WEBGPU_DEMO_LOG("[WebGPU] Depth texture created"); @@ -354,7 +373,7 @@ int main(int argc, const char* argv[]) textureDesc.mipLevelCount = 4; textureDesc.sampleCount = 1; - WGPUTexture texture = wgpuDeviceCreateTexture(device, &textureDesc); + WGPUTexture texture = wgpuDeviceCreateTexture(app.device, &textureDesc); WEBGPU_DEMO_CHECK(texture, "[WebGPU] Failed to create texture"); WEBGPU_DEMO_LOG("[WebGPU] Texture created"); @@ -450,7 +469,7 @@ int main(int argc, const char* argv[]) samplerDesc.compare = WGPUCompareFunction_Undefined; samplerDesc.maxAnisotropy = 1.0f; - WGPUSampler sampler = wgpuDeviceCreateSampler(device, &samplerDesc); + WGPUSampler sampler = wgpuDeviceCreateSampler(app.device, &samplerDesc); WEBGPU_DEMO_CHECK(sampler, "[WebGPU] Failed to create sampler"); WEBGPU_DEMO_LOG("[WebGPU] Sampler created"); @@ -505,7 +524,7 @@ int main(int argc, const char* argv[]) shaderModuleDesc.label = "Demo Shader"; shaderModuleDesc.nextInChain = (const WGPUChainedStruct*)&shaderModuleWGSLDesc; - WGPUShaderModule shaderModule = wgpuDeviceCreateShaderModule(device, &shaderModuleDesc); + WGPUShaderModule shaderModule = wgpuDeviceCreateShaderModule(app.device, &shaderModuleDesc); WEBGPU_DEMO_CHECK(shaderModule, "[WebGPU] Failed to create shader module"); WEBGPU_DEMO_LOG("[WebGPU] Shader module created"); @@ -540,7 +559,7 @@ int main(int argc, const char* argv[]) bindGroupLayoutDesc.label = "Demo Bind Group Layout"; bindGroupLayoutDesc.entryCount = bindGroupLayoutEntries.size(); bindGroupLayoutDesc.entries = bindGroupLayoutEntries.data(); - WGPUBindGroupLayout bindGroupLayout = wgpuDeviceCreateBindGroupLayout(device, &bindGroupLayoutDesc); + WGPUBindGroupLayout bindGroupLayout = wgpuDeviceCreateBindGroupLayout(app.device, &bindGroupLayoutDesc); WEBGPU_DEMO_CHECK(bindGroupLayout, "[WebGPU] Failed to create bind group layout"); WEBGPU_DEMO_LOG("[WebGPU] Bind group layout created"); @@ -569,7 +588,7 @@ int main(int argc, const char* argv[]) bindGroupDesc.layout = bindGroupLayout; bindGroupDesc.entryCount = bindGroupEntries.size(); bindGroupDesc.entries = bindGroupEntries.data(); - WGPUBindGroup bindGroup = wgpuDeviceCreateBindGroup(device, &bindGroupDesc); + WGPUBindGroup bindGroup = wgpuDeviceCreateBindGroup(app.device, &bindGroupDesc); WEBGPU_DEMO_CHECK(bindGroup, "[WebGPU] Failed to create bind group"); WEBGPU_DEMO_LOG("[WebGPU] Bind group created"); @@ -580,7 +599,7 @@ int main(int argc, const char* argv[]) pipelineLayoutDesc.label = "Demo Pipeline Layout"; pipelineLayoutDesc.bindGroupLayoutCount = 1; pipelineLayoutDesc.bindGroupLayouts = &bindGroupLayout; - WGPUPipelineLayout pipelineLayout = wgpuDeviceCreatePipelineLayout(device, &pipelineLayoutDesc); + WGPUPipelineLayout pipelineLayout = wgpuDeviceCreatePipelineLayout(app.device, &pipelineLayoutDesc); WEBGPU_DEMO_CHECK(pipelineLayout, "[WebGPU] Failed to create pipeline layout"); WEBGPU_DEMO_LOG("[WebGPU] Pipeline layout created"); @@ -663,11 +682,12 @@ int main(int argc, const char* argv[]) pipelineDesc.multisample = multisampleState; pipelineDesc.fragment = &fragmentState; - WGPURenderPipeline pipeline = wgpuDeviceCreateRenderPipeline(device, &pipelineDesc); + WGPURenderPipeline pipeline = wgpuDeviceCreateRenderPipeline(app.device, &pipelineDesc); WEBGPU_DEMO_CHECK(pipeline, "[WebGPU] Failed to create render pipeline"); WEBGPU_DEMO_LOG("[WebGPU] Render pipeline created"); - SLMat4f modelMatrix; + float camRotX = 0.0f; + float camRotY = 0.0f; double lastCursorX = 0.0f; double lastCursorY = 0.0f; @@ -682,29 +702,59 @@ int main(int argc, const char* argv[]) double cursorY; glfwGetCursorPos(window, &cursorX, &cursorY); - SLMat4f rotationX; - SLMat4f rotationY; - if (glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_1) == GLFW_PRESS) { - float deltaRotX = 0.5f * static_cast(cursorY - lastCursorY); - float deltaRotY = 0.5f * static_cast(cursorX - lastCursorX); - - rotationX.rotate(deltaRotX, SLVec3f::AXISX); - rotationY.rotate(deltaRotY, SLVec3f::AXISY); + camRotX -= 0.25f * static_cast(cursorY - lastCursorY); + camRotY -= 0.25f * static_cast(cursorX - lastCursorX); } - modelMatrix = rotationY * rotationX * modelMatrix; - lastCursorX = cursorX; lastCursorY = cursorY; // === Create a WebGPU texture view === // The texture view is where we render our image into. + if (app.requireSurfaceReconfiguration) + { + app.requireSurfaceReconfiguration = false; + + // Get the window size from the GLFW window. + glfwGetWindowSize(window, &surfaceWidth, &surfaceHeight); + + // The surface size might be zero if the window is minimized. + if (surfaceWidth != 0 && surfaceHeight != 0) + { + WEBGPU_DEMO_LOG("[WebGPU] Re-configuring surface"); + surfaceConfig.width = surfaceWidth; + surfaceConfig.height = surfaceHeight; + wgpuSurfaceConfigure(app.surface, &surfaceConfig); + + // Recreate the depth texture. + + wgpuTextureViewRelease(textureView); + wgpuTextureDestroy(depthTexture); + wgpuTextureRelease(depthTexture); + + depthTextureDesc.size.width = surfaceWidth; + depthTextureDesc.size.height = surfaceHeight; + + depthTexture = wgpuDeviceCreateTexture(app.device, &depthTextureDesc); + WEBGPU_DEMO_CHECK(depthTexture, "[WebGPU] Failed to re-create depth texture"); + WEBGPU_DEMO_LOG("[WebGPU] Depth texture re-created"); + + depthTextureView = wgpuTextureCreateView(depthTexture, &depthTextureViewDesc); + WEBGPU_DEMO_CHECK(depthTextureView, "[WebGPU] Failed to re-create depth texture view"); + WEBGPU_DEMO_LOG("[WebGPU] Depth texture view re-created"); + } + + // Skip this frame. + glfwPollEvents(); + continue; + } + // Get a texture from the surface to render into. WGPUSurfaceTexture surfaceTexture; - wgpuSurfaceGetCurrentTexture(surface, &surfaceTexture); + wgpuSurfaceGetCurrentTexture(app.surface, &surfaceTexture); // The surface might change over time. // For example, the window might be resized or minimized. @@ -712,18 +762,11 @@ int main(int argc, const char* argv[]) switch (surfaceTexture.status) { case WGPUSurfaceGetCurrentTextureStatus_Success: - // Everything is ok. - - // Check for a suboptimal texture and re-configure it if needed. + // Everything is ok, but still check for a suboptimal texture and re-configure it if needed. if (surfaceTexture.suboptimal) { WEBGPU_DEMO_LOG("[WebGPU] Re-configuring currently suboptimal surface"); - surfaceConfig.width = surfaceWidth; - surfaceConfig.height = surfaceHeight; - wgpuSurfaceConfigure(surface, &surfaceConfig); - - // Skip this frame. - glfwPollEvents(); + app.requireSurfaceReconfiguration = true; continue; } @@ -733,38 +776,7 @@ int main(int argc, const char* argv[]) case WGPUSurfaceGetCurrentTextureStatus_Outdated: case WGPUSurfaceGetCurrentTextureStatus_Lost: // The surface needs to be re-configured. - - // Get the window size from the GLFW window. - glfwGetWindowSize(window, &surfaceWidth, &surfaceHeight); - - // The surface size might be zero if the window is minimized. - if (surfaceWidth != 0 && surfaceHeight != 0) - { - WEBGPU_DEMO_LOG("[WebGPU] Re-configuring surface"); - surfaceConfig.width = surfaceWidth; - surfaceConfig.height = surfaceHeight; - wgpuSurfaceConfigure(surface, &surfaceConfig); - - // Recreate the depth texture. - - wgpuTextureViewRelease(textureView); - wgpuTextureDestroy(depthTexture); - wgpuTextureRelease(depthTexture); - - depthTextureDesc.size.width = surfaceWidth; - depthTextureDesc.size.height = surfaceHeight; - - depthTexture = wgpuDeviceCreateTexture(device, &depthTextureDesc); - WEBGPU_DEMO_CHECK(depthTexture, "[WebGPU] Failed to re-create depth texture"); - WEBGPU_DEMO_LOG("[WebGPU] Depth texture re-created"); - - depthTextureView = wgpuTextureCreateView(depthTexture, &depthTextureViewDesc); - WEBGPU_DEMO_CHECK(depthTextureView, "[WebGPU] Failed to re-create depth texture view"); - WEBGPU_DEMO_LOG("[WebGPU] Depth texture view re-created"); - } - - // Skip this frame. - glfwPollEvents(); + app.requireSurfaceReconfiguration = true; continue; case WGPUSurfaceGetCurrentTextureStatus_OutOfMemory: @@ -783,11 +795,16 @@ int main(int argc, const char* argv[]) // === Prepare uniform data === float aspectRatio = static_cast(surfaceWidth) / static_cast(surfaceHeight); + SLMat4f modelMatrix; + SLMat4f projectionMatrix; projectionMatrix.perspective(70.0f, aspectRatio, 0.1, 1000.0f); SLMat4f viewMatrix; - viewMatrix.translate(0.0f, 0.0f, -2.0f); + viewMatrix.rotate(camRotY, SLVec3f::AXISY); + viewMatrix.rotate(camRotX, SLVec3f::AXISX); + viewMatrix.translate(0.0f, 0.0f, 2.0f); + viewMatrix.invert(); // === Update uniforms === ShaderUniformData uniformData = {}; @@ -802,7 +819,7 @@ int main(int argc, const char* argv[]) WGPUCommandEncoderDescriptor cmdEncoderDesc = {}; cmdEncoderDesc.label = "Demo Command Encoder"; - WGPUCommandEncoder cmdEncoder = wgpuDeviceCreateCommandEncoder(device, &cmdEncoderDesc); + WGPUCommandEncoder cmdEncoder = wgpuDeviceCreateCommandEncoder(app.device, &cmdEncoderDesc); WEBGPU_DEMO_CHECK(cmdEncoder, "[WebGPU] Failed to create command encoder"); // === Create a WebGPU render pass === @@ -863,7 +880,7 @@ int main(int argc, const char* argv[]) // === Present the surface === // This presents our rendered texture to the screen. - wgpuSurfacePresent(surface); + wgpuSurfacePresent(app.surface); // === Clean up resources === wgpuCommandBufferRelease(cmdBuffer); @@ -895,9 +912,9 @@ int main(int argc, const char* argv[]) wgpuBufferRelease(indexBuffer); wgpuBufferDestroy(vertexBuffer); wgpuBufferRelease(vertexBuffer); - wgpuSurfaceRelease(surface); + wgpuSurfaceRelease(app.surface); wgpuQueueRelease(queue); - wgpuDeviceRelease(device); + wgpuDeviceRelease(app.device); wgpuAdapterRelease(adapter); wgpuInstanceRelease(instance); WEBGPU_DEMO_LOG("[WebGPU] Resources released"); From efc509cbfca68cb2c8e28dd1e7f0bcfd7030dd99 Mon Sep 17 00:00:00 2001 From: Marino von Wattenwyl Date: Thu, 9 Nov 2023 10:57:59 +0100 Subject: [PATCH 047/108] [WebGPU] Improve surface re-configuring in demo --- apps/webgpu/main.cpp | 719 +++++++++++++++++++++++-------------------- 1 file changed, 383 insertions(+), 336 deletions(-) diff --git a/apps/webgpu/main.cpp b/apps/webgpu/main.cpp index e4c2999a..316b3fa5 100644 --- a/apps/webgpu/main.cpp +++ b/apps/webgpu/main.cpp @@ -38,10 +38,43 @@ struct AppData { - WGPUDevice device = nullptr; - WGPUSurface surface = nullptr; + GLFWwindow* window = nullptr; + int surfaceWidth = 0; + int surfaceHeight = 0; + + WGPUInstance instance = nullptr; + WGPUAdapter adapter = nullptr; + WGPUDevice device = nullptr; + WGPUQueue queue = nullptr; + WGPUSurface surface = nullptr; + WGPUTexture depthTexture = nullptr; + WGPUTextureView depthTextureView = nullptr; + WGPUBuffer vertexBuffer = nullptr; + WGPUBuffer indexBuffer = nullptr; + WGPUBuffer uniformBuffer = nullptr; + WGPUTexture texture = nullptr; + WGPUTextureView textureView = nullptr; + WGPUSampler sampler = nullptr; + WGPUShaderModule shaderModule = nullptr; + WGPUBindGroupLayout bindGroupLayout = nullptr; + WGPUBindGroup bindGroup = nullptr; + WGPUPipelineLayout pipelineLayout = nullptr; + WGPURenderPipeline pipeline = nullptr; + + WGPUSurfaceConfiguration surfaceConfig; + WGPUTextureFormat depthTextureFormat; + WGPUTextureDescriptor depthTextureDesc; + WGPUTextureViewDescriptor depthTextureViewDesc; + + unsigned vertexDataSize; + unsigned indexCount; + unsigned indexDataSize; bool requireSurfaceReconfiguration = false; + + float camRotX = 0.0f; + float camRotY = 0.0f; + float camZ = 2.0f; }; struct alignas(16) ShaderUniformData @@ -57,10 +90,207 @@ static_assert(sizeof(ShaderUniformData) % 16 == 0, "uniform data size must be a extern "C" void* createMetalLayer(void* window); #endif -void onWindowResized(GLFWwindow* window, int width, int height) +void reconfigureSurface(AppData& app) +{ + // Get the window size from the GLFW window. + glfwGetWindowSize(app.window, &app.surfaceWidth, &app.surfaceHeight); + + // The surface size might be zero if the window is minimized. + if (app.surfaceWidth == 0 || app.surfaceHeight == 0) + return; + + WEBGPU_DEMO_LOG("[WebGPU] Re-configuring surface"); + app.surfaceConfig.width = app.surfaceWidth; + app.surfaceConfig.height = app.surfaceHeight; + wgpuSurfaceConfigure(app.surface, &app.surfaceConfig); + + // Recreate the depth texture. + + wgpuTextureViewRelease(app.depthTextureView); + wgpuTextureDestroy(app.depthTexture); + wgpuTextureRelease(app.depthTexture); + + app.depthTextureDesc.size.width = app.surfaceWidth; + app.depthTextureDesc.size.height = app.surfaceHeight; + + app.depthTexture = wgpuDeviceCreateTexture(app.device, &app.depthTextureDesc); + WEBGPU_DEMO_CHECK(app.depthTexture, "[WebGPU] Failed to re-create depth texture"); + WEBGPU_DEMO_LOG("[WebGPU] Depth texture re-created"); + + app.depthTextureView = wgpuTextureCreateView(app.depthTexture, &app.depthTextureViewDesc); + WEBGPU_DEMO_CHECK(app.depthTextureView, "[WebGPU] Failed to re-create depth texture view"); + WEBGPU_DEMO_LOG("[WebGPU] Depth texture view re-created"); +} + +void onPaint(AppData& app) +{ + // Get a texture from the surface to render into. + WGPUSurfaceTexture surfaceTexture; + wgpuSurfaceGetCurrentTexture(app.surface, &surfaceTexture); + + // The surface might change over time. + // For example, the window might be resized or minimized. + // We have to check the status and adapt to it. + switch (surfaceTexture.status) + { + case WGPUSurfaceGetCurrentTextureStatus_Success: + // Everything is ok, but still check for a suboptimal texture and re-configure it if needed. + if (surfaceTexture.suboptimal) + { + WEBGPU_DEMO_LOG("[WebGPU] Re-configuring currently suboptimal surface"); + reconfigureSurface(app); + wgpuSurfaceGetCurrentTexture(app.surface, &surfaceTexture); + } + + break; + + case WGPUSurfaceGetCurrentTextureStatus_Timeout: + case WGPUSurfaceGetCurrentTextureStatus_Outdated: + case WGPUSurfaceGetCurrentTextureStatus_Lost: + // The surface needs to be re-configured. + reconfigureSurface(app); + wgpuSurfaceGetCurrentTexture(app.surface, &surfaceTexture); + break; + + case WGPUSurfaceGetCurrentTextureStatus_OutOfMemory: + case WGPUSurfaceGetCurrentTextureStatus_DeviceLost: + // An error occured. + WEBGPU_DEMO_CHECK(false, "[WebGPU] Failed to acquire current surface texture"); + break; + + case WGPUSurfaceGetCurrentTextureStatus_Force32: + break; + } + + // === Create a WebGPU texture view === + // The texture view is where we render our image into. + + // Create a view into the texture to specify where and how to modify the texture. + WGPUTextureView view = wgpuTextureCreateView(surfaceTexture.texture, nullptr); + + // === Prepare uniform data === + float aspectRatio = static_cast(app.surfaceWidth) / static_cast(app.surfaceHeight); + + SLMat4f modelMatrix; + + SLMat4f projectionMatrix; + projectionMatrix.perspective(70.0f, aspectRatio, 0.1, 1000.0f); + + SLMat4f viewMatrix; + viewMatrix.rotate(app.camRotY, SLVec3f::AXISY); + viewMatrix.rotate(app.camRotX, SLVec3f::AXISX); + viewMatrix.translate(0.0f, 0.0f, 2.0f); + viewMatrix.invert(); + + // === Update uniforms === + ShaderUniformData uniformData = {}; + std::memcpy(uniformData.projectionMatrix, projectionMatrix.m(), sizeof(uniformData.projectionMatrix)); + std::memcpy(uniformData.viewMatrix, viewMatrix.m(), sizeof(uniformData.viewMatrix)); + std::memcpy(uniformData.modelMatrix, modelMatrix.m(), sizeof(uniformData.modelMatrix)); + wgpuQueueWriteBuffer(app.queue, app.uniformBuffer, 0, &uniformData, sizeof(ShaderUniformData)); + + // === Create a WebGPU command encoder === + // The encoder encodes the commands for the GPU into a command buffer. + + WGPUCommandEncoderDescriptor cmdEncoderDesc = {}; + cmdEncoderDesc.label = "Demo Command Encoder"; + + WGPUCommandEncoder cmdEncoder = wgpuDeviceCreateCommandEncoder(app.device, &cmdEncoderDesc); + WEBGPU_DEMO_CHECK(cmdEncoder, "[WebGPU] Failed to create command encoder"); + + // === Create a WebGPU render pass === + // The render pass specifies what attachments to use while rendering. + // A color attachment specifies what view to render into and what to do with the texture before and after + // rendering. We clear the texture before rendering and store the results after rendering. + // The depth attachment specifies what depth texture to use. + + WGPURenderPassColorAttachment colorAttachment = {}; + colorAttachment.view = view; + colorAttachment.loadOp = WGPULoadOp_Clear; + colorAttachment.storeOp = WGPUStoreOp_Store; + colorAttachment.clearValue.r = 0.3; + colorAttachment.clearValue.g = 0.0; + colorAttachment.clearValue.b = 0.2; + colorAttachment.clearValue.a = 1.0; + + WGPURenderPassDepthStencilAttachment depthStencilAttachment = {}; + depthStencilAttachment.view = app.depthTextureView; + depthStencilAttachment.depthLoadOp = WGPULoadOp_Clear; + depthStencilAttachment.depthStoreOp = WGPUStoreOp_Store; + depthStencilAttachment.depthClearValue = 1.0f; + depthStencilAttachment.depthReadOnly = false; + depthStencilAttachment.stencilLoadOp = WGPULoadOp_Clear; + depthStencilAttachment.stencilStoreOp = WGPUStoreOp_Store; + depthStencilAttachment.stencilClearValue = 0.0f; + depthStencilAttachment.stencilReadOnly = true; + + WGPURenderPassDescriptor renderPassDesc = {}; + renderPassDesc.label = "Demo Render Pass"; + renderPassDesc.colorAttachmentCount = 1; + renderPassDesc.colorAttachments = &colorAttachment; + renderPassDesc.depthStencilAttachment = &depthStencilAttachment; + + // === Encode the commands === + // The commands to begin a render pass, bind a pipeline, draw the triangle and end the render pass + // are encoded into a buffer. + + WGPURenderPassEncoder renderPassEncoder = wgpuCommandEncoderBeginRenderPass(cmdEncoder, &renderPassDesc); + wgpuRenderPassEncoderSetPipeline(renderPassEncoder, app.pipeline); + wgpuRenderPassEncoderSetVertexBuffer(renderPassEncoder, 0, app.vertexBuffer, 0, app.vertexDataSize); + wgpuRenderPassEncoderSetIndexBuffer(renderPassEncoder, app.indexBuffer, WGPUIndexFormat_Uint16, 0, app.indexDataSize); + wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, app.bindGroup, 0, nullptr); + wgpuRenderPassEncoderDrawIndexed(renderPassEncoder, app.indexCount, 1, 0, 0, 0); + wgpuRenderPassEncoderEnd(renderPassEncoder); + + // === Get the command buffer === + // The command encoder is finished to get the commands for the GPU to execute in a command buffer. + + WGPUCommandBufferDescriptor cmdBufferDesc = {}; + cmdBufferDesc.label = "Demo Command Buffer"; + + WGPUCommandBuffer cmdBuffer = wgpuCommandEncoderFinish(cmdEncoder, &cmdBufferDesc); + + // === Submit the command buffer to the GPU === + // The work for the GPU is submitted through the queue and executed. + wgpuQueueSubmit(app.queue, 1, &cmdBuffer); + + // === Present the surface === + // This presents our rendered texture to the screen. + wgpuSurfacePresent(app.surface); + + // === Clean up resources === + wgpuCommandBufferRelease(cmdBuffer); + wgpuRenderPassEncoderRelease(renderPassEncoder); + wgpuCommandEncoderRelease(cmdEncoder); + wgpuTextureViewRelease(view); + wgpuTextureRelease(surfaceTexture.texture); +} + +void onResize(GLFWwindow* window, int width, int height) { AppData& app = *((AppData*)glfwGetWindowUserPointer(window)); - app.requireSurfaceReconfiguration = true; + reconfigureSurface(app); + onPaint(app); +} + +void initGLFW(AppData& app) +{ + // === Initialize GLFW === + + WEBGPU_DEMO_CHECK(glfwInit(), "[GLFW] Failed to initialize"); + WEBGPU_DEMO_LOG("[GLFW] Initialized"); + + // === Create the GLFW window === + + // Prevent GLFW from creating an OpenGL context as the underlying graphics API probably won't be OpenGL. + glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + + app.window = glfwCreateWindow(1280, 720, "WebGPU Demo", nullptr, nullptr); + WEBGPU_DEMO_CHECK(app.window, "[GLFW] Failed to create window"); + WEBGPU_DEMO_LOG("[GLFW] Window created"); + + glfwSetWindowUserPointer(app.window, &app); + glfwSetFramebufferSizeCallback(app.window, onResize); } void handleAdapterRequest(WGPURequestAdapterStatus status, @@ -89,32 +319,13 @@ void handleDeviceRequest(WGPURequestDeviceStatus status, *outDevice = device; } -int main(int argc, const char* argv[]) +void initWebGPU(AppData& app) { - AppData app; - - // === Initialize GLFW === - - WEBGPU_DEMO_CHECK(glfwInit(), "[GLFW] Failed to initialize"); - WEBGPU_DEMO_LOG("[GLFW] Initialized"); - - // === Create the GLFW window === - - // Prevent GLFW from creating an OpenGL context as the underlying graphics API probably won't be OpenGL. - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); - - GLFWwindow* window = glfwCreateWindow(1280, 720, "WebGPU Demo", nullptr, nullptr); - WEBGPU_DEMO_CHECK(window, "[GLFW] Failed to create window"); - WEBGPU_DEMO_LOG("[GLFW] Window created"); - - glfwSetWindowUserPointer(window, &app); - glfwSetWindowSizeCallback(window, onWindowResized); - // === Create a WebGPU instance === // The instance is the root interface to WebGPU through which we create all other WebGPU resources. - WGPUInstance instance = wgpuCreateInstance(nullptr); - WEBGPU_DEMO_CHECK(instance, "[WebGPU] Failed to create instance"); + app.instance = wgpuCreateInstance(nullptr); + WEBGPU_DEMO_CHECK(app.instance, "[WebGPU] Failed to create instance"); WEBGPU_DEMO_LOG("[WebGPU] Instance created"); // === Acquire a WebGPU adapter === @@ -122,11 +333,10 @@ int main(int argc, const char* argv[]) WGPURequestAdapterOptions adapterOptions = {}; - WGPUAdapter adapter; - wgpuInstanceRequestAdapter(instance, &adapterOptions, handleAdapterRequest, &adapter); + wgpuInstanceRequestAdapter(app.instance, &adapterOptions, handleAdapterRequest, &app.adapter); WGPUSupportedLimits adapterLimits; - wgpuAdapterGetLimits(adapter, &adapterLimits); + wgpuAdapterGetLimits(app.adapter, &adapterLimits); // === Acquire a WebGPU device === // A device provides access to a GPU and is created from an adapter. @@ -157,13 +367,13 @@ int main(int argc, const char* argv[]) deviceDesc.requiredLimits = &requiredLimits; deviceDesc.defaultQueue.label = "Demo Queue"; - wgpuAdapterRequestDevice(adapter, &deviceDesc, handleDeviceRequest, &app.device); + wgpuAdapterRequestDevice(app.adapter, &deviceDesc, handleDeviceRequest, &app.device); // === Acquire a WebGPU queue === // The queue is where the commands for the GPU are submitted to. - WGPUQueue queue = wgpuDeviceGetQueue(app.device); - WEBGPU_DEMO_CHECK(queue, "[WebGPU] Failed to acquire queue"); + app.queue = wgpuDeviceGetQueue(app.device); + WEBGPU_DEMO_CHECK(app.queue, "[WebGPU] Failed to acquire queue"); WEBGPU_DEMO_LOG("[WebGPU] Queue acquired"); // === Create a WebGPU surface === @@ -174,23 +384,23 @@ int main(int argc, const char* argv[]) WGPUSurfaceDescriptorFromWindowsHWND nativeSurfaceDesc = {}; nativeSurfaceDesc.chain.sType = WGPUSType_SurfaceDescriptorFromWindowsHWND; nativeSurfaceDesc.hinstance = GetModuleHandle(nullptr); - nativeSurfaceDesc.hwnd = glfwGetWin32Window(window); + nativeSurfaceDesc.hwnd = glfwGetWin32Window(app.window); #elif defined(SYSTEM_LINUX) WGPUSurfaceDescriptorFromXlibWindow nativeSurfaceDesc = {}; nativeSurfaceDesc.chain.sType = WGPUSType_SurfaceDescriptorFromXlibWindow; nativeSurfaceDesc.display = glfwGetX11Display(); - nativeSurfaceDesc.window = glfwGetX11Window(window); + nativeSurfaceDesc.window = glfwGetX11Window(app.window); #elif defined(SYSTEM_DARWIN) WGPUSurfaceDescriptorFromMetalLayer nativeSurfaceDesc = {}; nativeSurfaceDesc.chain.sType = WGPUSType_SurfaceDescriptorFromMetalLayer; - nativeSurfaceDesc.layer = createMetalLayer(glfwGetCocoaWindow(window)); + nativeSurfaceDesc.layer = createMetalLayer(glfwGetCocoaWindow(app.window)); #endif WGPUSurfaceDescriptor surfaceDesc = {}; surfaceDesc.label = "Demo Surface"; surfaceDesc.nextInChain = (const WGPUChainedStruct*)&nativeSurfaceDesc; - app.surface = wgpuInstanceCreateSurface(instance, &surfaceDesc); + app.surface = wgpuInstanceCreateSurface(app.instance, &surfaceDesc); WEBGPU_DEMO_CHECK(app.surface, "[WebGPU] Failed to create surface"); WEBGPU_DEMO_LOG("[WebGPU] Surface created"); @@ -199,22 +409,20 @@ int main(int argc, const char* argv[]) // Query the surface capabilities from the adapter. WGPUSurfaceCapabilities surfaceCapabilities; - wgpuSurfaceGetCapabilities(app.surface, adapter, &surfaceCapabilities); + wgpuSurfaceGetCapabilities(app.surface, app.adapter, &surfaceCapabilities); // Get the window size from the GLFW window. - int surfaceWidth; - int surfaceHeight; - glfwGetWindowSize(window, &surfaceWidth, &surfaceHeight); - - WGPUSurfaceConfiguration surfaceConfig = {}; - surfaceConfig.device = app.device; - surfaceConfig.usage = WGPUTextureUsage_RenderAttachment; - surfaceConfig.format = surfaceCapabilities.formats[0]; - surfaceConfig.presentMode = WGPUPresentMode_Fifo; - surfaceConfig.alphaMode = surfaceCapabilities.alphaModes[0]; - surfaceConfig.width = surfaceWidth; - surfaceConfig.height = surfaceHeight; - wgpuSurfaceConfigure(app.surface, &surfaceConfig); + glfwGetFramebufferSize(app.window, &app.surfaceWidth, &app.surfaceHeight); + + app.surfaceConfig = {}; + app.surfaceConfig.device = app.device; + app.surfaceConfig.usage = WGPUTextureUsage_RenderAttachment; + app.surfaceConfig.format = surfaceCapabilities.formats[0]; + app.surfaceConfig.presentMode = WGPUPresentMode_Fifo; + app.surfaceConfig.alphaMode = surfaceCapabilities.alphaModes[0]; + app.surfaceConfig.width = app.surfaceWidth; + app.surfaceConfig.height = app.surfaceHeight; + wgpuSurfaceConfigure(app.surface, &app.surfaceConfig); WEBGPU_DEMO_LOG("[WebGPU] Surface configured"); // === Create the vertex buffer === @@ -273,20 +481,20 @@ int main(int argc, const char* argv[]) }; // clang-format on - unsigned vertexCount = vertexData.size() / 5; - unsigned vertexDataSize = vertexData.size() * sizeof(float); + unsigned vertexCount = vertexData.size() / 5; + app.vertexDataSize = vertexData.size() * sizeof(float); WGPUBufferDescriptor vertexBufferDesc = {}; vertexBufferDesc.label = "Demo Vertex Buffer"; - vertexBufferDesc.size = vertexDataSize; + vertexBufferDesc.size = app.vertexDataSize; vertexBufferDesc.usage = WGPUBufferUsage_CopyDst | WGPUBufferUsage_Vertex; - WGPUBuffer vertexBuffer = wgpuDeviceCreateBuffer(app.device, &vertexBufferDesc); - WEBGPU_DEMO_CHECK(vertexBuffer, "[WebGPU] Failed to create vertex buffer"); + app.vertexBuffer = wgpuDeviceCreateBuffer(app.device, &vertexBufferDesc); + WEBGPU_DEMO_CHECK(app.vertexBuffer, "[WebGPU] Failed to create vertex buffer"); WEBGPU_DEMO_LOG("[WebGPU] Vertex buffer created"); // Upload the data to the GPU. - wgpuQueueWriteBuffer(queue, vertexBuffer, 0, vertexData.data(), vertexDataSize); + wgpuQueueWriteBuffer(app.queue, app.vertexBuffer, 0, vertexData.data(), app.vertexDataSize); // === Create the index buffer === @@ -294,19 +502,19 @@ int main(int argc, const char* argv[]) for (std::uint16_t index = 0; index < 36; index++) indexData.push_back(index); - unsigned indexCount = indexData.size(); - unsigned indexDataSize = indexData.size() * sizeof(std::uint16_t); + app.indexCount = indexData.size(); + app.indexDataSize = indexData.size() * sizeof(std::uint16_t); WGPUBufferDescriptor indexBufferDesc = {}; indexBufferDesc.label = "Demo Index Buffer"; - indexBufferDesc.size = indexDataSize; + indexBufferDesc.size = app.indexDataSize; indexBufferDesc.usage = WGPUBufferUsage_CopyDst | WGPUBufferUsage_Index; - WGPUBuffer indexBuffer = wgpuDeviceCreateBuffer(app.device, &indexBufferDesc); - WEBGPU_DEMO_CHECK(indexBuffer, "[WebGPU] Failed to create index buffer"); + app.indexBuffer = wgpuDeviceCreateBuffer(app.device, &indexBufferDesc); + WEBGPU_DEMO_CHECK(app.indexBuffer, "[WebGPU] Failed to create index buffer"); WEBGPU_DEMO_LOG("[WebGPU] Index buffer created"); - wgpuQueueWriteBuffer(queue, indexBuffer, 0, indexData.data(), indexDataSize); + wgpuQueueWriteBuffer(app.queue, app.indexBuffer, 0, indexData.data(), app.indexDataSize); // === Create the uniform buffer === @@ -315,44 +523,44 @@ int main(int argc, const char* argv[]) uniformBufferDesc.size = sizeof(ShaderUniformData); uniformBufferDesc.usage = WGPUBufferUsage_CopyDst | WGPUBufferUsage_Uniform; - WGPUBuffer uniformBuffer = wgpuDeviceCreateBuffer(app.device, &uniformBufferDesc); - WEBGPU_DEMO_CHECK(indexBuffer, "[WebGPU] Failed to create uniform buffer"); + app.uniformBuffer = wgpuDeviceCreateBuffer(app.device, &uniformBufferDesc); + WEBGPU_DEMO_CHECK(app.indexBuffer, "[WebGPU] Failed to create uniform buffer"); WEBGPU_DEMO_LOG("[WebGPU] Uniform buffer created"); ShaderUniformData uniformData = {}; - wgpuQueueWriteBuffer(queue, uniformBuffer, 0, &uniformData, sizeof(ShaderUniformData)); + wgpuQueueWriteBuffer(app.queue, app.uniformBuffer, 0, &uniformData, sizeof(ShaderUniformData)); // === Create the depth texture === - WGPUTextureFormat depthTextureFormat = WGPUTextureFormat_Depth24Plus; - - WGPUTextureDescriptor depthTextureDesc = {}; - depthTextureDesc.dimension = WGPUTextureDimension_2D; - depthTextureDesc.format = depthTextureFormat; - depthTextureDesc.mipLevelCount = 1; - depthTextureDesc.sampleCount = 1; - depthTextureDesc.size.width = surfaceWidth; - depthTextureDesc.size.height = surfaceHeight; - depthTextureDesc.size.depthOrArrayLayers = 1; - depthTextureDesc.usage = WGPUTextureUsage_RenderAttachment; - depthTextureDesc.viewFormatCount = 1; - depthTextureDesc.viewFormats = &depthTextureFormat; - - WGPUTexture depthTexture = wgpuDeviceCreateTexture(app.device, &depthTextureDesc); - WEBGPU_DEMO_CHECK(depthTexture, "[WebGPU] Failed to create depth texture"); + app.depthTextureFormat = WGPUTextureFormat_Depth24Plus; + + app.depthTextureDesc = {}; + app.depthTextureDesc.dimension = WGPUTextureDimension_2D; + app.depthTextureDesc.format = app.depthTextureFormat; + app.depthTextureDesc.mipLevelCount = 1; + app.depthTextureDesc.sampleCount = 1; + app.depthTextureDesc.size.width = app.surfaceWidth; + app.depthTextureDesc.size.height = app.surfaceHeight; + app.depthTextureDesc.size.depthOrArrayLayers = 1; + app.depthTextureDesc.usage = WGPUTextureUsage_RenderAttachment; + app.depthTextureDesc.viewFormatCount = 1; + app.depthTextureDesc.viewFormats = &app.depthTextureFormat; + + app.depthTexture = wgpuDeviceCreateTexture(app.device, &app.depthTextureDesc); + WEBGPU_DEMO_CHECK(app.depthTexture, "[WebGPU] Failed to create depth texture"); WEBGPU_DEMO_LOG("[WebGPU] Depth texture created"); - WGPUTextureViewDescriptor depthTextureViewDesc = {}; - depthTextureViewDesc.aspect = WGPUTextureAspect_DepthOnly; - depthTextureViewDesc.baseArrayLayer = 0; - depthTextureViewDesc.arrayLayerCount = 1; - depthTextureViewDesc.baseMipLevel = 0; - depthTextureViewDesc.mipLevelCount = 1; - depthTextureViewDesc.dimension = WGPUTextureViewDimension_2D; - depthTextureViewDesc.format = depthTextureFormat; - - WGPUTextureView depthTextureView = wgpuTextureCreateView(depthTexture, &depthTextureViewDesc); - WEBGPU_DEMO_CHECK(depthTextureView, "[WebGPU] Failed to create depth texture view"); + app.depthTextureViewDesc = {}; + app.depthTextureViewDesc.aspect = WGPUTextureAspect_DepthOnly; + app.depthTextureViewDesc.baseArrayLayer = 0; + app.depthTextureViewDesc.arrayLayerCount = 1; + app.depthTextureViewDesc.baseMipLevel = 0; + app.depthTextureViewDesc.mipLevelCount = 1; + app.depthTextureViewDesc.dimension = WGPUTextureViewDimension_2D; + app.depthTextureViewDesc.format = app.depthTextureFormat; + + app.depthTextureView = wgpuTextureCreateView(app.depthTexture, &app.depthTextureViewDesc); + WEBGPU_DEMO_CHECK(app.depthTextureView, "[WebGPU] Failed to create depth texture view"); WEBGPU_DEMO_LOG("[WebGPU] Depth texture view created"); // === Create the texture === @@ -375,13 +583,13 @@ int main(int argc, const char* argv[]) textureDesc.mipLevelCount = 4; textureDesc.sampleCount = 1; - WGPUTexture texture = wgpuDeviceCreateTexture(app.device, &textureDesc); - WEBGPU_DEMO_CHECK(texture, "[WebGPU] Failed to create texture"); + app.texture = wgpuDeviceCreateTexture(app.device, &textureDesc); + WEBGPU_DEMO_CHECK(app.texture, "[WebGPU] Failed to create texture"); WEBGPU_DEMO_LOG("[WebGPU] Texture created"); // Where do we copyu the pixel data to? WGPUImageCopyTexture destination = {}; - destination.texture = texture; + destination.texture = app.texture; destination.mipLevel = 0; destination.origin.x = 0; destination.origin.y = 0; @@ -433,7 +641,7 @@ int main(int argc, const char* argv[]) pixelDataLayout.rowsPerImage = mipLevelSize.height; // Upload the data to the GPU. - wgpuQueueWriteTexture(queue, &destination, mipLevelImage.data, mipLevelBytes, &pixelDataLayout, &mipLevelSize); + wgpuQueueWriteTexture(app.queue, &destination, mipLevelImage.data, mipLevelBytes, &pixelDataLayout, &mipLevelSize); // Scale the image down for the next mip level. mipLevelSize.width /= 2; @@ -450,8 +658,8 @@ int main(int argc, const char* argv[]) textureViewDesc.dimension = WGPUTextureViewDimension_2D; textureViewDesc.format = textureDesc.format; - WGPUTextureView textureView = wgpuTextureCreateView(texture, &textureViewDesc); - WEBGPU_DEMO_CHECK(textureView, "[WebGPU] Failed to create texture view"); + app.textureView = wgpuTextureCreateView(app.texture, &textureViewDesc); + WEBGPU_DEMO_CHECK(app.textureView, "[WebGPU] Failed to create texture view"); WEBGPU_DEMO_LOG("[WebGPU] Texture view created"); // === Create the texture sampler === @@ -471,8 +679,8 @@ int main(int argc, const char* argv[]) samplerDesc.compare = WGPUCompareFunction_Undefined; samplerDesc.maxAnisotropy = 1.0f; - WGPUSampler sampler = wgpuDeviceCreateSampler(app.device, &samplerDesc); - WEBGPU_DEMO_CHECK(sampler, "[WebGPU] Failed to create sampler"); + app.sampler = wgpuDeviceCreateSampler(app.device, &samplerDesc); + WEBGPU_DEMO_CHECK(app.sampler, "[WebGPU] Failed to create sampler"); WEBGPU_DEMO_LOG("[WebGPU] Sampler created"); // === Create the shader module === @@ -526,8 +734,8 @@ int main(int argc, const char* argv[]) shaderModuleDesc.label = "Demo Shader"; shaderModuleDesc.nextInChain = (const WGPUChainedStruct*)&shaderModuleWGSLDesc; - WGPUShaderModule shaderModule = wgpuDeviceCreateShaderModule(app.device, &shaderModuleDesc); - WEBGPU_DEMO_CHECK(shaderModule, "[WebGPU] Failed to create shader module"); + app.shaderModule = wgpuDeviceCreateShaderModule(app.device, &shaderModuleDesc); + WEBGPU_DEMO_CHECK(app.shaderModule, "[WebGPU] Failed to create shader module"); WEBGPU_DEMO_LOG("[WebGPU] Shader module created"); // === Create the bind group layout === @@ -561,8 +769,9 @@ int main(int argc, const char* argv[]) bindGroupLayoutDesc.label = "Demo Bind Group Layout"; bindGroupLayoutDesc.entryCount = bindGroupLayoutEntries.size(); bindGroupLayoutDesc.entries = bindGroupLayoutEntries.data(); - WGPUBindGroupLayout bindGroupLayout = wgpuDeviceCreateBindGroupLayout(app.device, &bindGroupLayoutDesc); - WEBGPU_DEMO_CHECK(bindGroupLayout, "[WebGPU] Failed to create bind group layout"); + + app.bindGroupLayout = wgpuDeviceCreateBindGroupLayout(app.device, &bindGroupLayoutDesc); + WEBGPU_DEMO_CHECK(app.bindGroupLayout, "[WebGPU] Failed to create bind group layout"); WEBGPU_DEMO_LOG("[WebGPU] Bind group layout created"); // === Create the bind group === @@ -570,28 +779,28 @@ int main(int argc, const char* argv[]) WGPUBindGroupEntry uniformBinding = {}; uniformBinding.binding = 0; - uniformBinding.buffer = uniformBuffer; + uniformBinding.buffer = app.uniformBuffer; uniformBinding.offset = 0; uniformBinding.size = sizeof(ShaderUniformData); WGPUBindGroupEntry textureBinding = {}; textureBinding.binding = 1; - textureBinding.textureView = textureView; + textureBinding.textureView = app.textureView; WGPUBindGroupEntry samplerBinding = {}; samplerBinding.binding = 2; - samplerBinding.sampler = sampler; + samplerBinding.sampler = app.sampler; std::vector bindGroupEntries = {uniformBinding, textureBinding, samplerBinding}; WGPUBindGroupDescriptor bindGroupDesc = {}; - bindGroupDesc.layout = bindGroupLayout; + bindGroupDesc.layout = app.bindGroupLayout; bindGroupDesc.entryCount = bindGroupEntries.size(); bindGroupDesc.entries = bindGroupEntries.data(); - WGPUBindGroup bindGroup = wgpuDeviceCreateBindGroup(app.device, &bindGroupDesc); - WEBGPU_DEMO_CHECK(bindGroup, "[WebGPU] Failed to create bind group"); + app.bindGroup = wgpuDeviceCreateBindGroup(app.device, &bindGroupDesc); + WEBGPU_DEMO_CHECK(app.bindGroup, "[WebGPU] Failed to create bind group"); WEBGPU_DEMO_LOG("[WebGPU] Bind group created"); // === Create the pipeline layout === @@ -600,9 +809,10 @@ int main(int argc, const char* argv[]) WGPUPipelineLayoutDescriptor pipelineLayoutDesc = {}; pipelineLayoutDesc.label = "Demo Pipeline Layout"; pipelineLayoutDesc.bindGroupLayoutCount = 1; - pipelineLayoutDesc.bindGroupLayouts = &bindGroupLayout; - WGPUPipelineLayout pipelineLayout = wgpuDeviceCreatePipelineLayout(app.device, &pipelineLayoutDesc); - WEBGPU_DEMO_CHECK(pipelineLayout, "[WebGPU] Failed to create pipeline layout"); + pipelineLayoutDesc.bindGroupLayouts = &app.bindGroupLayout; + + app.pipelineLayout = wgpuDeviceCreatePipelineLayout(app.device, &pipelineLayoutDesc); + WEBGPU_DEMO_CHECK(app.pipelineLayout, "[WebGPU] Failed to create pipeline layout"); WEBGPU_DEMO_LOG("[WebGPU] Pipeline layout created"); // === Create the render pipeline === @@ -631,7 +841,7 @@ int main(int argc, const char* argv[]) // Configuration for the vertex shader stage WGPUVertexState vertexState = {}; - vertexState.module = shaderModule; + vertexState.module = app.shaderModule; vertexState.entryPoint = "vs_main"; vertexState.bufferCount = 1; vertexState.buffers = &vertexBufferLayout; @@ -645,7 +855,7 @@ int main(int argc, const char* argv[]) // Configuration for depth testing and stencil buffer WGPUDepthStencilState depthStencilState = {}; - depthStencilState.format = depthTextureFormat; + depthStencilState.format = app.depthTextureFormat; depthStencilState.depthWriteEnabled = true; depthStencilState.depthCompare = WGPUCompareFunction_Less; depthStencilState.stencilReadMask = 0; @@ -669,7 +879,7 @@ int main(int argc, const char* argv[]) colorTargetState.format = surfaceCapabilities.formats[0]; colorTargetState.writeMask = WGPUColorWriteMask_All; WGPUFragmentState fragmentState = {}; - fragmentState.module = shaderModule; + fragmentState.module = app.shaderModule; fragmentState.entryPoint = "fs_main"; fragmentState.targetCount = 1; fragmentState.targets = &colorTargetState; @@ -677,255 +887,92 @@ int main(int argc, const char* argv[]) // Configuration for the entire pipeline WGPURenderPipelineDescriptor pipelineDesc = {}; pipelineDesc.label = "Demo Pipeline"; - pipelineDesc.layout = pipelineLayout; + pipelineDesc.layout = app.pipelineLayout; pipelineDesc.vertex = vertexState; pipelineDesc.primitive = primitiveState; pipelineDesc.depthStencil = &depthStencilState; pipelineDesc.multisample = multisampleState; pipelineDesc.fragment = &fragmentState; - WGPURenderPipeline pipeline = wgpuDeviceCreateRenderPipeline(app.device, &pipelineDesc); - WEBGPU_DEMO_CHECK(pipeline, "[WebGPU] Failed to create render pipeline"); + app.pipeline = wgpuDeviceCreateRenderPipeline(app.device, &pipelineDesc); + WEBGPU_DEMO_CHECK(app.pipeline, "[WebGPU] Failed to create render pipeline"); WEBGPU_DEMO_LOG("[WebGPU] Render pipeline created"); +} - float camRotX = 0.0f; - float camRotY = 0.0f; +void deinitWebGPU(AppData& app) +{ + // === Release all WebGPU resources === + + wgpuRenderPipelineRelease(app.pipeline); + wgpuPipelineLayoutRelease(app.pipelineLayout); + wgpuBindGroupRelease(app.bindGroup); + wgpuBindGroupLayoutRelease(app.bindGroupLayout); + wgpuShaderModuleRelease(app.shaderModule); + wgpuSamplerRelease(app.sampler); + wgpuTextureViewRelease(app.textureView); + wgpuTextureDestroy(app.texture); + wgpuTextureRelease(app.texture); + wgpuTextureViewRelease(app.depthTextureView); + wgpuTextureDestroy(app.depthTexture); + wgpuTextureRelease(app.depthTexture); + wgpuBufferDestroy(app.uniformBuffer); + wgpuBufferRelease(app.uniformBuffer); + wgpuBufferDestroy(app.indexBuffer); + wgpuBufferRelease(app.indexBuffer); + wgpuBufferDestroy(app.vertexBuffer); + wgpuBufferRelease(app.vertexBuffer); + wgpuSurfaceRelease(app.surface); + wgpuQueueRelease(app.queue); + wgpuDeviceRelease(app.device); + wgpuAdapterRelease(app.adapter); + wgpuInstanceRelease(app.instance); + + WEBGPU_DEMO_LOG("[WebGPU] Resources released"); +} + +void deinitGLFW(AppData& app) +{ + // === Destroy the window and terminate GLFW === + + glfwDestroyWindow(app.window); + glfwTerminate(); + WEBGPU_DEMO_LOG("[GLFW] Window closed and terminated"); +} + +int main(int argc, const char* argv[]) +{ + AppData app; + initGLFW(app); + initWebGPU(app); double lastCursorX = 0.0f; double lastCursorY = 0.0f; // === Render loop === - while (!glfwWindowShouldClose(window)) + while (!glfwWindowShouldClose(app.window)) { // === Update cube model matrix === double cursorX; double cursorY; - glfwGetCursorPos(window, &cursorX, &cursorY); + glfwGetCursorPos(app.window, &cursorX, &cursorY); - if (glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_1) == GLFW_PRESS) + if (glfwGetMouseButton(app.window, GLFW_MOUSE_BUTTON_1) == GLFW_PRESS) { - camRotX -= 0.25f * static_cast(cursorY - lastCursorY); - camRotY -= 0.25f * static_cast(cursorX - lastCursorX); + app.camRotX -= 0.25f * static_cast(cursorY - lastCursorY); + app.camRotY -= 0.25f * static_cast(cursorX - lastCursorX); } lastCursorX = cursorX; lastCursorY = cursorY; - // === Create a WebGPU texture view === - // The texture view is where we render our image into. - - if (app.requireSurfaceReconfiguration) - { - app.requireSurfaceReconfiguration = false; - - // Get the window size from the GLFW window. - glfwGetWindowSize(window, &surfaceWidth, &surfaceHeight); - - // The surface size might be zero if the window is minimized. - if (surfaceWidth != 0 && surfaceHeight != 0) - { - WEBGPU_DEMO_LOG("[WebGPU] Re-configuring surface"); - surfaceConfig.width = surfaceWidth; - surfaceConfig.height = surfaceHeight; - wgpuSurfaceConfigure(app.surface, &surfaceConfig); - - // Recreate the depth texture. - - wgpuTextureViewRelease(textureView); - wgpuTextureDestroy(depthTexture); - wgpuTextureRelease(depthTexture); - - depthTextureDesc.size.width = surfaceWidth; - depthTextureDesc.size.height = surfaceHeight; - - depthTexture = wgpuDeviceCreateTexture(app.device, &depthTextureDesc); - WEBGPU_DEMO_CHECK(depthTexture, "[WebGPU] Failed to re-create depth texture"); - WEBGPU_DEMO_LOG("[WebGPU] Depth texture re-created"); - - depthTextureView = wgpuTextureCreateView(depthTexture, &depthTextureViewDesc); - WEBGPU_DEMO_CHECK(depthTextureView, "[WebGPU] Failed to re-create depth texture view"); - WEBGPU_DEMO_LOG("[WebGPU] Depth texture view re-created"); - } - - // Skip this frame. - glfwPollEvents(); - continue; - } - - // Get a texture from the surface to render into. - WGPUSurfaceTexture surfaceTexture; - wgpuSurfaceGetCurrentTexture(app.surface, &surfaceTexture); - - // The surface might change over time. - // For example, the window might be resized or minimized. - // We have to check the status and adapt to it. - switch (surfaceTexture.status) - { - case WGPUSurfaceGetCurrentTextureStatus_Success: - // Everything is ok, but still check for a suboptimal texture and re-configure it if needed. - if (surfaceTexture.suboptimal) - { - WEBGPU_DEMO_LOG("[WebGPU] Re-configuring currently suboptimal surface"); - app.requireSurfaceReconfiguration = true; - continue; - } - - break; - - case WGPUSurfaceGetCurrentTextureStatus_Timeout: - case WGPUSurfaceGetCurrentTextureStatus_Outdated: - case WGPUSurfaceGetCurrentTextureStatus_Lost: - // The surface needs to be re-configured. - app.requireSurfaceReconfiguration = true; - continue; - - case WGPUSurfaceGetCurrentTextureStatus_OutOfMemory: - case WGPUSurfaceGetCurrentTextureStatus_DeviceLost: - // An error occured. - WEBGPU_DEMO_CHECK(false, "[WebGPU] Failed to acquire current surface texture"); - break; - - case WGPUSurfaceGetCurrentTextureStatus_Force32: - break; - } - - // Create a view into the texture to specify where and how to modify the texture. - WGPUTextureView view = wgpuTextureCreateView(surfaceTexture.texture, nullptr); - - // === Prepare uniform data === - float aspectRatio = static_cast(surfaceWidth) / static_cast(surfaceHeight); - - SLMat4f modelMatrix; - - SLMat4f projectionMatrix; - projectionMatrix.perspective(70.0f, aspectRatio, 0.1, 1000.0f); - - SLMat4f viewMatrix; - viewMatrix.rotate(camRotY, SLVec3f::AXISY); - viewMatrix.rotate(camRotX, SLVec3f::AXISX); - viewMatrix.translate(0.0f, 0.0f, 2.0f); - viewMatrix.invert(); - - // === Update uniforms === - ShaderUniformData uniformData = {}; - std::memcpy(uniformData.projectionMatrix, projectionMatrix.m(), sizeof(uniformData.projectionMatrix)); - std::memcpy(uniformData.viewMatrix, viewMatrix.m(), sizeof(uniformData.viewMatrix)); - std::memcpy(uniformData.modelMatrix, modelMatrix.m(), sizeof(uniformData.modelMatrix)); - wgpuQueueWriteBuffer(queue, uniformBuffer, 0, &uniformData, sizeof(ShaderUniformData)); - - // === Create a WebGPU command encoder === - // The encoder encodes the commands for the GPU into a command buffer. - - WGPUCommandEncoderDescriptor cmdEncoderDesc = {}; - cmdEncoderDesc.label = "Demo Command Encoder"; - - WGPUCommandEncoder cmdEncoder = wgpuDeviceCreateCommandEncoder(app.device, &cmdEncoderDesc); - WEBGPU_DEMO_CHECK(cmdEncoder, "[WebGPU] Failed to create command encoder"); - - // === Create a WebGPU render pass === - // The render pass specifies what attachments to use while rendering. - // A color attachment specifies what view to render into and what to do with the texture before and after - // rendering. We clear the texture before rendering and store the results after rendering. - // The depth attachment specifies what depth texture to use. - - WGPURenderPassColorAttachment colorAttachment = {}; - colorAttachment.view = view; - colorAttachment.loadOp = WGPULoadOp_Clear; - colorAttachment.storeOp = WGPUStoreOp_Store; - colorAttachment.clearValue.r = 0.3; - colorAttachment.clearValue.g = 0.0; - colorAttachment.clearValue.b = 0.2; - colorAttachment.clearValue.a = 1.0; - - WGPURenderPassDepthStencilAttachment depthStencilAttachment = {}; - depthStencilAttachment.view = depthTextureView; - depthStencilAttachment.depthLoadOp = WGPULoadOp_Clear; - depthStencilAttachment.depthStoreOp = WGPUStoreOp_Store; - depthStencilAttachment.depthClearValue = 1.0f; - depthStencilAttachment.depthReadOnly = false; - depthStencilAttachment.stencilLoadOp = WGPULoadOp_Clear; - depthStencilAttachment.stencilStoreOp = WGPUStoreOp_Store; - depthStencilAttachment.stencilClearValue = 0.0f; - depthStencilAttachment.stencilReadOnly = true; - - WGPURenderPassDescriptor renderPassDesc = {}; - renderPassDesc.label = "Demo Render Pass"; - renderPassDesc.colorAttachmentCount = 1; - renderPassDesc.colorAttachments = &colorAttachment; - renderPassDesc.depthStencilAttachment = &depthStencilAttachment; - - // === Encode the commands === - // The commands to begin a render pass, bind a pipeline, draw the triangle and end the render pass - // are encoded into a buffer. - - WGPURenderPassEncoder renderPassEncoder = wgpuCommandEncoderBeginRenderPass(cmdEncoder, &renderPassDesc); - wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipeline); - wgpuRenderPassEncoderSetVertexBuffer(renderPassEncoder, 0, vertexBuffer, 0, vertexDataSize); - wgpuRenderPassEncoderSetIndexBuffer(renderPassEncoder, indexBuffer, WGPUIndexFormat_Uint16, 0, indexDataSize); - wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroup, 0, nullptr); - wgpuRenderPassEncoderDrawIndexed(renderPassEncoder, indexCount, 1, 0, 0, 0); - wgpuRenderPassEncoderEnd(renderPassEncoder); - - // === Get the command buffer === - // The command encoder is finished to get the commands for the GPU to execute in a command buffer. - - WGPUCommandBufferDescriptor cmdBufferDesc = {}; - cmdBufferDesc.label = "Demo Command Buffer"; - - WGPUCommandBuffer cmdBuffer = wgpuCommandEncoderFinish(cmdEncoder, &cmdBufferDesc); - - // === Submit the command buffer to the GPU === - // The work for the GPU is submitted through the queue and executed. - wgpuQueueSubmit(queue, 1, &cmdBuffer); - - // === Present the surface === - // This presents our rendered texture to the screen. - wgpuSurfacePresent(app.surface); - - // === Clean up resources === - wgpuCommandBufferRelease(cmdBuffer); - wgpuRenderPassEncoderRelease(renderPassEncoder); - wgpuCommandEncoderRelease(cmdEncoder); - wgpuTextureViewRelease(view); - wgpuTextureRelease(surfaceTexture.texture); - + onPaint(app); glfwPollEvents(); } - // === Release all WebGPU resources === - - wgpuRenderPipelineRelease(pipeline); - wgpuPipelineLayoutRelease(pipelineLayout); - wgpuBindGroupRelease(bindGroup); - wgpuBindGroupLayoutRelease(bindGroupLayout); - wgpuShaderModuleRelease(shaderModule); - wgpuSamplerRelease(sampler); - wgpuTextureViewRelease(textureView); - wgpuTextureDestroy(texture); - wgpuTextureRelease(texture); - wgpuTextureViewRelease(depthTextureView); - wgpuTextureDestroy(depthTexture); - wgpuTextureRelease(depthTexture); - wgpuBufferDestroy(uniformBuffer); - wgpuBufferRelease(uniformBuffer); - wgpuBufferDestroy(indexBuffer); - wgpuBufferRelease(indexBuffer); - wgpuBufferDestroy(vertexBuffer); - wgpuBufferRelease(vertexBuffer); - wgpuSurfaceRelease(app.surface); - wgpuQueueRelease(queue); - wgpuDeviceRelease(app.device); - wgpuAdapterRelease(adapter); - wgpuInstanceRelease(instance); - WEBGPU_DEMO_LOG("[WebGPU] Resources released"); - - // === Destroy the window and terminate GLFW === - - glfwDestroyWindow(window); - glfwTerminate(); - WEBGPU_DEMO_LOG("[GLFW] Window closed and terminated"); + deinitWebGPU(app); + deinitGLFW(app); return 0; } \ No newline at end of file From d3d5336a3ce729f49e616925a7a4e7fad2775cb9 Mon Sep 17 00:00:00 2001 From: Marino von Wattenwyl Date: Thu, 9 Nov 2023 12:23:50 +0100 Subject: [PATCH 048/108] [WebGPU] Add normal mapping to demo --- apps/webgpu/main.cpp | 138 ++++++++++++++++++++++++++----------------- 1 file changed, 84 insertions(+), 54 deletions(-) diff --git a/apps/webgpu/main.cpp b/apps/webgpu/main.cpp index 316b3fa5..b52d794d 100644 --- a/apps/webgpu/main.cpp +++ b/apps/webgpu/main.cpp @@ -77,6 +77,18 @@ struct AppData float camZ = 2.0f; }; +struct VertexData +{ + float positionX; + float positionY; + float positionZ; + float normalX; + float normalY; + float normalZ; + float uvX; + float uvY; +}; + struct alignas(16) ShaderUniformData { float projectionMatrix[16]; @@ -124,6 +136,9 @@ void reconfigureSurface(AppData& app) void onPaint(AppData& app) { + if (app.surfaceWidth == 0 || app.surfaceHeight == 0) + return; + // Get a texture from the surface to render into. WGPUSurfaceTexture surfaceTexture; wgpuSurfaceGetCurrentTexture(app.surface, &surfaceTexture); @@ -345,11 +360,11 @@ void initWebGPU(AppData& app) // which is how WebGPU prevents code from working on one machine and not on another. WGPURequiredLimits requiredLimits = {}; - requiredLimits.limits.maxVertexAttributes = 2u; + requiredLimits.limits.maxVertexAttributes = 3u; requiredLimits.limits.maxVertexBuffers = 1u; - requiredLimits.limits.maxBufferSize = 1024ull; - requiredLimits.limits.maxVertexBufferArrayStride = 5u * sizeof(float); - requiredLimits.limits.maxInterStageShaderComponents = 2u; + requiredLimits.limits.maxBufferSize = 2048ull; + requiredLimits.limits.maxVertexBufferArrayStride = sizeof(VertexData); + requiredLimits.limits.maxInterStageShaderComponents = 5u; requiredLimits.limits.maxBindGroups = 1u; requiredLimits.limits.maxBindingsPerBindGroup = 3u; requiredLimits.limits.maxUniformBuffersPerShaderStage = 1u; @@ -429,60 +444,60 @@ void initWebGPU(AppData& app) // The vertex buffer contains the input data for the shader. // clang-format off - std::vector vertexData = + std::vector vertexData = { // left - -0.5, 0.5, -0.5, 0.0f, 0.0f, - -0.5, -0.5, -0.5, 0.0f, 1.0f, - -0.5, 0.5, 0.5, 1.0f, 0.0f, - -0.5, 0.5, 0.5, 1.0f, 0.0f, - -0.5, -0.5, -0.5, 0.0f, 1.0f, - -0.5, -0.5, 0.5, 1.0f, 1.0f, + {-0.5, 0.5, -0.5, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f}, + {-0.5, -0.5, -0.5, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f}, + {-0.5, 0.5, 0.5, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f}, + {-0.5, 0.5, 0.5, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f}, + {-0.5, -0.5, -0.5, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f}, + {-0.5, -0.5, 0.5, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f}, // right - 0.5, 0.5, 0.5, 0.0f, 0.0f, - 0.5, -0.5, 0.5, 0.0f, 1.0f, - 0.5, 0.5, -0.5, 1.0f, 0.0f, - 0.5, 0.5, -0.5, 1.0f, 0.0f, - 0.5, -0.5, 0.5, 0.0f, 1.0f, - 0.5, -0.5, -0.5, 1.0f, 1.0f, + { 0.5, 0.5, 0.5, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f}, + { 0.5, -0.5, 0.5, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f}, + { 0.5, 0.5, -0.5, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f}, + { 0.5, 0.5, -0.5, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f}, + { 0.5, -0.5, 0.5, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f}, + { 0.5, -0.5, -0.5, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f}, // bottom - -0.5, -0.5, 0.5, 0.0f, 0.0f, - -0.5, -0.5, -0.5, 0.0f, 1.0f, - 0.5, -0.5, 0.5, 1.0f, 0.0f, - 0.5, -0.5, 0.5, 1.0f, 0.0f, - -0.5, -0.5, -0.5, 0.0f, 1.0f, - 0.5, -0.5, -0.5, 1.0f, 1.0f, + {-0.5, -0.5, 0.5, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f}, + {-0.5, -0.5, -0.5, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f}, + { 0.5, -0.5, 0.5, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f}, + { 0.5, -0.5, 0.5, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f}, + {-0.5, -0.5, -0.5, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f}, + { 0.5, -0.5, -0.5, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f}, // top - -0.5, 0.5, -0.5, 0.0f, 0.0f, - -0.5, 0.5, 0.5, 0.0f, 1.0f, - 0.5, 0.5, -0.5, 1.0f, 0.0f, - 0.5, 0.5, -0.5, 1.0f, 0.0f, - -0.5, 0.5, 0.5, 0.0f, 1.0f, - 0.5, 0.5, 0.5, 1.0f, 1.0f, + {-0.5, 0.5, -0.5, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f}, + {-0.5, 0.5, 0.5, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f}, + { 0.5, 0.5, -0.5, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f}, + { 0.5, 0.5, -0.5, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f}, + {-0.5, 0.5, 0.5, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f}, + { 0.5, 0.5, 0.5, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f}, // back - 0.5, 0.5, -0.5, 0.0f, 0.0f, - 0.5, -0.5, -0.5, 0.0f, 1.0f, - -0.5, 0.5, -0.5, 1.0f, 0.0f, - -0.5, 0.5, -0.5, 1.0f, 0.0f, - 0.5, -0.5, -0.5, 0.0f, 1.0f, - -0.5, -0.5, -0.5, 1.0f, 1.0f, + { 0.5, 0.5, -0.5, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f}, + { 0.5, -0.5, -0.5, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f}, + {-0.5, 0.5, -0.5, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f}, + {-0.5, 0.5, -0.5, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f}, + { 0.5, -0.5, -0.5, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f}, + {-0.5, -0.5, -0.5, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f}, // front - -0.5, 0.5, 0.5, 0.0f, 0.0f, - -0.5, -0.5, 0.5, 0.0f, 1.0f, - 0.5, 0.5, 0.5, 1.0f, 0.0f, - 0.5, 0.5, 0.5, 1.0f, 0.0f, - -0.5, -0.5, 0.5, 0.0f, 1.0f, - 0.5, -0.5, 0.5, 1.0f, 1.0f, + {-0.5, 0.5, 0.5, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f}, + {-0.5, -0.5, 0.5, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f}, + { 0.5, 0.5, 0.5, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f}, + { 0.5, 0.5, 0.5, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f}, + {-0.5, -0.5, 0.5, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f}, + { 0.5, -0.5, 0.5, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f}, }; // clang-format on - unsigned vertexCount = vertexData.size() / 5; - app.vertexDataSize = vertexData.size() * sizeof(float); + unsigned vertexCount = vertexData.size(); + app.vertexDataSize = vertexData.size() * sizeof(VertexData); WGPUBufferDescriptor vertexBufferDesc = {}; vertexBufferDesc.label = "Demo Vertex Buffer"; @@ -587,7 +602,7 @@ void initWebGPU(AppData& app) WEBGPU_DEMO_CHECK(app.texture, "[WebGPU] Failed to create texture"); WEBGPU_DEMO_LOG("[WebGPU] Texture created"); - // Where do we copyu the pixel data to? + // Where do we copy the pixel data to? WGPUImageCopyTexture destination = {}; destination.texture = app.texture; destination.mipLevel = 0; @@ -691,7 +706,8 @@ void initWebGPU(AppData& app) const char* shaderSource = R"( struct VertexInput { @location(0) position: vec3f, - @location(1) uvs: vec2f, + @location(1) normal: vec3f, + @location(2) uv: vec2f, }; struct Uniforms { @@ -702,9 +718,12 @@ void initWebGPU(AppData& app) struct VertexOutput { @builtin(position) position: vec4f, - @location(0) uvs: vec2f, + @location(0) worldNormal: vec3f, + @location(1) uv: vec2f, } + const LIGHT_DIR = vec3f(4.0, -8.0, 1.0); + @group(0) @binding(0) var uniforms: Uniforms; @group(0) @binding(1) var texture: texture_2d; @group(0) @binding(2) var textureSampler: sampler; @@ -716,13 +735,19 @@ void initWebGPU(AppData& app) var out: VertexOutput; out.position = uniforms.projectionMatrix * uniforms.viewMatrix * worldPos; - out.uvs = in.uvs; + out.worldNormal = in.normal; + out.uv = in.uv; return out; } @fragment fn fs_main(in: VertexOutput) -> @location(0) vec4f { - return textureSample(texture, textureSampler, in.uvs).rgba; + var baseColor = textureSample(texture, textureSampler, in.uv); + + var diffuse = dot(-normalize(in.worldNormal), normalize(LIGHT_DIR)); + diffuse = diffuse * 0.5 + 0.5; + + return vec4(baseColor.rgb * diffuse, baseColor.a); } )"; @@ -825,16 +850,21 @@ void initWebGPU(AppData& app) positionAttribute.offset = 0; positionAttribute.shaderLocation = 0; - WGPUVertexAttribute uvsAttribute = {}; - uvsAttribute.format = WGPUVertexFormat_Float32x2; - uvsAttribute.offset = 3 * sizeof(float); - uvsAttribute.shaderLocation = 1; + WGPUVertexAttribute normalAttribute = {}; + normalAttribute.format = WGPUVertexFormat_Float32x3; + normalAttribute.offset = 3 * sizeof(float); + normalAttribute.shaderLocation = 1; + + WGPUVertexAttribute uvAttribute = {}; + uvAttribute.format = WGPUVertexFormat_Float32x2; + uvAttribute.offset = 6 * sizeof(float); + uvAttribute.shaderLocation = 2; - std::vector vertexAttributes = {positionAttribute, uvsAttribute}; + std::vector vertexAttributes = {positionAttribute, normalAttribute, uvAttribute}; // Description of the vertex buffer layout for the vertex shader stage WGPUVertexBufferLayout vertexBufferLayout = {}; - vertexBufferLayout.arrayStride = 5ull * sizeof(float); + vertexBufferLayout.arrayStride = sizeof(VertexData); vertexBufferLayout.stepMode = WGPUVertexStepMode_Vertex; vertexBufferLayout.attributeCount = vertexAttributes.size(); vertexBufferLayout.attributes = vertexAttributes.data(); From c4963b915493524809a92683611977c9a72cde3e Mon Sep 17 00:00:00 2001 From: Marcus Hudritsch Date: Thu, 9 Nov 2023 15:21:59 +0100 Subject: [PATCH 049/108] Update main.cpp --- apps/webgpu/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/webgpu/main.cpp b/apps/webgpu/main.cpp index e4c2999a..7e154334 100644 --- a/apps/webgpu/main.cpp +++ b/apps/webgpu/main.cpp @@ -130,7 +130,7 @@ int main(int argc, const char* argv[]) // === Acquire a WebGPU device === // A device provides access to a GPU and is created from an adapter. - // We specify the capabilites that we require our device to have in requiredLimits. + // We specify the capabilities that we require our device to have in requiredLimits. // We cannot access more resources than specified in the required limits, // which is how WebGPU prevents code from working on one machine and not on another. From f0420edab9e946498d63b2a5be4836eac7d49cfa Mon Sep 17 00:00:00 2001 From: Marino von Wattenwyl Date: Mon, 13 Nov 2023 10:44:52 +0100 Subject: [PATCH 050/108] [WebGPU] Document WebGPU concepts --- apps/webgpu/main.cpp | 200 ++++++++++++++++++++++++++++++++----------- 1 file changed, 151 insertions(+), 49 deletions(-) diff --git a/apps/webgpu/main.cpp b/apps/webgpu/main.cpp index 71c488b1..93f9b41c 100644 --- a/apps/webgpu/main.cpp +++ b/apps/webgpu/main.cpp @@ -28,7 +28,7 @@ #include #define WEBGPU_DEMO_LOG(msg) std::cout << (msg) << std::endl - + #define WEBGPU_DEMO_CHECK(condition, errorMsg) \ if (!(condition)) \ { \ @@ -36,7 +36,134 @@ std::exit(1); \ } -struct AppData +/* +To interact with WebGPU, we create objects with a call to wgpu*Create*. This function normally takes the parent +object as a parameter and a WGPU*Descriptor that contains the specification for the object. The returned object +is released after usage with a call to wgpuRelease*. An object can have a label that is used when reporting errors. + +WebGPU concepts: + - WGPUInstance + The core interface through which all other objects are created. + + - WGPUAdapter + Represents a physical device and is used to query its capabilities and limits. + + - WGPUDevice + Represents a logical device we interact with. It is created by specifying the capabilities we require + and fails if the adapter does not support them. We cannot create more resources than specified in the + limits at creation. This is done to prevent a function from working on one device but not on another. + + - WGPUQueue + The queue is where we submit the commands for the GPU to. Everything that is executed on the GPU goes + through the queue. Examples are executing rendering or compute pipelines, writing to or reading from buffers. + + - WGPUSurface + The surface we draw onto. How it is acquired depends on the OS so we have to manually convert window handles + from the different platforms to surfaces. The surface has to be reconfigured every time the window size + changes. + + - WGPUBuffer + Represents a chunk of memory on the GPU. They can be used for example as vertex buffers, uniforms buffers or + storage buffers. Buffers are created with a fixed size and usage flags that specify e.g. whether we can copy + to this buffer or whether it is used as an index buffer. The memory for the buffer is automatically allocated + at creation but has to be deallocated manually using wgpuBufferDestroy. wgpuQueueWriteBuffer is used to upload + buffer data to the GPU. + + - WGPUTexture + Represents pixel data in the memory of the GPU. It is similar to a buffer in that memory for it is allocated at + creation, that it has usage flags and that the memory is deallocated using wgpuTextureDestroy. Data is uploaded + using wgpuQueueWriteTexture. A texture additionally has a size, a format, a mip level count and a sample count. + Textures can be used in shaders or as a render attachment (e.g. the depth buffer). Mip maps have to be created + manually. + + - WGPUTextureView + To use a texture, a texture view has to be created. It specifies which part of the texture is accessed + (which base array layer, how many array layers, which base mip level, which format, ...). + + - WGPUTextureSampler + To read a texture in a shader, a sampler is commonly used. Textures can also be accessed directly without a + sampler by specifying texel coordinates, which is more like reading the data from a buffer. To get access to + features like filtering, mipmapping or clamping, a sampler is used. + + - WGPURenderPipeline + Represents a configuration of the GPU pipeline. It specifies completely how input data is transformed into + output pixels by settings the configuration for the GPU pipeline stages. + + The WebGPU render pipeline model looks like this: + 1. vertex fetch + 2. vertex shader + 3. primitive assembly + 4. rasterization + 5. fragment shader + 6. stencil test and write + 7. depth test and write + 8. blending + 9. write to attachments + + The descriptor for this structure contains the following configurations: + + - layout: WGPUPipelineLayout + - vertex: WGPUVertexState + Configuration for the vertex fetch and vertex shader stages. + Specifies the shader module to run as well as the buffers and constants used. + A vertex buffer is specified using a WGPUVertexBufferLayout structure that contains a list of + attributes (WGPUVertexAttribute) along with the stride and step mode (per vertex or per instance). + Attributes are specified through a format, an offset in the data and a location in the shader. + The location is a number hard-coded for the attribute in the shader code. + - primitive: WGPUPrimitiveState + Configuration for the primitive assembly and rasterization stages. + Specifies the topology (triangles, triangle strips, lines, ...), what index format + is used, how the face orientation of triangles is defined and the cull mode. + - depthStencil: WGPUDepthStencilState + Configuration for the depth test and stencil test stages. + Specifies the format of the depth/stencil buffer and how depth and stencil testing are performed. + - multisample: WGPUMultisampleState + Configuration for multisampling. + Specifies the number of samples per pixel as well as additional parameters for muiltisampling. + - fragment: WGPUFragmentState + Configuration for the fragment shader and blending stages. + Specifies the shader module to run as well as the buffers and constants used. + This state also specifies a list of color targets that are rendered into which contains + additional configuration such as the color attachement format and the blending mode. + + - WGPUShaderModule + Represents a shader on the GPU. It is created by specifying code in either the WebGPU shading language (WGSL) + or the SPIR-V format (not support on the Web). This code is then compiled by the WebGPU implementation to a + backend-specific shader format such as SPIR-V for Vulkan or MSL for Metal. A shader can have multiple entry + points, making it possible to use one shader module for both the vertex shader and fragment shader stage. + + - WGPUBindGroup + A list of resources bound in a shader. Resources can be buffers, samplers or texture views. Each bind group + entry contains a binding (a unique number assigned to the resource in the shader code), the buffer, sampler or + texture view bound, an offset and a size. Multiple bind groups can be set per render pass. + + - WGPUBindGroupLayout + A list of layouts for bind groups. A bind group references its layout and they both have to have the same + number of entries. The entries describe the binding, which shader stages can access it as well as additional + info depending on the type. For example, buffer bind group layout entries specify whether they are a uniform + buffer, a storage buffer or read-only storage. + + - WGPUPipelineLayout + Specifies the bind groups used in the pipeline. + + - WGPUCommandEncoder + Work for the GPU has to be recorded into a command buffer. This is done using a command encoder. We call + functions on the encoder (wgpuCommandEncoder*) to encode the commands into a buffer that can be accessed by + calling wgpuCommandEncoderFinish. + + - WGPUCommandBuffer + When all the GPU commands are recorded, wgpuCommandEncoderFinish is called on the queue which returns a + command buffer. This buffer can then be submitted to the GPU using wgpuQueueSubmit. + + - WGPURenderPassEncoder + Specifies how a render pipeline is executed. It encodes the pipeline used along with the required vertex + buffers, index buffers, bind groups, drawing commands, etc. Accessing these render commands is done using a + specialized object called a render pass encoder. It is created from a command encoder using + wgpuCommandEncoderBeginRenderPass. + +*/ + +struct App { GLFWwindow* window = nullptr; int surfaceWidth = 0; @@ -47,11 +174,11 @@ struct AppData WGPUDevice device = nullptr; WGPUQueue queue = nullptr; WGPUSurface surface = nullptr; - WGPUTexture depthTexture = nullptr; - WGPUTextureView depthTextureView = nullptr; WGPUBuffer vertexBuffer = nullptr; WGPUBuffer indexBuffer = nullptr; WGPUBuffer uniformBuffer = nullptr; + WGPUTexture depthTexture = nullptr; + WGPUTextureView depthTextureView = nullptr; WGPUTexture texture = nullptr; WGPUTextureView textureView = nullptr; WGPUSampler sampler = nullptr; @@ -70,8 +197,6 @@ struct AppData unsigned indexCount; unsigned indexDataSize; - bool requireSurfaceReconfiguration = false; - float camRotX = 0.0f; float camRotY = 0.0f; float camZ = 2.0f; @@ -102,7 +227,7 @@ static_assert(sizeof(ShaderUniformData) % 16 == 0, "uniform data size must be a extern "C" void* createMetalLayer(void* window); #endif -void reconfigureSurface(AppData& app) +void reconfigureSurface(App& app) { // Get the window size from the GLFW window. glfwGetWindowSize(app.window, &app.surfaceWidth, &app.surfaceHeight); @@ -134,7 +259,7 @@ void reconfigureSurface(AppData& app) WEBGPU_DEMO_LOG("[WebGPU] Depth texture view re-created"); } -void onPaint(AppData& app) +void onPaint(App& app) { if (app.surfaceWidth == 0 || app.surfaceHeight == 0) return; @@ -283,12 +408,12 @@ void onPaint(AppData& app) void onResize(GLFWwindow* window, int width, int height) { - AppData& app = *((AppData*)glfwGetWindowUserPointer(window)); + App& app = *((App*)glfwGetWindowUserPointer(window)); reconfigureSurface(app); onPaint(app); } -void initGLFW(AppData& app) +void initGLFW(App& app) { // === Initialize GLFW === @@ -334,7 +459,7 @@ void handleDeviceRequest(WGPURequestDeviceStatus status, *outDevice = device; } -void initWebGPU(AppData& app) +void initWebGPU(App& app) { // === Create a WebGPU instance === // The instance is the root interface to WebGPU through which we create all other WebGPU resources. @@ -450,48 +575,36 @@ void initWebGPU(AppData& app) {-0.5, 0.5, -0.5, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f}, {-0.5, -0.5, -0.5, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f}, {-0.5, 0.5, 0.5, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f}, - {-0.5, 0.5, 0.5, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f}, - {-0.5, -0.5, -0.5, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f}, {-0.5, -0.5, 0.5, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f}, // right { 0.5, 0.5, 0.5, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f}, { 0.5, -0.5, 0.5, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f}, { 0.5, 0.5, -0.5, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f}, - { 0.5, 0.5, -0.5, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f}, - { 0.5, -0.5, 0.5, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f}, { 0.5, -0.5, -0.5, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f}, // bottom {-0.5, -0.5, 0.5, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f}, {-0.5, -0.5, -0.5, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f}, { 0.5, -0.5, 0.5, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f}, - { 0.5, -0.5, 0.5, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f}, - {-0.5, -0.5, -0.5, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f}, { 0.5, -0.5, -0.5, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f}, // top {-0.5, 0.5, -0.5, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f}, {-0.5, 0.5, 0.5, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f}, { 0.5, 0.5, -0.5, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f}, - { 0.5, 0.5, -0.5, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f}, - {-0.5, 0.5, 0.5, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f}, { 0.5, 0.5, 0.5, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f}, // back { 0.5, 0.5, -0.5, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f}, { 0.5, -0.5, -0.5, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f}, {-0.5, 0.5, -0.5, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f}, - {-0.5, 0.5, -0.5, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f}, - { 0.5, -0.5, -0.5, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f}, {-0.5, -0.5, -0.5, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f}, // front {-0.5, 0.5, 0.5, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f}, {-0.5, -0.5, 0.5, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f}, { 0.5, 0.5, 0.5, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f}, - { 0.5, 0.5, 0.5, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f}, - {-0.5, -0.5, 0.5, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f}, { 0.5, -0.5, 0.5, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f}, }; // clang-format on @@ -513,9 +626,17 @@ void initWebGPU(AppData& app) // === Create the index buffer === - std::vector indexData = {}; - for (std::uint16_t index = 0; index < 36; index++) - indexData.push_back(index); + // clang-format off + std::vector indexData = + { + 0, 1, 2, 2, 1, 3, // left + 4, 5, 6, 6, 5, 7, // right + 8, 9, 10, 10, 9, 11, // bottom + 12, 13, 14, 14, 13, 15, // top + 16, 17, 18, 18, 17, 19, // back + 20, 21, 22, 22, 21, 23, // front + }; + // clang-format on app.indexCount = indexData.size(); app.indexDataSize = indexData.size() * sizeof(std::uint16_t); @@ -626,25 +747,6 @@ void initWebGPU(AppData& app) for (unsigned mipLevel = 0; mipLevel < textureDesc.mipLevelCount; mipLevel++) { - // === Test colors === - // - // std::uint64_t mipLevelColors[] = - // { - // 0xFF0000FF, - // 0xFFFF00FF, - // 0x00FF00FF, - // 0x0000FFFF - // }; - // - // for (unsigned y = 0; y < mipLevelSize.height; y++) - // { - // for (unsigned x = 0; x < mipLevelSize.width; x++) - // { - // unsigned pixelIndex = x + y * mipLevelSize.width; - // std::memcpy(&mipLevelData[4ull * pixelIndex], &mipLevelColors[mipLevel], 4); - // } - // } - cv::Mat mipLevelImage; cv::Size cvSize(static_cast(mipLevelSize.width), static_cast(mipLevelSize.height)); cv::resize(image, mipLevelImage, cvSize); @@ -881,7 +983,7 @@ void initWebGPU(AppData& app) primitiveState.topology = WGPUPrimitiveTopology_TriangleList; primitiveState.stripIndexFormat = WGPUIndexFormat_Undefined; primitiveState.frontFace = WGPUFrontFace_CCW; - primitiveState.cullMode = WGPUCullMode_None; + primitiveState.cullMode = WGPUCullMode_Back; // Configuration for depth testing and stencil buffer WGPUDepthStencilState depthStencilState = {}; @@ -929,7 +1031,7 @@ void initWebGPU(AppData& app) WEBGPU_DEMO_LOG("[WebGPU] Render pipeline created"); } -void deinitWebGPU(AppData& app) +void deinitWebGPU(App& app) { // === Release all WebGPU resources === @@ -960,7 +1062,7 @@ void deinitWebGPU(AppData& app) WEBGPU_DEMO_LOG("[WebGPU] Resources released"); } -void deinitGLFW(AppData& app) +void deinitGLFW(App& app) { // === Destroy the window and terminate GLFW === @@ -971,7 +1073,7 @@ void deinitGLFW(AppData& app) int main(int argc, const char* argv[]) { - AppData app; + App app; initGLFW(app); initWebGPU(app); From e664fef40905a78f37581e24658b95c09181e89d Mon Sep 17 00:00:00 2001 From: luc Date: Mon, 13 Nov 2023 17:51:13 +0100 Subject: [PATCH 051/108] Rename (renderInstanced, instanced, instancedRendering) boolean to only renderInstanced for consistancy --- apps/app_demo_slproject/source/AppDemoGui.cpp | 6 +++--- modules/sl/source/gl/SLGLProgramGenerated.cpp | 8 ++++---- modules/sl/source/gl/SLGLProgramGenerated.h | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/apps/app_demo_slproject/source/AppDemoGui.cpp b/apps/app_demo_slproject/source/AppDemoGui.cpp index 120b4584..32abad82 100644 --- a/apps/app_demo_slproject/source/AppDemoGui.cpp +++ b/apps/app_demo_slproject/source/AppDemoGui.cpp @@ -3774,10 +3774,10 @@ void AppDemoGui::buildProperties(SLScene* s, SLSceneView* sv) ps->isGenerated(false); } - bool instanced = ps->renderInstanced(); - if (ImGui::Checkbox("Instanced draw", &instanced)) + bool renderInstanced = ps->renderInstanced(); + if (ImGui::Checkbox("Instanced draw", &renderInstanced)) { - ps->drawInstanced(instanced); + ps->drawInstanced(renderInstanced); ps->isGenerated(false); } diff --git a/modules/sl/source/gl/SLGLProgramGenerated.cpp b/modules/sl/source/gl/SLGLProgramGenerated.cpp index 9ca65363..f191d48f 100644 --- a/modules/sl/source/gl/SLGLProgramGenerated.cpp +++ b/modules/sl/source/gl/SLGLProgramGenerated.cpp @@ -1856,7 +1856,7 @@ void SLGLProgramGenerated::buildProgramName(SLMaterial* mat, void SLGLProgramGenerated::buildProgramNamePS(SLMaterial* mat, string& programName, bool isDrawProg, - bool instancedRendering) + bool renderInstanced) { assert(mat && "No material pointer passed!"); programName = "gen"; @@ -1871,7 +1871,7 @@ void SLGLProgramGenerated::buildProgramNamePS(SLMaterial* mat, { programName += "-Draw"; - if (instancedRendering) + if (renderInstanced) programName += "-Inst"; programName += mat->texturesString(); @@ -1976,7 +1976,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, bool isRenderInstanced) +void SLGLProgramGenerated::buildProgramCodePS(SLMaterial* mat, bool isDrawProg, bool renderInstanced) { if (mat->name() == "IBLMat") { @@ -1989,7 +1989,7 @@ void SLGLProgramGenerated::buildProgramCodePS(SLMaterial* mat, bool isDrawProg, if (isDrawProg) { - if (isRenderInstanced) + if (renderInstanced) buildPerPixParticleInstanced(mat); else buildPerPixParticle(mat); diff --git a/modules/sl/source/gl/SLGLProgramGenerated.h b/modules/sl/source/gl/SLGLProgramGenerated.h index bd2fe05b..39738407 100644 --- a/modules/sl/source/gl/SLGLProgramGenerated.h +++ b/modules/sl/source/gl/SLGLProgramGenerated.h @@ -99,9 +99,9 @@ class SLGLProgramGenerated : public SLGLProgram static void buildProgramNamePS(SLMaterial* mat, string& programName, bool isDrawProg, - bool instancedRendering); + bool renderInstanced); - void buildProgramCodePS(SLMaterial* mat, bool isDrawProg, bool isRenderInstanced = false); + void buildProgramCodePS(SLMaterial* mat, bool isDrawProg, bool renderInstanced = false); void buildProgramCode(SLMaterial* mat, SLVLight* lights); void beginShader(SLCamera* cam, From 32f7b049e0941bf4338b01f2962ea8056f089ce5 Mon Sep 17 00:00:00 2001 From: luc Date: Mon, 13 Nov 2023 18:03:27 +0100 Subject: [PATCH 052/108] If geometry shader not supported, particle system automatically use draw instanced --- apps/app_demo_slproject/source/AppDemoGui.cpp | 46 +- .../app_demo_slproject/source/AppDemoLoad.cpp | 722 +++++++++--------- modules/sl/source/mesh/SLParticleSystem.cpp | 10 +- 3 files changed, 379 insertions(+), 399 deletions(-) diff --git a/apps/app_demo_slproject/source/AppDemoGui.cpp b/apps/app_demo_slproject/source/AppDemoGui.cpp index 32abad82..7f18410b 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(); } diff --git a/apps/app_demo_slproject/source/AppDemoLoad.cpp b/apps/app_demo_slproject/source/AppDemoLoad.cpp index 34390c1b..d65088ca 100644 --- a/apps/app_demo_slproject/source/AppDemoLoad.cpp +++ b/apps/app_demo_slproject/source/AppDemoLoad.cpp @@ -5868,419 +5868,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/modules/sl/source/mesh/SLParticleSystem.cpp b/modules/sl/source/mesh/SLParticleSystem.cpp index bef75869..bc8a2886 100644 --- a/modules/sl/source/mesh/SLParticleSystem.cpp +++ b/modules/sl/source/mesh/SLParticleSystem.cpp @@ -35,7 +35,15 @@ SLParticleSystem::SLParticleSystem(SLAssetManager* assetMgr, assert(!name.empty()); // To be added to constructor - _renderInstanced = renderInstanced; + + 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 From d21a4e174da68fd7b7b2e5508a297f6ecbc68ea9 Mon Sep 17 00:00:00 2001 From: luc Date: Mon, 13 Nov 2023 19:11:56 +0100 Subject: [PATCH 053/108] fix particle system error with material destruction --- .../app_demo_slproject/source/AppDemoLoad.cpp | 2 - modules/sl/source/mesh/SLParticleSystem.cpp | 67 +++++++++++-------- modules/sl/source/mesh/SLParticleSystem.h | 3 + 3 files changed, 42 insertions(+), 30 deletions(-) diff --git a/apps/app_demo_slproject/source/AppDemoLoad.cpp b/apps/app_demo_slproject/source/AppDemoLoad.cpp index d65088ca..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); diff --git a/modules/sl/source/mesh/SLParticleSystem.cpp b/modules/sl/source/mesh/SLParticleSystem.cpp index bc8a2886..07bcb9a5 100644 --- a/modules/sl/source/mesh/SLParticleSystem.cpp +++ b/modules/sl/source/mesh/SLParticleSystem.cpp @@ -36,6 +36,8 @@ SLParticleSystem::SLParticleSystem(SLAssetManager* assetMgr, // To be added to constructor + _assetManager = assetMgr; + if (SLGLState::instance()->glHasGeometryShaders()) { _renderInstanced = renderInstanced; @@ -60,10 +62,6 @@ SLParticleSystem::SLParticleSystem(SLAssetManager* assetMgr, _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); @@ -328,6 +326,18 @@ void SLParticleSystem::generate() 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); @@ -484,6 +494,9 @@ void SLParticleSystem::generate() 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); @@ -539,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 @@ -597,7 +593,6 @@ void SLParticleSystem::draw(SLSceneView* sv, SLNode* node, SLuint instances) ///////////////////////////////////// // Init particles vector and init VAO ///////////////////////////////////// - if (!_isGenerated) generate(); @@ -908,6 +903,27 @@ void SLParticleSystem::draw(SLSceneView* sv, SLNode* node, SLuint instances) // 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() @@ -918,12 +934,7 @@ void SLParticleSystem::deleteData() //! deleteData deletes all mesh data and VAOs void SLParticleSystem::deleteDataGpu() { - _vao1.deleteGL(); - _vao2.deleteGL(); - _renderVao1.deleteGL(); - _renderVao2.deleteGL(); SLMesh::deleteDataGpu(); - _mat->deleteDataGpu(); } //----------------------------------------------------------------------------- /*! SLParticleSystem::buildAABB builds the passed axis-aligned bounding box in diff --git a/modules/sl/source/mesh/SLParticleSystem.h b/modules/sl/source/mesh/SLParticleSystem.h index d856e62f..17fcb340 100644 --- a/modules/sl/source/mesh/SLParticleSystem.h +++ b/modules/sl/source/mesh/SLParticleSystem.h @@ -273,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 From 79b7ebee5061c4b39a60d5cded7cebec37050e84 Mon Sep 17 00:00:00 2001 From: Marino von Wattenwyl Date: Thu, 16 Nov 2023 11:30:14 +0100 Subject: [PATCH 054/108] [WebGPU] Update demo docs --- apps/webgpu/CMakeLists.txt | 2 +- apps/webgpu/{main.cpp => webgpu_demo.cpp} | 139 ++++++++++++++++------ 2 files changed, 104 insertions(+), 37 deletions(-) rename apps/webgpu/{main.cpp => webgpu_demo.cpp} (92%) diff --git a/apps/webgpu/CMakeLists.txt b/apps/webgpu/CMakeLists.txt index fff48076..564ea90b 100644 --- a/apps/webgpu/CMakeLists.txt +++ b/apps/webgpu/CMakeLists.txt @@ -60,7 +60,7 @@ if (SYSTEM_NAME_UPPER MATCHES "DARWIN") set(TARGET_SPECIFIC_SOURCES metal_layer.mm) endif () -add_executable(webgpu-demo main.cpp ${TARGET_SPECIFIC_SOURCES}) +add_executable(webgpu-demo webgpu_demo.cpp ${TARGET_SPECIFIC_SOURCES}) target_link_libraries(webgpu-demo PRIVATE wgpu sl_cv sl_math ${glfw_LIBS}) if (SYSTEM_NAME_UPPER MATCHES "LINUX") diff --git a/apps/webgpu/main.cpp b/apps/webgpu/webgpu_demo.cpp similarity index 92% rename from apps/webgpu/main.cpp rename to apps/webgpu/webgpu_demo.cpp index 93f9b41c..64a97a4a 100644 --- a/apps/webgpu/main.cpp +++ b/apps/webgpu/webgpu_demo.cpp @@ -1,47 +1,45 @@ -#if defined(_WIN32) -# define SYSTEM_WINDOWS -# define GLFW_EXPOSE_NATIVE_WIN32 -# define WIN32_LEAN_AND_MEAN -# include -#elif defined(__linux__) -# define SYSTEM_LINUX -# define GLFW_EXPOSE_NATIVE_X11 -#elif defined(__APPLE__) -# ifndef SYSTEM_DARWIN -# define SYSTEM_DARWIN -# endif -# define GLFW_EXPOSE_NATIVE_COCOA -#endif +/* -#include -#include -#include -#include -#include -#include -#include +-- Overview -#include -#include -#include -#include -#include +WebGPU is a graphics API standard developed by the World Wide Web Consortium (W3C). It is an abstraction layer +over modern graphics APIs like Vulkan, D3D12 and Metal and thus uses modern concepts such as queues, command buffers +or pipelines. WebGPU is supposed to be 'safe' API and is generally simpler to use than Vulkan. Both graphics and +compute functionality is available. -#define WEBGPU_DEMO_LOG(msg) std::cout << (msg) << std::endl - -#define WEBGPU_DEMO_CHECK(condition, errorMsg) \ - if (!(condition)) \ - { \ - std::cerr << (errorMsg) << std::endl; \ - std::exit(1); \ - } +The primary target for WebGPU was the Web as a replacement for the old WebGL API. This means that +the specification is written for a JavaScript API. However, Google and Mozilla have decided to provide their in-browser +implementations as native libraries, so we can use WebGPU in native apps written in C, C++ or Rust. The implementers +have agreed on a common interface for their libraries in form of a header called `webgpu.h` +(https://github.com/webgpu-native/webgpu-headers/). -/* -To interact with WebGPU, we create objects with a call to wgpu*Create*. This function normally takes the parent +There are currently three implementations of this header: + - wgpu-native: Mozilla's implementation for Firefox, written in Rust + - Dawn: Google's implementation for Chromium, written in C++ + - Emscripten: Translates the webgpu.h calls to JavaScript calls in the browser + +WebGPU uses its own shader language called WGSL (WebGPU Shader Language). This is the only shader language supported +in the browsers even though the native implementations also support SPIR-V. + +To make porting to WebGPU easier, the two browser vendors provide tools for translating other shader languages to WGSL: + - Naga: Mozilla's shader translator, can be used only as an executable + - Tint: Google's shader translator, can be used as both a C++ library and an executable + +This demo uses the wgpu-native implementation because Mozilla releases pre-built binaries that can easily be +downloaded from CMake. Since wgpu-native uses the same interface header as Dawn, it would also be possible to +link Dawn instead of wgpu-native, but we would have to build and distribute it ourselves. + + +-- Usage + +WebGPU follows a stateless design as opposed to OpenGL, where much state has to be set globally before making a +draw call. To use WebGPU, we create objects with a call to wgpu*Create*. This function generally takes the parent object as a parameter and a WGPU*Descriptor that contains the specification for the object. The returned object is released after usage with a call to wgpuRelease*. An object can have a label that is used when reporting errors. +We can now call functions on the object to interact with them. WebGPU concepts: + - WGPUInstance The core interface through which all other objects are created. @@ -161,8 +159,77 @@ WebGPU concepts: specialized object called a render pass encoder. It is created from a command encoder using wgpuCommandEncoderBeginRenderPass. + +-- WebGPU vs. Vulkan + +Here's a list of things I've noticed are handled differently from Vulkan (as of 2023-11-26): + + - There is no multithreading + Unlike Vukan, WebGPU is currently single-threaded and doesn't allow encoding command buffers on + multiple threads. + Status: https://gpuweb.github.io/gpuweb/explainer/#multithreading + + - There is only one queue + Vulkan allows you to create multiple queues from different families. For example, you can create a + graphics and a compute queue and submit commands to them that are processed in parallel on some GPUs. + In WebGPU, there is only one queue that is acquired using wgpuDeviceGetQueue. + Discussion: https://github.com/gpuweb/gpuweb/issues/1065 + + - There is no manual memory allocation + In WebGPU, buffers take care of memory allocation internally. Awesome! + + - There is no swap chain + The swap chain configuration is part of the surface configuration. + + - Error handling is very different + In Vulkan, there is by default no validation and programs just silently crash and burn. It is possible + to enable validation layers that print errors with references to the spec. The reason for this is that + Vulkan wants to avoid validation overhead in release builds. In WebGPU, validation seems mostly up to the + implementations. We currently use the wgpu-native implementation, which seems to catch all errors and prints + most of the time a nice error message with a human-readable error message including the labels of the + problematic objects, suggests fixes and even generates a stack trace. I'm not sure what the overhead of + this validation is, but I excpect there to be an option to turn it off in the future. + */ +#if defined(_WIN32) +# define SYSTEM_WINDOWS +# define GLFW_EXPOSE_NATIVE_WIN32 +# define WIN32_LEAN_AND_MEAN +# include +#elif defined(__linux__) +# define SYSTEM_LINUX +# define GLFW_EXPOSE_NATIVE_X11 +#elif defined(__APPLE__) +# ifndef SYSTEM_DARWIN +# define SYSTEM_DARWIN +# endif +# define GLFW_EXPOSE_NATIVE_COCOA +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define WEBGPU_DEMO_LOG(msg) std::cout << (msg) << std::endl + +#define WEBGPU_DEMO_CHECK(condition, errorMsg) \ + if (!(condition)) \ + { \ + std::cerr << (errorMsg) << std::endl; \ + std::exit(1); \ + } + struct App { GLFWwindow* window = nullptr; From ce1ae04729d79269025ae2694205ac23e3218a72 Mon Sep 17 00:00:00 2001 From: Marcus Hudritsch Date: Mon, 20 Nov 2023 10:25:02 +0100 Subject: [PATCH 055/108] Update SLGLState.cpp --- modules/sl/source/gl/SLGLState.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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) From 72c4cdf192fc35354daaddd8194b293a998a6d24 Mon Sep 17 00:00:00 2001 From: Marcus Hudritsch Date: Mon, 20 Nov 2023 16:59:34 +0100 Subject: [PATCH 056/108] Cosmetics --- modules/sl/source/SLAssetManager.cpp | 2 +- modules/sl/source/SLMaterial.cpp | 18 ++++++++++-------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/modules/sl/source/SLAssetManager.cpp b/modules/sl/source/SLAssetManager.cpp index 1fedf31f..05498ce2 100644 --- a/modules/sl/source/SLAssetManager.cpp +++ b/modules/sl/source/SLAssetManager.cpp @@ -95,7 +95,7 @@ bool SLAssetManager::removeMesh(SLMesh* mesh) } return false; } - +//----------------------------------------------------------------------------- //! Removes the specified program from the meshes resource vector. bool SLAssetManager::removeProgram(SLGLProgram* program) { diff --git a/modules/sl/source/SLMaterial.cpp b/modules/sl/source/SLMaterial.cpp index 3f8c0bab..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,8 +372,7 @@ SLMaterial::~SLMaterial() _errorTexture = nullptr; } } - - +//------------------------------------------------------------------------------ void SLMaterial::deleteDataGpu() { if (_program) @@ -384,8 +383,6 @@ void SLMaterial::deleteDataGpu() _program = nullptr; } } - - //----------------------------------------------------------------------------- /*! If this material has not yet a shader program assigned (SLMaterial::_program) @@ -403,7 +400,10 @@ void SLMaterial::generateProgramPS(bool renderInstanced) // Check first the asset manager if the requested program type already exists string programNameDraw; - SLGLProgramGenerated::buildProgramNamePS(this, programNameDraw, true, renderInstanced); + SLGLProgramGenerated::buildProgramNamePS(this, + programNameDraw, + true, + renderInstanced); _program = _assetManager->getProgramByName(programNameDraw); // If the program was not found by name generate a new one @@ -478,7 +478,8 @@ void SLMaterial::generateProgramPS(bool renderInstanced) 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); } @@ -487,7 +488,8 @@ void SLMaterial::generateProgramPS(bool renderInstanced) 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); } } From 373ea04fea7889b3d9747095f7582b5d11fad0e5 Mon Sep 17 00:00:00 2001 From: Marcus Hudritsch Date: Tue, 21 Nov 2023 09:23:01 +0100 Subject: [PATCH 057/108] Fixed SL_EXIT_MSG again No we see shader compile errors again. --- modules/sl/source/SL.h | 2 +- modules/sl/source/gl/SLGLShader.cpp | 1 + modules/utils/source/Utils.cpp | 6 +++--- 3 files changed, 5 insertions(+), 4 deletions(-) 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/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/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"; From 28df38ba5cd3d1331b5bcfa0e0dcc4516679b2e6 Mon Sep 17 00:00:00 2001 From: luc Date: Mon, 27 Nov 2023 10:11:45 +0100 Subject: [PATCH 058/108] remove useless and non GLSL compilant texNum in fragshader --- modules/sl/source/gl/SLGLProgramGenerated.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/modules/sl/source/gl/SLGLProgramGenerated.cpp b/modules/sl/source/gl/SLGLProgramGenerated.cpp index f191d48f..2eee9230 100644 --- a/modules/sl/source/gl/SLGLProgramGenerated.cpp +++ b/modules/sl/source/gl/SLGLProgramGenerated.cpp @@ -168,15 +168,11 @@ const string vertOutput_PS_struct_texNum = R"( const string vertOutput_PS_struct_End = R"( } vert; )"; -const string vertOutput_PS_instanced_texNum = R"( -out uint texNum; // Num of texture in flipbook)"; 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 fragInput_PS_instanced_texNum = R"( -in uint texNum; // Num of texture in flipbook)"; const string fragMain_PS_v_c = R"( vec4 color = u_color; // Particle color)"; @@ -2336,7 +2332,6 @@ void SLGLProgramGenerated::buildPerPixParticleInstanced(SLMaterial* mat) fragCode += shaderHeader(); // Fragment shader inputs - if (FlBoTex) fragCode += fragInput_PS_instanced_texNum; fragCode += fragInput_PS_v_tC; if (Co && !CoOvLi) fragCode += fragInput_PS_u_c; if (CoOvLi) From 7122373bc7fe221bb9e0866b3105238bc4a1c1bb Mon Sep 17 00:00:00 2001 From: Marcus Hudritsch Date: Mon, 27 Nov 2023 10:51:46 +0100 Subject: [PATCH 059/108] Update AppDemoGui.cpp --- apps/app_demo_slproject/source/AppDemoGui.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/apps/app_demo_slproject/source/AppDemoGui.cpp b/apps/app_demo_slproject/source/AppDemoGui.cpp index 7f18410b..55d5f5bd 100644 --- a/apps/app_demo_slproject/source/AppDemoGui.cpp +++ b/apps/app_demo_slproject/source/AppDemoGui.cpp @@ -4370,8 +4370,6 @@ void AppDemoGui::buildProperties(SLScene* s, SLSceneView* sv) } } - ImGui::Text("Supports GPU skinning: %s", m->supportsGPUSkinning() ? "Yes" : "No"); - ImGui::TreePop(); } } From 67fcfeb2feec4b98cdeaa86f9e568ae722cc127f Mon Sep 17 00:00:00 2001 From: Marcus Hudritsch Date: Mon, 27 Nov 2023 11:18:58 +0100 Subject: [PATCH 060/108] Version 4.1 for development --- apps/app_demo_slproject/source/AppDemoGui.cpp | 28 ------------------- apps/source/AppDemo.cpp | 2 +- docs/Doxyfile | 2 +- 3 files changed, 2 insertions(+), 30 deletions(-) diff --git a/apps/app_demo_slproject/source/AppDemoGui.cpp b/apps/app_demo_slproject/source/AppDemoGui.cpp index 55d5f5bd..c9f40bbf 100644 --- a/apps/app_demo_slproject/source/AppDemoGui.cpp +++ b/apps/app_demo_slproject/source/AppDemoGui.cpp @@ -1577,38 +1577,14 @@ void AppDemoGui::buildMenuBar(SLScene* s, SLSceneView* sv) { SLstring zip = "glTF-Sample-Models.zip"; - /*if (ImGui::MenuItem("Clear Coat Test", nullptr, sid == SID_glTF_ClearCoatTest)) - { - SLstring fileToLoad = AppDemo::configPath + "models/glTF-Sample-Models/2.0/ClearCoatTest/glTF/ClearCoatTest.gltf"; - if (Utils::fileExists(fileToLoad)) - s->onLoad(am, s, sv, SID_glTF_ClearCoatTest); - else - downloadModelAndLoadScene(s, sv, zip, pathSrc, pathDst, fileToLoad, SID_glTF_ClearCoatTest); - }*/ if (ImGui::MenuItem("Damaged Helmet", nullptr, sid == SID_glTF_DamagedHelmet)) - { s->onLoad(am, s, sv, SID_glTF_DamagedHelmet); - // SLstring fileToLoad = AppDemo::configPath + "models/glTF-Sample-Models/2.0/DamagedHelmet/glTF/DamagedHelmet.gltf"; - // loadSceneWithLargeModel(s, sv, zip, fileToLoad, SID_glTF_DamagedHelmet); - } if (ImGui::MenuItem("Flight Helmet", nullptr, sid == SID_glTF_FlightHelmet)) - { s->onLoad(am, s, sv, SID_glTF_FlightHelmet); - // SLstring fileToLoad = AppDemo::configPath + "models/glTF-Sample-Models/2.0/FlightHelmet/glTF/FlightHelmet.gltf"; - // loadSceneWithLargeModel(s, sv, zip, fileToLoad, SID_glTF_FlightHelmet); - } if (ImGui::MenuItem("Sponza Palace", nullptr, sid == SID_glTF_Sponza)) - { s->onLoad(am, s, sv, SID_glTF_Sponza); - // SLstring fileToLoad = AppDemo::configPath + "models/glTF-Sample-Models/2.0/Sponza/glTF/Sponza.gltf"; - // loadSceneWithLargeModel(s, sv, zip, fileToLoad, SID_glTF_Sponza); - } if (ImGui::MenuItem("Water Bottle", nullptr, sid == SID_glTF_WaterBottle)) - { s->onLoad(am, s, sv, SID_glTF_WaterBottle); - // SLstring fileToLoad = AppDemo::configPath + "models/glTF-Sample-Models/2.0/WaterBottle/glTF/WaterBottle.gltf"; - // loadSceneWithLargeModel(s, sv, zip, fileToLoad, SID_glTF_WaterBottle); - } ImGui::EndMenu(); } @@ -1618,11 +1594,7 @@ void AppDemoGui::buildMenuBar(SLScene* s, SLSceneView* sv) SLstring zip = "GLTF-FanucCRX.zip"; if (ImGui::MenuItem("Fanuc-CRX", nullptr, sid == SID_Robotics_FanucCRX_FK)) - { s->onLoad(am, s, sv, SID_Robotics_FanucCRX_FK); - // SLstring fileToLoad = AppDemo::configPath + "models/GLTF-FanucCRX/Fanuc-CRX.gltf"; - // loadSceneWithLargeModel(s, sv, zip, fileToLoad, SID_Robotics_FanucCRX_FK); - } ImGui::EndMenu(); } diff --git a/apps/source/AppDemo.cpp b/apps/source/AppDemo.cpp index a8efbca9..14d8a1ae 100644 --- a/apps/source/AppDemo.cpp +++ b/apps/source/AppDemo.cpp @@ -29,7 +29,7 @@ SLDeviceRotation AppDemo::devRot; SLDeviceLocation AppDemo::devLoc; SLstring AppDemo::name = "SLProjectApp"; SLstring AppDemo::appTag = "SLProject"; -SLstring AppDemo::version = "4.0.000"; +SLstring AppDemo::version = "4.1.000"; #ifdef _DEBUG SLstring AppDemo::configuration = "Debug"; #else diff --git a/docs/Doxyfile b/docs/Doxyfile index 414bd717..a435f3f7 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -47,7 +47,7 @@ PROJECT_NAME = SLProject # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = 4.0.000 +PROJECT_NUMBER = 4.1.000 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a From bd6a7030806fe6c02bb7eed1d6693fa9e201e197 Mon Sep 17 00:00:00 2001 From: Marcus Hudritsch Date: Mon, 27 Nov 2023 11:26:37 +0100 Subject: [PATCH 061/108] Update deploy-wasm-emscripten.yml --- .github/workflows/deploy-wasm-emscripten.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/deploy-wasm-emscripten.yml b/.github/workflows/deploy-wasm-emscripten.yml index 4372e6bd..9228fde5 100644 --- a/.github/workflows/deploy-wasm-emscripten.yml +++ b/.github/workflows/deploy-wasm-emscripten.yml @@ -5,6 +5,7 @@ on: push: branches: - main + - develop jobs: deploy: From f6c3c9fc4d35cc828c682a9ad9cb98e84344e641 Mon Sep 17 00:00:00 2001 From: Marcus Hudritsch Date: Mon, 27 Nov 2023 14:29:46 +0100 Subject: [PATCH 062/108] Cosmetics Crashes on MacOS on wgpuAdapterGetLimits --- apps/webgpu/webgpu_demo.cpp | 41 +++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/apps/webgpu/webgpu_demo.cpp b/apps/webgpu/webgpu_demo.cpp index 64a97a4a..d8059660 100644 --- a/apps/webgpu/webgpu_demo.cpp +++ b/apps/webgpu/webgpu_demo.cpp @@ -42,47 +42,47 @@ WebGPU concepts: - WGPUInstance The core interface through which all other objects are created. - + - WGPUAdapter Represents a physical device and is used to query its capabilities and limits. - + - WGPUDevice Represents a logical device we interact with. It is created by specifying the capabilities we require and fails if the adapter does not support them. We cannot create more resources than specified in the limits at creation. This is done to prevent a function from working on one device but not on another. - + - WGPUQueue The queue is where we submit the commands for the GPU to. Everything that is executed on the GPU goes through the queue. Examples are executing rendering or compute pipelines, writing to or reading from buffers. - + - WGPUSurface The surface we draw onto. How it is acquired depends on the OS so we have to manually convert window handles from the different platforms to surfaces. The surface has to be reconfigured every time the window size changes. - + - WGPUBuffer Represents a chunk of memory on the GPU. They can be used for example as vertex buffers, uniforms buffers or storage buffers. Buffers are created with a fixed size and usage flags that specify e.g. whether we can copy to this buffer or whether it is used as an index buffer. The memory for the buffer is automatically allocated at creation but has to be deallocated manually using wgpuBufferDestroy. wgpuQueueWriteBuffer is used to upload buffer data to the GPU. - + - WGPUTexture Represents pixel data in the memory of the GPU. It is similar to a buffer in that memory for it is allocated at creation, that it has usage flags and that the memory is deallocated using wgpuTextureDestroy. Data is uploaded using wgpuQueueWriteTexture. A texture additionally has a size, a format, a mip level count and a sample count. Textures can be used in shaders or as a render attachment (e.g. the depth buffer). Mip maps have to be created manually. - + - WGPUTextureView To use a texture, a texture view has to be created. It specifies which part of the texture is accessed (which base array layer, how many array layers, which base mip level, which format, ...). - + - WGPUTextureSampler To read a texture in a shader, a sampler is commonly used. Textures can also be accessed directly without a sampler by specifying texel coordinates, which is more like reading the data from a buffer. To get access to features like filtering, mipmapping or clamping, a sampler is used. - + - WGPURenderPipeline Represents a configuration of the GPU pipeline. It specifies completely how input data is transformed into output pixels by settings the configuration for the GPU pipeline stages. @@ -107,14 +107,14 @@ WebGPU concepts: A vertex buffer is specified using a WGPUVertexBufferLayout structure that contains a list of attributes (WGPUVertexAttribute) along with the stride and step mode (per vertex or per instance). Attributes are specified through a format, an offset in the data and a location in the shader. - The location is a number hard-coded for the attribute in the shader code. + The location is a number hard-coded for the attribute in the shader code. - primitive: WGPUPrimitiveState Configuration for the primitive assembly and rasterization stages. Specifies the topology (triangles, triangle strips, lines, ...), what index format is used, how the face orientation of triangles is defined and the cull mode. - depthStencil: WGPUDepthStencilState Configuration for the depth test and stencil test stages. - Specifies the format of the depth/stencil buffer and how depth and stencil testing are performed. + Specifies the format of the depth/stencil buffer and how depth and stencil testing are performed. - multisample: WGPUMultisampleState Configuration for multisampling. Specifies the number of samples per pixel as well as additional parameters for muiltisampling. @@ -139,15 +139,15 @@ WebGPU concepts: A list of layouts for bind groups. A bind group references its layout and they both have to have the same number of entries. The entries describe the binding, which shader stages can access it as well as additional info depending on the type. For example, buffer bind group layout entries specify whether they are a uniform - buffer, a storage buffer or read-only storage. + buffer, a storage buffer or read-only storage. - WGPUPipelineLayout Specifies the bind groups used in the pipeline. - + - WGPUCommandEncoder Work for the GPU has to be recorded into a command buffer. This is done using a command encoder. We call functions on the encoder (wgpuCommandEncoder*) to encode the commands into a buffer that can be accessed by - calling wgpuCommandEncoderFinish. + calling wgpuCommandEncoderFinish. - WGPUCommandBuffer When all the GPU commands are recorded, wgpuCommandEncoderFinish is called on the queue which returns a @@ -157,7 +157,7 @@ WebGPU concepts: Specifies how a render pipeline is executed. It encodes the pipeline used along with the required vertex buffers, index buffers, bind groups, drawing commands, etc. Accessing these render commands is done using a specialized object called a render pass encoder. It is created from a command encoder using - wgpuCommandEncoderBeginRenderPass. + wgpuCommandEncoderBeginRenderPass. -- WebGPU vs. Vulkan @@ -188,7 +188,7 @@ Here's a list of things I've noticed are handled differently from Vulkan (as of implementations. We currently use the wgpu-native implementation, which seems to catch all errors and prints most of the time a nice error message with a human-readable error message including the labels of the problematic objects, suggests fixes and even generates a stack trace. I'm not sure what the overhead of - this validation is, but I excpect there to be an option to turn it off in the future. + this validation is, but I excpect there to be an option to turn it off in the future. */ @@ -222,7 +222,7 @@ Here's a list of things I've noticed are handled differently from Vulkan (as of #include #define WEBGPU_DEMO_LOG(msg) std::cout << (msg) << std::endl - + #define WEBGPU_DEMO_CHECK(condition, errorMsg) \ if (!(condition)) \ { \ @@ -540,7 +540,12 @@ void initWebGPU(App& app) WGPURequestAdapterOptions adapterOptions = {}; - wgpuInstanceRequestAdapter(app.instance, &adapterOptions, handleAdapterRequest, &app.adapter); + wgpuInstanceRequestAdapter(app.instance, + &adapterOptions, + handleAdapterRequest, + &app.adapter); + WEBGPU_DEMO_CHECK(app.adapter, "[WebGPU] Failed to create adapter"); + WEBGPU_DEMO_LOG("[WebGPU] Adapter created"); WGPUSupportedLimits adapterLimits; wgpuAdapterGetLimits(app.adapter, &adapterLimits); From fb17c6dd2c8ef6914c1d7e7ac1915bd0ad02f46a Mon Sep 17 00:00:00 2001 From: Marino von Wattenwyl Date: Mon, 27 Nov 2023 15:48:13 +0100 Subject: [PATCH 063/108] [Emscripten] Print fatal errors to console --- modules/utils/source/Utils.cpp | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/modules/utils/source/Utils.cpp b/modules/utils/source/Utils.cpp index 2f06388b..3376cc1b 100644 --- a/modules/utils/source/Utils.cpp +++ b/modules/utils/source/Utils.cpp @@ -67,14 +67,6 @@ namespace fs = std::experimental::filesystem; using asio::ip::tcp; #endif -#ifdef __EMSCRIPTEN__ -// clang-format off -EM_JS(void, alert, (const char* string), { - alert(UTF8ToString(string)); -}) -// clang-format on -#endif - using std::fstream; namespace Utils @@ -1144,8 +1136,12 @@ void exitMsg(const char* tag, line, file); #elif defined(__EMSCRIPTEN__) - std::string message = "SLProject error"; - alert(message.c_str()); + std::cerr << "--------------------------------\n" + << "Fatal Error\n" + << "Tag: " << tag << '\n' + << "Location: " << file << ":" << line << '\n' + << "Message: " << msg << '\n' + << "--------------------------------" << std::endl; #else log(tag, "Exit %s at line %d in %s\n", From dc00d3def29c4618fcef50b877c4675e6aa7bb21 Mon Sep 17 00:00:00 2001 From: Marcus Hudritsch Date: Mon, 27 Nov 2023 17:18:34 +0100 Subject: [PATCH 064/108] Update SLTexFont.cpp --- modules/sl/source/SLTexFont.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/sl/source/SLTexFont.cpp b/modules/sl/source/SLTexFont.cpp index e6ed18ac..cf4d3c73 100644 --- a/modules/sl/source/SLTexFont.cpp +++ b/modules/sl/source/SLTexFont.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -82,7 +83,7 @@ according texture coordinate. void SLTexFont::create(SLstring fontFilename) { // Check the font filename with path - if (!Utils::fileExists(fontFilename)) + if (!SLFileStorage::exists(fontFilename, IOK_font)) { SLstring msg = "SLTexFont::create: File not found: " + fontFilename; SL_EXIT_MSG(msg.c_str()); From 379b3cf9eff4336f2c02622b3251ab731c2969df Mon Sep 17 00:00:00 2001 From: Marcus Hudritsch Date: Mon, 27 Nov 2023 18:25:16 +0100 Subject: [PATCH 065/108] Removed some warnings --- apps/source/CVCapture.cpp | 2 +- modules/utils/source/AverageTiming.cpp | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/apps/source/CVCapture.cpp b/apps/source/CVCapture.cpp index 8b2e0d16..289483d4 100644 --- a/apps/source/CVCapture.cpp +++ b/apps/source/CVCapture.cpp @@ -101,7 +101,7 @@ CVSize2i CVCapture::open(int deviceNum) Utils::log("SLProject", "Exception during OpenCV video capture creation: %s", e.what()); } #else - WebCameraFacing facing; + WebCameraFacing facing = WebCameraFacing::FRONT; if (_videoType == VT_MAIN) facing = WebCameraFacing::BACK; else if (_videoType == VT_SCND) diff --git a/modules/utils/source/AverageTiming.cpp b/modules/utils/source/AverageTiming.cpp index f0ec90aa..8929ec9f 100644 --- a/modules/utils/source/AverageTiming.cpp +++ b/modules/utils/source/AverageTiming.cpp @@ -133,7 +133,9 @@ void AverageTiming::doGetTimingMessage(char* m) { blocks.push_back(block.second); } - std::sort(blocks.begin(), blocks.end(), [](AverageTimingBlock* lhs, AverageTimingBlock* rhs) -> bool + std::sort(blocks.begin(), + blocks.end(), + [](AverageTimingBlock* lhs, AverageTimingBlock* rhs) -> bool { return lhs->posV < rhs->posV; }); // find reference time @@ -142,7 +144,8 @@ void AverageTiming::doGetTimingMessage(char* m) { refTime = (*blocks.begin())->val.average(); // insert number of measurement calls - snprintf(m + strlen(m), sizeof(m), "Num. calls: %i\n", (int)(*blocks.begin())->nCalls); + volatile int sizeofm = sizeof(m); // workaround against a warning in the next line + snprintf(m + strlen(m), sizeofm, "Num. calls: %i\n", (int)(*blocks.begin())->nCalls); } // calculate longest blockname From 824d4548ea68994ede998297403e9bf23695e2b0 Mon Sep 17 00:00:00 2001 From: Marcus Hudritsch Date: Tue, 28 Nov 2023 14:42:48 +0100 Subject: [PATCH 066/108] Improved cmake for ios --- apps/app_demo_slproject/ios/CMakeLists.txt | 6 ++---- scripts/generate_xcode_project_ios.sh | 0 scripts/generate_xcode_project_ios_hsm4.sh | 0 3 files changed, 2 insertions(+), 4 deletions(-) mode change 100644 => 100755 scripts/generate_xcode_project_ios.sh mode change 100644 => 100755 scripts/generate_xcode_project_ios_hsm4.sh diff --git a/apps/app_demo_slproject/ios/CMakeLists.txt b/apps/app_demo_slproject/ios/CMakeLists.txt index b406f078..8f41ab93 100644 --- a/apps/app_demo_slproject/ios/CMakeLists.txt +++ b/apps/app_demo_slproject/ios/CMakeLists.txt @@ -13,19 +13,17 @@ find_library(OPENGLES OpenGLES required) find_library(GLKIT GLKit required) find_library(CORELOCATION CoreLocation required) -set(target Demo-SL) +set(target AppDemoSLProject) #if this variable is not defined, we make automatic signing with "iPhone Developer" if(XCODE_CODESIGNIDENTITY) message(STATUS "manual signing") set(APP_BUNDLE_IDENTIFIER "ch.bfh.ti.cpvrlab.demo-sl") # <== Set to your app's bundle identifier (Attention the erlebar one is with "cpvr") - set(XCODE_PROVISIONINGPROFILE "Demo SL Development") set(XCODE_DEVELOPMENTTEAM "858Y9EWZ4B") else() message(STATUS "automatic signing") - #set(APP_BUNDLE_IDENTIFIER "com.${target}") # <== Set to your app's bundle identifier - + set(APP_BUNDLE_IDENTIFIER "com.${target}") # <== Set to your app's bundle identifier set(XCODE_CODESIGNIDENTITY "iPhone Developer") set(XCODE_DEVELOPMENTTEAM ${XCODE_DEVELOPMENTTEAM}) endif() diff --git a/scripts/generate_xcode_project_ios.sh b/scripts/generate_xcode_project_ios.sh old mode 100644 new mode 100755 diff --git a/scripts/generate_xcode_project_ios_hsm4.sh b/scripts/generate_xcode_project_ios_hsm4.sh old mode 100644 new mode 100755 From c6072b059ed1c463a3636976c7fc7905eb7193fb Mon Sep 17 00:00:00 2001 From: Marino von Wattenwyl Date: Thu, 30 Nov 2023 14:59:34 +0100 Subject: [PATCH 067/108] [WebGPU Demo] Fix crash in wgpuAdapterGetLimits --- apps/webgpu/webgpu_demo.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/webgpu/webgpu_demo.cpp b/apps/webgpu/webgpu_demo.cpp index d8059660..07db8f2f 100644 --- a/apps/webgpu/webgpu_demo.cpp +++ b/apps/webgpu/webgpu_demo.cpp @@ -547,7 +547,7 @@ void initWebGPU(App& app) WEBGPU_DEMO_CHECK(app.adapter, "[WebGPU] Failed to create adapter"); WEBGPU_DEMO_LOG("[WebGPU] Adapter created"); - WGPUSupportedLimits adapterLimits; + WGPUSupportedLimits adapterLimits = {}; wgpuAdapterGetLimits(app.adapter, &adapterLimits); // === Acquire a WebGPU device === From be31261f7ab3c2832e2e8b509a1420211dc81938 Mon Sep 17 00:00:00 2001 From: Marcus Hudritsch Date: Fri, 1 Dec 2023 13:56:30 +0100 Subject: [PATCH 068/108] Excluded WebGPU for Emscripten --- apps/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/CMakeLists.txt b/apps/CMakeLists.txt index d4889f7a..5f9a5172 100644 --- a/apps/CMakeLists.txt +++ b/apps/CMakeLists.txt @@ -10,7 +10,7 @@ else() add_subdirectory(app_demo_node) add_subdirectory(app_demo_slproject) - if (SL_BUILD_WEBGPU_DEMO) + if (SL_BUILD_WEBGPU_DEMO AND NOT ("${SYSTEM_NAME_UPPER}" MATCHES "EMSCRIPTEN")) add_subdirectory(webgpu) endif () endif() \ No newline at end of file From 37681e1aae8144047b7b3de2d4e333fb607f2fc8 Mon Sep 17 00:00:00 2001 From: Marcus Hudritsch Date: Thu, 7 Dec 2023 13:53:49 +0100 Subject: [PATCH 069/108] Fixed particle shaders for iOS --- apps/app_demo_slproject/source/AppDemoGui.cpp | 11 +- modules/sl/source/gl/SLGLProgramGenerated.cpp | 396 ++++++++---------- modules/sl/source/mesh/SLMesh.cpp | 4 +- modules/sl/source/mesh/SLParticleSystem.cpp | 100 ++--- modules/sl/source/mesh/SLParticleSystem.h | 21 +- modules/utils/externals/CMakeLists.txt | 42 ++ 6 files changed, 271 insertions(+), 303 deletions(-) diff --git a/apps/app_demo_slproject/source/AppDemoGui.cpp b/apps/app_demo_slproject/source/AppDemoGui.cpp index c9f40bbf..d74b83c1 100644 --- a/apps/app_demo_slproject/source/AppDemoGui.cpp +++ b/apps/app_demo_slproject/source/AppDemoGui.cpp @@ -3736,11 +3736,14 @@ void AppDemoGui::buildProperties(SLScene* s, SLSceneView* sv) ps->isGenerated(false); } - bool renderInstanced = ps->renderInstanced(); - if (ImGui::Checkbox("Instanced draw", &renderInstanced)) + if (SLGLState::instance()->glHasGeometryShaders()) { - ps->drawInstanced(renderInstanced); - ps->isGenerated(false); + bool drawInstanced = ps->drawInstanced(); + if (ImGui::Checkbox("Instanced draw", &drawInstanced)) + { + ps->drawInstanced(drawInstanced); + ps->isGenerated(false); + } } // TTL (Time to live) diff --git a/modules/sl/source/gl/SLGLProgramGenerated.cpp b/modules/sl/source/gl/SLGLProgramGenerated.cpp index 2eee9230..b86f0973 100644 --- a/modules/sl/source/gl/SLGLProgramGenerated.cpp +++ b/modules/sl/source/gl/SLGLProgramGenerated.cpp @@ -44,8 +44,8 @@ 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_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"( @@ -53,22 +53,21 @@ 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"( -uniform mat4 u_mMatrix; // Model matrix (object to world transform) -uniform mat4 u_vMatrix; // View matrix (world to camera transform) -uniform mat4 u_pMatrix; // Projection matrix (camera to normalize device coords.))"; +uniform mat4 u_mMatrix; // Model matrix (object to world transform) +uniform mat4 u_vMatrix; // View matrix (world to camera transform) +uniform mat4 u_pMatrix; // Projection matrix (camera to normalize device coords.))"; const string vertInput_u_matrix_vOmv = R"( -uniform mat4 u_vOmvMatrix; // view or modelview matrix)"; +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 +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"( @@ -87,8 +86,8 @@ 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 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"( @@ -141,7 +140,7 @@ vec3 colorByAge(float age) 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) { @@ -149,7 +148,6 @@ vec3 colorByAge(float age) vec3 color = vec3(u_colorArr[cachePos], u_colorArr[cachePos + 1], u_colorArr[cachePos + 2]); return color; })"; - //----------------------------------------------------------------------------- const string vertOutput_PS_struct_Begin = R"( @@ -168,41 +166,27 @@ const string vertOutput_PS_struct_texNum = R"( 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 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"( +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"( +const string fragMain_PS_instanced_c = R"( vec4 color = u_color; // Particle color)"; -const string fragMain_PS_instanced_transparency = R"( +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 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_tf_p = R"( @@ -224,34 +208,29 @@ const string vertOutput_PS_tf_initP = R"( out vec3 tf_initialPosition; // To transform feedback)"; //----------------------------------------------------------------------------- -const string vertMain_Begin = R"( +const string main_Begin = R"( +//----------------------------------------------------------------------------- void main() {)"; - -const string vertMain_instanced_Begin = R"( -void main() -{ - vec3 position = a_positionP; -)"; - -const string vertMain_v_P_VS = R"( +//----------------------------------------------------------------------------- +const string vertMain_v_P_VS = R"( mat4 mvMatrix = u_vMatrix * u_mMatrix; v_P_VS = vec3(mvMatrix * ${localPosition}); // vertex position in view space)"; -const string vertMain_v_P_WS_Sm = R"( +const string vertMain_v_P_WS_Sm = R"( v_P_WS = vec3(u_mMatrix * ${localPosition}); // vertex position in world space)"; -const string vertMain_v_N_VS = R"( +const string vertMain_v_N_VS = R"( mat3 invMvMatrix = mat3(inverse(mvMatrix)); mat3 nMatrix = transpose(invMvMatrix); v_N_VS = vec3(nMatrix * ${localNormal}); // vertex normal in view space)"; -const string vertMain_v_R_OS = R"( +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"( +const string vertMain_v_uv0 = R"( v_uv0 = a_uv0; // pass diffuse color tex.coord. 1 for interpolation)"; -const string vertMain_v_uv1 = R"( +const string vertMain_v_uv1 = R"( v_uv1 = a_uv1; // pass diffuse color tex.coord. 1 for interpolation)"; -const string vertMain_skinning = R"( +const string vertMain_skinning = R"( vec4 skinnedPosition; vec3 skinnedNormal; @@ -275,7 +254,7 @@ const string vertMain_skinning = R"( skinnedNormal = a_normal; } )"; -const string vertMain_skinning_Nm = R"( +const string vertMain_skinning_Nm = R"( vec4 skinnedPosition; vec3 skinnedNormal; vec4 skinnedTangent; @@ -302,7 +281,7 @@ const string vertMain_skinning_Nm = R"( skinnedTangent = a_tangent; } )"; -const string vertMain_TBN_Nm = R"( +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 @@ -330,144 +309,124 @@ const string vertMain_TBN_Nm = R"( //----------------------------------------------------------------------------- // Things that goes directly to geometry shader -const string vertMain_PS_v_t_default = R"( +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 else vert.transparency = 1.0;)"; -const string vertMain_PS_v_t_begin = R"( +const string vertMain_PS_v_t_begin = R"( if(age < 0.0) vert.transparency = 0.0; // To be discard, because the particle is to be born else { vert.transparency = age / u_tTL; // Get by the ratio age:lifetime)"; -const string vertMain_PS_v_t_linear = R"( +const string vertMain_PS_v_t_linear = R"( vert.transparency = 1.0 - vert.transparency; // Linear)"; -const string vertMain_PS_v_t_curve = R"( +const string vertMain_PS_v_t_curve = R"( vert.transparency = pow(vert.transparency,3.0) * u_al_bernstein.x + pow(vert.transparency,2.0) * u_al_bernstein.y + vert.transparency * u_al_bernstein.z + u_al_bernstein.w; // Get transparency by bezier curve)"; -const string vertMain_PS_v_t_end = R"( +const string vertMain_PS_v_t_end = R"( })"; -const string vertMain_PS_v_r = R"( +const string vertMain_PS_v_r = R"( vert.rotation = a_rotation;)"; -const string vertMain_PS_v_s = R"( +const string vertMain_PS_v_s = R"( vert.size = age / u_tTL;)"; -const string vertMain_PS_v_s_curve = R"( +const string vertMain_PS_v_s_curve = R"( vert.size = pow(vert.size,3.0) * u_si_bernstein.x + 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"( +const string vertMain_PS_v_doColorOverLT = R"( vert.color = colorByAge(age/u_tTL);)"; - -const string vertOutput_PS_age = R"( +const string vertOutput_PS_age = R"( out float v_age; // Age of a particle)"; - -const string fragInput_PS_age = R"( +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"( +const string vertMain_PS_v_texNum = R"( vert.texNum = a_texNum;)"; - -const string vertMain_PS_v_a = R"( +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"( +const string vertMain_PS_v_tC = R"( v_texCoord = 0.5 * (a_positionP.xy + vec2(1.0));)"; - -const string vertMain_PS_v_age = R"( +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; +const string vertMain_PS_v_tC_flipbook = R"( + int actCI = int(mod(float(a_texNum), float(u_col))); float actC = float(actCI); - float actR = float(actRI); + float actR = floor(float(int(a_texNum) - actCI) / float(u_col)); 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"( +const string vertMain_PS_instanced_position = R"( + vec3 position = a_positionP; +)"; +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_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"( +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"( +const string vertMain_PS_instanced_v_t_linear = R"( transparency = 1.0 - transparency; // Linear)"; -const string vertMain_PS_instanced_v_t_curve = R"( +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"( +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"( +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"( +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"( +const string vertMain_PS_EndAll = R"( // Modelview matrix multiplication with (particle position + particle generator position) // Calculate position in view space gl_Position = u_vOmvMatrix * vec4(a_position, 1); } )"; - const string vertMain_PS_EndAll_VertBillboard = R"( gl_Position = vec4(a_position, 1); } )"; - -const string vertMain_EndAll = R"( +const string vertMain_EndAll = R"( // pass the vertex w. the fix-function transform gl_Position = u_pMatrix * mvMatrix * ${localPosition}; @@ -475,9 +434,6 @@ const string vertMain_EndAll = R"( )"; //----------------------------------------------------------------------------- const string vertMain_PS_U_Begin = R"( - -void main() -{ vec4 P = vec4(a_position.xyz, 1.0); // Need to be here for the compilation gl_Position = P; // Need to be here for the compilation)"; const string vertMain_PS_U_v_init_p = R"( @@ -496,8 +452,7 @@ const string vertMain_PS_U_v_init_texNum = R"( tf_texNum = a_texNum; // Init the output variable)"; const string vertMain_PS_U_v_init_initP = R"( tf_initialPosition = a_initialPosition; // Init the output variable)"; - -const string vertMain_PS_U_bornDead = R"( +const string vertMain_PS_U_bornDead = R"( tf_startTime += u_difTime; // Add time to resume after frustum culling if( u_time >= tf_startTime ) @@ -505,51 +460,48 @@ const string vertMain_PS_U_bornDead = R"( float age = u_time - tf_startTime; // Get the age of the particle if( age > u_tTL) { )"; -const string vertMain_PS_U_reset_p = R"( +const string vertMain_PS_U_reset_p = R"( // The particle is past its lifetime, recycle. tf_position = u_pGPosition; // Reset position)"; -const string vertMain_PS_U_reset_shape_p = R"( +const string vertMain_PS_U_reset_shape_p = R"( // The particle is past its lifetime, recycle. tf_position = a_initialPosition + u_pGPosition; // Reset position)"; -const string vertMain_PS_U_reset_v = R"( +const string vertMain_PS_U_reset_v = R"( tf_velocity = a_initialVelocity; // Reset velocity)"; -const string vertMain_PS_U_reset_st_counterGap = R"( +const string vertMain_PS_U_reset_st_counterGap = R"( tf_startTime = u_time + (age - u_tTL); // Reset start time to actual time with counter gap)"; -const string vertMain_PS_U_reset_st = R"( +const string vertMain_PS_U_reset_st = R"( tf_startTime = u_time; // Reset start time to actual time)"; -const string vertMain_PS_U_alive_p = R"( +const string vertMain_PS_U_alive_p = R"( } else { // The particle is alive, update. tf_position += tf_velocity * u_deltaTime; // Scale the translation by the delta time)"; -const string vertMain_PS_U_v_rConst = R"( +const string vertMain_PS_U_v_rConst = R"( tf_rotation = mod(tf_rotation + (u_angularVelo*u_deltaTime), TWOPI);)"; -const string vertMain_PS_U_v_rRange = R"( +const string vertMain_PS_U_v_rRange = R"( tf_rotation = mod(tf_rotation + (tf_angularVelo*u_deltaTime), TWOPI);)"; -const string vertMain_PS_U_alive_a_const = R"( +const string vertMain_PS_U_alive_a_const = R"( tf_velocity += tf_initialVelocity * u_deltaTime * u_accConst; // Amplify the velocity)"; -const string vertMain_PS_U_alive_a_diffDir = R"( +const string vertMain_PS_U_alive_a_diffDir = R"( tf_velocity += u_deltaTime * u_acceleration; // Amplify the velocity)"; -const string vertMain_PS_U_alive_g = R"( +const string vertMain_PS_U_alive_g = R"( tf_velocity += u_deltaTime * u_gravity; // Apply gravity)"; -const string vertMain_PS_U_alive_texNum = R"( +const string vertMain_PS_U_alive_texNum = R"( if(u_condFB == 1) { tf_texNum++; // Increment to draw next texture (flipbook) tf_texNum = uint(mod(float(tf_texNum), float(u_col * u_row))); // Modulo to not exceed the max and reset })"; -const string vertMain_PS_U_EndAll = R"( +const string vertMain_PS_U_EndAll = R"( } } })"; - - //----------------------------------------------------------------------------- -const string geomConfig_PS = R"( +const string geomConfig_PS = R"( layout (points) in; // Primitives that we received from vertex shader layout (triangle_strip, max_vertices = 4) out; // Primitives that we will output and number of vertex that will be output)"; - const string geomInput_PS_struct_Begin = R"( in vertex { )"; const string geomInput_PS_struct_t = R"( @@ -566,33 +518,27 @@ const string geomInput_PS_struct_End = R"( } vert[]; )"; //----------------------------------------------------------------------------- const string geomInput_u_matrix_p = R"( -uniform mat4 u_pMatrix; // Projection matrix)"; +uniform mat4 u_pMatrix; // Projection matrix)"; const string geomInput_u_matrix_vertBillboard = R"( -uniform mat4 u_vYawPMatrix; // Projection matrix)"; +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)"; +uniform float u_scale; // Particle scale +uniform float u_radiusW; // Particle width radius) +uniform float u_radiusH; // Particle height radius)"; const string geomInput_PS_u_c = R"( -uniform vec4 u_color; // Particle color)"; +uniform vec4 u_color; // Particle color)"; const string geomInput_PS_u_col = R"( -uniform int u_col; // Number of column of flipbook texture)"; +uniform int u_col; // Number of column of flipbook texture)"; const string geomInput_PS_u_row = R"( -uniform int u_row; // Number of row of flipbook texture)"; +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)"; +out vec4 v_particleColor; // The resulting color per vertex)"; const string geomOutput_PS_v_tC = R"( -out vec2 v_texCoord; // Texture coordinate at vertex)"; - +out vec2 v_texCoord; // Texture coordinate at vertex)"; const string vertOutput_PS_v_tC = R"( -out vec2 v_texCoord; // Texture coordinate at vertex)"; +out vec2 v_texCoord; // Texture coordinate at vertex)"; //----------------------------------------------------------------------------- -const string geomMain_PS_Begin = R"( - -void main() -{)"; - const string geomMain_PS_v_s = R"( float scale = u_scale;)"; const string geomMain_PS_v_sS = R"( @@ -816,42 +762,38 @@ const string geomMain_PS_EndAll = R"( } )"; //----------------------------------------------------------------------------- const string fragInput_v_P_VS = R"( -in vec3 v_P_VS; // Interpol. point of illumination in view space (VS))"; +in vec3 v_P_VS; // Interpol. point of illumination in view space (VS))"; const string fragInput_v_P_WS = R"( -in vec3 v_P_WS; // Interpol. point of illumination in world space (WS))"; +in vec3 v_P_WS; // Interpol. point of illumination in world space (WS))"; const string fragInput_v_N_VS = R"( -in vec3 v_N_VS; // Interpol. normal at v_P_VS in view space)"; +in vec3 v_N_VS; // Interpol. normal at v_P_VS in view space)"; const string fragInput_v_R_OS = R"( -in vec3 v_R_OS; // Interpol. reflect in object space)"; +in vec3 v_R_OS; // Interpol. reflect in object space)"; const string fragInput_v_uv0 = R"( -in vec2 v_uv0; // Texture coordinate varying)"; +in vec2 v_uv0; // Texture coordinate varying)"; const string fragInput_v_uv1 = R"( -in vec2 v_uv1; // Texture coordinate varying)"; +in vec2 v_uv1; // Texture coordinate varying)"; const string fragInput_v_lightVecTS = R"( in vec3 v_eyeDirTS; // Vector to the eye in tangent space in vec3 v_lightDirTS[NUM_LIGHTS]; // Vector to light 0 in tangent space in vec3 v_spotDirTS[NUM_LIGHTS]; // Spot direction in tangent space)"; //----------------------------------------------------------------------------- const string fragInput_PS_v_pC = R"( -in vec4 v_particleColor; // interpolated color from the geometry shader)"; +in vec4 v_particleColor; // interpolated color from the geometry shader)"; const string fragInput_PS_v_tC = R"( -in vec2 v_texCoord; // interpolated texture coordinate)"; +in vec2 v_texCoord; // interpolated texture coordinate)"; //----------------------------------------------------------------------------- const string fragInput_PS_u_overG = R"( -uniform float u_oneOverGamma; // 1.0f / Gamma correction value)"; +uniform float u_oneOverGamma; // 1.0f / Gamma correction value)"; const string fragInput_PS_u_wireFrame = R"( -uniform bool u_doWireFrame; // Boolean for wireFrame)"; +uniform bool u_doWireFrame; // Boolean for wireFrame)"; //----------------------------------------------------------------------------- const string fragMain_PS_TF = R"( -out vec4 o_fragColor; // output fragment color - -void main() -{ o_fragColor = vec4(0,0,0,0); // Need to be here for the compilation } )"; //----------------------------------------------------------------------------- -const string fragMain_PS = R"( +const string fragMain_PS = R"( // Just set the interpolated color from the vertex shader o_fragColor = v_particleColor; @@ -862,13 +804,7 @@ const string fragMain_PS = R"( if(o_fragColor.a < 0.001) discard; )"; - -const string fragMain_PS_begin = R"( -void main() -{ -)"; - -const string fragMain_PS_withoutColor = R"( +const string fragMain_PS_withoutColor = R"( // componentwise multiply w. texture color if(!u_doWireFrame) o_fragColor = texture(u_matTextureDiffuse0, v_texCoord); @@ -880,7 +816,6 @@ const string fragMain_PS_withoutColor = R"( if(o_fragColor.a < 0.001) discard; )"; - const string fragMain_PS_instanced_withoutColor = R"( // componentwise multiply w. texture color if(!u_doWireFrame) @@ -893,8 +828,7 @@ const string fragMain_PS_instanced_withoutColor = R"( if(o_fragColor.a < 0.001) discard; )"; - -const string fragMain_instanced_PS_end = R"( +const string fragMain_instanced_PS_end = R"( // Just set the interpolated color from the vertex shader o_fragColor = color; // componentwise multiply w. texture color @@ -904,8 +838,7 @@ const string fragMain_instanced_PS_end = R"( if(o_fragColor.a < 0.001) discard; )"; - -const string fragMain_PS_endAll = R"( +const string fragMain_PS_endAll = R"( //Same color for each wireframe if(u_doWireFrame) o_fragColor = vec4(0,0,0,1.0); @@ -913,7 +846,7 @@ const string fragMain_PS_endAll = R"( o_fragColor.rgb = pow(o_fragColor.rgb, vec3(u_oneOverGamma)); })"; //----------------------------------------------------------------------------- -const string fragInput_u_lightAll = R"( +const string fragInput_u_lightAll = R"( uniform bool u_lightIsOn[NUM_LIGHTS]; // flag if light is on uniform vec4 u_lightPosVS[NUM_LIGHTS]; // position of light in view space @@ -929,52 +862,52 @@ uniform bool u_lightDoAtt[NUM_LIGHTS]; // flag if att. must be calc uniform vec4 u_globalAmbi; // Global ambient scene color uniform float u_oneOverGamma; // 1.0f / Gamma correction value )"; -const string fragInput_u_matBlinnAll = R"( +const string fragInput_u_matBlinnAll = R"( 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 float u_matShin; // shininess exponent )"; -const string fragInput_u_matAmbi = R"( +const string fragInput_u_matAmbi = R"( uniform vec4 u_matAmbi; // ambient color reflection coefficient (ka))"; -const string fragInput_u_matDiff = R"( +const string fragInput_u_matDiff = R"( uniform vec4 u_matDiff; // diffuse color reflection coefficient (kd))"; -const string fragInput_u_matEmis = R"( +const string fragInput_u_matEmis = R"( uniform vec4 u_matEmis; // emissive color (ke))"; -const string fragInput_u_matRough = R"( +const string fragInput_u_matRough = R"( uniform float u_matRough; // roughness factor (0-1))"; -const string fragInput_u_matMetal = R"( +const string fragInput_u_matMetal = R"( uniform float u_matMetal; // metalness factor (0-1)"; //----------------------------------------------------------------------------- -const string fragInput_u_matTexDm = R"( +const string fragInput_u_matTexDm = R"( uniform sampler2D u_matTextureDiffuse0; // Diffuse color map)"; -const string fragInput_u_matTexNm = R"( +const string fragInput_u_matTexNm = R"( uniform sampler2D u_matTextureNormal0; // Normal bump map)"; -const string fragInput_u_matTexEm = R"( +const string fragInput_u_matTexEm = R"( uniform sampler2D u_matTextureEmissive0; // PBR material emissive texture)"; -const string fragInput_u_matTexOm = R"( +const string fragInput_u_matTexOm = R"( uniform sampler2D u_matTextureOcclusion0; // Ambient occlusion map)"; -const string fragInput_u_matTexRm = R"( +const string fragInput_u_matTexRm = R"( uniform sampler2D u_matTextureRoughness0; // PBR material roughness texture)"; -const string fragInput_u_matTexMm = R"( +const string fragInput_u_matTexMm = R"( uniform sampler2D u_matTextureMetallic0; // PBR material metallic texture)"; -const string fragInput_u_matTexRmMm = R"( +const string fragInput_u_matTexRmMm = R"( uniform sampler2D u_matTextureRoughMetal0; // PBR material roughness-metallic texture)"; -const string fragInput_u_matTexOmRmMm = R"( +const string fragInput_u_matTexOmRmMm = R"( uniform sampler2D u_matTextureOccluRoughMetal0; // PBR material occlusion-roughness-metalic texture)"; -const string fragInput_u_matGetsSm = R"( +const string fragInput_u_matGetsSm = R"( uniform bool u_matGetsShadows; // flag if material receives shadows)"; -const string fragInput_u_skyCookEnvMaps = R"( -uniform samplerCube u_skyIrradianceCubemap; // PBR skybox irradiance light -uniform samplerCube u_skyRoughnessCubemap; // PBR skybox cubemap for rough reflections -uniform sampler2D u_skyBrdfLutTexture; // PBR lighting lookup table for BRDF -uniform float u_skyExposure; // PBR skybox exposure)"; +const string fragInput_u_skyCookEnvMaps = R"( +uniform samplerCube u_skyIrradianceCubemap; // PBR skybox irradiance light +uniform samplerCube u_skyRoughnessCubemap; // PBR skybox cubemap for rough reflections +uniform sampler2D u_skyBrdfLutTexture; // PBR lighting lookup table for BRDF +uniform float u_skyExposure; // PBR skybox exposure)"; //----------------------------------------------------------------------------- const string fragInput_u_cam = R"( -uniform int u_camProjType; // type of stereo +uniform int u_camProjType; // type of stereo uniform int u_camStereoEye; // -1=left, 0=center, 1=right uniform mat3 u_camStereoColors; // color filter matrix uniform bool u_camFogIsOn; // flag if fog is on @@ -1318,10 +1251,6 @@ void doColoredShadows(in vec3 N) } })"; //----------------------------------------------------------------------------- -const string fragMain_Begin = R"( -//----------------------------------------------------------------------------- -void main() -{)"; const string fragMain_0_Intensities = R"( vec4 Ia = vec4(0.0); // Accumulated ambient light intensity at v_P_VS vec4 Id = vec4(0.0); // Accumulated diffuse light intensity at v_P_VS @@ -1717,8 +1646,6 @@ const string fragMainCook_3_FragColorSky = R"( )"; //----------------------------------------------------------------------------- const string fragMainVideoBkgd = R"( -void main() -{ float x = (gl_FragCoord.x - u_camBkgdLeft) / u_camBkgdWidth; float y = (gl_FragCoord.y - u_camBkgdBottom) / u_camBkgdHeight; @@ -1972,7 +1899,9 @@ 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, bool renderInstanced) +void SLGLProgramGenerated::buildProgramCodePS(SLMaterial* mat, + bool isDrawProg, + bool renderInstanced) { if (mat->name() == "IBLMat") { @@ -2045,7 +1974,7 @@ void SLGLProgramGenerated::buildPerPixCook(SLMaterial* mat, SLVLight* lights) if (Nm) vertCode += vertOutput_v_lightVecTS; // Vertex shader main loop - vertCode += vertMain_Begin; + vertCode += main_Begin; if (skinning) vertCode += Nm ? vertMain_skinning_Nm : vertMain_skinning; vertCode += vertMain_v_P_VS; if (Sm) vertCode += vertMain_v_P_WS_Sm; @@ -2103,7 +2032,7 @@ void SLGLProgramGenerated::buildPerPixCook(SLMaterial* mat, SLVLight* lights) if (Sm) fragCode += fragFunctionDoColoredShadows; // Fragment shader main loop - fragCode += fragMain_Begin; + fragCode += main_Begin; fragCode += fragMain_0_Intensities; fragCode += Nm ? fragMain_1_EN_in_TS : fragMain_1_EN_in_VS; fragCode += Dm ? fragMainCook_1_matDiff_Dm : fragMainCook_1_matDiff; @@ -2170,7 +2099,7 @@ void SLGLProgramGenerated::buildPerPixBlinn(SLMaterial* mat, SLVLight* lights) if (Nm) vertCode += vertOutput_v_lightVecTS; // Vertex shader main loop - vertCode += vertMain_Begin; + vertCode += main_Begin; if (skinning) vertCode += Nm ? vertMain_skinning_Nm : vertMain_skinning; vertCode += vertMain_v_P_VS; if (Sm) vertCode += vertMain_v_P_WS_Sm; @@ -2222,7 +2151,7 @@ void SLGLProgramGenerated::buildPerPixBlinn(SLMaterial* mat, SLVLight* lights) if (Sm) fragCode += fragFunctionDoColoredShadows; // Fragment shader main loop - fragCode += fragMain_Begin; + fragCode += main_Begin; fragCode += fragMain_0_Intensities; fragCode += Nm ? fragMain_1_EN_in_TS : fragMain_1_EN_in_VS; fragCode += Em ? fragMain_1_matEmis_Em : fragMain_1_matEmis; @@ -2267,14 +2196,14 @@ void SLGLProgramGenerated::buildPerPixParticleInstanced(SLMaterial* mat) // Vertex shader inputs vertCode += vertInput_PS_a_positionP; - vertCode += vertInput_PS_a_p; //position - vertCode += vertInput_PS_a_st; // start time + 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_a_texNum; // per particle texture number vertCode += vertInput_PS_u_col; vertCode += vertInput_PS_u_row; - vertCode += vertInput_PS_a_texNum; // per particle texture number } // Vertex shader uniforms @@ -2297,7 +2226,8 @@ void SLGLProgramGenerated::buildPerPixParticleInstanced(SLMaterial* mat) if (CoOvLi) vertCode += vertOutput_PS_age; // Vertex shader main loop - vertCode += vertMain_instanced_Begin; + vertCode += main_Begin; + vertCode += vertMain_PS_instanced_position; vertCode += vertMain_PS_v_a; if (FlBoTex) vertCode += vertMain_PS_v_tC_flipbook; @@ -2335,7 +2265,8 @@ void SLGLProgramGenerated::buildPerPixParticleInstanced(SLMaterial* mat) fragCode += fragInput_PS_v_tC; if (Co && !CoOvLi) fragCode += fragInput_PS_u_c; if (CoOvLi) - { fragCode += fragInput_PS_u_tTL; + { + fragCode += fragInput_PS_u_tTL; fragCode += fragInput_PS_age; fragCode += fragInput_PS_u_colorOvLF; } @@ -2352,8 +2283,7 @@ void SLGLProgramGenerated::buildPerPixParticleInstanced(SLMaterial* mat) if (CoOvLi) fragCode += fragFunction_PS_ColorOverLT; // Fragment shader main loop - fragCode += fragMain_PS_begin; - + fragCode += main_Begin; if (Co && !CoOvLi) fragCode += fragMain_PS_instanced_c; @@ -2424,7 +2354,7 @@ void SLGLProgramGenerated::buildPerPixParticle(SLMaterial* mat) if (Co && CoOvLi) vertCode += vertFunction_PS_ColorOverLT; // Vertex shader main loop - vertCode += vertMain_Begin; + vertCode += main_Begin; vertCode += vertMain_PS_v_a; if (AlOvLi) vertCode += vertMain_PS_v_t_begin; @@ -2479,7 +2409,7 @@ void SLGLProgramGenerated::buildPerPixParticle(SLMaterial* mat) geomCode += geomOutput_PS_v_tC; // geometry shader main loop - geomCode += geomMain_PS_Begin; + geomCode += main_Begin; geomCode += geomMain_PS_v_s; if (SiOvLi) geomCode += geomMain_PS_v_sS; geomCode += geomMain_PS_v_rad; @@ -2519,7 +2449,7 @@ void SLGLProgramGenerated::buildPerPixParticle(SLMaterial* mat) fragCode += fragOutputs_o_fragColor; // Fragment shader main loop - fragCode += fragMain_PS_begin; + fragCode += main_Begin; fragCode += Co ? fragMain_PS : fragMain_PS_withoutColor; fragCode += fragMain_PS_endAll; @@ -2578,6 +2508,7 @@ void SLGLProgramGenerated::buildPerPixParticleUpdate(SLMaterial* mat) if (rot) vertCode += vertConstant_PS_pi; // Add constant PI // Vertex shader main loop + vertCode += main_Begin; vertCode += vertMain_PS_U_Begin; vertCode += vertMain_PS_U_v_init_p; vertCode += vertMain_PS_U_v_init_v; @@ -2605,6 +2536,8 @@ void SLGLProgramGenerated::buildPerPixParticleUpdate(SLMaterial* mat) fragCode += shaderHeader(); // Fragment shader inputs + fragCode += fragOutputs_o_fragColor; + fragCode += main_Begin; fragCode += fragMain_PS_TF; addCodeToShader(_shaders[1], fragCode, _name + ".frag"); @@ -2624,7 +2557,7 @@ void SLGLProgramGenerated::buildPerPixVideoBkgdSm(SLVLight* lights) vertCode += vertOutput_v_P_VS; vertCode += vertOutput_v_P_WS; vertCode += vertOutput_v_N_VS; - vertCode += vertMain_Begin; + vertCode += main_Begin; vertCode += vertMain_v_P_VS; vertCode += vertMain_v_P_WS_Sm; vertCode += vertMain_v_N_VS; @@ -2656,6 +2589,7 @@ in vec3 v_N_VS; // Interpol. normal at v_P_VS in view space fragCode += fragFunctionDoStereoSeparation; fragCode += fragFunctionShadowTest(lights); fragCode += fragFunctionDoColoredShadows; + fragCode += main_Begin; fragCode += fragMainVideoBkgd; fragCode += fragMain_5_FogGammaStereo; addCodeToShader(_shaders[1], fragCode, _name + ".frag"); diff --git a/modules/sl/source/mesh/SLMesh.cpp b/modules/sl/source/mesh/SLMesh.cpp index 7ebd30b7..22622312 100644 --- a/modules/sl/source/mesh/SLMesh.cpp +++ b/modules/sl/source/mesh/SLMesh.cpp @@ -454,7 +454,9 @@ void SLMesh::draw(SLSceneView* sv, SLNode* node, SLuint instances) sp->uniform1i("u_skinningEnabled", skinningEnabled); if (skinningEnabled && !_jointMatrices.empty()) - sp->uniformMatrix4fv("u_jointMatrices", _jointMatrices.size(), (SLfloat*)&_jointMatrices[0]); + sp->uniformMatrix4fv("u_jointMatrices", + (SLsizei)_jointMatrices.size(), + (SLfloat*)&_jointMatrices[0]); } SLint locTM = sp->getUniformLocation("u_tMatrix"); diff --git a/modules/sl/source/mesh/SLParticleSystem.cpp b/modules/sl/source/mesh/SLParticleSystem.cpp index 07bcb9a5..37d9d0e7 100644 --- a/modules/sl/source/mesh/SLParticleSystem.cpp +++ b/modules/sl/source/mesh/SLParticleSystem.cpp @@ -30,25 +30,17 @@ SLParticleSystem::SLParticleSystem(SLAssetManager* assetMgr, SLGLTexture* texC, const SLstring& name, SLGLTexture* texFlipbook, - const bool renderInstanced) : SLMesh(assetMgr, name) + const bool drawInstanced) : SLMesh(assetMgr, name) { assert(!name.empty()); // To be added to constructor - _assetManager = assetMgr; + _assetManager = assetMgr; + _drawInstanced = SLGLState::instance()->glHasGeometryShaders() ? drawInstanced : true; + _primitive = PT_points; - 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 + P.push_back(SLVec3f(0, 0, 0)); // Trick SL project because it wants mesh to have vertex I32.push_back(0); if (amount > UINT_MAX) // Need to change for number of floats @@ -64,7 +56,6 @@ SLParticleSystem::SLParticleSystem(SLAssetManager* assetMgr, _updateTime.init(60, 0.0f); _drawTime.init(60, 0.0f); - } //----------------------------------------------------------------------------- //! Function which return a position in a sphere @@ -176,7 +167,7 @@ SLVec3f SLParticleSystem::getPointInCone() { float y = 0.0f; float radius = _shapeRadius; - if (!_doShapeSpawnBase) // Spawn inside volume + if (!_doShapeSpawnBase) // Spawn inside volume { y = Utils::random(0.0f, _shapeHeight); // NEED TO HAVE MORE value near 1 when we have smaller base that top radius = _shapeRadius + tan(_shapeAngle * DEG2RAD) * y; @@ -194,7 +185,7 @@ SLVec3f SLParticleSystem::getPointOnCone() { float y = 0.0f; float radius = _shapeRadius; - if (!_doShapeSpawnBase) // Spawn inside volume + if (!_doShapeSpawnBase) // Spawn inside volume { y = Utils::random(0.0f, _shapeHeight); // NEED TO HAVE MORE value near 1 when we have smaller base that top radius = _shapeRadius + tan(_shapeAngle * DEG2RAD) * y; @@ -319,7 +310,7 @@ void SLParticleSystem::generate() SLVuint tempTexNum; SLVVec3f tempInitP; - if (_renderInstanced) + if (_drawInstanced) _primitive = PT_triangles; else _primitive = PT_points; @@ -357,7 +348,7 @@ void SLParticleSystem::generate() { if (_doShape && _shapeType == ST_Sphere) // Position in or on sphere { - if (!_doShapeSurface) // In volume + if (!_doShapeSurface) // In volume tempP[i] = getPointInSphere(_shapeRadius, SLVec3f(distribution(generator), distribution(generator), @@ -386,19 +377,19 @@ void SLParticleSystem::generate() else // Position is not a volume, spawn from start point (particle emitter position) tempP[i] = SLVec3f(0, 0, 0); - if (!_doDirectionSpeed) // Use normal velocity + if (!_doDirectionSpeed) // Use normal velocity { - if (_velocityType == 0) // Random value + if (_velocityType == 0) // Random value { tempV[i].x = Utils::random(_velocityRndMin.x, _velocityRndMax.x); // Random value for x velocity tempV[i].y = Utils::random(_velocityRndMin.y, _velocityRndMax.y); // Random value for y velocity tempV[i].z = Utils::random(_velocityRndMin.z, _velocityRndMax.z); // Random value for z velocity } - else if (_velocityType == 1) // Constant + else if (_velocityType == 1) // Constant { - tempV[i].x = _velocityConst.x; // Constant value for x velocity - tempV[i].y = _velocityConst.y; // Constant value for y velocity - tempV[i].z = _velocityConst.z; // Constant value for z velocity + tempV[i].x = _velocityConst.x; // Constant value for x velocity + tempV[i].y = _velocityConst.y; // Constant value for y velocity + tempV[i].z = _velocityConst.z; // Constant value for z velocity } } else // DO direction and speed @@ -427,16 +418,16 @@ void SLParticleSystem::generate() // When the first particle dies the last one begin to live tempST[i] = GlobalTimer::timeS() + ((float)i * (_timeToLive / (float)_amount)); // Time to start - if (_doAcceleration || _doGravity) // Acceleration + if (_doAcceleration || _doGravity) // Acceleration tempInitV[i] = tempV[i]; - if (_doRotation) // Rotation (constant angular velocity) - tempR[i] = Utils::random(0.0f * DEG2RAD, 360.0f * DEG2RAD); // Start rotation of the particle - if (_doRotation && _doRotRange) // Random angular velocity for each particle + if (_doRotation) // Rotation (constant angular velocity) + tempR[i] = Utils::random(0.0f * DEG2RAD, 360.0f * DEG2RAD); // Start rotation of the particle + if (_doRotation && _doRotRange) // Random angular velocity for each particle tempAngulareVelo[i] = Utils::random(_angularVelocityRange.x * DEG2RAD, - _angularVelocityRange.y * DEG2RAD); // Start rotation of the particle - if (_doFlipBookTexture) // Flipbook texture + _angularVelocityRange.y * DEG2RAD); // Start rotation of the particle + if (_doFlipBookTexture) // Flipbook texture tempTexNum[i] = Utils::random(0, _flipbookRows * _flipbookColumns - 1); - if (_doShape) // Shape feature + if (_doShape) // Shape feature tempInitP[i] = tempP[i]; } @@ -477,7 +468,7 @@ void SLParticleSystem::generate() _vao2.setAttrib(AT_initialPosition, AT_initialPosition, &tempInitP); _vao2.generateTF((SLuint)tempP.size()); - if (_renderInstanced) + if (_drawInstanced) { P.clear(); I32.clear(); @@ -494,7 +485,6 @@ void SLParticleSystem::generate() I32.push_back(3); I32.push_back(0); - _renderVao1.deleteGL(); _renderVao2.deleteGL(); /* Generate vao for rendering with draw instanced */ @@ -600,9 +590,7 @@ void SLParticleSystem::draw(SLSceneView* sv, SLNode* node, SLuint instances) // Generate programs //////////////////// if (!_mat->program() || !_mat->programTF()) - { - _mat->generateProgramPS(_renderInstanced); - } + _mat->generateProgramPS(_drawInstanced); //////////////////////////////////////////////// // Calculate time and paused and frustum culling @@ -621,17 +609,17 @@ void SLParticleSystem::draw(SLSceneView* sv, SLNode* node, SLuint instances) _notVisibleTimeS = 0.0f; // No more culling, the difference time has been applied, no further need _lastTimeBeforePauseS = 0.0f; // No more paused, the difference time has been applied, no further need } - else if (!_isVisibleInFrustum) // If particle system was not visible, this one is called just once when the particle is draw again (Do nothing if paused, because update call is not done) + else if (!_isVisibleInFrustum) // If particle system was not visible, this one is called just once when the particle is draw again (Do nothing if paused, because update call is not done) { _isVisibleInFrustum = true; difTime = GlobalTimer::timeS() - _notVisibleTimeS; // Use time since the particle system was not visible // maybe add later average delta time (because maybe bug when fast not visible long time, visible, not visible, visible - deltaTime = _deltaTimeUpdateS; // Last delta time, because when culled draw is not called therefore the actual delta time will be too big - if (_lastTimeBeforePauseS > _notVisibleTimeS) // If was paused when not visible. Need to take _notVisibleTimeS because it's since this value that the particle system is not drew. - _lastTimeBeforePauseS = _notVisibleTimeS; // Get the value of since the particle system is not drew - _notVisibleTimeS = 0.0f; // No more culling, the difference time has been applied, no further need + deltaTime = _deltaTimeUpdateS; // Last delta time, because when culled draw is not called therefore the actual delta time will be too big + if (_lastTimeBeforePauseS > _notVisibleTimeS) // If was paused when not visible. Need to take _notVisibleTimeS because it's since this value that the particle system is not drew. + _lastTimeBeforePauseS = _notVisibleTimeS; // Get the value of since the particle system is not drew + _notVisibleTimeS = 0.0f; // No more culling, the difference time has been applied, no further need } - else if (!_isPaused && _lastTimeBeforePauseS != 0.0f) // If particle system was resumed + else if (!_isPaused && _lastTimeBeforePauseS != 0.0f) // If particle system was resumed { difTime = GlobalTimer::timeS() - _lastTimeBeforePauseS; // Use time since the particle system was paused _lastTimeBeforePauseS = 0.0f; // No more paused, the difference time has been applied, no further need @@ -726,7 +714,7 @@ void SLParticleSystem::draw(SLSceneView* sv, SLNode* node, SLuint instances) _vao1.beginTF(_vao2.tfoID()); _vao1.drawArrayAs(PT_points); _vao1.endTF(); - if (_renderInstanced) + if (_drawInstanced) _vao = _renderVao1; else _vao = _vao2; @@ -736,7 +724,7 @@ void SLParticleSystem::draw(SLSceneView* sv, SLNode* node, SLuint instances) _vao2.beginTF(_vao1.tfoID()); _vao2.drawArrayAs(PT_points); _vao2.endTF(); - if (_renderInstanced) + if (_drawInstanced) _vao = _renderVao2; else _vao = _vao1; @@ -888,8 +876,8 @@ void SLParticleSystem::draw(SLSceneView* sv, SLNode* node, SLuint instances) stateGL->blendFunc(GL_SRC_ALPHA, GL_ONE); /////////////////////// - if (_renderInstanced) - SLMesh::draw(sv, node, 2*_amount); //2 triangles per particle + if (_drawInstanced) + SLMesh::draw(sv, node, 2 * _amount); //2 triangles per particle else SLMesh::draw(sv, node); /////////////////////// @@ -904,7 +892,6 @@ void SLParticleSystem::draw(SLSceneView* sv, SLNode* node, SLuint instances) _drawBuf = 1 - _drawBuf; } - /*! Change the current use texture, this will switch between the normal texture and the flipbook texture (and vice versa) @@ -923,7 +910,6 @@ void SLParticleSystem::changeTexture() } } - //----------------------------------------------------------------------------- //! deleteData deletes all mesh data and VAOs void SLParticleSystem::deleteData() @@ -996,7 +982,7 @@ void SLParticleSystem::buildAABB(SLAABBox& aabb, const SLMat4f& wmNode) minP = SLVec3f(-radius, -0.0, -radius); if (!_doShapeSpawnBase) // Spawn inside volume maxP = SLVec3f(radius, _shapeHeight, radius); - else // Spawn base volume + else // Spawn base volume maxP = SLVec3f(radius, 0.0f, radius); if (_doDirectionSpeed && _doShapeOverride) { @@ -1012,7 +998,7 @@ void SLParticleSystem::buildAABB(SLAABBox& aabb, const SLMat4f& wmNode) minP = SLVec3f(-radius, -0.0, -radius); if (!_doShapeSpawnBase) // Spawn inside volume maxP = SLVec3f(radius, _shapeHeight, radius); - else // Spawn base volume + else // Spawn base volume maxP = SLVec3f(radius, 0.0f, radius); if (_doDirectionSpeed && _doShapeOverride) @@ -1027,7 +1013,7 @@ void SLParticleSystem::buildAABB(SLAABBox& aabb, const SLMat4f& wmNode) if (_doAcceleration || _doGravity) // If acceleration or gravity is enabled { - if (!_doDirectionSpeed) // If direction is not enable + if (!_doDirectionSpeed) // If direction is not enable { if (_velocityType == 0) { @@ -1101,13 +1087,13 @@ void SLParticleSystem::buildAABB(SLAABBox& aabb, const SLMat4f& wmNode) maxP.x -= maxV.x * (_timeToLive + timeForXGrav); // I remove the position with the velocity that it will not do because it go against the velocity (becareful! here timeForXGrav is negative) else if (timeForXGrav > 0.0f) // If the gravity go with the velocity maxP.x += 0.5f * _gravity.x * (_timeToLive * _timeToLive); - if (timeForYGrav < 0.0f) // If the gravity go against the velocity + if (timeForYGrav < 0.0f) // If the gravity go against the velocity maxP.y -= maxV.y * (_timeToLive + timeForYGrav); - else if (timeForYGrav > 0.0f) // If the gravity go with the velocity + else if (timeForYGrav > 0.0f) // If the gravity go with the velocity maxP.y += 0.5f * _gravity.y * (_timeToLive * _timeToLive); - if (timeForZGrav < 0.0f) // If the gravity go against the velocity + if (timeForZGrav < 0.0f) // If the gravity go against the velocity maxP.z -= maxV.z * (_timeToLive + timeForZGrav); - else if (timeForZGrav > 0.0f) // If the gravity go with the velocity + else if (timeForZGrav > 0.0f) // If the gravity go with the velocity maxP.z += 0.5f * _gravity.z * (_timeToLive * _timeToLive); // Time remaining after the gravity has nullified the velocity for the particle to die (for each axes) @@ -1124,11 +1110,11 @@ void SLParticleSystem::buildAABB(SLAABBox& aabb, const SLMat4f& wmNode) } // ACCELERATION (Need to rework to work like gravity) - if (_doAcceleration && _doAccDiffDir) // Need to be rework ( for negative value) + if (_doAcceleration && _doAccDiffDir) // Need to be rework ( for negative value) { maxP += 0.5f * _acceleration * (_timeToLive * _timeToLive); // Apply acceleration after time } - else if (_doAcceleration && !_doAccDiffDir) // Need to be rework + else if (_doAcceleration && !_doAccDiffDir) // Need to be rework { // minP += 0.5f * _accelerationConst * (_timeToLive * _timeToLive); //Apply constant acceleration maxP += 0.5f * _accelerationConst * maxV * (_timeToLive * _timeToLive); // Apply constant acceleration //Not good diff --git a/modules/sl/source/mesh/SLParticleSystem.h b/modules/sl/source/mesh/SLParticleSystem.h index 17fcb340..fceffcc2 100644 --- a/modules/sl/source/mesh/SLParticleSystem.h +++ b/modules/sl/source/mesh/SLParticleSystem.h @@ -24,7 +24,9 @@ * particle vertex and orient them as a billboard to the viewer. Geometry * shaders are only supported under OpenGL >= 4.0 and OpenGL ES >= 3.2. This is * the case on most desktop systems and on Android SDK > 24 but not on iOS which - * has only OpenGL ES 3.0.\n. + * has only OpenGL ES 3.0\n. + * For all systems that don't support geometry shaders we use an alternative + * with instanced drawing\n. * The particle system supports many options of which many can be turned on the * do* methods. All options can also be modified in the UI when the mesh is * selected. See the different demo scenes in the app_demo_slproject under the @@ -39,9 +41,9 @@ class SLParticleSystem : public SLMesh const SLVec3f& velocityRandomEnd, const SLfloat& timeToLive, SLGLTexture* texC, - const SLstring& name = "Particle system", - SLGLTexture* texFlipbook = nullptr, - const bool renderInstanced = false); + const SLstring& name = "Particle system", + SLGLTexture* texFlipbook = nullptr, + const bool drawInstanced = false); void draw(SLSceneView* sv, SLNode* node, SLuint instances = 1); void deleteData(); @@ -55,10 +57,9 @@ class SLParticleSystem : public SLMesh void pauseOrResume(); void calcNormals() { N.push_back(SLVec3f(0, 1, 0)); }; - // Getters - SLbool renderInstanced() { return _renderInstanced; } + SLbool drawInstanced() { return _drawInstanced; } SLVec3f acceleration() { return _acceleration; } SLfloat accelerationConst() { return _accelerationConst; } SLint amount() { return _amount; } @@ -122,7 +123,7 @@ class SLParticleSystem : public SLMesh SLVec3f velocityRndMax() { return _velocityRndMax; } // Setters - void drawInstanced(bool instanced) {_renderInstanced = instanced; } + void drawInstanced(bool instanced) { _drawInstanced = instanced; } void amount(SLint i) { _amount = i; } void accConst(SLfloat f) { _accelerationConst = f; } void acceleration(SLVec3f v) { _acceleration = v; } @@ -273,8 +274,8 @@ 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 + // Used to recreate material (the shader changes depending on 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 @@ -389,7 +390,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 + SLbool _drawInstanced = false; //!< Boolean for instanced rendering }; //----------------------------------------------------------------------------- #endif diff --git a/modules/utils/externals/CMakeLists.txt b/modules/utils/externals/CMakeLists.txt index 787d8344..a0cac534 100644 --- a/modules/utils/externals/CMakeLists.txt +++ b/modules/utils/externals/CMakeLists.txt @@ -1,5 +1,47 @@ if (NOT "${SYSTEM_NAME_UPPER}" STREQUAL "IOS" AND NOT "${SYSTEM_NAME_UPPER}" STREQUAL "EMSCRIPTEN") add_subdirectory(zlib) + + set(DEFAULT_COMPILE_OPTIONS ${DEFAULT_COMPILE_OPTIONS} + -Wno-c++98-compat + -Wno-c++98-compat-pedantic + -Wno-covered-switch-default + -Wno-double-promotion + -Wno-exit-time-destructors + -Wno-global-constructors + -Wno-gnu-zero-variadic-macro-arguments + -Wno-documentation + -Wno-missing-variable-declarations + -Wno-newline-eof + -Wno-old-style-cast + -Wno-switch-enum + -Wno-unused-but-set-variable + -Wno-unused-command-line-argument + -Wno-unused-function + -Wno-unused-macros + -Wno-unused-parameter + -Wno-unused-private-field + -Wno-unused-variable + -Wno-unused-value + -Wno-used-but-marked-unused + -Wno-extra-tokens + -Wno-reorder + -Wno-switch + -Wno-char-subscripts + -Wno-injected-class-name + -Wno-format-security + -Wno-invalid-noreturn + + $<$: + > + + $<$: + + > + + $<$: + -pthread + > + ) endif () if (NOT "${SYSTEM_NAME_UPPER}" STREQUAL "EMSCRIPTEN") From 775a5d9f5d14a718d3deb01dffe95ca8f4d078ac Mon Sep 17 00:00:00 2001 From: Marcus Hudritsch Date: Thu, 7 Dec 2023 14:31:42 +0100 Subject: [PATCH 070/108] Updated release key for android --- apps/app_demo_slproject/android/app/build.gradle | 5 +++-- .../android/cpvrlab-release-key | Bin 2180 -> 0 bytes .../android/slproject-release-key | Bin 0 -> 2742 bytes 3 files changed, 3 insertions(+), 2 deletions(-) delete mode 100644 apps/app_demo_slproject/android/cpvrlab-release-key create mode 100644 apps/app_demo_slproject/android/slproject-release-key diff --git a/apps/app_demo_slproject/android/app/build.gradle b/apps/app_demo_slproject/android/app/build.gradle index a3faacfa..222d75e5 100644 --- a/apps/app_demo_slproject/android/app/build.gradle +++ b/apps/app_demo_slproject/android/app/build.gradle @@ -2,10 +2,11 @@ apply plugin: 'com.android.application' android { signingConfigs { + // Please create your own release key with Build > Generate Signed Bundle or APK release { - keyAlias 'cpvrlab' + keyAlias 'slproject-release-key' keyPassword '?' // Change to your key password - storeFile file('../cpvrlab-release-key') + storeFile file('../slproject-release-key') storePassword '?' // Change to your store password } } diff --git a/apps/app_demo_slproject/android/cpvrlab-release-key b/apps/app_demo_slproject/android/cpvrlab-release-key deleted file mode 100644 index cfbb91e66ef2a23a53ac19d900a7fa264555e1e7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2180 zcmb`H=U0=75{L69A(Rk6g0d*tASD91LP8)I0s_(%g&?3PPK-@Nr?{2XZ#F&N;={Y-f2?o2;F)LXu2vsguNe)aYAn9Ah6%-!!? z){H-@DpdWoo0!?a?tqcbk7S+y1c2xus--AJBRwcPk!MdH7LFODIXWP>G#hFZ`!5ru zr0*f`pn zl<51ofB(V8djjm(=jLW~d$B@z;SPB$Y!O z*uqi=?@_rWlHtuMGd_JT_3JwUIo-U}2?2&VAxMdaqXL!nk+qVb{-cnsH^rVQ8B{>)_{RORe{+^yU+sBpOtE zppxo%;lZFKv$0=B^yHNfN67MlpHn{Hzds`$%QpNu+(=1Z!g4M@>Q}S}^TMZ{V)?Vy z!JAL@)`S>tWacqjx|eARIa_m*lRkegU3yF0ZrJ=cZgdM`dwyH}9Gy+lPjnzU$y-d1 z#GgD;-f>FfJ#0wEc5Wf=F1k9vGEa4^s2~=`%FzMOHl-*e%eUah)(dz2GQ031VA@0R zB8SkxPP4eq;FUa9;;jAiP|pabvW8!eOQeOACkY!MtQI6)$<4QU9vkjv^lYpPPe3a6 zBZjTl7@bfEZ!?=1xm2l=%yc}FGLX(2nz};_Yn(GUN3%w2!JN`Vy;(e z_R()}a!>|LRg^w0u?0*?awHmlooyzCV~v7W8Q> zQY*fiG#p;;E$9O4#Ca3#?zxe{tPzrUEwm`6IHR`LO){#H}CjY&f()guJ)oL zWh!-RjL=s$@QUS}UXc&m-SW&a@0Gfod<&SBjMT)~%lhWm|yidx<*nhi`&t63X zM*@K$Bpef>fn$PExezb_21C#45j}ATn4of$Y1AeH0N{Ke+&MfPCkTVsKw(I*fhit= zgZ~8rNaz`AP{2RPe}Gr0et2P=&|h64B;<^-DPHVAwg&`9{v%`D&yooIDV#c96R$=f zsF55c5$c2k#QzWfzr&b-?7x>ekUfM6h=OoTKnTnP08qRKQPUkII+K{i&~06eG1w8& zH9w3rhZBv^q=NZYOidE!l3UW+Y=Hb&J#|Lgg(OtFsXukr0j*Xbi&0#2On_|A^?J$u#m!geb(M}mGAu*FE8{&WV7t|OtRXfSd$OD zBwJ1)zYSadNKTk?7iit;y7@@QD6*OFwwJ~C7-J2SNTI@d@4z@_OxL^Ei(7>HsG!6_ z2v2W(UXc90;DaEi%{5{z7fzGqRg1-SJ}&w4yMo*2hQ?M%oXg91oZgPru2g@QFM|}~ z?6AS_qq-9!6K{W7acL{>uu$|TK3%d3s2Xu83TCBuQBYFv=%nEF0LzA>J}=8k_8Fz} z7H>LlJzTus7?k}H>&2ww|C?T9yl=LGEWlfhPjpn*UAux=l`U51cr~RcnpCej5XrC- rt$JPA?iQoAL+LsEe8s-4XKWpy&T`wwDw)?DotlH$$ommI%H-6aQGC8c diff --git a/apps/app_demo_slproject/android/slproject-release-key b/apps/app_demo_slproject/android/slproject-release-key new file mode 100644 index 0000000000000000000000000000000000000000..79046abc33513406a59a691391093960f46d0bcc GIT binary patch literal 2742 zcma);X*d+@9>!Dy6^jcKOcYBgC)Z&K|nB;40nLB$wcc%Z*Txv zfVpJ2F@y{^IJ9-JWay{=ilDC{WN7`NU43{eVC?^C9f1Quxn!uup{a^>`_q7OU@u__ z|Hva)SqPH#5v@z1le;_#IcJCV=@ojeb5alllH>*fMX*O;tp9!y#0CXm1z~JgqV<79 zFa#(A;Z~Iy*mb2U{i@bJj^dsfvxbl%pDnZPHQ6!I1*k-XS|ZBaNavXb9;9B;&!nfX zNnaH0HfBNEq!X5ZN3^oLZn`^V1@f(<+C+Y6Y&7Kfad_r+e;>CybKS?btLyEE^ns8S zGfi;zn-+P;Jq-Vgr`A>O%(nAd#ESr!4vJvN8}P!BV5!(^CV;12yrNMMe7irxOhAyT zBq_bjxdwG6wktg2>k8HC&vF$i1k<|Lr(M5N)K04(TW_q{d^?I^!S(%42))7?cj49*+S6IgP6$YQIDO3iNBB&fLm+YsUADI1)(Fl{ zGfLfKV|}d5K;bL3SGhKjPW|iR?&iHuQj5ozmms=>`d%bDCTEKu@Ut4z@`6uX}EY==)8xf3d^ceI~ar%hAJ> zrM{Ar^-5Ta1(kRQxbDI$w})a@!{p9LoHdXDBg#MTKT4pLN^RyMLO37X{A+p0t0#Rb zKDfFeJ>S=hpRc_ww1Z#xo{NIEmt9abemI+QEmxxEROk8Mlv) zpExdsB+=}p+h54z$`X2vS0GbcdC7@lgQ+fS%9%$+SM13;GgpizKdcXYJ!|S$TijlA zlcd)4Q7S`5t5210spZ$z^!@2M)0M)&Qw?ICE~ho@s5G(G-rm4ld;}w!RW_{3fZ|46 z33!>A@&-F0P%yr3AlE_d+lfZli_|V5#!^D)?&s^mkv-QFgCSeIdt+4700Z5O{Mv#1 z*9?TWOx<(sm4uUfQKya*eryX_?#*kWcT{)>G?U_VRm{3vK2=&3R8|U?SV26p_v5GX z%@{eQU#vE{dc|I&HKR=gKb*0sF5=?rL5d!K5+yy}V$ZX=y1@CcVycfwHH6g;p{}ku zi^#47-7k#3b#P(>^Pz$_DEMn4<9l0qUW8Ze*JQ6Ts<@(Fdc54&uLFjNP>Q3p;P7M)M% z_7YGEUZGc2vo%LQ{`#dmD&>7g%1SI)OHy)}U;X5qu4-|P%Tp!Wto#K;h_!JGYRvCg zmh$EOXD=_*$EzJ(oJk$u+%J0?>LW=4>#z4P*!U9Y0P`~VD$i}kHB>g?Mzgr@;3rc~ z&NDhVuKs*;+t))oH{JXbAIc{!t@(3?zdDBdX>B^7YsM%Od(d?1_xC`5srsl)vCcx5 z=b4C!s*i+(&Lu@BtnwdRvLO{QLNWk6C zFoB$)mTNVG9d*L8++ipn&Q>`zPe8!^4L{WA{_cR!#JL$BrO%(!a$FRSwdD&-%{?th z63U9?uvI?MNH;Nmtp^^Z_~%I$S=fO`XIoZ10G{_a0;QyWBI85Fvaau|ZQ_yrRlDJA zXOHswXH+#@{k9=3<)>DlyO(Y|H7U=yN#SwYKs{G}pMz7BIoehzm;CZcWth+|9%=ok z*Sm2eX3;9m?Zs~F>2U_DIh}e+#2G4LbH8-CBa`;?$qQ$C=;(LTut)GP_3+@5!28dh zhrOGIC3ZexZfsEp)!Pl;>RI;{l3QkyO*=eCL~$7rVT16T-jT_9Zv36PMcCYt+n0Hg zMJB~d74-WYlaMa@;mwIrAS(*H1B}`m7>8H+IoRjwG#Z1eV(j05Rj2NfMko7Yd7{4K z9OY=f?0(`k+(S(Pj_2jIv4+3~tIFU9PUe1~Z2tK>QNK>2OOfoSIkBC1(y~ESiXrR* znN(s{EYC8cSC&##Al^D!%jm(w%+orM!F95e=nyY1v!-cEm(8)LzQmEZq=~5yk~B2$ z8{&0;wCF~nS%Vd!;nk^}d=nYZok5o3BZy=ka`1xDPd=sWVCUWjq8hskDU}PPS78#>8u|ZeNSvc1SE($tlnb9kI~bOApB2syPvvI=`llnEJkuFEcvUL`(=*kI`D{zsIA-s4W{i!VtAzHbMY~ABU)bA+xvH(Y8C%P zbn9;&+$O4jWC1L#Zm;3>uC0rsjXatw37?PC3&^4j<8JRzrXKFBYkFp6!ed*R_Br@M zWbU2#z#4ux&+|WGqKWK=5Nn?(oEo^Hd7K7B#0-n8Q}$1JpASj|J5@Nq0Nb7_n*`5Zv=2bz- QAoEQzcip~ Date: Thu, 7 Dec 2023 14:59:50 +0100 Subject: [PATCH 071/108] Update SLGLProgramGenerated.cpp --- modules/sl/source/gl/SLGLProgramGenerated.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/sl/source/gl/SLGLProgramGenerated.cpp b/modules/sl/source/gl/SLGLProgramGenerated.cpp index b86f0973..b93846da 100644 --- a/modules/sl/source/gl/SLGLProgramGenerated.cpp +++ b/modules/sl/source/gl/SLGLProgramGenerated.cpp @@ -203,7 +203,7 @@ out float tf_rotation; // To transform feedback)"; const string vertOutput_PS_tf_r_angularVelo = R"( out float tf_angularVelo; // To transform feedback)"; const string vertOutput_PS_tf_texNum = R"( -out uint tf_texNum; // To transform feedback)"; +flat out uint tf_texNum; // To transform feedback)"; const string vertOutput_PS_tf_initP = R"( out vec3 tf_initialPosition; // To transform feedback)"; From 8b8695daa9de99cb7b99bb27c3c10de663e801f7 Mon Sep 17 00:00:00 2001 From: Marcus Hudritsch Date: Fri, 8 Dec 2023 17:29:14 +0100 Subject: [PATCH 072/108] Fixed supportGPUSkinning problem I removed mat->supportsGPUSkinning and replaced it by passing this information at the shader creation moment in SLMesh::draw when the material gets activated --- modules/sl/source/SLMaterial.cpp | 17 ++++-- modules/sl/source/SLMaterial.h | 8 +-- modules/sl/source/gl/SLGLProgramGenerated.cpp | 53 ++++++++++--------- modules/sl/source/gl/SLGLProgramGenerated.h | 17 +++--- modules/sl/source/input/SLAssimpImporter.cpp | 13 +++-- modules/sl/source/mesh/SLMesh.cpp | 40 +++++++------- modules/sl/source/node/SLNode.cpp | 8 +-- 7 files changed, 85 insertions(+), 71 deletions(-) diff --git a/modules/sl/source/SLMaterial.cpp b/modules/sl/source/SLMaterial.cpp index 25674510..283a95ed 100644 --- a/modules/sl/source/SLMaterial.cpp +++ b/modules/sl/source/SLMaterial.cpp @@ -502,9 +502,11 @@ void SLMaterial::generateProgramPS(bool renderInstanced) At the end the shader program will begin its usage with SLGLProgram::beginUse. @param cam Pointer to the active camera @param lights Pointer to the scene vector of lights - @param skybox Pointer to the skybox + @param supportsSkinning flag if skinning in shader should be supported */ -void SLMaterial::activate(SLCamera* cam, SLVLight* lights, SLSkybox* skybox) +void SLMaterial::activate(SLCamera* cam, + SLVLight* lights, + SLbool supportGPUSkinning) { SLGLState* stateGL = SLGLState::instance(); @@ -525,12 +527,19 @@ void SLMaterial::activate(SLCamera* cam, SLVLight* lights, SLSkybox* skybox) { // Check first the asset manager if the requested program type already exists string programName; - SLGLProgramGenerated::buildProgramName(this, lights, programName); + SLGLProgramGenerated::buildProgramName(this, + lights, + supportGPUSkinning, + programName); _program = _assetManager->getProgramByName(programName); // If the program was not found by name generate a new one if (!_program) - _program = new SLGLProgramGenerated(_assetManager, programName, this, lights); + _program = new SLGLProgramGenerated(_assetManager, + programName, + this, + lights, + supportGPUSkinning); } // Check if shader had a compile error and the error texture should be shown diff --git a/modules/sl/source/SLMaterial.h b/modules/sl/source/SLMaterial.h index d1f4cf90..a1a55fc3 100644 --- a/modules/sl/source/SLMaterial.h +++ b/modules/sl/source/SLMaterial.h @@ -109,12 +109,9 @@ class SLMaterial : public SLObject ~SLMaterial() override; void generateProgramPS(bool renderInstanced = false); - void activate(SLCamera* cam, - SLVLight* lights, - SLSkybox* skybox = nullptr); + void activate(SLCamera* cam, SLVLight* lights, SLbool supportGPUSkinning); SLint passToUniforms(SLGLProgram* program, SLint nextTexUnit); - void deleteDataGpu(); //! Returns true if there is any transparency in diffuse alpha or textures @@ -201,7 +198,6 @@ 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 @@ -224,7 +220,6 @@ 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; } @@ -255,7 +250,6 @@ class SLMaterial : public SLObject 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 diff --git a/modules/sl/source/gl/SLGLProgramGenerated.cpp b/modules/sl/source/gl/SLGLProgramGenerated.cpp index b93846da..bb0cc546 100644 --- a/modules/sl/source/gl/SLGLProgramGenerated.cpp +++ b/modules/sl/source/gl/SLGLProgramGenerated.cpp @@ -1715,10 +1715,12 @@ const string fragMainVideoBkgd = R"( */ void SLGLProgramGenerated::buildProgramName(SLMaterial* mat, SLVLight* lights, + SLbool supportGPUSkinning, string& programName) { assert(mat && "No material pointer passed!"); assert(lights && !lights->empty() && "No lights passed!"); + programName = "gen"; if (mat->hasTextureType(TT_videoBkgd)) @@ -1744,14 +1746,14 @@ void SLGLProgramGenerated::buildProgramName(SLMaterial* mat, programName += "D"; // Directional light } else if (light->spotCutOffDEG() < 180.0f) - programName += "S"; // Spot light + programName += "S"; // Spotlight else - programName += "P"; // Point light + programName += "P"; // Pointlight if (light->createsShadows()) programName += "s"; // Creates shadows } - if (mat->supportsGPUSkinning()) + if (supportGPUSkinning) programName += "-S"; } //----------------------------------------------------------------------------- @@ -1851,7 +1853,8 @@ void SLGLProgramGenerated::buildProgramNamePS(SLMaterial* mat, * @param lights Pointer of vector of lights */ void SLGLProgramGenerated::buildProgramCode(SLMaterial* mat, - SLVLight* lights) + SLVLight* lights, + SLbool supportGPUSkinning) { if (mat->name() == "IBLMat") { @@ -1876,11 +1879,11 @@ void SLGLProgramGenerated::buildProgramCode(SLMaterial* mat, if (mat->reflectionModel() == RM_BlinnPhong) { - buildPerPixBlinn(mat, lights); + buildPerPixBlinn(mat, lights, supportGPUSkinning); } else if (mat->reflectionModel() == RM_CookTorrance) { - buildPerPixCook(mat, lights); + buildPerPixCook(mat, lights, supportGPUSkinning); } else if (mat->reflectionModel() == RM_Custom) { @@ -1925,7 +1928,9 @@ void SLGLProgramGenerated::buildProgramCodePS(SLMaterial* mat, //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- -void SLGLProgramGenerated::buildPerPixCook(SLMaterial* mat, SLVLight* lights) +void SLGLProgramGenerated::buildPerPixCook(SLMaterial* mat, + SLVLight* lights, + SLbool supportGPUSkinning) { assert(mat && lights); assert(_shaders.size() > 1 && @@ -1948,9 +1953,6 @@ 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()); @@ -1959,11 +1961,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; + if (supportGPUSkinning) 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; + if (supportGPUSkinning) vertCode += vertInput_u_skinning; // Vertex shader outputs vertCode += vertOutput_v_P_VS; @@ -1975,7 +1977,7 @@ void SLGLProgramGenerated::buildPerPixCook(SLMaterial* mat, SLVLight* lights) // Vertex shader main loop vertCode += main_Begin; - if (skinning) vertCode += Nm ? vertMain_skinning_Nm : vertMain_skinning; + if (supportGPUSkinning) 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; @@ -1985,9 +1987,9 @@ void SLGLProgramGenerated::buildPerPixCook(SLMaterial* mat, SLVLight* lights) 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"); + setVariable(vertCode, "localPosition", supportGPUSkinning ? "skinnedPosition" : "a_position"); + setVariable(vertCode, "localNormal", supportGPUSkinning ? "skinnedNormal" : "a_normal"); + if (Nm) setVariable(vertCode, "localTangent", supportGPUSkinning ? "skinnedTangent" : "a_tangent"); addCodeToShader(_shaders[0], vertCode, _name + ".vert"); @@ -2055,7 +2057,9 @@ void SLGLProgramGenerated::buildPerPixCook(SLMaterial* mat, SLVLight* lights) addCodeToShader(_shaders[1], fragCode, _name + ".frag"); } //----------------------------------------------------------------------------- -void SLGLProgramGenerated::buildPerPixBlinn(SLMaterial* mat, SLVLight* lights) +void SLGLProgramGenerated::buildPerPixBlinn(SLMaterial* mat, + SLVLight* lights, + SLbool supportGPUSkinning) { assert(mat && lights); assert(_shaders.size() > 1 && @@ -2073,9 +2077,6 @@ 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()); @@ -2085,10 +2086,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; + if (supportGPUSkinning) vertCode += vertInput_a_skinning; vertCode += vertInput_u_matrices_all; if (Nm) vertCode += vertInput_u_lightNm; - if (skinning) vertCode += vertInput_u_skinning; + if (supportGPUSkinning) vertCode += vertInput_u_skinning; // Vertex shader outputs vertCode += vertOutput_v_P_VS; @@ -2100,7 +2101,7 @@ void SLGLProgramGenerated::buildPerPixBlinn(SLMaterial* mat, SLVLight* lights) // Vertex shader main loop vertCode += main_Begin; - if (skinning) vertCode += Nm ? vertMain_skinning_Nm : vertMain_skinning; + if (supportGPUSkinning) 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; @@ -2110,9 +2111,9 @@ void SLGLProgramGenerated::buildPerPixBlinn(SLMaterial* mat, SLVLight* lights) 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"); + setVariable(vertCode, "localPosition", supportGPUSkinning ? "skinnedPosition" : "a_position"); + setVariable(vertCode, "localNormal", supportGPUSkinning ? "skinnedNormal" : "a_normal"); + if (Nm) setVariable(vertCode, "localTangent", supportGPUSkinning ? "skinnedTangent" : "a_tangent"); addCodeToShader(_shaders[0], vertCode, _name + ".vert"); diff --git a/modules/sl/source/gl/SLGLProgramGenerated.h b/modules/sl/source/gl/SLGLProgramGenerated.h index 39738407..48a40bd4 100644 --- a/modules/sl/source/gl/SLGLProgramGenerated.h +++ b/modules/sl/source/gl/SLGLProgramGenerated.h @@ -59,14 +59,15 @@ class SLGLProgramGenerated : public SLGLProgram SLGLProgramGenerated(SLAssetManager* am, const string& programName, SLMaterial* mat, - SLVLight* lights) + SLVLight* lights, + SLbool supportGPUSkinning) : SLGLProgram(am, "", "", "", programName) { - buildProgramCode(mat, lights); + buildProgramCode(mat, lights, supportGPUSkinning); } //! ctor for generated shader program PS @@ -95,23 +96,27 @@ class SLGLProgramGenerated : public SLGLProgram static bool lightsDoShadowMapping(SLVLight* lights); static void buildProgramName(SLMaterial* mat, SLVLight* lights, + SLbool supportGPUSkinning, string& programName); static void buildProgramNamePS(SLMaterial* mat, string& programName, bool isDrawProg, bool renderInstanced); - void buildProgramCodePS(SLMaterial* mat, bool isDrawProg, bool renderInstanced = false); + void buildProgramCodePS(SLMaterial* mat, + bool isDrawProg, + bool renderInstanced = false); void buildProgramCode(SLMaterial* mat, - SLVLight* lights); + SLVLight* lights, + SLbool supportGPUSkinning); void beginShader(SLCamera* cam, SLMaterial* mat, SLVLight* lights) override { beginUse(cam, mat, lights); } void endShader() override { endUse(); } private: - void buildPerPixCook(SLMaterial* mat, SLVLight* lights); - void buildPerPixBlinn(SLMaterial* mat, SLVLight* lights); + void buildPerPixCook(SLMaterial* mat, SLVLight* lights, SLbool supportGPUSkinning); + void buildPerPixBlinn(SLMaterial* mat, SLVLight* lights, SLbool supportGPUSkinning); void buildPerPixParticle(SLMaterial* mat); void buildPerPixParticleInstanced(SLMaterial* mat); void buildPerPixParticleUpdate(SLMaterial* mat); diff --git a/modules/sl/source/input/SLAssimpImporter.cpp b/modules/sl/source/input/SLAssimpImporter.cpp index a05de729..0ff14f31 100644 --- a/modules/sl/source/input/SLAssimpImporter.cpp +++ b/modules/sl/source/input/SLAssimpImporter.cpp @@ -691,7 +691,6 @@ 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) @@ -841,7 +840,6 @@ SLMaterial* SLAssimpImporter::loadMaterial(SLAssetManager* am, aiMat->Get(AI_MATKEY_OPACITY, opacity); aiMat->Get(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLIC_FACTOR, metalness); aiMat->Get(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_ROUGHNESS_FACTOR, roughness); - aiString texRoughnessMetallic; // increase shininess if specular color is not low. // The aiMat will otherwise be too bright @@ -1297,10 +1295,11 @@ SLAnimation* SLAssimpImporter::loadAnimation(SLAnimManager& animManager, aiAnima SLuint id = 0; SLbool isJointNode = (affectedNode == nullptr); - // @todo: this is currently a work around but it can happen that we receive normal node animationtracks and joint animationtracks - // we don't allow node animation tracks in a skeleton animation, so we should split an animation in two seperate - // animations if this happens. for now we just ignore node animation tracks if we already have joint tracks - // ofc this will crash if the first track is a node anim but its just temporary + // @todo: this is currently a work around but it can happen that we receive normal node animation tracks + // and joint animation tracks we don't allow node animation tracks in a skeleton animation, so we + // should split an animation in two separate animations if this happens. for now we just ignore node + // animation tracks if we already have joint tracks ofc this will crash if the first track is a node + // anim but its just temporary if (!isJointNode && isSkeletonAnim) continue; @@ -1444,7 +1443,7 @@ children has a mesh. aiNode can contain only transform or joint nodes without any visuals. @todo this function doesn't look well optimized. It's currently used if the option to - only load nodes containing meshes somewhere in their heirarchy is enabled. + only load nodes containing meshes somewhere in their hierarchy is enabled. This means we call it on ancestor nodes first. This also means that we will redundantly traverse the same exact nodes multiple times. This isn't a pressing issue at the moment but should be tackled when this importer is being optimized diff --git a/modules/sl/source/mesh/SLMesh.cpp b/modules/sl/source/mesh/SLMesh.cpp index 22622312..1b8c75d8 100644 --- a/modules/sl/source/mesh/SLMesh.cpp +++ b/modules/sl/source/mesh/SLMesh.cpp @@ -20,8 +20,8 @@ using std::set; #ifdef __clang__ - #pragma clang diagnostic push - #pragma clang diagnostic ignored "-Weverything" +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Weverything" #endif #include #include @@ -438,7 +438,10 @@ void SLMesh::draw(SLSceneView* sv, SLNode* node, SLuint instances) ///////////////////////////// // 3.a) Apply mesh material if exists & differs from current - _mat->activate(sv->camera(), &sv->s()->lights()); + // If a material is not created so far, it will be created here + _mat->activate(sv->camera(), + &sv->s()->lights(), + (!Ji.empty() && !Jw.empty())); // 3.b) Pass the standard matrices to the shader program SLGLProgram* sp = _mat->program(); @@ -447,14 +450,14 @@ void SLMesh::draw(SLSceneView* sv, SLNode* node, SLuint instances) sp->uniformMatrix4fv("u_pMatrix", 1, (SLfloat*)&stateGL->projectionMatrix); // Pass skeleton joint matrices to the shader program - if (_mat->supportsGPUSkinning()) + if (!Ji.empty() && !Jw.empty()) { // Only perform skinning in the shader if we haven't performed CPU skinning and if there are joint IDs - SLbool skinningEnabled = !_isCPUSkinned && !Ji.empty(); + SLbool skinningEnabled = !_isCPUSkinned; sp->uniform1i("u_skinningEnabled", skinningEnabled); if (skinningEnabled && !_jointMatrices.empty()) - sp->uniformMatrix4fv("u_jointMatrices", + sp->uniformMatrix4fv("u_jointMatrices", (SLsizei)_jointMatrices.size(), (SLfloat*)&_jointMatrices[0]); } @@ -758,7 +761,7 @@ 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()) + if (!Ji.empty() && !Jw.empty()) { assert(Ji.size() == P.size()); assert(Jw.size() == P.size()); @@ -773,9 +776,9 @@ void SLMesh::generateVAO(SLGLVertexArray& vao) 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); + 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 @@ -785,9 +788,9 @@ void SLMesh::generateVAO(SLGLVertexArray& vao) 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); + 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); @@ -909,9 +912,10 @@ 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) + // 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 && (!Ji.empty() && !Jw.empty()) && !_isCPUSkinned) transformSkin(true, [](SLMesh*) {}); if (_accelStruct) @@ -1599,8 +1603,8 @@ void SLMesh::transformSkin(bool forceCPUSkinning, _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) + // if the results of the skinning process are required somewhere else. + if (forceCPUSkinning) { _finalP = &skinnedP; _finalN = &skinnedN; diff --git a/modules/sl/source/node/SLNode.cpp b/modules/sl/source/node/SLNode.cpp index cee233cf..ad232a42 100644 --- a/modules/sl/source/node/SLNode.cpp +++ b/modules/sl/source/node/SLNode.cpp @@ -1106,10 +1106,12 @@ 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(bool forceCPUSkinning, - const std::function& cbInformNodes) +bool SLNode::updateMeshSkins(bool forceCPUSkinning, + const function& cbInformNodes) { - if (drawBit(SL_DB_WITHEDGES) || drawBit(SL_DB_ONLYEDGES) || drawBit(SL_DB_VOXELS)) + if (drawBit(SL_DB_WITHEDGES) || + drawBit(SL_DB_ONLYEDGES) || + drawBit(SL_DB_VOXELS)) forceCPUSkinning = true; bool hasChanges = false; From 997ecfb2ff414167090792b0f90735c2fc950354 Mon Sep 17 00:00:00 2001 From: Marcus Hudritsch Date: Fri, 8 Dec 2023 18:00:47 +0100 Subject: [PATCH 073/108] Cosmetics and formating --- modules/sl/source/gl/SLGLVertexArray.cpp | 57 ++++++--- modules/sl/source/mesh/SLParticleSystem.cpp | 130 ++++++++++++++------ 2 files changed, 133 insertions(+), 54 deletions(-) diff --git a/modules/sl/source/gl/SLGLVertexArray.cpp b/modules/sl/source/gl/SLGLVertexArray.cpp index 79e4d8c2..ec700a81 100644 --- a/modules/sl/source/gl/SLGLVertexArray.cpp +++ b/modules/sl/source/gl/SLGLVertexArray.cpp @@ -89,11 +89,11 @@ void SLGLVertexArray::setAttrib(SLGLAttributeType type, } //----------------------------------------------------------------------------- - void SLGLVertexArray::setExternalVBO(SLGLVertexBuffer* vbo, SLuint divisor) - { - _externalVBOf = vbo; +void SLGLVertexArray::setExternalVBO(SLGLVertexBuffer* vbo, SLuint divisor) +{ + _externalVBOf = vbo; _externalDivisor = divisor; - } +} //----------------------------------------------------------------------------- /*! Defines the vertex indices for the element drawing. Without indices vertex @@ -419,9 +419,13 @@ void SLGLVertexArray::drawElementsAs(SLGLPrimitiveType primitiveType, GET_GL_ERROR; } //----------------------------------------------------------------------------- -/*! Draws the vertex attributes as a specified primitive type as the vertices -are defined in the attribute arrays. -*/ +/*! + * Draws the vertex attributes as a specified primitive type as the vertices + * are defined in the attribute arrays. + * @param primitiveType + * @param firstVertex + * @param countVertices + */ void SLGLVertexArray::drawArrayAs(SLGLPrimitiveType primitiveType, SLint firstVertex, SLsizei countVertices) @@ -433,9 +437,11 @@ void SLGLVertexArray::drawArrayAs(SLGLPrimitiveType primitiveType, if (countVertices == 0) countVertices = (SLsizei)_numVertices; - //////////////////////////////////////////////////////// - glDrawArrays(primitiveType, firstVertex, countVertices); - //////////////////////////////////////////////////////// + ////////////////////////////////// + glDrawArrays(primitiveType, + firstVertex, + countVertices); + ////////////////////////////////// // Update statistics totalDrawCalls++; @@ -458,14 +464,22 @@ void SLGLVertexArray::drawArrayAs(SLGLPrimitiveType primitiveType, GET_GL_ERROR; } - +//----------------------------------------------------------------------------- +/*! + * + * @param primitiveType + * @param countInstance + * @param numIndexes + * @param indexOffset + */ void SLGLVertexArray::drawElementsInstanced(SLGLPrimitiveType primitiveType, SLsizei countInstance, SLuint numIndexes, - SLuint indexOffset - ) + SLuint indexOffset) { - assert(_numIndicesElements && _idVBOIndices && "No index VBO generated for VAO"); + 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!) @@ -479,8 +493,11 @@ void SLGLVertexArray::drawElementsInstanced(SLGLPrimitiveType primitiveType, 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)); + glDrawElementsInstanced(primitiveType, + (SLsizei)numIndexes, + _indexDataType, + (void*)(size_t)(indexOffset * (SLuint)indexTypeSize), + countInstance); //////////////////////////////////////////////////////// GET_GL_ERROR; @@ -534,9 +551,11 @@ void SLGLVertexArray::drawEdges(SLCol4f color, glLineWidth(lineWidth); #endif - ////////////////////////////////////////////////////////////////// - drawElementsAs(PT_lines, _numIndicesEdges, _numIndicesElements); - ////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////// + drawElementsAs(PT_lines, + _numIndicesEdges, + _numIndicesElements); + ////////////////////////////////////////////// #ifndef SL_GLES if (lineWidth != 1.0f) diff --git a/modules/sl/source/mesh/SLParticleSystem.cpp b/modules/sl/source/mesh/SLParticleSystem.cpp index 37d9d0e7..2d1dc253 100644 --- a/modules/sl/source/mesh/SLParticleSystem.cpp +++ b/modules/sl/source/mesh/SLParticleSystem.cpp @@ -426,7 +426,8 @@ void SLParticleSystem::generate() tempAngulareVelo[i] = Utils::random(_angularVelocityRange.x * DEG2RAD, _angularVelocityRange.y * DEG2RAD); // Start rotation of the particle if (_doFlipBookTexture) // Flipbook texture - tempTexNum[i] = Utils::random(0, _flipbookRows * _flipbookColumns - 1); + tempTexNum[i] = Utils::random(0, + _flipbookRows * _flipbookColumns - 1); if (_doShape) // Shape feature tempInitP[i] = tempP[i]; } @@ -439,15 +440,25 @@ void SLParticleSystem::generate() _vao1.setAttrib(AT_startTime, AT_startTime, &tempST); if (_doAcceleration || _doGravity) - _vao1.setAttrib(AT_initialVelocity, AT_initialVelocity, &tempInitV); + _vao1.setAttrib(AT_initialVelocity, + AT_initialVelocity, + &tempInitV); if (_doRotation) - _vao1.setAttrib(AT_rotation, AT_rotation, &tempR); + _vao1.setAttrib(AT_rotation, + AT_rotation, + &tempR); if (_doRotation && _doRotRange) - _vao1.setAttrib(AT_angularVelo, AT_angularVelo, &tempAngulareVelo); + _vao1.setAttrib(AT_angularVelo, + AT_angularVelo, + &tempAngulareVelo); if (_doFlipBookTexture) - _vao1.setAttrib(AT_texNum, AT_texNum, &tempTexNum); + _vao1.setAttrib(AT_texNum, + AT_texNum, + &tempTexNum); if (_doShape) - _vao1.setAttrib(AT_initialPosition, AT_initialPosition, &tempInitP); + _vao1.setAttrib(AT_initialPosition, + AT_initialPosition, + &tempInitP); _vao1.generateTF((SLuint)tempP.size()); // Configure second VAO @@ -457,11 +468,17 @@ void SLParticleSystem::generate() _vao2.setAttrib(AT_startTime, AT_startTime, &tempST); if (_doAcceleration || _doGravity) - _vao2.setAttrib(AT_initialVelocity, AT_initialVelocity, &tempInitV); + _vao2.setAttrib(AT_initialVelocity, + AT_initialVelocity, + &tempInitV); if (_doRotation) - _vao2.setAttrib(AT_rotation, AT_rotation, &tempR); + _vao2.setAttrib(AT_rotation, + AT_rotation, + &tempR); if (_doRotation && _doRotRange) - _vao2.setAttrib(AT_angularVelo, AT_angularVelo, &tempAngulareVelo); + _vao2.setAttrib(AT_angularVelo, + AT_angularVelo, + &tempAngulareVelo); if (_doFlipBookTexture) _vao2.setAttrib(AT_texNum, AT_texNum, &tempTexNum); if (_doShape) @@ -600,34 +617,68 @@ void SLParticleSystem::draw(SLSceneView* sv, SLNode* node, SLuint instances) float deltaTime = GlobalTimer::timeS() - _startUpdateTimeS; // Actual delta time // Calculate time difference for frustum culling and paused - if (!_isVisibleInFrustum && !_isPaused && _lastTimeBeforePauseS != 0.0f) // If particle system was not visible, and was resumed when it was not visible + // If particle system was not visible, and was resumed when it was not visible + if (!_isVisibleInFrustum && !_isPaused && _lastTimeBeforePauseS != 0.0f) { _isVisibleInFrustum = true; - difTime = GlobalTimer::timeS() - min(_lastTimeBeforePauseS, _notVisibleTimeS); // Paused was set before not visible (will take _lastTimeBeforePauseS), if set after (will take _notVisibleTimeS) - // maybe add later average delta time (because maybe bug when fast not visible long time, visible, not visible, visible - deltaTime = _deltaTimeUpdateS; // Last delta time, because when culled draw is not called therefore the actual delta time will be too big - _notVisibleTimeS = 0.0f; // No more culling, the difference time has been applied, no further need - _lastTimeBeforePauseS = 0.0f; // No more paused, the difference time has been applied, no further need + + // Paused was set before not visible (will take _lastTimeBeforePauseS), + // if set after (will take _notVisibleTimeS) + difTime = GlobalTimer::timeS() - min(_lastTimeBeforePauseS, _notVisibleTimeS); + + // maybe add later average delta time + // (because maybe bug when fast not visible long time, visible, not visible, visible + // Last delta time, because when culled draw is not called therefore + // the actual delta time will be too big + deltaTime = _deltaTimeUpdateS; + + // No more culling, the difference time has been applied, no further need + _notVisibleTimeS = 0.0f; + + // No more paused, the difference time has been applied, no further need + _lastTimeBeforePauseS = 0.0f; } - else if (!_isVisibleInFrustum) // If particle system was not visible, this one is called just once when the particle is draw again (Do nothing if paused, because update call is not done) + + // If particle system was not visible, this one is called just once when the + // particle is draw again (Do nothing if paused, because update call is not done) + else if (!_isVisibleInFrustum) { _isVisibleInFrustum = true; - difTime = GlobalTimer::timeS() - _notVisibleTimeS; // Use time since the particle system was not visible - // maybe add later average delta time (because maybe bug when fast not visible long time, visible, not visible, visible - deltaTime = _deltaTimeUpdateS; // Last delta time, because when culled draw is not called therefore the actual delta time will be too big - if (_lastTimeBeforePauseS > _notVisibleTimeS) // If was paused when not visible. Need to take _notVisibleTimeS because it's since this value that the particle system is not drew. - _lastTimeBeforePauseS = _notVisibleTimeS; // Get the value of since the particle system is not drew - _notVisibleTimeS = 0.0f; // No more culling, the difference time has been applied, no further need + + // Use time since the particle system was not visible + difTime = GlobalTimer::timeS() - _notVisibleTimeS; + + // maybe add later average delta time (because maybe bug when + // fast not visible long time, visible, not visible, visible + // Last delta time, because when culled draw is not called + // therefore the actual delta time will be too big + deltaTime = _deltaTimeUpdateS; + + // If was paused when not visible. Need to take _notVisibleTimeS because + // it's since this value that the particle system is not drew. + if (_lastTimeBeforePauseS > _notVisibleTimeS) + // Get the value of since the particle system is not drew + _lastTimeBeforePauseS = _notVisibleTimeS; + + // No more culling, the difference time has been applied, no further need + _notVisibleTimeS = 0.0f; } - else if (!_isPaused && _lastTimeBeforePauseS != 0.0f) // If particle system was resumed + + // If particle system was resumed + else if (!_isPaused && _lastTimeBeforePauseS != 0.0f) { - difTime = GlobalTimer::timeS() - _lastTimeBeforePauseS; // Use time since the particle system was paused - _lastTimeBeforePauseS = 0.0f; // No more paused, the difference time has been applied, no further need + // Use time since the particle system was paused + difTime = GlobalTimer::timeS() - _lastTimeBeforePauseS; + + // No more paused, the difference time has been applied, no further need + _lastTimeBeforePauseS = 0.0f; - // Take default delta time, because when just paused no need to take last delta time, the draw call continue to be called + // Take default delta time, because when just paused no need to + // take last delta time, the draw call continue to be called } - // Calculate the elapsed time for the updating, need to change to use a real profiler (can't measure time like this on the GPU) + // Calculate the elapsed time for the updating, need to change to + // use a real profiler (can't measure time like this on the GPU) _startUpdateTimeMS = GlobalTimer::timeMS(); // MS above, S below @@ -660,13 +711,20 @@ void SLParticleSystem::draw(SLSceneView* sv, SLNode* node, SLuint instances) if (_doAcceleration) { if (_doAccDiffDir) - spTF->uniform3f("u_acceleration", _acceleration.x, _acceleration.y, _acceleration.z); + spTF->uniform3f("u_acceleration", + _acceleration.x, + _acceleration.y, + _acceleration.z); else - spTF->uniform1f("u_accConst", _accelerationConst); + spTF->uniform1f("u_accConst", + _accelerationConst); } if (_doGravity) - spTF->uniform3f("u_gravity", _gravity.x, _gravity.y, _gravity.z); + spTF->uniform3f("u_gravity", + _gravity.x, + _gravity.y, + _gravity.z); spTF->uniform1f("u_tTL", _timeToLive); @@ -742,7 +800,8 @@ void SLParticleSystem::draw(SLSceneView* sv, SLNode* node, SLuint instances) SLGLState* stateGL = SLGLState::instance(); - // Start calculation of the elapsed time for the drawing, need to change to use a real profiler (can't measure time like this on the GPU) + // Start calculation of the elapsed time for the drawing, need to change + // to use a real profiler (can't measure time like this on the GPU) _startDrawTimeMS = GlobalTimer::timeMS(); // Billboard type @@ -867,7 +926,8 @@ void SLParticleSystem::draw(SLSceneView* sv, SLNode* node, SLuint instances) spD->uniform1f("u_oneOverGamma", 1.0f); // Check wireframe - if (sv->drawBits()->get(SL_DB_MESHWIRED) || node->drawBits()->get(SL_DB_MESHWIRED)) + if (sv->drawBits()->get(SL_DB_MESHWIRED) || + node->drawBits()->get(SL_DB_MESHWIRED)) spD->uniform1i("u_doWireFrame", 1); else spD->uniform1i("u_doWireFrame", 0); @@ -883,7 +943,8 @@ void SLParticleSystem::draw(SLSceneView* sv, SLNode* node, SLuint instances) /////////////////////// if (_doColor && _doBlendBrightness) - stateGL->blendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + stateGL->blendFunc(GL_SRC_ALPHA, + GL_ONE_MINUS_SRC_ALPHA); // End calculation of the elapsed time for the drawing _drawTime.set(GlobalTimer::timeMS() - _startDrawTimeMS); @@ -891,7 +952,7 @@ void SLParticleSystem::draw(SLSceneView* sv, SLNode* node, SLuint instances) // Swap buffer _drawBuf = 1 - _drawBuf; } - +//----------------------------------------------------------------------------- /*! Change the current use texture, this will switch between the normal texture and the flipbook texture (and vice versa) @@ -909,7 +970,6 @@ void SLParticleSystem::changeTexture() mat()->addTexture(_textureFirst); } } - //----------------------------------------------------------------------------- //! deleteData deletes all mesh data and VAOs void SLParticleSystem::deleteData() From 47bfa2cf54aba3d98014211b46d106dcee75ac90 Mon Sep 17 00:00:00 2001 From: Marcus Hudritsch Date: Sat, 9 Dec 2023 12:26:41 +0100 Subject: [PATCH 074/108] Multiple Fixes - Fixed Erleb-AR/Sutz model loading - clang tidy fixes --- apps/app_demo_slproject/source/AppDemoGui.cpp | 11 +--- .../app_demo_slproject/source/AppDemoLoad.cpp | 4 +- modules/sl/source/gl/SLGLVertexArray.cpp | 57 +++++++++---------- modules/sl/source/gl/SLGLVertexArray.h | 32 +++++------ modules/sl/source/gl/SLGLVertexArrayExt.cpp | 6 +- modules/sl/source/mesh/SLMesh.cpp | 6 +- 6 files changed, 53 insertions(+), 63 deletions(-) diff --git a/apps/app_demo_slproject/source/AppDemoGui.cpp b/apps/app_demo_slproject/source/AppDemoGui.cpp index d74b83c1..6bf7d899 100644 --- a/apps/app_demo_slproject/source/AppDemoGui.cpp +++ b/apps/app_demo_slproject/source/AppDemoGui.cpp @@ -1279,7 +1279,6 @@ void AppDemoGui::build(SLScene* s, SLSceneView* sv) balda_stahl = nullptr; balda_glas = nullptr; } - if (AppDemo::sceneID == SID_ErlebARAugustaRauricaTmpTht || AppDemo::sceneID == SID_ErlebARAugustaRauricaTht || AppDemo::sceneID == SID_ErlebARAugustaRauricaTmp) @@ -1302,7 +1301,6 @@ void AppDemoGui::build(SLScene* s, SLSceneView* sv) ImGui::End(); } - if (AppDemo::sceneID == SID_ErlebARAventicumAmphiteatre) { ImGui::Begin("Avenche-Amphitheatre", @@ -1323,7 +1321,6 @@ void AppDemoGui::build(SLScene* s, SLSceneView* sv) ImGui::End(); } - if (AppDemo::sceneID == SID_ErlebARAventicumCigognier) { ImGui::Begin("Avenche-Cigognier", @@ -1343,7 +1340,6 @@ void AppDemoGui::build(SLScene* s, SLSceneView* sv) } ImGui::End(); } - if (AppDemo::sceneID == SID_ErlebARAventicumTheatre) { ImGui::Begin("Avenche-Theatre", @@ -1364,7 +1360,6 @@ void AppDemoGui::build(SLScene* s, SLSceneView* sv) ImGui::End(); } - if (AppDemo::sceneID == SID_ErlebARSutzKirchrain18) { ImGui::Begin("Sutz-Kirchrain18", @@ -1774,16 +1769,14 @@ void AppDemoGui::buildMenuBar(SLScene* s, SLSceneView* sv) SLstring modelAV1_AO = erlebarPath + "avenches/avenches-amphitheater.gltf"; SLstring modelAV2_AO = erlebarPath + "avenches/avenches-cigognier.gltf"; SLstring modelAV3 = erlebarPath + "avenches/avenches-theater.gltf"; - SLstring modelSU1 = erlebarPath + "sutzKirchrain18/Sutz-Kirchrain18.gltf"; - SLstring modelEV1 = erlebarPath + "evilardCheminDuRoc2/EvilardCheminDuRoc2.gltf"; + SLstring modelSU1 = erlebarPath + "sutz/Sutz-Kirchrain18.gltf"; if (Utils::fileExists(modelAR1) || Utils::fileExists(modelAR2) || Utils::fileExists(modelAR3) || Utils::fileExists(modelAV3) || Utils::fileExists(modelBR2) || - Utils::fileExists(modelSU1) || - Utils::fileExists(modelEV1)) + Utils::fileExists(modelSU1)) { if (ImGui::BeginMenu("Erleb-AR")) { diff --git a/apps/app_demo_slproject/source/AppDemoLoad.cpp b/apps/app_demo_slproject/source/AppDemoLoad.cpp index bcc9f6ff..d0565cf2 100644 --- a/apps/app_demo_slproject/source/AppDemoLoad.cpp +++ b/apps/app_demo_slproject/source/AppDemoLoad.cpp @@ -5323,7 +5323,7 @@ resolution shadows near the camera and lower resolution shadows further away."); SLAssimpImporter importer; SLNode* sutzK18 = importer.load(s->animManager(), am, - dataPath + "erleb-AR/models/sutzKirchrain18/Sutz-Kirchrain18.gltf", + dataPath + "erleb-AR/models/sutz/Sutz-Kirchrain18.gltf", texPath, nullptr, true, // delete tex images after build @@ -5375,7 +5375,7 @@ resolution shadows near the camera and lower resolution shadows further away."); AppDemo::devRot.offsetMode(ROM_oneFingerX); // This loads the DEM file and overwrites the altitude of originLatLonAlt and defaultLatLonAlt - SLstring tif = dataPath + "erleb-AR/models/sutzKirchrain18/Sutz-Kirchrain18-DEM-WGS84.tif"; + SLstring tif = dataPath + "erleb-AR/models/sutz/Sutz-Kirchrain18-DEM-WGS84.tif"; AppDemo::devLoc.loadGeoTiff(tif); #if defined(SL_OS_MACIOS) || defined(SL_OS_ANDROID) diff --git a/modules/sl/source/gl/SLGLVertexArray.cpp b/modules/sl/source/gl/SLGLVertexArray.cpp index ec700a81..2a8cab0b 100644 --- a/modules/sl/source/gl/SLGLVertexArray.cpp +++ b/modules/sl/source/gl/SLGLVertexArray.cpp @@ -22,14 +22,14 @@ SLuint SLGLVertexArray::totalPrimitivesRendered = 0; SLGLVertexArray::SLGLVertexArray() { _vaoID = 0; - _VBOf.clear(); + _vbo.clear(); _idVBOIndices = 0; _numIndicesElements = 0; _numIndicesEdges = 0; _numVertices = 0; _indexDataElements = nullptr; _indexDataEdges = nullptr; - _externalVBOf = nullptr; + _externalVbo = nullptr; } //----------------------------------------------------------------------------- /*! Deletes the OpenGL objects for the vertex array and the vertex buffer. @@ -41,8 +41,8 @@ void SLGLVertexArray::deleteGL() glDeleteVertexArrays(1, &_vaoID); _vaoID = 0; - if (_VBOf.id()) - _VBOf.clear(); + if (_vbo.id()) + _vbo.clear(); if (_idVBOIndices) { @@ -74,7 +74,7 @@ void SLGLVertexArray::setAttrib(SLGLAttributeType type, if (type == AT_position && location == -1) SL_EXIT_MSG("The position attribute has no variable location."); - if (_VBOf.attribIndex(type) >= 0) + if (_vbo.attribIndex(type) >= 0) SL_EXIT_MSG("Attribute type already exists."); SLGLAttribute va; @@ -85,16 +85,14 @@ void SLGLVertexArray::setAttrib(SLGLAttributeType type, va.location = location; va.bufferSizeBytes = 0; - _VBOf.attribs().push_back(va); + _vbo.attribs().push_back(va); } - //----------------------------------------------------------------------------- void SLGLVertexArray::setExternalVBO(SLGLVertexBuffer* vbo, SLuint divisor) { - _externalVBOf = vbo; + _externalVbo = vbo; _externalDivisor = divisor; } - //----------------------------------------------------------------------------- /*! Defines the vertex indices for the element drawing. Without indices vertex array can only be drawn with SLGLVertexArray::drawArrayAs. @@ -134,7 +132,7 @@ void SLGLVertexArray::updateAttrib(SLGLAttributeType type, assert(elementSize > 0 && elementSize < 5 && "Element size invalid"); // Get attribute index and check element size - SLint indexf = _VBOf.attribIndex(type); + SLint indexf = _vbo.attribIndex(type); if (indexf == -1) SL_EXIT_MSG("Attribute type does not exist in VAO."); @@ -144,7 +142,7 @@ void SLGLVertexArray::updateAttrib(SLGLAttributeType type, // update the appropriate VBO if (indexf > -1) - _VBOf.updateAttrib(type, elementSize, dataPointer); + _vbo.updateAttrib(type, elementSize, dataPointer); glBindVertexArray(0); GET_GL_ERROR; @@ -203,15 +201,15 @@ void SLGLVertexArray::generate(SLuint numVertices, /////////////////////////////// // Generate the vertex buffer object for float attributes - if (_VBOf.attribs().size()) + if (_vbo.attribs().size()) { - _VBOf.generate(numVertices, usage, outputInterleaved); - _VBOf.bindAndEnableAttrib(divisor); + _vbo.generate(numVertices, usage, outputInterleaved); + _vbo.bindAndEnableAttrib(divisor); } - if (_externalVBOf != nullptr) + if (_externalVbo != nullptr) { - _externalVBOf->bindAndEnableAttrib(_externalDivisor); + _externalVbo->bindAndEnableAttrib(_externalDivisor); } ///////////////////////////////////////////////////////////////// @@ -245,7 +243,7 @@ void SLGLVertexArray::generate(SLuint numVertices, glGenBuffers(1, &_idVBOIndices); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _idVBOIndices); glBufferData(GL_ELEMENT_ARRAY_BUFFER, - _numIndicesElements * (SLuint)typeSize, + (GLsizeiptr)_numIndicesElements * (SLuint)typeSize, _indexDataElements, GL_STATIC_DRAW); SLGLVertexBuffer::totalBufferCount++; @@ -281,15 +279,15 @@ void SLGLVertexArray::generateTF(SLuint numVertices, /////////////////////////////// // Generate the vertex buffer object for float attributes - if (_VBOf.attribs().size()) + if (_vbo.attribs().size()) { - _VBOf.generate(numVertices, usage, outputInterleaved); - _VBOf.bindAndEnableAttrib(divisor); + _vbo.generate(numVertices, usage, outputInterleaved); + _vbo.bindAndEnableAttrib(divisor); } - if (_externalVBOf != nullptr) + if (_externalVbo != nullptr) { - _externalVBOf->bindAndEnableAttrib(_externalDivisor); + _externalVbo->bindAndEnableAttrib(_externalDivisor); } /////////////////////////////// @@ -297,7 +295,7 @@ void SLGLVertexArray::generateTF(SLuint numVertices, /////////////////////////////// glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, _tfoID); - glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, _VBOf.id()); + glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, _vbo.id()); ///////////////////////////////////////////////////////////////// // Create Element Array Buffer for Indices for elements and edges @@ -330,7 +328,7 @@ void SLGLVertexArray::generateTF(SLuint numVertices, glGenBuffers(1, &_idVBOIndices); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _idVBOIndices); glBufferData(GL_ELEMENT_ARRAY_BUFFER, - _numIndicesElements * (SLuint)typeSize, + (GLsizeiptr)_numIndicesElements * (SLuint)typeSize, _indexDataElements, GL_STATIC_DRAW); SLGLVertexBuffer::totalBufferCount++; @@ -430,7 +428,7 @@ void SLGLVertexArray::drawArrayAs(SLGLPrimitiveType primitiveType, SLint firstVertex, SLsizei countVertices) { - assert((_VBOf.id()) && "No VBO generated for VAO."); + assert((_vbo.id()) && "No VBO generated for VAO."); glBindVertexArray(_vaoID); @@ -473,7 +471,7 @@ void SLGLVertexArray::drawArrayAs(SLGLPrimitiveType primitiveType, * @param indexOffset */ void SLGLVertexArray::drawElementsInstanced(SLGLPrimitiveType primitiveType, - SLsizei countInstance, + SLuint countInstance, SLuint numIndexes, SLuint indexOffset) { @@ -494,10 +492,10 @@ void SLGLVertexArray::drawElementsInstanced(SLGLPrimitiveType primitiveType, //////////////////////////////////////////////////////// glDrawElementsInstanced(primitiveType, - (SLsizei)numIndexes, + (GLsizei)numIndexes, _indexDataType, (void*)(size_t)(indexOffset * (SLuint)indexTypeSize), - countInstance); + (GLsizei )countInstance); //////////////////////////////////////////////////////// GET_GL_ERROR; @@ -520,7 +518,6 @@ void SLGLVertexArray::drawElementsInstanced(SLGLPrimitiveType primitiveType, 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. @@ -530,7 +527,7 @@ void SLGLVertexArray::drawElementsInstanced(SLGLPrimitiveType primitiveType, void SLGLVertexArray::drawEdges(SLCol4f color, SLfloat lineWidth) { - if (!_VBOf.id()) + if (!_vbo.id()) SL_EXIT_MSG("No VBO generated for VAO in drawArrayAsColored."); // Prepare shader diff --git a/modules/sl/source/gl/SLGLVertexArray.h b/modules/sl/source/gl/SLGLVertexArray.h index 852e9017..d324a3eb 100644 --- a/modules/sl/source/gl/SLGLVertexArray.h +++ b/modules/sl/source/gl/SLGLVertexArray.h @@ -22,7 +22,7 @@ VBO of type SLGLVertexBuffer.\n VAOs where introduces OpenGL 3.0 and reduce the overhead per draw call. All vertex attributes (e.g. position, normals, texture coords, etc.) must be - float at the input. All float attributes will be in one VBO (_VBOf). + float at the input. All float attributes will be in one VBO (_vbo). Vertices can be drawn either directly as in the array (SLGLVertexArray::drawArrayAs) or by element (SLGLVertexArray::drawElementsAs) with a separate indices buffer.\n The setup of a VAO has multiple steps:\n @@ -49,14 +49,14 @@ class SLGLVertexArray void clearAttribs() { deleteGL(); - _VBOf.clear(); + _vbo.clear(); } //! Returns either the VAO id or the VBO id - SLint vaoID() const { return _vaoID; } + SLuint vaoID() const { return _vaoID; } //! Returns the TFO id - SLint tfoID() const { return _tfoID; } + SLuint tfoID() const { return _tfoID; } //! Adds a vertex attribute with data pointer and an element size void setAttrib(SLGLAttributeType type, @@ -136,7 +136,7 @@ class SLGLVertexArray } //! Attach a VBO that has been created outside of this VAO - void setExternalVBO(SLGLVertexBuffer *vbo, SLuint divisor = 0); + void setExternalVBO(SLGLVertexBuffer* vbo, SLuint divisor = 0); //! Updates a specific vertex attribute in the VBO void updateAttrib(SLGLAttributeType type, @@ -193,18 +193,18 @@ class SLGLVertexArray //! Draws the VAO as an array with instance primitive type void drawElementsInstanced(SLGLPrimitiveType primitiveType, - SLsizei countInstance = 0, - SLuint numIndexes = 0, - SLuint indexOffset = 0); + SLuint 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); // Some getters - SLuint numVertices() const { return _numVertices; } - SLuint numIndicesElements() const { return _numIndicesElements; } - SLuint numIndicesEdges() const { return _numIndicesEdges; } - SLGLVertexBuffer* vbo() { return &_VBOf; } + SLuint numVertices() const { return _numVertices; } + SLuint numIndicesElements() const { return _numIndicesElements; } + SLuint numIndicesEdges() const { return _numIndicesEdges; } + SLGLVertexBuffer* vbo() { return &_vbo; } // Some statistics static SLuint totalDrawCalls; //! static total no. of draw calls @@ -215,13 +215,13 @@ class SLGLVertexArray 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 + SLGLVertexBuffer _vbo; //! Vertex buffer object for float attributes + SLGLVertexBuffer* _externalVbo; //! Vertex buffer object that has been 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 + size_t _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 + size_t _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/SLGLVertexArrayExt.cpp b/modules/sl/source/gl/SLGLVertexArrayExt.cpp index 16a2b438..290e794f 100644 --- a/modules/sl/source/gl/SLGLVertexArrayExt.cpp +++ b/modules/sl/source/gl/SLGLVertexArrayExt.cpp @@ -32,7 +32,7 @@ void SLGLVertexArrayExt::generateVertexPos(SLuint numVertices, SLint location = AT_position; // Add attribute if it doesn't exist - if (_VBOf.attribIndex(AT_position) == -1) + if (_vbo.attribIndex(AT_position) == -1) { setAttrib(AT_position, elementSize, location, dataPointer); generate(numVertices, BU_static, false); @@ -51,7 +51,7 @@ void SLGLVertexArrayExt::drawArrayAsColored(SLGLPrimitiveType primitiveType, { assert(countVertices <= _numVertices); - if (!_VBOf.id()) + if (!_vbo.id()) SL_EXIT_MSG("No VBO generated for VAO in drawArrayAsColored."); // Prepare shader @@ -98,7 +98,7 @@ void SLGLVertexArrayExt::drawElementAsColored(SLGLPrimitiveType primitiveType, { assert(countVertices <= _numVertices); - if (!_VBOf.id()) + if (!_vbo.id()) SL_EXIT_MSG("No VBO generated for VAO in drawArrayAsColored."); // Prepare shader diff --git a/modules/sl/source/mesh/SLMesh.cpp b/modules/sl/source/mesh/SLMesh.cpp index 1b8c75d8..8ad9d748 100644 --- a/modules/sl/source/mesh/SLMesh.cpp +++ b/modules/sl/source/mesh/SLMesh.cpp @@ -846,7 +846,7 @@ void SLMesh::computeHardEdgesIndices(float angleDEG, { F.resize((Eigen::Index)I32.size() / 3, 3); for (int j = 0, i = 0; i < I32.size(); j++, i += 3) - F.row(j) << I32[i], I32[i + 1], I32[i + 2]; + F.row(j) << (int)I32[i], (int)I32[i + 1], (int)I32[i + 2]; } // extract sharp edges @@ -869,9 +869,9 @@ void SLMesh::computeHardEdgesIndices(float angleDEG, // ei = fi + |F| * c -> ei % |F| = fi % |F| = fi; fi is the face adjacent to ei // ej = fj + |F| * c -> ej % |F| = fj % |F| = fj; fj is the face adjacent to ej const int ei = uE2E[u][i]; // edge i - const int fi = ei % newF.rows(); + const int fi = ei % (int)newF.rows(); const int ej = uE2E[u][j]; // edge j - const int fj = ej % newF.rows(); + const int fj = ej % (int)newF.rows(); Eigen::Matrix ni = faceN.row(fi); Eigen::Matrix nj = faceN.row(fj); Eigen::Matrix ev = (newV.row(edges(ei, 1)) - newV.row(edges(ei, 0))).normalized(); From 3365c243ce2fad28412cfefe7110998b1fa94a3e Mon Sep 17 00:00:00 2001 From: Marcus Hudritsch Date: Sat, 9 Dec 2023 12:44:53 +0100 Subject: [PATCH 075/108] Removed most glHasGeometryShader --- apps/app_demo_slproject/source/AppDemoGui.cpp | 20 +- .../app_demo_slproject/source/AppDemoLoad.cpp | 267 +++++++++--------- modules/sl/source/SLMaterial.cpp | 8 +- modules/sl/source/SLMaterial.h | 2 +- modules/sl/source/gl/SLGLProgramGenerated.cpp | 8 +- modules/sl/source/gl/SLGLProgramGenerated.h | 4 +- modules/sl/source/mesh/SLParticleSystem.cpp | 5 +- 7 files changed, 153 insertions(+), 161 deletions(-) diff --git a/apps/app_demo_slproject/source/AppDemoGui.cpp b/apps/app_demo_slproject/source/AppDemoGui.cpp index 6bf7d899..2d81b5d2 100644 --- a/apps/app_demo_slproject/source/AppDemoGui.cpp +++ b/apps/app_demo_slproject/source/AppDemoGui.cpp @@ -3703,6 +3703,16 @@ void AppDemoGui::buildProperties(SLScene* s, SLSceneView* sv) SLParticleSystem* ps = dynamic_cast(singleFullMesh); // Need to check if good practice ImGui::PushItemWidth(ImGui::GetWindowWidth() * 0.5f); + if (SLGLState::instance()->glHasGeometryShaders()) + { + bool drawInstanced = ps->drawInstanced(); + if (ImGui::Checkbox("Instanced draw", &drawInstanced)) + { + ps->drawInstanced(drawInstanced); + ps->isGenerated(false); + } + } + // Pause and Resume bool isPaused = ps->isPaused(); if (isPaused) @@ -3729,16 +3739,6 @@ void AppDemoGui::buildProperties(SLScene* s, SLSceneView* sv) ps->isGenerated(false); } - if (SLGLState::instance()->glHasGeometryShaders()) - { - bool drawInstanced = ps->drawInstanced(); - if (ImGui::Checkbox("Instanced draw", &drawInstanced)) - { - ps->drawInstanced(drawInstanced); - ps->isGenerated(false); - } - } - // TTL (Time to live) if (ImGui::CollapsingHeader("Time to live")) { diff --git a/apps/app_demo_slproject/source/AppDemoLoad.cpp b/apps/app_demo_slproject/source/AppDemoLoad.cpp index d0565cf2..d9ae1c45 100644 --- a/apps/app_demo_slproject/source/AppDemoLoad.cpp +++ b/apps/app_demo_slproject/source/AppDemoLoad.cpp @@ -3584,7 +3584,7 @@ resolution shadows near the camera and lower resolution shadows further away."); scene->addChild(cam1); std::uniform_real_distribution dist(0.0f, 1.0f); - std::default_random_engine randEngine; + std::default_random_engine randEngine; // create astroboys around the center astroboy SLint size = 4; @@ -5628,7 +5628,7 @@ resolution shadows near the camera and lower resolution shadows further away."); #ifndef SL_GLES SLuint numSamples = 10; #else - SLuint numSamples = 4; + SLuint numSamples = 4; #endif stringstream ss; @@ -5720,7 +5720,7 @@ resolution shadows near the camera and lower resolution shadows further away."); #ifndef APP_USES_GLES SLuint numSamples = 10; #else - SLuint numSamples = 6; + SLuint numSamples = 6; #endif // Scene @@ -5817,52 +5817,53 @@ resolution shadows near the camera and lower resolution shadows further away."); sv->camera(cam1); } - if (sceneID == SID_ParticleSystem_First) //............................................... + + else if (sceneID == SID_ParticleSystem_First) //............................................... { - if (stateGL->glHasGeometryShaders()) - { - // Set scene name and info string - s->name("First particle system"); - s->info("First scene with a particle system"); + // Set scene name and info string + s->name("First particle system"); + s->info("First scene with a particle 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.5f, 4); - cam1->lookAt(0, 1.5f, 0); - scene->addChild(cam1); + // Create and add camera + SLCamera* cam1 = new SLCamera("Camera 1"); + cam1->translation(0, 1.5f, 4); + cam1->lookAt(0, 1.5f, 0); + scene->addChild(cam1); - // 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 a light source node - SLLightSpot* light1 = new SLLightSpot(am, s, 0.3f); - light1->translation(5, 5, 5); - light1->name("light node"); - scene->addChild(light1); + // Create a light source node + SLLightSpot* light1 = new SLLightSpot(am, s, 0.3f); + light1->translation(5, 5, 5); + light1->name("light node"); + scene->addChild(light1); - // Create meshes and nodes - SLParticleSystem* ps = new SLParticleSystem(am, - 50, - SLVec3f(0.04f, 0.4f, 0.1f), - SLVec3f(-0.11f, 0.7f, -0.1f), - 4.0f, - texC, - "Particle System", - texFlipbook); - SLNode* pSNode = new SLNode(ps, "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)); - sv->camera(cam1); - sv->doWaitOnIdle(false); - } + // Create meshes and nodes + SLParticleSystem* ps = new SLParticleSystem(am, + 50, + SLVec3f(0.04f, 0.4f, 0.1f), + SLVec3f(-0.11f, 0.7f, -0.1f), + 4.0f, + texC, + "Particle System", + texFlipbook); + + SLNode* pSNode = new SLNode(ps, "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)); + sv->camera(cam1); + sv->doWaitOnIdle(false); } else if (sceneID == SID_ParticleSystem_Demo) //................................................ { @@ -6711,109 +6712,103 @@ resolution shadows near the camera and lower resolution shadows further away."); } else if (sceneID == SID_Benchmark8_ParticleSystemFireComplex) //............................... { - if (stateGL->glHasGeometryShaders()) - { - s->name("Fire Complex Test Scene"); - s->info(s->name()); + s->name("Fire Complex Test Scene"); + s->info(s->name()); - SLCamera* cam1 = new SLCamera("Camera 1"); - cam1->clipNear(0.1f); - cam1->clipFar(1000); - cam1->translation(0, 10, 40); - cam1->focalDist(100); - cam1->lookAt(0, 0, 0); - cam1->background().colors(SLCol4f(0.3f, 0.3f, 0.3f)); - cam1->setInitialState(); + SLCamera* cam1 = new SLCamera("Camera 1"); + cam1->clipNear(0.1f); + cam1->clipFar(1000); + cam1->translation(0, 10, 40); + cam1->focalDist(100); + cam1->lookAt(0, 0, 0); + cam1->background().colors(SLCol4f(0.3f, 0.3f, 0.3f)); + cam1->setInitialState(); - // Root scene node - SLNode* root = new SLNode; - s->root3D(root); - root->addChild(cam1); - const int NUM_NODES = 250; - - // Create textures and materials - SLGLTexture* texFireCld = new SLGLTexture(am, texPath + "ParticleFirecloudTransparent_C.png"); - SLGLTexture* texFireFlm = new SLGLTexture(am, texPath + "ParticleFlames_00_8x4_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"); - - SLVNode nodes(NUM_NODES); - for (int i = 0; i < NUM_NODES; ++i) - { - SLNode* fireComplex = createComplexFire(am, - s, - false, - texFireCld, - texFireFlm, - 8, - 4, - texCircle, - texSmokeB, - texSmokeW); - fireComplex->translate(-20.0f + (float)(i % 20) * 2, - 0.0f, - -(float)((i - (i % 20)) / 20) * 4, - TS_object); - root->addChild(fireComplex); - } + // Root scene node + SLNode* root = new SLNode; + s->root3D(root); + root->addChild(cam1); + const int NUM_NODES = 250; - sv->camera(cam1); - sv->doWaitOnIdle(false); + // Create textures and materials + SLGLTexture* texFireCld = new SLGLTexture(am, texPath + "ParticleFirecloudTransparent_C.png"); + SLGLTexture* texFireFlm = new SLGLTexture(am, texPath + "ParticleFlames_00_8x4_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"); + + SLVNode nodes(NUM_NODES); + for (int i = 0; i < NUM_NODES; ++i) + { + SLNode* fireComplex = createComplexFire(am, + s, + false, + texFireCld, + texFireFlm, + 8, + 4, + texCircle, + texSmokeB, + texSmokeW); + fireComplex->translate(-20.0f + (float)(i % 20) * 2, + 0.0f, + -(float)((i - (float)(i % 20)) / 20) * 4, + TS_object); + root->addChild(fireComplex); } + + sv->camera(cam1); + sv->doWaitOnIdle(false); } else if (sceneID == SID_Benchmark9_ParticleSystemManyParticles) //............................. { - if (stateGL->glHasGeometryShaders()) - { - s->name("Particle System number Scene"); - s->info(s->name()); + s->name("Particle System number Scene"); + s->info(s->name()); - SLCamera* cam1 = new SLCamera("Camera 1"); - cam1->clipNear(0.1f); - cam1->clipFar(1000); - cam1->translation(0, 0, 400); - cam1->focalDist(400); - cam1->lookAt(0, 0, 0); - cam1->background().colors(SLCol4f(0.3f, 0.3f, 0.3f)); - cam1->setInitialState(); + SLCamera* cam1 = new SLCamera("Camera 1"); + cam1->clipNear(0.1f); + cam1->clipFar(1000); + cam1->translation(0, 0, 400); + cam1->focalDist(400); + cam1->lookAt(0, 0, 0); + cam1->background().colors(SLCol4f(0.3f, 0.3f, 0.3f)); + cam1->setInitialState(); + + // Root scene node + SLNode* root = new SLNode; + root->addChild(cam1); - // Root scene node - SLNode* root = new SLNode; - root->addChild(cam1); - - // 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, - 1000000, - SLVec3f(-10.0f, -10.0f, -10.0f), - SLVec3f(10.0f, 10.0f, 10.0f), - 4.0f, - texC, - "Particle System", - texFlipbook); - ps->doAlphaOverLT(false); - ps->doSizeOverLT(false); - ps->doRotation(false); - ps->doShape(true); - ps->shapeType(ST_Box); - ps->shapeScale(100.0f, 100.0f, 100.0f); - ps->doDirectionSpeed(true); - ps->doBlendBrightness(true); - ps->doColor(true); - ps->color(SLCol4f(0.875f, 0.156f, 0.875f, 1.0f)); - ps->speed(0.0f); - SLMesh* pSMesh = ps; - SLNode* pSNode = new SLNode(pSMesh, "Particle system node"); - root->addChild(pSNode); + // 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"); - sv->camera(cam1); - sv->doWaitOnIdle(false); - s->root3D(root); - } + // Create meshes and nodes + SLParticleSystem* ps = new SLParticleSystem(am, + 1000000, + SLVec3f(-10.0f, -10.0f, -10.0f), + SLVec3f(10.0f, 10.0f, 10.0f), + 4.0f, + texC, + "Particle System", + texFlipbook); + ps->doAlphaOverLT(false); + ps->doSizeOverLT(false); + ps->doRotation(false); + ps->doShape(true); + ps->shapeType(ST_Box); + ps->shapeScale(100.0f, 100.0f, 100.0f); + ps->doDirectionSpeed(true); + ps->doBlendBrightness(true); + ps->doColor(true); + ps->color(SLCol4f(0.875f, 0.156f, 0.875f, 1.0f)); + ps->speed(0.0f); + SLMesh* pSMesh = ps; + SLNode* pSNode = new SLNode(pSMesh, "Particle system node"); + root->addChild(pSNode); + + sv->camera(cam1); + sv->doWaitOnIdle(false); + s->root3D(root); } //////////////////////////////////////////////////////////////////////////// diff --git a/modules/sl/source/SLMaterial.cpp b/modules/sl/source/SLMaterial.cpp index 283a95ed..238b3b52 100644 --- a/modules/sl/source/SLMaterial.cpp +++ b/modules/sl/source/SLMaterial.cpp @@ -388,7 +388,7 @@ void SLMaterial::deleteDataGpu() 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(bool renderInstanced) +void SLMaterial::generateProgramPS(bool drawInstanced) { // If no shader program is attached add a generated shader program // A 3D object can be stored without material or shader program information. @@ -403,14 +403,14 @@ void SLMaterial::generateProgramPS(bool renderInstanced) SLGLProgramGenerated::buildProgramNamePS(this, programNameDraw, true, - renderInstanced); + drawInstanced); _program = _assetManager->getProgramByName(programNameDraw); // If the program was not found by name generate a new one if (!_program) { std::string geom = ""; - if (!renderInstanced) + if (!drawInstanced) geom = "Geom"; _program = new SLGLProgramGenerated(_assetManager, @@ -429,7 +429,7 @@ void SLMaterial::generateProgramPS(bool renderInstanced) // Check first the asset manager if the requested programTF type already exists string programNameUpdate; - SLGLProgramGenerated::buildProgramNamePS(this, programNameUpdate, false, renderInstanced); + SLGLProgramGenerated::buildProgramNamePS(this, programNameUpdate, false, drawInstanced); _programTF = _assetManager->getProgramByName(programNameUpdate); if (!_programTF) { diff --git a/modules/sl/source/SLMaterial.h b/modules/sl/source/SLMaterial.h index a1a55fc3..608395ec 100644 --- a/modules/sl/source/SLMaterial.h +++ b/modules/sl/source/SLMaterial.h @@ -108,7 +108,7 @@ class SLMaterial : public SLObject SLGLProgram* program); ~SLMaterial() override; - void generateProgramPS(bool renderInstanced = false); + void generateProgramPS(bool drawInstanced = false); void activate(SLCamera* cam, SLVLight* lights, SLbool supportGPUSkinning); SLint passToUniforms(SLGLProgram* program, SLint nextTexUnit); diff --git a/modules/sl/source/gl/SLGLProgramGenerated.cpp b/modules/sl/source/gl/SLGLProgramGenerated.cpp index bb0cc546..c98006b8 100644 --- a/modules/sl/source/gl/SLGLProgramGenerated.cpp +++ b/modules/sl/source/gl/SLGLProgramGenerated.cpp @@ -1781,7 +1781,7 @@ void SLGLProgramGenerated::buildProgramName(SLMaterial* mat, void SLGLProgramGenerated::buildProgramNamePS(SLMaterial* mat, string& programName, bool isDrawProg, - bool renderInstanced) + bool drawInstanced) { assert(mat && "No material pointer passed!"); programName = "gen"; @@ -1796,7 +1796,7 @@ void SLGLProgramGenerated::buildProgramNamePS(SLMaterial* mat, { programName += "-Draw"; - if (renderInstanced) + if (drawInstanced) programName += "-Inst"; programName += mat->texturesString(); @@ -1904,7 +1904,7 @@ void SLGLProgramGenerated::buildProgramCode(SLMaterial* mat, */ void SLGLProgramGenerated::buildProgramCodePS(SLMaterial* mat, bool isDrawProg, - bool renderInstanced) + bool drawInstanced) { if (mat->name() == "IBLMat") { @@ -1917,7 +1917,7 @@ void SLGLProgramGenerated::buildProgramCodePS(SLMaterial* mat, if (isDrawProg) { - if (renderInstanced) + if (drawInstanced) buildPerPixParticleInstanced(mat); else buildPerPixParticle(mat); diff --git a/modules/sl/source/gl/SLGLProgramGenerated.h b/modules/sl/source/gl/SLGLProgramGenerated.h index 48a40bd4..99062d26 100644 --- a/modules/sl/source/gl/SLGLProgramGenerated.h +++ b/modules/sl/source/gl/SLGLProgramGenerated.h @@ -101,11 +101,11 @@ class SLGLProgramGenerated : public SLGLProgram static void buildProgramNamePS(SLMaterial* mat, string& programName, bool isDrawProg, - bool renderInstanced); + bool drawInstanced); void buildProgramCodePS(SLMaterial* mat, bool isDrawProg, - bool renderInstanced = false); + bool drawInstanced = false); void buildProgramCode(SLMaterial* mat, SLVLight* lights, SLbool supportGPUSkinning); diff --git a/modules/sl/source/mesh/SLParticleSystem.cpp b/modules/sl/source/mesh/SLParticleSystem.cpp index 2d1dc253..4104b423 100644 --- a/modules/sl/source/mesh/SLParticleSystem.cpp +++ b/modules/sl/source/mesh/SLParticleSystem.cpp @@ -34,10 +34,8 @@ SLParticleSystem::SLParticleSystem(SLAssetManager* assetMgr, { assert(!name.empty()); - // To be added to constructor - _assetManager = assetMgr; - _drawInstanced = SLGLState::instance()->glHasGeometryShaders() ? drawInstanced : true; + _drawInstanced = !SLGLState::instance()->glHasGeometryShaders() || drawInstanced; _primitive = PT_points; P.push_back(SLVec3f(0, 0, 0)); // Trick SL project because it wants mesh to have vertex @@ -559,7 +557,6 @@ void SLParticleSystem::generateBernsteinPSize() // 1 _bernsteinPYSize.w = StaEnd[1]; } - //----------------------------------------------------------------------------- /*! 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 From 39054c1182fbac8e333a1c649af96bc43411bab5 Mon Sep 17 00:00:00 2001 From: Marcus Hudritsch Date: Sat, 9 Dec 2023 16:52:10 +0100 Subject: [PATCH 076/108] Changed SID order --- .../glfw/AppDemoMainGLFW.cpp | 16 ++++++++----- apps/source/SLInterface.h | 8 +++---- modules/sl/source/SLEnums.h | 23 ++++++++++--------- modules/sl/source/mesh/SLParticleSystem.cpp | 2 ++ 4 files changed, 28 insertions(+), 21 deletions(-) diff --git a/apps/app_demo_slproject/glfw/AppDemoMainGLFW.cpp b/apps/app_demo_slproject/glfw/AppDemoMainGLFW.cpp index eb398952..a45ab392 100644 --- a/apps/app_demo_slproject/glfw/AppDemoMainGLFW.cpp +++ b/apps/app_demo_slproject/glfw/AppDemoMainGLFW.cpp @@ -119,13 +119,13 @@ SLKey mapKeyToSLKey(SLint key) case GLFW_KEY_DOWN: return K_down; case GLFW_KEY_LEFT: return K_left; case GLFW_KEY_RIGHT: return K_right; - case GLFW_KEY_LEFT_SHIFT: return K_shift; + case GLFW_KEY_LEFT_SHIFT: case GLFW_KEY_RIGHT_SHIFT: return K_shift; - case GLFW_KEY_LEFT_CONTROL: return K_ctrl; + case GLFW_KEY_LEFT_CONTROL: case GLFW_KEY_RIGHT_CONTROL: return K_ctrl; - case GLFW_KEY_LEFT_ALT: return K_alt; + case GLFW_KEY_LEFT_ALT: case GLFW_KEY_RIGHT_ALT: return K_alt; - case GLFW_KEY_LEFT_SUPER: return K_super; // Apple command key + case GLFW_KEY_LEFT_SUPER: case GLFW_KEY_RIGHT_SUPER: return K_super; // Apple command key case GLFW_KEY_TAB: return K_tab; case GLFW_KEY_ENTER: return K_enter; @@ -381,6 +381,7 @@ static void onKeyPress(GLFWwindow* myWindow, AppDemo::scene, sv, SID_Empty); + SL_LOG("----------------------------------------------"); SL_LOG("Loading SceneID: %d", AppDemo::sceneID); } else if (key == K_left && sv && AppDemo::sceneID > 0) @@ -389,6 +390,7 @@ static void onKeyPress(GLFWwindow* myWindow, AppDemo::scene, sv, (SLSceneID)(AppDemo::sceneID - 1)); + SL_LOG("----------------------------------------------"); SL_LOG("Loading SceneID: %d", AppDemo::sceneID); } else if (key == K_right && sv && AppDemo::sceneID < SID_Maximal - 1) @@ -397,6 +399,7 @@ static void onKeyPress(GLFWwindow* myWindow, AppDemo::scene, sv, (SLSceneID)(AppDemo::sceneID + 1)); + SL_LOG("----------------------------------------------"); SL_LOG("Loading SceneID: %d", AppDemo::sceneID); } } @@ -521,8 +524,9 @@ void initSL(SLVstring& cmdLineArgs) // setup platform dependent data path AppDemo::calibFilePath = configDir; - AppDemo::calibIniPath = projectRoot + "/data/calibrations/"; // for calibInitPath - CVCapture::instance()->loadCalibrations(Utils::ComputerInfos::get(), AppDemo::calibFilePath); // for calibrations made + AppDemo::calibIniPath = projectRoot + "/data/calibrations/"; + CVCapture::instance()->loadCalibrations(Utils::ComputerInfos::get(), + AppDemo::calibFilePath); ///////////////////////////////////////////////////////// slCreateAppAndScene(cmdLineArgs, diff --git a/apps/source/SLInterface.h b/apps/source/SLInterface.h index d58a05d1..084583bb 100644 --- a/apps/source/SLInterface.h +++ b/apps/source/SLInterface.h @@ -44,7 +44,7 @@ void slCreateAppAndScene(SLVstring& cmdLineArgs, const SLstring& configPath, const SLstring& applicationName, void* onSceneLoadCallback = nullptr); - +//----------------------------------------------------------------------------- SLint slCreateSceneView(SLAssetManager* am, SLScene* scene, int screenWidth, @@ -57,7 +57,7 @@ SLint slCreateSceneView(SLAssetManager* am, void* onImGuiBuild = nullptr, void* onImGuiLoadConfig = nullptr, void* onImGuiSaveConfig = nullptr); - +//----------------------------------------------------------------------------- SLSceneView* slNewSceneView(SLScene* s, int dotsPerInch, SLInputManager& inputManager); bool slShouldClose(); void slShouldClose(bool val); @@ -65,7 +65,7 @@ void slTerminate(); void slResize(int sceneViewIndex, int width, int height); bool slUpdateParallelJob(); bool slPaintAllViews(); - +//----------------------------------------------------------------------------- void slMouseDown(int sceneViewIndex, SLMouseButton button, int x, int y, SLKey modifier); void slMouseMove(int sceneViewIndex, int x, int y); void slMouseUp(int sceneViewIndex, SLMouseButton button, int x, int y, SLKey modifier); @@ -80,7 +80,7 @@ void slMouseWheel(int sceneViewIndex, int pos, SLKey modifier); void slKeyPress(int sceneViewIndex, SLKey key, SLKey modifier); void slKeyRelease(int sceneViewIndex, SLKey key, SLKey modifier); void slCharInput(int sceneViewIndex, unsigned int character); - +//----------------------------------------------------------------------------- bool slUsesRotation(); void slRotationQUAT(float quatX, float quatY, float quatZ, float quatW); bool slUsesLocation(); diff --git a/modules/sl/source/SLEnums.h b/modules/sl/source/SLEnums.h index 475a174c..a892a639 100644 --- a/modules/sl/source/SLEnums.h +++ b/modules/sl/source/SLEnums.h @@ -166,16 +166,6 @@ enum SLSceneID SID_RTLens, SID_RTTest, - SID_ErlebARBernChristoffel, - SID_ErlebARBielBFH, - SID_ErlebARAugustaRauricaTmp, - SID_ErlebARAugustaRauricaTht, - SID_ErlebARAugustaRauricaTmpTht, - SID_ErlebARAventicumAmphiteatre, - SID_ErlebARAventicumCigognier, - SID_ErlebARAventicumTheatre, - SID_ErlebARSutzKirchrain18, - SID_ParticleSystem_First, SID_ParticleSystem_Demo, SID_ParticleSystem_DustStorm, @@ -194,7 +184,18 @@ enum SLSceneID SID_Benchmark8_ParticleSystemFireComplex, SID_Benchmark9_ParticleSystemManyParticles, - SID_Maximal + SID_Maximal, + + // These scenes are not part of the public data + SID_ErlebARBernChristoffel, + SID_ErlebARBielBFH, + SID_ErlebARAugustaRauricaTmp, + SID_ErlebARAugustaRauricaTht, + SID_ErlebARAugustaRauricaTmpTht, + SID_ErlebARAventicumAmphiteatre, + SID_ErlebARAventicumCigognier, + SID_ErlebARAventicumTheatre, + SID_ErlebARSutzKirchrain18 }; //----------------------------------------------------------------------------- //! Mouse button codes diff --git a/modules/sl/source/mesh/SLParticleSystem.cpp b/modules/sl/source/mesh/SLParticleSystem.cpp index 4104b423..121a6eca 100644 --- a/modules/sl/source/mesh/SLParticleSystem.cpp +++ b/modules/sl/source/mesh/SLParticleSystem.cpp @@ -597,12 +597,14 @@ 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(_drawInstanced); From 67ce89050b4ea8dd81d457ea1a6ad10733a91f39 Mon Sep 17 00:00:00 2001 From: Marcus Hudritsch Date: Sat, 9 Dec 2023 17:35:52 +0100 Subject: [PATCH 077/108] OpenCV build with included 3rdparties --- .../build_opencv_w_contrib_for_macArm64.sh | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/externals/prebuild_scripts/build_opencv_w_contrib_for_macArm64.sh b/externals/prebuild_scripts/build_opencv_w_contrib_for_macArm64.sh index 3f3f5b16..0f56dc7c 100755 --- a/externals/prebuild_scripts/build_opencv_w_contrib_for_macArm64.sh +++ b/externals/prebuild_scripts/build_opencv_w_contrib_for_macArm64.sh @@ -73,10 +73,14 @@ cmake \ -DBUILD_TESTS=OFF \ -DWITH_MATLAB=OFF \ -DBUILD_ZLIB=ON \ +-DWITH_PNG=ON \ -DBUILD_PNG=ON \ -DBUILD_JPEG=ON \ +-DWITH_JPEG=ON \ -DBUILD_TIFF=ON \ +-DWITH_WEBP=ON \ -DBUILD_WEBP=ON \ +-DWITH_OPENEXR=ON \ -DBUILD_OPENEXR=ON \ -DOPENCV_ENABLE_NONFREE=ON \ -DOPENCV_EXTRA_MODULES_PATH=../../../opencv_contrib/modules \ @@ -110,10 +114,14 @@ cmake \ -DBUILD_TESTS=OFF \ -DWITH_MATLAB=OFF \ -DBUILD_ZLIB=ON \ +-DWITH_PNG=ON \ -DBUILD_PNG=ON \ -DBUILD_JPEG=ON \ +-DWITH_JPEG=ON \ -DBUILD_TIFF=ON \ +-DWITH_WEBP=ON \ -DBUILD_WEBP=ON \ +-DWITH_OPENEXR=ON \ -DBUILD_OPENEXR=ON \ -DOPENCV_ENABLE_NONFREE=ON \ -DOPENCV_EXTRA_MODULES_PATH=../../../opencv_contrib/modules \ From 7c03129f516a2fbe76853e1e2bfb4c17944e85ab Mon Sep 17 00:00:00 2001 From: Marcus Hudritsch Date: Sat, 9 Dec 2023 19:29:08 +0100 Subject: [PATCH 078/108] Added onlyErrorLogs cmd line argument --- .../glfw/AppDemoMainGLFW.cpp | 9 +++ modules/cv/source/CVTrackedFaces.cpp | 58 +++++++++++++------ modules/sl/source/SLSceneView.cpp | 3 +- modules/sl/source/gl/SLGLProgramGenerated.cpp | 4 -- modules/sl/source/input/SLAssimpImporter.cpp | 6 +- modules/utils/source/Utils.cpp | 55 +++++++----------- modules/utils/source/Utils.h | 3 + 7 files changed, 76 insertions(+), 62 deletions(-) diff --git a/apps/app_demo_slproject/glfw/AppDemoMainGLFW.cpp b/apps/app_demo_slproject/glfw/AppDemoMainGLFW.cpp index a45ab392..2d4f98b5 100644 --- a/apps/app_demo_slproject/glfw/AppDemoMainGLFW.cpp +++ b/apps/app_demo_slproject/glfw/AppDemoMainGLFW.cpp @@ -567,6 +567,15 @@ int main(int argc, char* argv[]) for (int i = 0; i < argc; i++) cmdLineArgs.push_back(SLstring(argv[i])); + // parse cmd line arguments + for (int i=1; iloadModel(faceMarkModelFilename); // Init averaged 2D facial landmark points @@ -117,7 +119,13 @@ bool CVTrackedFaces::track(CVMat imageGray, int max = (int)((float)imageGray.rows * 0.8f); // the smaller max the faster CVSize minSize(min, min); CVSize maxSize(max, max); - _faceDetector->detectMultiScale(imageGray, faces, 1.05, 3, 0, minSize, maxSize); + _faceDetector->detectMultiScale(imageGray, + faces, + 1.05, + 3, + 0, + minSize, + maxSize); // Enlarge the face rect at the bottom to cover also the chin for (auto& face : faces) @@ -130,8 +138,8 @@ bool CVTrackedFaces::track(CVMat imageGray, // Detect Landmarks // ////////////////////// - CVVVPoint2f landmarks; - bool foundLandmarks = _facemark->fit(imageBgr, faces, landmarks); + CVVVPoint2f lm; + bool foundLandmarks = _facemark->fit(imageBgr, faces, lm); float time3MS = _timer.elapsedTimeInMilliSec(); CVTracked::detect2TimesMS.set(time3MS - time2MS); @@ -139,23 +147,24 @@ bool CVTrackedFaces::track(CVMat imageGray, if (foundLandmarks) { - for (unsigned long i = 0; i < landmarks.size(); i++) + for (unsigned long i = 0; i < lm.size(); i++) { // Landmark indexes from // https://cdn-images-1.medium.com/max/1600/1*AbEg31EgkbXSQehuNJBlWg.png - _avgPosePoints2D[0].set(CVVec2f(landmarks[i][30].x, landmarks[i][30].y)); // Nose tip - _avgPosePoints2D[1].set(CVVec2f(landmarks[i][31].x, landmarks[i][31].y)); // Nose hole left - _avgPosePoints2D[2].set(CVVec2f(landmarks[i][35].x, landmarks[i][35].y)); // Nose hole right - _avgPosePoints2D[3].set(CVVec2f(landmarks[i][36].x, landmarks[i][36].y)); // Left eye left corner - _avgPosePoints2D[4].set(CVVec2f(landmarks[i][39].x, landmarks[i][39].y)); // Left eye right corner - _avgPosePoints2D[5].set(CVVec2f(landmarks[i][42].x, landmarks[i][42].y)); // Right eye left corner - _avgPosePoints2D[6].set(CVVec2f(landmarks[i][45].x, landmarks[i][45].y)); // Right eye right corner - _avgPosePoints2D[7].set(CVVec2f(landmarks[i][48].x, landmarks[i][48].y)); // Left mouth corner - _avgPosePoints2D[8].set(CVVec2f(landmarks[i][54].x, landmarks[i][54].y)); // Right mouth corner + _avgPosePoints2D[0].set(CVVec2f(lm[i][30].x, lm[i][30].y)); // Nose tip + _avgPosePoints2D[1].set(CVVec2f(lm[i][31].x, lm[i][31].y)); // Nose hole left + _avgPosePoints2D[2].set(CVVec2f(lm[i][35].x, lm[i][35].y)); // Nose hole right + _avgPosePoints2D[3].set(CVVec2f(lm[i][36].x, lm[i][36].y)); // Left eye left corner + _avgPosePoints2D[4].set(CVVec2f(lm[i][39].x, lm[i][39].y)); // Left eye right corner + _avgPosePoints2D[5].set(CVVec2f(lm[i][42].x, lm[i][42].y)); // Right eye left corner + _avgPosePoints2D[6].set(CVVec2f(lm[i][45].x, lm[i][45].y)); // Right eye right corner + _avgPosePoints2D[7].set(CVVec2f(lm[i][48].x, lm[i][48].y)); // Left mouth corner + _avgPosePoints2D[8].set(CVVec2f(lm[i][54].x, lm[i][54].y)); // Right mouth corner // Convert averaged 2D points to OpenCV points2d for (unsigned long p = 0; p < _avgPosePoints2D.size(); p++) - _cvPosePoints2D[p] = CVPoint2f(_avgPosePoints2D[p].average()[0], _avgPosePoints2D[p].average()[1]); + _cvPosePoints2D[p] = CVPoint2f(_avgPosePoints2D[p].average()[0], + _avgPosePoints2D[p].average()[1]); // delaunayTriangulate(imageBgr, landmarks[i], drawDetection); @@ -166,15 +175,26 @@ bool CVTrackedFaces::track(CVMat imageGray, if (_drawDetection) { // Draw rectangle of detected face - cv::rectangle(imageBgr, faces[i], cv::Scalar(255, 0, 0), 2); + cv::rectangle(imageBgr, + faces[i], + cv::Scalar(255, 0, 0), + 2); // Draw detected landmarks - for (auto& j : landmarks[i]) - cv::circle(imageBgr, j, 2, cv::Scalar(0, 0, 255), -1); + for (auto& j : lm[i]) + cv::circle(imageBgr, + j, + 2, + cv::Scalar(0, 0, 255), + -1); // Draw averaged face points used for pose estimation for (unsigned long p = 0; p < _avgPosePoints2D.size(); p++) - cv::circle(imageBgr, _cvPosePoints2D[p], 5, cv::Scalar(0, 255, 0), 1); + cv::circle(imageBgr, + _cvPosePoints2D[p], + 5, + cv::Scalar(0, 255, 0), + 1); } // Do pose estimation for the first face found diff --git a/modules/sl/source/SLSceneView.cpp b/modules/sl/source/SLSceneView.cpp index d58af6cf..a39b574e 100644 --- a/modules/sl/source/SLSceneView.cpp +++ b/modules/sl/source/SLSceneView.cpp @@ -1496,12 +1496,11 @@ SLbool SLSceneView::onDoubleClick(SLMouseButton button, _s->root3D()->hitRec(&pickRay); if (pickRay.hitNode) - cout << "NODE HIT: " << pickRay.hitNode->name() << endl; + SL_LOG("NODE HIT: %s", pickRay.hitNode->name().c_str()); } if (pickRay.length < FLT_MAX) { - if (mod & K_shift) { _s->selectNodeMesh(pickRay.hitNode, pickRay.hitMesh); diff --git a/modules/sl/source/gl/SLGLProgramGenerated.cpp b/modules/sl/source/gl/SLGLProgramGenerated.cpp index c98006b8..e80e0de1 100644 --- a/modules/sl/source/gl/SLGLProgramGenerated.cpp +++ b/modules/sl/source/gl/SLGLProgramGenerated.cpp @@ -1856,10 +1856,6 @@ void SLGLProgramGenerated::buildProgramCode(SLMaterial* mat, SLVLight* lights, SLbool supportGPUSkinning) { - if (mat->name() == "IBLMat") - { - std::cout << "build program code for IBLMat" << std::endl; - } assert(mat && "No material pointer passed!"); assert(!lights->empty() && "No lights passed!"); assert(_shaders.size() > 1 && diff --git a/modules/sl/source/input/SLAssimpImporter.cpp b/modules/sl/source/input/SLAssimpImporter.cpp index 0ff14f31..dae3f0ed 100644 --- a/modules/sl/source/input/SLAssimpImporter.cpp +++ b/modules/sl/source/input/SLAssimpImporter.cpp @@ -1488,9 +1488,9 @@ SLstring SLAssimpImporter::checkFilePath(const SLstring& modelPath, if (showWarning) { - SLstring msg = "SLAssimpImporter: Texture file not found: \n" + aiTexFile + - "\non model path: " + modelPath + "\n"; - SL_WARN_MSG(msg.c_str()); + SLstring msg = "WARNING: SLAssimpImporter: Texture file not found: \n" + + aiTexFile + "\non model path: " + modelPath + "\n"; + SL_LOG(msg.c_str()); } // Return path for texture not found image; diff --git a/modules/utils/source/Utils.cpp b/modules/utils/source/Utils.cpp index 3376cc1b..a87bbacd 100644 --- a/modules/utils/source/Utils.cpp +++ b/modules/utils/source/Utils.cpp @@ -74,8 +74,11 @@ namespace Utils /////////////////////////////// // Global variables // /////////////////////////////// + std::unique_ptr customLog; +bool onlyErrorLogs = false; + /////////////////////////////// // String Handling Functions // /////////////////////////////// @@ -141,7 +144,7 @@ string trimLeftString(const string& s, const string& drop) return r; } //----------------------------------------------------------------------------- -// Splits an input string at a delimeter character into a string vector +// Splits an input string at a delimiter character into a string vector void splitString(const string& s, char delimiter, vector& splits) @@ -159,7 +162,7 @@ void splitString(const string& s, } } //----------------------------------------------------------------------------- -// Replaces in the source string the from string by the to string +// Replaces in the source-string the from-string by the to-string void replaceString(string& source, const string& from, const string& to) @@ -1115,6 +1118,9 @@ void log(const char* tag, const char* format, ...) if (customLog) customLog->post(msg); + if (Utils::onlyErrorLogs) + return; + #if defined(ANDROID) || defined(ANDROID_NDK) __android_log_print(ANDROID_LOG_INFO, tag, msg); #else @@ -1128,28 +1134,7 @@ void exitMsg(const char* tag, const int line, const char* file) { -#if defined(ANDROID) || defined(ANDROID_NDK) - __android_log_print(ANDROID_LOG_FATAL, - tag, - "Exit %s at line %d in %s\n", - msg, - line, - file); -#elif defined(__EMSCRIPTEN__) - std::cerr << "--------------------------------\n" - << "Fatal Error\n" - << "Tag: " << tag << '\n' - << "Location: " << file << ":" << line << '\n' - << "Message: " << msg << '\n' - << "--------------------------------" << std::endl; -#else - log(tag, - "Exit %s at line %d in %s\n", - msg, - line, - file); -#endif - + errorMsg(tag, msg, line, file); exit(-1); } //----------------------------------------------------------------------------- @@ -1167,11 +1152,12 @@ void warnMsg(const char* tag, line, file); #else - log(tag, - "Warning %s at line %d in %s\n", - msg, - line, - file); + std::cout << "--------------------------------\n" + << "Warning:\n" + << "Tag: " << tag << '\n' + << "Location: " << file << ":" << line << '\n' + << "Message: " << msg << '\n' + << "--------------------------------" << std::endl; #endif } //----------------------------------------------------------------------------- @@ -1189,11 +1175,12 @@ void errorMsg(const char* tag, line, file); #else - log(tag, - "Error %s at line %d in %s\n", - msg, - line, - file); + std::cout << "--------------------------------\n" + << "Error:\n" + << "Tag: " << tag << '\n' + << "Location: " << file << ":" << line << '\n' + << "Message: " << msg << '\n' + << "--------------------------------" << std::endl; #endif } //----------------------------------------------------------------------------- diff --git a/modules/utils/source/Utils.h b/modules/utils/source/Utils.h index 029aaf41..ea7fe654 100644 --- a/modules/utils/source/Utils.h +++ b/modules/utils/source/Utils.h @@ -198,6 +198,9 @@ string findFile(const string& filename, //! will also output into this file. Instantiate it with initFileLog function. static std::unique_ptr fileLog; +//! if this flag is set to true all calls to log get ignored +extern bool onlyErrorLogs; + //! Instantiates FileLog instance void initFileLog(const std::string& logDir, bool forceFlush); From a534470f152ef84465edc0e24f7411064e89a20c Mon Sep 17 00:00:00 2001 From: Marcus Hudritsch Date: Sun, 10 Dec 2023 13:51:26 +0100 Subject: [PATCH 079/108] Some questions for Luc --- .../glfw/AppDemoMainGLFW.cpp | 4 +- apps/app_demo_slproject/source/AppDemoGui.cpp | 2 +- .../app_demo_slproject/source/AppDemoLoad.cpp | 4190 +++++++++-------- modules/sl/source/SLEnums.h | 8 +- modules/sl/source/gl/SLGLVertexArray.cpp | 12 +- modules/sl/source/gl/SLGLVertexArray.h | 2 +- modules/sl/source/mesh/SLParticleSystem.cpp | 151 +- modules/sl/source/mesh/SLParticleSystem.h | 15 +- 8 files changed, 2187 insertions(+), 2197 deletions(-) diff --git a/apps/app_demo_slproject/glfw/AppDemoMainGLFW.cpp b/apps/app_demo_slproject/glfw/AppDemoMainGLFW.cpp index 2d4f98b5..ca47ee9d 100644 --- a/apps/app_demo_slproject/glfw/AppDemoMainGLFW.cpp +++ b/apps/app_demo_slproject/glfw/AppDemoMainGLFW.cpp @@ -393,7 +393,7 @@ static void onKeyPress(GLFWwindow* myWindow, SL_LOG("----------------------------------------------"); SL_LOG("Loading SceneID: %d", AppDemo::sceneID); } - else if (key == K_right && sv && AppDemo::sceneID < SID_Maximal - 1) + else if (key == K_right && sv && AppDemo::sceneID < SID_MaxNoBenchmarks - 1) { appDemoLoadScene(AppDemo::assetManager, AppDemo::scene, @@ -568,7 +568,7 @@ int main(int argc, char* argv[]) cmdLineArgs.push_back(SLstring(argv[i])); // parse cmd line arguments - for (int i=1; ionLoad(am, s, sv, AppDemo::sceneID + 1); if (ImGui::MenuItem("Previous Scene", diff --git a/apps/app_demo_slproject/source/AppDemoLoad.cpp b/apps/app_demo_slproject/source/AppDemoLoad.cpp index d9ae1c45..0d3fbe93 100644 --- a/apps/app_demo_slproject/source/AppDemoLoad.cpp +++ b/apps/app_demo_slproject/source/AppDemoLoad.cpp @@ -4154,883 +4154,1005 @@ resolution shadows near the camera and lower resolution shadows further away."); sv->doWaitOnIdle(false); // for constant video feed } - else if (sceneID == SID_ErlebARBernChristoffel) //............................................. + + else if (sceneID == SID_ParticleSystem_First) //............................................... { - s->name("Christoffel Tower AR"); - s->info("Augmented Reality Christoffel Tower"); + // Set scene name and info string + s->name("First particle system"); + s->info("First scene with a particle system"); - // Create video texture on global pointer updated in AppDemoVideo - videoTexture = new SLGLTexture(am, texPath + "LiveVideoError.png", GL_LINEAR, GL_LINEAR); - videoTexture->texType(TT_videoBkgd); + // Create a scene group node + SLNode* scene = new SLNode("scene node"); + s->root3D(scene); - // Create see through video background material without shadow mapping - SLMaterial* matVideoBkgd = new SLMaterial(am, "matVideoBkgd", videoTexture); - matVideoBkgd->reflectionModel(RM_Custom); + // Create and add camera + SLCamera* cam1 = new SLCamera("Camera 1"); + cam1->translation(0, 1.5f, 4); + cam1->lookAt(0, 1.5f, 0); + scene->addChild(cam1); - // Create see through video background material with shadow mapping - SLMaterial* matVideoBkgdSM = new SLMaterial(am, "matVideoBkgdSM", videoTexture); - matVideoBkgdSM->reflectionModel(RM_Custom); - matVideoBkgdSM->ambient(SLCol4f(0.6f, 0.6f, 0.6f)); - matVideoBkgdSM->getsShadows(true); + // 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"); - SLCamera* cam1 = new SLCamera("Camera 1"); - cam1->translation(0, 2, 0); - cam1->lookAt(-10, 2, 0); - cam1->clipNear(1); - cam1->clipFar(700); - cam1->setInitialState(); - cam1->devRotLoc(&AppDemo::devRot, &AppDemo::devLoc); - cam1->background().texture(videoTexture); + // Create a light source node + SLLightSpot* light1 = new SLLightSpot(am, s, 0.3f); + light1->translation(5, 5, 5); + light1->name("light node"); + scene->addChild(light1); - // Turn on main video - CVCapture::instance()->videoType(VT_MAIN); + // Create meshes and nodes + SLParticleSystem* ps = new SLParticleSystem(am, + 50, + SLVec3f(0.04f, 0.4f, 0.1f), + SLVec3f(-0.11f, 0.7f, -0.1f), + 4.0f, + texC, + "Particle System", + texFlipbook); - // Create directional light for the sunlight - SLLightDirect* sunLight = new SLLightDirect(am, s, 2.0f); - sunLight->translate(-44.89f, 18.05f, -26.07f); - sunLight->powers(1.0f, 1.5f, 1.0f); - sunLight->attenuation(1, 0, 0); - sunLight->doSunPowerAdaptation(true); - sunLight->createsShadows(true); - sunLight->createShadowMapAutoSize(cam1, SLVec2i(2048, 2048), 4); - sunLight->shadowMap()->cascadesFactor(3.0); - // sunLight->createShadowMap(-100, 150, SLVec2f(200, 150), SLVec2i(4096, 4096)); - sunLight->doSmoothShadows(true); - sunLight->castsShadows(false); - sunLight->shadowMinBias(0.001f); - sunLight->shadowMaxBias(0.003f); - AppDemo::devLoc.sunLightNode(sunLight); // Let the sun be rotated by time and location + SLNode* pSNode = new SLNode(ps, "Particle system node"); + scene->addChild(pSNode); - // Import the main model - SLAssimpImporter importer; - SLNode* bern = importer.load(s->animManager(), - am, - dataPath + "erleb-AR/models/bern/bern-christoffel.gltf", - texPath, - nullptr, - false, - true, - nullptr, - 0.3f); // ambient factor + // 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)); + sv->camera(cam1); + sv->doWaitOnIdle(false); + } + else if (sceneID == SID_ParticleSystem_Demo) //................................................ + { + // 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."); - // Make city with hard edges and without shadow mapping - SLNode* Umg = bern->findChild("Umgebung-Swisstopo"); - Umg->setMeshMat(matVideoBkgd, true); - Umg->setDrawBitsRec(SL_DB_WITHEDGES, true); - Umg->castsShadows(false); + // Create a scene group node + SLNode* scene = new SLNode("scene node"); + s->root3D(scene); - // Hide some objects - bern->findChild("Baldachin-Glas")->drawBits()->set(SL_DB_HIDDEN, true); - bern->findChild("Baldachin-Stahl")->drawBits()->set(SL_DB_HIDDEN, true); + // 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 the video background shader on the baldachin and the ground with shadow mapping - bern->findChild("Baldachin-Stahl")->setMeshMat(matVideoBkgdSM, true); - bern->findChild("Baldachin-Glas")->setMeshMat(matVideoBkgdSM, true); - bern->findChild("Chr-Alt-Stadtboden")->setMeshMat(matVideoBkgdSM, true); - bern->findChild("Chr-Neu-Stadtboden")->setMeshMat(matVideoBkgdSM, true); + // 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); - // Hide the new (last) version of the Christoffel tower - bern->findChild("Chr-Neu")->drawBits()->set(SL_DB_HIDDEN, true); + // 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) //........................................... + { + // 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 material for water - SLGLTexture* cubemap = new SLGLTexture(am, - dataPath + "erleb-AR/models/bern/Sea1+X1024.jpg", - dataPath + "erleb-AR/models/bern/Sea1-X1024.jpg", - dataPath + "erleb-AR/models/bern/Sea1+Y1024.jpg", - dataPath + "erleb-AR/models/bern/Sea1-Y1024.jpg", - dataPath + "erleb-AR/models/bern/Sea1+Z1024.jpg", - dataPath + "erleb-AR/models/bern/Sea1-Z1024.jpg"); - // Material for water - SLMaterial* matWater = new SLMaterial(am, "water", SLCol4f::BLACK, SLCol4f::BLACK, 100, 0.1f, 0.9f, 1.5f); - matWater->translucency(1000); - matWater->transmissive(SLCol4f::WHITE); - matWater->addTexture(cubemap); - matWater->program(new SLGLProgramGeneric(am, - shaderPath + "Reflect.vert", - shaderPath + "Reflect.frag")); - bern->findChild("Chr-Wasser")->setMeshMat(matWater, true); + // Create a scene group node + SLNode* scene = new SLNode("scene node"); + s->root3D(scene); - // Add axis object a world origin (Loeb Ecke) - SLNode* axis = new SLNode(new SLCoordAxis(am), "Axis Node"); - axis->setDrawBitsRec(SL_DB_MESHWIRED, false); - axis->rotate(-90, 1, 0, 0); - axis->castsShadows(false); + // 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); - // Bridge rotation animation - SLNode* bridge = bern->findChild("Chr-Alt-Tor"); - SLAnimation* bridgeAnim = s->animManager().createNodeAnimation("Gate animation", 8.0f, true, EC_inOutQuint, AL_pingPongLoop); - bridgeAnim->createNodeAnimTrackForRotation(bridge, 90, bridge->forwardOS()); + // 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"); - // Gate translation animation - SLNode* gate = bern->findChild("Chr-Alt-Gatter"); - SLAnimation* gateAnim = s->animManager().createNodeAnimation("Gatter Animation", 8.0f, true, EC_inOutQuint, AL_pingPongLoop); - gateAnim->createNodeAnimTrackForTranslation(gate, SLVec3f(0.0f, -3.6f, 0.0f)); + // 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); - SLNode* scene = new SLNode("Scene"); + 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); + } + else if (sceneID == SID_ParticleSystem_Fountain) //............................................ + { + // 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); - scene->addChild(sunLight); - scene->addChild(axis); - scene->addChild(bern); + + // 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); - // initialize sensor stuff - AppDemo::devLoc.originLatLonAlt(46.94763, 7.44074, 542.2); // Loeb Ecken - AppDemo::devLoc.defaultLatLonAlt(46.94841, 7.43970, 542.2 + 1.7); // Bahnhof Ausgang in Augenhöhe + // 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"); - AppDemo::devLoc.nameLocations().push_back(SLLocation("Loeb Ecken, Origin", 46, 56, 51.451, 7, 26, 26.676, 542.2)); - AppDemo::devLoc.nameLocations().push_back(SLLocation("Milchgässli, Velomarkierung, (N)", 46, 56, 54.197, 7, 26, 23.366, 541.2 + 1.7)); - AppDemo::devLoc.nameLocations().push_back(SLLocation("Spitalgasse (E)", 46, 56, 51.703, 7, 26, 27.565, 542.1 + 1.7)); - AppDemo::devLoc.nameLocations().push_back(SLLocation("Tramhaltestelle UBS, eckiger Schachtd. (S)", 46, 56, 50.366, 7, 26, 24.544, 542.3 + 1.7)); - AppDemo::devLoc.nameLocations().push_back(SLLocation("Ecke Schauplatz-Christoffelgasse (S)", 46, 56, 50.139, 7, 26, 27.225, 542.1 + 1.7)); - AppDemo::devLoc.nameLocations().push_back(SLLocation("Bubenbergplatz (S)", 46, 56, 50.304, 7, 26, 22.113, 542.4 + 1.7)); - AppDemo::devLoc.nameLocations().push_back(SLLocation("Heiliggeistkirche (Dole, N-W)", 46, 56, 53.500, 7, 26, 25.499, 541.6 + 1.7)); - AppDemo::devLoc.originLatLonAlt(AppDemo::devLoc.nameLocations()[0].posWGS84LatLonAlt); - AppDemo::devLoc.activeNamedLocation(1); // This sets the location 1 as defaultENU - AppDemo::devLoc.locMaxDistanceM(1000.0f); // Max. Distanz. zum Loeb Ecken - AppDemo::devLoc.improveOrigin(false); // Keine autom. Verbesserung vom Origin - AppDemo::devLoc.useOriginAltitude(true); - AppDemo::devLoc.hasOrigin(true); - AppDemo::devLoc.offsetMode(LOM_twoFingerY); - AppDemo::devRot.zeroYawAtStart(false); - AppDemo::devRot.offsetMode(ROM_oneFingerX); - - // This loads the DEM file and overwrites the altitude of originLatLonAlt and defaultLatLonAlt - SLstring tif = dataPath + "erleb-AR/models/bern/DEM-Bern-2600_1199-WGS84.tif"; - AppDemo::devLoc.loadGeoTiff(tif); + // 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); -#if defined(SL_OS_MACIOS) || defined(SL_OS_ANDROID) - AppDemo::devLoc.isUsed(true); - AppDemo::devRot.isUsed(true); - cam1->camAnim(SLCamAnim::CA_deviceRotLocYUp); -#else - AppDemo::devLoc.isUsed(false); - AppDemo::devRot.isUsed(false); - SLVec3d pos_d = AppDemo::devLoc.defaultENU() - AppDemo::devLoc.originENU(); - SLVec3f pos_f((SLfloat)pos_d.x, (SLfloat)pos_d.y, (SLfloat)pos_d.z); - cam1->translation(pos_f); - cam1->focalDist(pos_f.length()); - cam1->lookAt(SLVec3f::ZERO); - cam1->camAnim(SLCamAnim::CA_turntableYUp); -#endif + // 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); - sv->doWaitOnIdle(false); // for constant video feed - sv->camera(cam1); + // 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_ErlebARBielBFH) //..................................................... + else if (sceneID == SID_ParticleSystem_Sun) //................................................. { - s->name("Biel-BFH AR"); - s->info("Augmented Reality at Biel-BFH"); + // 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 video texture on global pointer updated in AppDemoVideo - videoTexture = new SLGLTexture(am, texPath + "LiveVideoError.png", GL_LINEAR, GL_LINEAR); + // Create a scene group node + SLNode* scene = new SLNode("scene node"); + s->root3D(scene); - // Define shader that shows on all pixels the video background - SLGLProgram* spVideoBackground = new SLGLProgramGeneric(am, - shaderPath + "PerPixTmBackground.vert", - shaderPath + "PerPixTmBackground.frag"); - SLMaterial* matVideoBkgd = new SLMaterial(am, - "matVideoBkgd", - videoTexture, - nullptr, - nullptr, - nullptr, - spVideoBackground); + // 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"); - SLCamera* cam1 = new SLCamera("Camera 1"); - cam1->translation(0, 2, 0); - cam1->lookAt(-10, 2, 0); - cam1->clipNear(1); - cam1->clipFar(1000); - cam1->setInitialState(); - cam1->devRotLoc(&AppDemo::devRot, &AppDemo::devLoc); - cam1->background().texture(videoTexture); + // 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); - // Turn on main video - CVCapture::instance()->videoType(VT_MAIN); + 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 directional light for the sunlight - SLLightDirect* sunLight = new SLLightDirect(am, s, 5.0f); - sunLight->powers(1.0f, 1.0f, 1.0f); - sunLight->attenuation(1, 0, 0); - sunLight->doSunPowerAdaptation(true); - sunLight->createsShadows(true); - sunLight->createShadowMap(-100, 150, SLVec2f(150, 150), SLVec2i(4096, 4096)); - sunLight->doSmoothShadows(true); - sunLight->castsShadows(false); + SLMesh* pSMesh = ps; + SLNode* pSNode = new SLNode(pSMesh, "Particle Sun node"); + scene->addChild(pSNode); - // Let the sun be rotated by time and location - AppDemo::devLoc.sunLightNode(sunLight); + // 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) //.......................................... + { + // 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"); - SLAssimpImporter importer; - SLNode* bfh = importer.load(s->animManager(), - am, - dataPath + "erleb-AR/models/biel/Biel-BFH-Rolex.gltf", - texPath); + // Create a scene group node + SLNode* scene = new SLNode("scene node"); + s->root3D(scene); - bfh->setMeshMat(matVideoBkgd, true); + // 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"); - // Make terrain a video shine trough - // bfh->findChild("Terrain")->setMeshMat(matVideoBkgd, true); + // 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); - /* Make buildings transparent - SLNode* buildings = bfh->findChild("Buildings"); - SLNode* roofs = bfh->findChild("Roofs"); - auto updateTranspFnc = [](SLMaterial* m) {m->kt(0.5f);}; - buildings->updateMeshMat(updateTranspFnc, true); - roofs->updateMeshMat(updateTranspFnc, true); + 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)); - // Set ambient on all child nodes - bfh->updateMeshMat([](SLMaterial* m) { m->ambient(SLCol4f(.2f, .2f, .2f)); }, true); - */ + SLMesh* pSMesh = ps; + SLNode* pSNode = new SLNode(pSMesh, "Particle Ring Fire node"); + pSNode->rotate(90, 1, 0, 0); + scene->addChild(pSNode); - // Add axis object a world origin - SLNode* axis = new SLNode(new SLCoordAxis(am), "Axis Node"); - axis->scale(2); - axis->rotate(-90, 1, 0, 0); + // 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) //......................................... + { + // 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"); - SLNode* scene = new SLNode("Scene"); + // Create a scene group node + SLNode* scene = new SLNode("scene node"); s->root3D(scene); - scene->addChild(sunLight); - scene->addChild(axis); - scene->addChild(bfh); + + // 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); - // initialize sensor stuff - AppDemo::devLoc.originLatLonAlt(47.14271, 7.24337, 488.2); // Ecke Giosa - AppDemo::devLoc.defaultLatLonAlt(47.14260, 7.24310, 488.7 + 1.7); // auf Parkplatz - AppDemo::devLoc.locMaxDistanceM(1000.0f); - AppDemo::devLoc.improveOrigin(false); - AppDemo::devLoc.useOriginAltitude(true); - AppDemo::devLoc.hasOrigin(true); - AppDemo::devLoc.offsetMode(LOM_twoFingerY); - AppDemo::devRot.zeroYawAtStart(false); - AppDemo::devRot.offsetMode(ROM_oneFingerX); + // 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"); - // This loads the DEM file and overwrites the altitude of originLatLonAlt and defaultLatLonAlt - SLstring tif = dataPath + "erleb-AR/models/biel/DEM_Biel-BFH_WGS84.tif"; - AppDemo::devLoc.loadGeoTiff(tif); + SLNode* complexFire = createComplexFire(am, + s, + true, + texTorchSmk, + texFireFlm, + 8, + 8, + texCircle, + texSmokeB, + texSmokeW); + scene->addChild(complexFire); -#if defined(SL_OS_MACIOS) || defined(SL_OS_ANDROID) - AppDemo::devLoc.isUsed(true); - AppDemo::devRot.isUsed(true); - cam1->camAnim(SLCamAnim::CA_deviceRotLocYUp); -#else - AppDemo::devLoc.isUsed(false); - AppDemo::devRot.isUsed(false); - SLVec3d pos_d = AppDemo::devLoc.defaultENU() - AppDemo::devLoc.originENU(); - SLVec3f pos_f((SLfloat)pos_d.x, (SLfloat)pos_d.y, (SLfloat)pos_d.z); - cam1->translation(pos_f); - cam1->focalDist(pos_f.length()); - cam1->lookAt(SLVec3f::ZERO); - cam1->camAnim(SLCamAnim::CA_turntableYUp); -#endif + // Room around + { + // Room parent node + SLNode* room = new SLNode("Room"); + scene->addChild(room); - sv->doWaitOnIdle(false); // for constant video feed - sv->camera(cam1); - sv->drawBits()->on(SL_DB_ONLYEDGES); - } - else if (sceneID == SID_ErlebARAugustaRauricaTmp) //........................................... - { - s->name("Augusta Raurica Temple AR"); - s->info(s->name()); + // 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); - // Create video texture on global pointer updated in AppDemoVideo - videoTexture = new SLGLTexture(am, texPath + "LiveVideoError.png", GL_LINEAR, GL_LINEAR); - videoTexture->texType(TT_videoBkgd); + // 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 - // Create see through video background material without shadow mapping - SLMaterial* matVideoBkgd = new SLMaterial(am, "matVideoBkgd", videoTexture); - matVideoBkgd->reflectionModel(RM_Custom); + // 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); - // Create see through video background material with shadow mapping - SLMaterial* matVideoBkgdSM = new SLMaterial(am, "matVideoBkgdSM", videoTexture); - matVideoBkgdSM->reflectionModel(RM_Custom); - matVideoBkgdSM->ambient(SLCol4f(0.6f, 0.6f, 0.6f)); - matVideoBkgdSM->getsShadows(true); + // 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); - // Set the camera - SLCamera* cam1 = new SLCamera("Camera 1"); - cam1->translation(0, 50, -150); - cam1->lookAt(0, 0, 0); - cam1->clipNear(1); - cam1->clipFar(400); - cam1->focalDist(150); - cam1->devRotLoc(&AppDemo::devRot, &AppDemo::devLoc); - cam1->background().texture(videoTexture); + // 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); - // Turn on main video - CVCapture::instance()->videoType(VT_MAIN); + // 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); - string shdDir = shaderPath; - string texDir = texPath; - string datDir = dataPath + "erleb-AR/models/augst/"; + // 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 directional light for the sunlight - SLLightDirect* sunLight = new SLLightDirect(am, s, 1.0f); - sunLight->translate(-42, 10, 13); - sunLight->powers(1.0f, 1.5f, 1.0f); - sunLight->attenuation(1, 0, 0); - sunLight->doSunPowerAdaptation(true); - sunLight->createsShadows(true); - sunLight->createShadowMapAutoSize(cam1, SLVec2i(2048, 2048), 4); - sunLight->shadowMap()->cascadesFactor(3.0); - // sunLight->createShadowMap(-100, 250, SLVec2f(210, 180), SLVec2i(4096, 4096)); - sunLight->doSmoothShadows(true); - sunLight->castsShadows(false); - sunLight->shadowMinBias(0.001f); - sunLight->shadowMaxBias(0.001f); - AppDemo::devLoc.sunLightNode(sunLight); // Let the sun be rotated by time and location + // 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); - // Load main model - SLAssimpImporter importer; //(LV_diagnostic); - SLNode* thtAndTmp = importer.load(s->animManager(), - am, - datDir + "augst-thtL2-tmpL1.gltf", - texDir, - nullptr, - true, // delete tex images after build - true, // only meshes - nullptr, // no replacement material - 0.4f); // 40% ambient reflection + // 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); - // Rotate to the true geographic rotation - thtAndTmp->rotate(16.7f, 0, 1, 0, TS_parent); + // 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); - // Let the video shine through on some objects without shadow mapping - SLNode* tmpUnderground = thtAndTmp->findChild("TmpUnderground"); - if (tmpUnderground) tmpUnderground->setMeshMat(matVideoBkgd, true); - SLNode* thtUnderground = thtAndTmp->findChild("ThtUnderground"); - if (thtUnderground) thtUnderground->setMeshMat(matVideoBkgd, true); + // 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); - // Let the video shine through on some objects with shadow mapping - SLNode* tmpFloor = thtAndTmp->findChild("TmpFloor"); - if (tmpFloor) tmpFloor->setMeshMat(matVideoBkgdSM, true); + // 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); + } - SLNode* terrain = thtAndTmp->findChild("Terrain"); - if (terrain) - { - terrain->setMeshMat(matVideoBkgdSM, true); - terrain->castsShadows(false); - } - SLNode* thtFrontTerrain = thtAndTmp->findChild("ThtFrontTerrain"); - if (thtFrontTerrain) + else if (sceneID == SID_Benchmark1_LargeModel) //.............................................. + { + SLstring largeFile = modelPath + "PLY/xyzrgb_dragon/xyzrgb_dragon.ply"; + + if (SLFileStorage::exists(largeFile, IOK_model)) { - thtFrontTerrain->setMeshMat(matVideoBkgdSM, true); - thtFrontTerrain->castsShadows(false); - } + s->name("Large Model Benchmark Scene"); + s->info("Large Model with 7.2 mio. triangles."); - // Add axis object a world origin - SLNode* axis = new SLNode(new SLCoordAxis(am), "Axis Node"); - axis->setDrawBitsRec(SL_DB_MESHWIRED, false); - axis->rotate(-90, 1, 0, 0); - axis->castsShadows(false); + // Material for glass + SLMaterial* diffuseMat = new SLMaterial(am, "diffuseMat", SLCol4f::WHITE, SLCol4f::WHITE); - // Set some ambient light - thtAndTmp->updateMeshMat([](SLMaterial* m) - { m->ambient(SLCol4f(.25f, .25f, .25f)); }, - true); - SLNode* scene = new SLNode("Scene"); - s->root3D(scene); - scene->addChild(sunLight); - scene->addChild(axis); - scene->addChild(thtAndTmp); - scene->addChild(cam1); + SLCamera* cam1 = new SLCamera("Camera 1"); + cam1->translation(0, 0, 220); + cam1->lookAt(0, 0, 0); + cam1->clipNear(1); + cam1->clipFar(10000); + cam1->focalDist(220); + cam1->background().colors(SLCol4f(0.7f, 0.7f, 0.7f), SLCol4f(0.2f, 0.2f, 0.2f)); + cam1->setInitialState(); + cam1->devRotLoc(&AppDemo::devRot, &AppDemo::devLoc); - // initialize sensor stuff - AppDemo::devLoc.useOriginAltitude(false); - AppDemo::devLoc.nameLocations().push_back(SLLocation("Center of theatre, Origin", 47, 31, 59.461, 7, 43, 19.446, 282.6)); - AppDemo::devLoc.nameLocations().push_back(SLLocation("Treppe Tempel", 47, 31, 58.933, 7, 43, 16.799, 290.5 + 1.7)); - AppDemo::devLoc.nameLocations().push_back(SLLocation("Abzweigung (Dolendeckel)", 47, 31, 57.969, 7, 43, 17.946, 286.5 + 1.7)); - AppDemo::devLoc.nameLocations().push_back(SLLocation("Marker bei Tempel", 47, 31, 59.235, 7, 43, 15.161, 293.1 + 1.7)); - AppDemo::devLoc.nameLocations().push_back(SLLocation("Theater 1. Rang Zugang Ost", 47, 31, 59.698, 7, 43, 20.518, 291.0 + 1.7)); - AppDemo::devLoc.nameLocations().push_back(SLLocation("Theater 1. Rang Nord", 47, 32, 0.216, 7, 43, 19.173, 291.0 + 1.7)); - AppDemo::devLoc.originLatLonAlt(AppDemo::devLoc.nameLocations()[0].posWGS84LatLonAlt); - AppDemo::devLoc.activeNamedLocation(1); // This sets the location 1 as defaultENU - AppDemo::devLoc.locMaxDistanceM(1000.0f); // Max. allowed distance to origin - AppDemo::devLoc.improveOrigin(false); // No autom. origin improvement - AppDemo::devLoc.hasOrigin(true); - AppDemo::devLoc.offsetMode(LOM_twoFingerY); - AppDemo::devRot.zeroYawAtStart(false); - AppDemo::devRot.offsetMode(ROM_oneFingerX); + SLLightSpot* light1 = new SLLightSpot(am, s, 200, 200, 200, 1); + light1->powers(0.1f, 1.0f, 1.0f); + light1->attenuation(1, 0, 0); - // This loads the DEM file and overwrites the altitude of originLatLonAlt and defaultLatLonAlt - SLstring tif = datDir + "DTM-Theater-Tempel-WGS84.tif"; - AppDemo::devLoc.loadGeoTiff(tif); + SLAssimpImporter importer; + gDragonModel = importer.load(s->animManager(), + am, + largeFile, + texPath, + nullptr, + false, // delete tex images after build + true, + diffuseMat, + 0.2f, + false, + nullptr, + SLProcess_Triangulate | SLProcess_JoinIdenticalVertices); -#if defined(SL_OS_MACIOS) || defined(SL_OS_ANDROID) - AppDemo::devLoc.isUsed(true); - AppDemo::devRot.isUsed(true); - cam1->camAnim(SLCamAnim::CA_deviceRotLocYUp); -#else - AppDemo::devLoc.isUsed(false); - AppDemo::devRot.isUsed(false); - SLVec3d pos_d = AppDemo::devLoc.defaultENU() - AppDemo::devLoc.originENU(); - SLVec3f pos_f((SLfloat)pos_d.x, (SLfloat)pos_d.y, (SLfloat)pos_d.z); - cam1->translation(pos_f); - cam1->focalDist(pos_f.length()); - cam1->lookAt(SLVec3f::ZERO); - cam1->camAnim(SLCamAnim::CA_turntableYUp); -#endif + SLNode* scene = new SLNode("Scene"); + s->root3D(scene); + scene->addChild(light1); + scene->addChild(gDragonModel); + scene->addChild(cam1); - sv->doWaitOnIdle(false); // for constant video feed - sv->camera(cam1); + sv->camera(cam1); + } } - else if (sceneID == SID_ErlebARAugustaRauricaTht) //........................................... + else if (sceneID == SID_Benchmark2_MassiveNodes) //............................................ { - s->name("Augusta Raurica Theater AR"); + s->name("Massive Data Benchmark Scene"); s->info(s->name()); - // Create video texture on global pointer updated in AppDemoVideo - videoTexture = new SLGLTexture(am, texPath + "LiveVideoError.png", GL_LINEAR, GL_LINEAR); - videoTexture->texType(TT_videoBkgd); - - // Create see through video background material without shadow mapping - SLMaterial* matVideoBkgd = new SLMaterial(am, "matVideoBkgd", videoTexture); - matVideoBkgd->reflectionModel(RM_Custom); - - // Create see through video background material with shadow mapping - SLMaterial* matVideoBkgdSM = new SLMaterial(am, "matVideoBkgdSM", videoTexture); - matVideoBkgdSM->reflectionModel(RM_Custom); - matVideoBkgdSM->ambient(SLCol4f(0.6f, 0.6f, 0.6f)); - matVideoBkgdSM->getsShadows(true); - - // Setup the camera SLCamera* cam1 = new SLCamera("Camera 1"); - cam1->translation(0, 50, -150); + cam1->clipNear(0.1f); + cam1->clipFar(100); + cam1->translation(0, 0, 50); cam1->lookAt(0, 0, 0); - cam1->clipNear(1); - cam1->clipFar(400); - cam1->focalDist(150); - cam1->devRotLoc(&AppDemo::devRot, &AppDemo::devLoc); - cam1->background().texture(videoTexture); - - // Turn on main video - CVCapture::instance()->videoType(VT_MAIN); - - string shdDir = shaderPath; - string texDir = texPath; - string datDir = dataPath + "erleb-AR/models/augst/"; - - // Create directional light for the sunlight - SLLightDirect* sunLight = new SLLightDirect(am, s, 1.0f); - sunLight->translate(-42, 10, 13); - sunLight->powers(1.0f, 1.5f, 1.0f); - sunLight->attenuation(1, 0, 0); - sunLight->doSunPowerAdaptation(true); - sunLight->createsShadows(true); - sunLight->createShadowMapAutoSize(cam1, SLVec2i(2048, 2048), 4); - sunLight->shadowMap()->cascadesFactor(3.0); - // sunLight->createShadowMap(-100, 250, SLVec2f(210, 180), SLVec2i(4096, 4096)); - sunLight->doSmoothShadows(true); - sunLight->castsShadows(false); - sunLight->shadowMinBias(0.001f); - sunLight->shadowMaxBias(0.001f); - AppDemo::devLoc.sunLightNode(sunLight); // Let the sun be rotated by time and location - - // Load main model - SLAssimpImporter importer; //(LV_diagnostic); - SLNode* thtAndTmp = importer.load(s->animManager(), - am, - datDir + "augst-thtL1-tmpL2.gltf", - texDir, - nullptr, - true, // delete tex images after build - true, // only meshes - nullptr, // no replacement material - 0.4f); // 40% ambient reflection - - // Rotate to the true geographic rotation - thtAndTmp->rotate(16.7f, 0, 1, 0, TS_parent); + cam1->focalDist(50); + cam1->background().colors(SLCol4f(0.1f, 0.1f, 0.1f)); + cam1->setInitialState(); - // Let the video shine through on some objects without shadow mapping - SLNode* tmpUnderground = thtAndTmp->findChild("TmpUnderground"); - if (tmpUnderground) tmpUnderground->setMeshMat(matVideoBkgd, true); - SLNode* thtUnderground = thtAndTmp->findChild("ThtUnderground"); - if (thtUnderground) thtUnderground->setMeshMat(matVideoBkgd, true); + SLLightSpot* light1 = new SLLightSpot(am, s, 15, 15, 15, 0.3f); + light1->powers(0.2f, 0.8f, 1.0f); + light1->attenuation(1, 0, 0); - // Let the video shine through on some objects with shadow mapping - SLNode* tmpFloor = thtAndTmp->findChild("TmpFloor"); - if (tmpFloor) tmpFloor->setMeshMat(matVideoBkgdSM, true); + SLNode* scene = new SLNode; + s->root3D(scene); + scene->addChild(cam1); + scene->addChild(light1); - SLNode* terrain = thtAndTmp->findChild("Terrain"); - if (terrain) + // Generate NUM_MAT materials + const int NUM_MAT = 20; + SLVMaterial mat; + for (int i = 0; i < NUM_MAT; ++i) { - terrain->setMeshMat(matVideoBkgdSM, true); - terrain->castsShadows(false); + SLGLTexture* texC = new SLGLTexture(am, texPath + "earth2048_C_Q95.jpg"); + SLstring matName = "mat-" + std::to_string(i); + mat.push_back(new SLMaterial(am, matName.c_str(), texC)); + SLCol4f color; + color.hsva2rgba(SLVec4f(Utils::TWOPI * (float)i / (float)NUM_MAT, 1.0f, 1.0f)); + mat[i]->diffuse(color); } - SLNode* thtFrontTerrain = thtAndTmp->findChild("ThtFrontTerrain"); - if (thtFrontTerrain) + + // create a 3D array of spheres + SLint halfSize = 10; + SLuint n = 0; + for (SLint iZ = -halfSize; iZ <= halfSize; ++iZ) { - thtFrontTerrain->setMeshMat(matVideoBkgdSM, true); - thtFrontTerrain->castsShadows(false); + for (SLint iY = -halfSize; iY <= halfSize; ++iY) + { + for (SLint iX = -halfSize; iX <= halfSize; ++iX) + { + // Choose a random material index + SLuint res = 36; + SLint iMat = (SLint)Utils::random(0, NUM_MAT - 1); + SLstring nodeName = "earth-" + std::to_string(n); + + // Create a new sphere and node and translate it + SLSphere* earth = new SLSphere(am, 0.3f, res, res, nodeName, mat[iMat]); + SLNode* sphere = new SLNode(earth); + sphere->translate(float(iX), float(iY), float(iZ), TS_object); + scene->addChild(sphere); + // SL_LOG("Earth: %000d (Mat: %00d)", n, iMat); + n++; + } + } } - // Add axis object a world origin - SLNode* axis = new SLNode(new SLCoordAxis(am), "Axis Node"); - axis->setDrawBitsRec(SL_DB_MESHWIRED, false); - axis->rotate(-90, 1, 0, 0); - axis->castsShadows(false); + sv->camera(cam1); + sv->doWaitOnIdle(false); + } + else if (sceneID == SID_Benchmark3_NodeAnimations) //.......................................... + { + s->name("Massive Node Animation Benchmark Scene"); + s->info(s->name()); - // Set some ambient light - thtAndTmp->updateMeshMat([](SLMaterial* m) - { m->ambient(SLCol4f(.25f, .25f, .25f)); }, - true); - SLNode* scene = new SLNode("Scene"); + SLCamera* cam1 = new SLCamera("Camera 1"); + cam1->clipNear(0.1f); + cam1->clipFar(100); + cam1->translation(0, 2.5f, 20); + cam1->focalDist(20); + cam1->lookAt(0, 2.5f, 0); + cam1->background().colors(SLCol4f(0.1f, 0.1f, 0.1f)); + cam1->setInitialState(); + + SLLightSpot* light1 = new SLLightSpot(am, s, 15, 15, 15, 0.3f); + light1->powers(0.2f, 0.8f, 1.0f); + light1->attenuation(1, 0, 0); + + SLNode* scene = new SLNode; s->root3D(scene); - scene->addChild(sunLight); - scene->addChild(axis); - scene->addChild(thtAndTmp); scene->addChild(cam1); + scene->addChild(light1); - // initialize sensor stuff - AppDemo::devLoc.useOriginAltitude(false); - AppDemo::devLoc.nameLocations().push_back(SLLocation("Center of theatre, Origin", 47, 31, 59.461, 7, 43, 19.446, 282.6)); - AppDemo::devLoc.nameLocations().push_back(SLLocation("Treppe Tempel", 47, 31, 58.933, 7, 43, 16.799, 290.5 + 1.7)); - AppDemo::devLoc.nameLocations().push_back(SLLocation("Abzweigung (Dolendeckel)", 47, 31, 57.969, 7, 43, 17.946, 286.5 + 1.7)); - AppDemo::devLoc.nameLocations().push_back(SLLocation("Marker bei Tempel", 47, 31, 59.235, 7, 43, 15.161, 293.1 + 1.7)); - AppDemo::devLoc.nameLocations().push_back(SLLocation("Theater 1. Rang Zugang Ost", 47, 31, 59.698, 7, 43, 20.518, 291.0 + 1.7)); - AppDemo::devLoc.nameLocations().push_back(SLLocation("Theater 1. Rang Nord", 47, 32, 0.216, 7, 43, 19.173, 291.0 + 1.7)); - AppDemo::devLoc.originLatLonAlt(AppDemo::devLoc.nameLocations()[0].posWGS84LatLonAlt); - AppDemo::devLoc.activeNamedLocation(1); // This sets the location 1 as defaultENU - AppDemo::devLoc.locMaxDistanceM(1000.0f); // Max. allowed distance to origin - AppDemo::devLoc.improveOrigin(false); // No autom. origin improvement - AppDemo::devLoc.hasOrigin(true); - AppDemo::devLoc.offsetMode(LOM_twoFingerY); - AppDemo::devRot.zeroYawAtStart(false); - AppDemo::devRot.offsetMode(ROM_oneFingerX); + // Generate NUM_MAT materials + const int NUM_MAT = 20; + SLVMaterial mat; + for (int i = 0; i < NUM_MAT; ++i) + { + SLGLTexture* texC = new SLGLTexture(am, texPath + "earth2048_C_Q95.jpg"); // color map + SLstring matName = "mat-" + std::to_string(i); + mat.push_back(new SLMaterial(am, matName.c_str(), texC)); + SLCol4f color; + color.hsva2rgba(SLVec4f(Utils::TWOPI * (float)i / (float)NUM_MAT, 1.0f, 1.0f)); + mat[i]->diffuse(color); + } - // This loads the DEM file and overwrites the altitude of originLatLonAlt and defaultLatLonAlt - SLstring tif = datDir + "DTM-Theater-Tempel-WGS84.tif"; - AppDemo::devLoc.loadGeoTiff(tif); + // create rotating sphere group + SLint maxDepth = 5; -#if defined(SL_OS_MACIOS) || defined(SL_OS_ANDROID) - AppDemo::devLoc.isUsed(true); - AppDemo::devRot.isUsed(true); - cam1->camAnim(SLCamAnim::CA_deviceRotLocYUp); -#else - AppDemo::devLoc.isUsed(false); - AppDemo::devRot.isUsed(false); - SLVec3d pos_d = AppDemo::devLoc.defaultENU() - AppDemo::devLoc.originENU(); - SLVec3f pos_f((SLfloat)pos_d.x, (SLfloat)pos_d.y, (SLfloat)pos_d.z); - cam1->translation(pos_f); - cam1->focalDist(pos_f.length()); - cam1->lookAt(SLVec3f::ZERO); - cam1->camAnim(SLCamAnim::CA_turntableYUp); -#endif + SLint resolution = 18; + scene->addChild(RotatingSphereGroup(am, + s, + maxDepth, + 0, + 0, + 0, + 1, + resolution, + mat)); - sv->doWaitOnIdle(false); // for constant video feed sv->camera(cam1); + sv->doWaitOnIdle(false); } - else if (sceneID == SID_ErlebARAugustaRauricaTmpTht) //........................................ + else if (sceneID == SID_Benchmark4_SkinnedAnimations) //....................................... { - s->name("Augusta Raurica AR Temple and Theater"); + SLint size = 20; + SLint numAstroboys = size * size; + SLchar name[512]; + snprintf(name, sizeof(name), "Massive Skinned Animation Benchmark w. %d individual Astroboys", numAstroboys); + s->name(name); s->info(s->name()); - // Create video texture on global pointer updated in AppDemoVideo - videoTexture = new SLGLTexture(am, texPath + "LiveVideoError.png", GL_LINEAR, GL_LINEAR); - videoTexture->texType(TT_videoBkgd); + // Create materials + SLMaterial* m1 = new SLMaterial(am, "m1", SLCol4f::GRAY); + m1->specular(SLCol4f::BLACK); - // Create see through video background material without shadow mapping - SLMaterial* matVideoBkgd = new SLMaterial(am, "matVideoBkgd", videoTexture); - matVideoBkgd->reflectionModel(RM_Custom); - - // Create see through video background material with shadow mapping - SLMaterial* matVideoBkgdSM = new SLMaterial(am, "matVideoBkgdSM", videoTexture); - matVideoBkgdSM->reflectionModel(RM_Custom); - matVideoBkgdSM->ambient(SLCol4f(0.6f, 0.6f, 0.6f)); - matVideoBkgdSM->getsShadows(true); + // Define a light + SLLightSpot* light1 = new SLLightSpot(am, s, 100, 40, 100, 1); + light1->powers(0.1f, 1.0f, 1.0f); + light1->attenuation(1, 0, 0); - // Setup the camera - SLCamera* cam1 = new SLCamera("Camera 1"); - cam1->translation(0, 50, -150); + // Define camera + SLCamera* cam1 = new SLCamera; + cam1->translation(0, 30, 0); cam1->lookAt(0, 0, 0); - cam1->clipNear(1); - cam1->clipFar(400); - cam1->focalDist(150); + cam1->focalDist(cam1->translationOS().length()); + cam1->background().colors(SLCol4f(0.1f, 0.4f, 0.8f)); + cam1->setInitialState(); cam1->devRotLoc(&AppDemo::devRot, &AppDemo::devLoc); - cam1->background().texture(videoTexture); - - // Turn on main video - CVCapture::instance()->videoType(VT_MAIN); - string shdDir = shaderPath; - string texDir = texPath; - string datDir = dataPath + "erleb-AR/models/augst/"; + // Floor rectangle + SLNode* rect = new SLNode(new SLRectangle(am, + SLVec2f(-20, -20), + SLVec2f(20, 20), + SLVec2f(0, 0), + SLVec2f(50, 50), + 50, + 50, + "Floor", + m1)); + rect->rotate(90, -1, 0, 0); - // Create directional light for the sunlight - SLLightDirect* sunLight = new SLLightDirect(am, s, 1.0f); - sunLight->translate(-42, 10, 13); - sunLight->powers(1.0f, 1.5f, 1.0f); - sunLight->attenuation(1, 0, 0); - sunLight->doSunPowerAdaptation(true); - sunLight->createsShadows(true); - sunLight->createShadowMapAutoSize(cam1, SLVec2i(2048, 2048), 4); - sunLight->shadowMap()->cascadesFactor(3.0); - // Old stanard single map shadow map - // sunLight->createShadowMap(-100, 250, SLVec2f(210, 180), SLVec2i(4096, 4096)); - sunLight->doSmoothShadows(true); - sunLight->castsShadows(false); - sunLight->shadowMinBias(0.001f); - sunLight->shadowMaxBias(0.001f); - AppDemo::devLoc.sunLightNode(sunLight); // Let the sun be rotated by time and location + SLAssimpImporter importer; - // Load main model - SLAssimpImporter importer; //(LV_diagnostic); - SLNode* thtAndTmp = importer.load(s->animManager(), - am, - datDir + "augst-thtL1L2-tmpL1L2.gltf", - texDir, - nullptr, - true, // delete tex images after build - true, // only meshes - nullptr, // no replacement material - 0.4f); // 40% ambient reflection + // Assemble scene + SLNode* scene = new SLNode("scene group"); + s->root3D(scene); + scene->addChild(light1); + scene->addChild(rect); + scene->addChild(cam1); - // Rotate to the true geographic rotation - thtAndTmp->rotate(16.7f, 0, 1, 0, TS_parent); + // create army with individual astroboys + SLfloat offset = 1.0f; + SLfloat z = (float)(size - 1) * offset * 0.5f; - // Let the video shine through on some objects without shadow mapping - SLNode* tmpUnderground = thtAndTmp->findChild("TmpUnderground"); - if (tmpUnderground) tmpUnderground->setMeshMat(matVideoBkgd, true); - SLNode* thtUnderground = thtAndTmp->findChild("ThtUnderground"); - if (thtUnderground) thtUnderground->setMeshMat(matVideoBkgd, true); + for (SLint iZ = 0; iZ < size; ++iZ) + { + SLfloat x = -(float)(size - 1) * offset * 0.5f; - // Let the video shine through on some objects with shadow mapping - SLNode* tmpFloor = thtAndTmp->findChild("TmpFloor"); - if (tmpFloor) tmpFloor->setMeshMat(matVideoBkgdSM, true); + for (SLint iX = 0; iX < size; ++iX) + { + SLNode* astroboy = importer.load(s->animManager(), + am, + modelPath + "DAE/AstroBoy/AstroBoy.dae", + texPath); - SLNode* terrain = thtAndTmp->findChild("Terrain"); - if (terrain) - { - terrain->setMeshMat(matVideoBkgdSM, true); - terrain->castsShadows(false); + s->animManager().lastAnimPlayback()->playForward(); + s->animManager().lastAnimPlayback()->playbackRate(Utils::random(0.5f, 1.5f)); + astroboy->translate(x, 0, z, TS_object); + scene->addChild(astroboy); + x += offset; + } + z -= offset; } - SLNode* thtFrontTerrain = thtAndTmp->findChild("ThtFrontTerrain"); - if (thtFrontTerrain) + + sv->camera(cam1); + } + else if (sceneID == SID_Benchmark5_ColumnsNoLOD || + sceneID == SID_Benchmark6_ColumnsLOD) //.............................................. + { + SLstring modelFile = modelPath + "GLTF/CorinthianColumn/Corinthian-Column-Round-LOD.gltf"; + SLstring texCFile = modelPath + "GLTF/CorinthianColumn/PavementSlateSquare2_2K_DIF.jpg"; + SLstring texNFile = modelPath + "GLTF/CorinthianColumn/PavementSlateSquare2_2K_NRM.jpg"; + + if (SLFileStorage::exists(modelFile, IOK_model) && + SLFileStorage::exists(texCFile, IOK_image) && + SLFileStorage::exists(texNFile, IOK_image)) { - thtFrontTerrain->setMeshMat(matVideoBkgdSM, true); - thtFrontTerrain->castsShadows(false); - } + SLchar name[512]; + SLint size; + if (sceneID == SID_Benchmark5_ColumnsNoLOD) + { + size = 25; + snprintf(name, sizeof(name), "%d corinthian columns without LOD", size * size); + s->name(name); + } + else + { + size = 50; + snprintf(name, sizeof(name), "%d corinthian columns with LOD", size * size); + s->name(name); + } + s->info(s->name() + " with cascaded shadow mapping. In the Day-Time dialogue you can change the sun angle."); - // Add axis object a world origin - SLNode* axis = new SLNode(new SLCoordAxis(am), "Axis Node"); - axis->setDrawBitsRec(SL_DB_MESHWIRED, false); - axis->rotate(-90, 1, 0, 0); - axis->castsShadows(false); + // Create ground material + SLGLTexture* texFloorDif = new SLGLTexture(am, texCFile, SL_ANISOTROPY_MAX, GL_LINEAR); + SLGLTexture* texFloorNrm = new SLGLTexture(am, texNFile, SL_ANISOTROPY_MAX, GL_LINEAR); + SLMaterial* matFloor = new SLMaterial(am, "matFloor", texFloorDif, texFloorNrm); - // Set some ambient light - thtAndTmp->updateMeshMat([](SLMaterial* m) - { m->ambient(SLCol4f(.25f, .25f, .25f)); }, - true); - SLNode* scene = new SLNode("Scene"); - s->root3D(scene); - scene->addChild(sunLight); - scene->addChild(axis); - scene->addChild(thtAndTmp); - scene->addChild(cam1); + // Define camera + SLCamera* cam1 = new SLCamera; + cam1->translation(0, 1.7f, 20); + cam1->lookAt(0, 1.7f, 0); + cam1->focalDist(cam1->translationOS().length()); + cam1->clipFar(600); + cam1->background().colors(SLCol4f(0.1f, 0.4f, 0.8f)); + cam1->setInitialState(); - // initialize sensor stuff - AppDemo::devLoc.useOriginAltitude(false); - AppDemo::devLoc.nameLocations().push_back(SLLocation("Center of theatre, Origin", 47, 31, 59.461, 7, 43, 19.446, 282.6)); - AppDemo::devLoc.nameLocations().push_back(SLLocation("Treppe Tempel", 47, 31, 58.933, 7, 43, 16.799, 290.5 + 1.7)); - AppDemo::devLoc.nameLocations().push_back(SLLocation("Abzweigung (Dolendeckel)", 47, 31, 57.969, 7, 43, 17.946, 286.5 + 1.7)); - AppDemo::devLoc.nameLocations().push_back(SLLocation("Marker bei Tempel", 47, 31, 59.235, 7, 43, 15.161, 293.1 + 1.7)); - AppDemo::devLoc.nameLocations().push_back(SLLocation("Theater 1. Rang Zugang Ost", 47, 31, 59.698, 7, 43, 20.518, 291.0 + 1.7)); - AppDemo::devLoc.nameLocations().push_back(SLLocation("Theater 1. Rang Nord", 47, 32, 0.216, 7, 43, 19.173, 291.0 + 1.7)); - AppDemo::devLoc.originLatLonAlt(AppDemo::devLoc.nameLocations()[0].posWGS84LatLonAlt); - AppDemo::devLoc.activeNamedLocation(1); // This sets the location 1 as defaultENU - AppDemo::devLoc.locMaxDistanceM(1000.0f); // Max. allowed distance to origin - AppDemo::devLoc.improveOrigin(false); // No autom. origin improvement - AppDemo::devLoc.hasOrigin(true); - AppDemo::devLoc.offsetMode(LOM_twoFingerY); - AppDemo::devRot.zeroYawAtStart(false); - AppDemo::devRot.offsetMode(ROM_oneFingerX); + // Create directional light for the sunlight + SLLightDirect* sunLight = new SLLightDirect(am, s, 1.0f); + sunLight->powers(0.25f, 1.0f, 1.0f); + sunLight->attenuation(1, 0, 0); + sunLight->translation(0, 1.7f, 0); + sunLight->lookAt(-1, 0, -1); + sunLight->doSunPowerAdaptation(true); - // Level of Detail switch for Temple and Theater - SLNode* tmpAltar = thtAndTmp->findChild("TmpAltar"); - SLNode* tmpL1 = thtAndTmp->findChild("Tmp-L1"); - SLNode* tmpL2 = thtAndTmp->findChild("Tmp-L2"); - SLNode* thtL1 = thtAndTmp->findChild("Tht-L1"); - SLNode* thtL2 = thtAndTmp->findChild("Tht-L2"); - thtL1->drawBits()->set(SL_DB_HIDDEN, false); - thtL2->drawBits()->set(SL_DB_HIDDEN, true); - tmpL1->drawBits()->set(SL_DB_HIDDEN, false); - tmpL2->drawBits()->set(SL_DB_HIDDEN, true); + // Add cascaded shadow mapping + sunLight->createsShadows(true); + sunLight->createShadowMapAutoSize(cam1); + sunLight->doSmoothShadows(true); + sunLight->castsShadows(false); + sunLight->shadowMinBias(0.003f); + sunLight->shadowMaxBias(0.012f); - // Add level of detail switch callback lambda - cam1->onCamUpdateCB([=](SLSceneView* sv) - { - SLVec3f posCam = sv->camera()->updateAndGetWM().translation(); - SLVec3f posAlt = tmpAltar->updateAndGetWM().translation(); - SLVec3f distCamAlt = posCam - posAlt; - float tmpDist = distCamAlt.length(); - float thtDist = posCam.length(); - - // If the temple is closer than the theater activate level 1 and deactivate level 2 - if (tmpDist < thtDist) + // Let the sun be rotated by time and location + AppDemo::devLoc.sunLightNode(sunLight); + AppDemo::devLoc.originLatLonAlt(47.14271, 7.24337, 488.2); // Ecke Giosa + AppDemo::devLoc.defaultLatLonAlt(47.14260, 7.24310, 488.7 + 1.7); // auf Parkplatz + + // Floor rectangle + SLNode* rect = new SLNode(new SLRectangle(am, + SLVec2f(-200, -200), + SLVec2f(200, 200), + SLVec2f(0, 0), + SLVec2f(50, 50), + 50, + 50, + "Floor", + matFloor)); + rect->rotate(90, -1, 0, 0); + rect->castsShadows(false); + + // Load the corinthian column + SLAssimpImporter importer; + SLNode* columnLOD = importer.load(s->animManager(), + am, + modelFile, + texPath, + nullptr, + false, // delete tex images after build + true, // only meshes + nullptr, // no replacement material + 1.0f); // 40% ambient reflection + + SLNode* columnL0 = columnLOD->findChild("Corinthian-Column-Round-L0"); + SLNode* columnL1 = columnLOD->findChild("Corinthian-Column-Round-L1"); + SLNode* columnL2 = columnLOD->findChild("Corinthian-Column-Round-L2"); + SLNode* columnL3 = columnLOD->findChild("Corinthian-Column-Round-L3"); + + // Assemble scene + SLNode* scene = new SLNode("Scene"); + s->root3D(scene); + scene->addChild(sunLight); + scene->addChild(rect); + scene->addChild(cam1); + + // create loads of pillars + SLint numColumns = size * size; + SLfloat offset = 5.0f; + SLfloat z = (float)(size - 1) * offset * 0.5f; + + for (SLint iZ = 0; iZ < size; ++iZ) { - thtL1->drawBits()->set(SL_DB_HIDDEN, true); - thtL2->drawBits()->set(SL_DB_HIDDEN, false); - tmpL1->drawBits()->set(SL_DB_HIDDEN, false); - tmpL2->drawBits()->set(SL_DB_HIDDEN, true); + SLfloat x = -(float)(size - 1) * offset * 0.5f; + + for (SLint iX = 0; iX < size; ++iX) + { + SLint iZX = iZ * size + iX; + + if (sceneID == SID_Benchmark5_ColumnsNoLOD) + { + // Without just the level 0 node + string strNode = "Node" + std::to_string(iZX); + SLNode* column = new SLNode(columnL1->mesh(), strNode + "-L0"); + column->translate(x, 0, z, TS_object); + scene->addChild(column); + } + else + { + // With LOD parent node and 3 levels + string strLOD = "LOD" + std::to_string(iZX); + SLNodeLOD* lod_group = new SLNodeLOD(strLOD); + lod_group->translate(x, 0, z, TS_object); + lod_group->addChildLOD(new SLNode(columnL1->mesh(), strLOD + "-L0"), 0.1f, 3); + lod_group->addChildLOD(new SLNode(columnL2->mesh(), strLOD + "-L1"), 0.01f, 3); + lod_group->addChildLOD(new SLNode(columnL3->mesh(), strLOD + "-L2"), 0.0001f, 3); + scene->addChild(lod_group); + } + x += offset; + } + z -= offset; } - else - { - thtL1->drawBits()->set(SL_DB_HIDDEN, false); - thtL2->drawBits()->set(SL_DB_HIDDEN, true); - tmpL1->drawBits()->set(SL_DB_HIDDEN, true); - tmpL2->drawBits()->set(SL_DB_HIDDEN, false); - } }); - // This loads the DEM file and overwrites the altitude of originLatLonAlt and defaultLatLonAlt - SLstring tif = datDir + "DTM-Theater-Tempel-WGS84.tif"; - AppDemo::devLoc.loadGeoTiff(tif); + // Set active camera & the root pointer + sv->camera(cam1); + sv->doWaitOnIdle(false); + } + } + else if (sceneID == SID_Benchmark7_JansUniverse) //............................................ + { + s->name("Jan's Universe Test Scene"); + s->info(s->name()); + + SLCamera* cam1 = new SLCamera("Camera 1"); + cam1->clipNear(0.1f); + cam1->clipFar(1000); + cam1->translation(0, 0, 75); + cam1->focalDist(75); + cam1->lookAt(0, 0, 0); + cam1->background().colors(SLCol4f(0.3f, 0.3f, 0.3f)); + cam1->setInitialState(); -#if defined(SL_OS_MACIOS) || defined(SL_OS_ANDROID) - AppDemo::devLoc.isUsed(true); - AppDemo::devRot.isUsed(true); - cam1->camAnim(SLCamAnim::CA_deviceRotLocYUp); + // Root scene node + SLNode* scene = new SLNode; + s->root3D(scene); + scene->addChild(cam1); + + // Generate NUM_MAT cook-torrance materials +#ifndef SL_GLES + const int NUM_MAT_MESH = 100; + SLuint const levels = 6; + SLuint const childCount = 8; #else - AppDemo::devLoc.isUsed(false); - AppDemo::devRot.isUsed(false); - SLVec3d pos_d = AppDemo::devLoc.defaultENU() - AppDemo::devLoc.originENU(); - SLVec3f pos_f((SLfloat)pos_d.x, (SLfloat)pos_d.y, (SLfloat)pos_d.z); - cam1->translation(pos_f); - cam1->focalDist(pos_f.length()); - cam1->lookAt(SLVec3f::ZERO); - cam1->camAnim(SLCamAnim::CA_turntableYUp); + const int NUM_MAT_MESH = 20; + SLuint const levels = 6; + SLuint const childCount = 8; #endif + SLVMaterial materials(NUM_MAT_MESH); + for (int i = 0; i < NUM_MAT_MESH; ++i) + { + /* + SLGLProgram* spTex = new SLGLProgramGeneric(am, + shaderPath + "PerPixCookTm.vert", + shaderPath + "PerPixCookTm.frag");*/ + SLstring matName = "mat-" + std::to_string(i); + materials[i] = new SLMaterial(am, + matName.c_str(), + nullptr, + new SLGLTexture(am, texPath + "rusty-metal_2048_C.jpg"), + nullptr, // new SLGLTexture(am, texPath + "rusty-metal_2048_N.jpg"), + new SLGLTexture(am, texPath + "rusty-metal_2048_M.jpg"), + new SLGLTexture(am, texPath + "rusty-metal_2048_R.jpg"), + nullptr, + nullptr); + SLCol4f color; + color.hsva2rgba(SLVec4f(Utils::TWOPI * (float)i / (float)NUM_MAT_MESH, 1.0f, 1.0f)); + materials[i]->diffuse(color); + } + + // Generate NUM_MESH sphere meshes + SLVMesh meshes(NUM_MAT_MESH); + for (int i = 0; i < NUM_MAT_MESH; ++i) + { + SLstring meshName = "mesh-" + std::to_string(i); + meshes[i] = new SLSphere(am, 1.0f, 32, 32, meshName.c_str(), materials[i % NUM_MAT_MESH]); + } + + // Create universe + generateUniverse(am, s, scene, 0, levels, childCount, materials, meshes); - sv->doWaitOnIdle(false); // for constant video feed sv->camera(cam1); + sv->doWaitOnIdle(false); } - else if (sceneID == SID_ErlebARAventicumAmphiteatre) //........................................ + else if (sceneID == SID_Benchmark8_ParticleSystemFireComplex) //............................... { - s->name("Aventicum Amphitheatre AR (AO)"); - s->info("Augmented Reality for Aventicum Amphitheatre (AO)"); - - // Create video texture on global pointer updated in AppDemoVideo - videoTexture = new SLGLTexture(am, texPath + "LiveVideoError.png", GL_LINEAR, GL_LINEAR); - videoTexture->texType(TT_videoBkgd); - - // Create see through video background material without shadow mapping - SLMaterial* matVideoBkgd = new SLMaterial(am, "matVideoBkgd", videoTexture); - matVideoBkgd->reflectionModel(RM_Custom); - - // Create see through video background material with shadow mapping - SLMaterial* matVideoBkgdSM = new SLMaterial(am, "matVideoBkgdSM", videoTexture); - matVideoBkgdSM->reflectionModel(RM_Custom); - matVideoBkgdSM->ambient(SLCol4f(0.6f, 0.6f, 0.6f)); - matVideoBkgdSM->getsShadows(true); + s->name("Fire Complex Test Scene"); + s->info(s->name()); - // Setup the camera SLCamera* cam1 = new SLCamera("Camera 1"); - cam1->translation(0, 50, -150); + cam1->clipNear(0.1f); + cam1->clipFar(1000); + cam1->translation(0, 10, 40); + cam1->focalDist(100); cam1->lookAt(0, 0, 0); - cam1->clipNear(1); - cam1->clipFar(400); - cam1->focalDist(150); + cam1->background().colors(SLCol4f(0.3f, 0.3f, 0.3f)); cam1->setInitialState(); - cam1->devRotLoc(&AppDemo::devRot, &AppDemo::devLoc); - cam1->background().texture(videoTexture); - - // Turn on main video - CVCapture::instance()->videoType(VT_MAIN); - - // Create directional light for the sunlight - SLLightDirect* sunLight = new SLLightDirect(am, s, 1.0f); - sunLight->powers(1.0f, 1.5f, 1.0f); - sunLight->attenuation(1, 0, 0); - sunLight->translation(0, 1, 0); - sunLight->doSunPowerAdaptation(true); - sunLight->createsShadows(true); - sunLight->createShadowMapAutoSize(cam1, SLVec2i(2048, 2048), 4); - sunLight->shadowMap()->cascadesFactor(3.0); - // sunLight->createShadowMap(-70, 70, SLVec2f(140, 100), SLVec2i(4096, 4096)); - sunLight->doSmoothShadows(true); - sunLight->castsShadows(false); - sunLight->shadowMinBias(0.001f); - sunLight->shadowMaxBias(0.003f); - - // Let the sun be rotated by time and location - AppDemo::devLoc.sunLightNode(sunLight); - SLAssimpImporter importer; - SLNode* amphiTheatre = importer.load(s->animManager(), - am, - dataPath + "erleb-AR/models/avenches/avenches-amphitheater.gltf", - texPath, - nullptr, - false, // delete tex images after build - true, // only meshes - nullptr, // no replacement material - 0.4f); // 40% ambient reflection + // Root scene node + SLNode* root = new SLNode; + s->root3D(root); + root->addChild(cam1); + const int NUM_NODES = 250; - // Rotate to the true geographic rotation - amphiTheatre->rotate(13.25f, 0, 1, 0, TS_parent); + // Create textures and materials + SLGLTexture* texFireCld = new SLGLTexture(am, texPath + "ParticleFirecloudTransparent_C.png"); + SLGLTexture* texFireFlm = new SLGLTexture(am, texPath + "ParticleFlames_00_8x4_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"); - // Let the video shine through some objects - amphiTheatre->findChild("Tht-Aussen-Untergrund")->setMeshMat(matVideoBkgd, true); - amphiTheatre->findChild("Tht-Eingang-Ost-Boden")->setMeshMat(matVideoBkgdSM, true); - amphiTheatre->findChild("Tht-Arenaboden")->setMeshMat(matVideoBkgdSM, true); - // amphiTheatre->findChild("Tht-Aussen-Terrain")->setMeshMat(matVideoBkgdSM, true); + SLVNode nodes(NUM_NODES); + for (int i = 0; i < NUM_NODES; ++i) + { + SLNode* fireComplex = createComplexFire(am, + s, + false, + texFireCld, + texFireFlm, + 8, + 4, + texCircle, + texSmokeB, + texSmokeW); + fireComplex->translate(-20.0f + (float)(i % 20) * 2, + 0.0f, + -(float)((i - (float)(i % 20)) / 20) * 4, + TS_object); + root->addChild(fireComplex); + } - // Add axis object a world origin - SLNode* axis = new SLNode(new SLCoordAxis(am), "Axis Node"); - axis->setDrawBitsRec(SL_DB_MESHWIRED, false); - axis->rotate(-90, 1, 0, 0); - axis->castsShadows(false); + sv->camera(cam1); + sv->doWaitOnIdle(false); + } + else if (sceneID == SID_Benchmark9_ParticleSystemManyParticles) //............................. + { + s->name("Particle System number Scene"); + s->info(s->name()); - SLNode* scene = new SLNode("Scene"); - s->root3D(scene); - scene->addChild(sunLight); - scene->addChild(axis); - scene->addChild(amphiTheatre); - scene->addChild(cam1); + SLCamera* cam1 = new SLCamera("Camera 1"); + cam1->clipNear(0.1f); + cam1->clipFar(1000); + cam1->translation(0, 0, 400); + cam1->focalDist(400); + cam1->lookAt(0, 0, 0); + cam1->background().colors(SLCol4f(0.3f, 0.3f, 0.3f)); + cam1->setInitialState(); - // initialize sensor stuff - AppDemo::devLoc.useOriginAltitude(false); - AppDemo::devLoc.nameLocations().push_back(SLLocation("Arena Centre, Origin", 46, 52, 51.685, 7, 2, 33.458, 461.4)); - AppDemo::devLoc.nameLocations().push_back(SLLocation("Entrance East, Manhole Cover", 46, 52, 52.344, 7, 2, 37.600, 461.4 + 1.7)); - AppDemo::devLoc.nameLocations().push_back(SLLocation("Arena, Sewer Cover West", 46, 52, 51.484, 7, 2, 32.307, 461.3 + 1.7)); - AppDemo::devLoc.nameLocations().push_back(SLLocation("Arena, Sewer Cover East", 46, 52, 51.870, 7, 2, 34.595, 461.1 + 1.7)); - AppDemo::devLoc.nameLocations().push_back(SLLocation("Stand South, Sewer Cover", 46, 52, 50.635, 7, 2, 34.099, 471.7 + 1.7)); - AppDemo::devLoc.nameLocations().push_back(SLLocation("Stand West, Sewer Cover", 46, 52, 51.889, 7, 2, 31.567, 471.7 + 1.7)); - AppDemo::devLoc.originLatLonAlt(AppDemo::devLoc.nameLocations()[0].posWGS84LatLonAlt); - AppDemo::devLoc.activeNamedLocation(1); // This sets the location 1 as defaultENU - AppDemo::devLoc.locMaxDistanceM(1000.0f); // Max. Distanz. zum Nullpunkt - AppDemo::devLoc.improveOrigin(false); // Keine autom. Verbesserung vom Origin - AppDemo::devLoc.hasOrigin(true); - AppDemo::devLoc.offsetMode(LOM_twoFingerY); - AppDemo::devRot.zeroYawAtStart(false); - AppDemo::devRot.offsetMode(ROM_oneFingerX); + // Root scene node + SLNode* root = new SLNode; + root->addChild(cam1); - // This loads the DEM file and overwrites the altitude of originLatLonAlt and defaultLatLonAlt - SLstring tif = dataPath + "erleb-AR/models/avenches/DTM-Aventicum-WGS84.tif"; - AppDemo::devLoc.loadGeoTiff(tif); + // 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"); -#if defined(SL_OS_MACIOS) || defined(SL_OS_ANDROID) - AppDemo::devLoc.isUsed(true); - AppDemo::devRot.isUsed(true); - cam1->camAnim(SLCamAnim::CA_deviceRotLocYUp); -#else - AppDemo::devLoc.isUsed(false); - AppDemo::devRot.isUsed(false); - SLVec3d pos_d = AppDemo::devLoc.defaultENU() - AppDemo::devLoc.originENU(); - SLVec3f pos_f((SLfloat)pos_d.x, (SLfloat)pos_d.y, (SLfloat)pos_d.z); - cam1->translation(pos_f); - cam1->focalDist(pos_f.length()); - cam1->lookAt(SLVec3f::ZERO); - cam1->camAnim(SLCamAnim::CA_turntableYUp); -#endif + // Create meshes and nodes + SLParticleSystem* ps = new SLParticleSystem(am, + 1000000, + SLVec3f(-10.0f, -10.0f, -10.0f), + SLVec3f(10.0f, 10.0f, 10.0f), + 4.0f, + texC, + "Particle System", + texFlipbook); + ps->doAlphaOverLT(false); + ps->doSizeOverLT(false); + ps->doRotation(false); + ps->doShape(true); + ps->shapeType(ST_Box); + ps->shapeScale(100.0f, 100.0f, 100.0f); + ps->doDirectionSpeed(true); + ps->doBlendBrightness(true); + ps->doColor(true); + ps->color(SLCol4f(0.875f, 0.156f, 0.875f, 1.0f)); + ps->speed(0.0f); + SLMesh* pSMesh = ps; + SLNode* pSNode = new SLNode(pSMesh, "Particle system node"); + root->addChild(pSNode); - sv->doWaitOnIdle(false); // for constant video feed sv->camera(cam1); + sv->doWaitOnIdle(false); + s->root3D(root); } - else if (sceneID == SID_ErlebARAventicumCigognier) //.......................................... - { - s->name("Aventicum Cigognier AR (AO)"); - s->info("Augmented Reality for Aventicum Cigognier Temple"); + + // These scenes assets are not publicly distributed + else if (sceneID == SID_ErlebARBernChristoffel) //............................................. + { + s->name("Christoffel Tower AR"); + s->info("Augmented Reality Christoffel Tower"); // Create video texture on global pointer updated in AppDemoVideo videoTexture = new SLGLTexture(am, texPath + "LiveVideoError.png", GL_LINEAR, GL_LINEAR); @@ -5046,13 +5168,11 @@ resolution shadows near the camera and lower resolution shadows further away."); matVideoBkgdSM->ambient(SLCol4f(0.6f, 0.6f, 0.6f)); matVideoBkgdSM->getsShadows(true); - // Setup the camera SLCamera* cam1 = new SLCamera("Camera 1"); - cam1->translation(0, 50, -150); - cam1->lookAt(0, 0, 0); + cam1->translation(0, 2, 0); + cam1->lookAt(-10, 2, 0); cam1->clipNear(1); - cam1->clipFar(400); - cam1->focalDist(150); + cam1->clipFar(700); cam1->setInitialState(); cam1->devRotLoc(&AppDemo::devRot, &AppDemo::devLoc); cam1->background().texture(videoTexture); @@ -5061,72 +5181,116 @@ resolution shadows near the camera and lower resolution shadows further away."); CVCapture::instance()->videoType(VT_MAIN); // Create directional light for the sunlight - SLLightDirect* sunLight = new SLLightDirect(am, s, 1.0f); - sunLight->powers(1.0f, 1.0f, 1.0f); + SLLightDirect* sunLight = new SLLightDirect(am, s, 2.0f); + sunLight->translate(-44.89f, 18.05f, -26.07f); + sunLight->powers(1.0f, 1.5f, 1.0f); sunLight->attenuation(1, 0, 0); - sunLight->translation(0, 10, 0); - sunLight->lookAt(10, 0, 10); sunLight->doSunPowerAdaptation(true); sunLight->createsShadows(true); sunLight->createShadowMapAutoSize(cam1, SLVec2i(2048, 2048), 4); sunLight->shadowMap()->cascadesFactor(3.0); - // sunLight->createShadowMap(-70, 120, SLVec2f(150, 150), SLVec2i(2048, 2048)); + // sunLight->createShadowMap(-100, 150, SLVec2f(200, 150), SLVec2i(4096, 4096)); sunLight->doSmoothShadows(true); sunLight->castsShadows(false); sunLight->shadowMinBias(0.001f); sunLight->shadowMaxBias(0.003f); + AppDemo::devLoc.sunLightNode(sunLight); // Let the sun be rotated by time and location - // Let the sun be rotated by time and location - AppDemo::devLoc.sunLightNode(sunLight); - + // Import the main model SLAssimpImporter importer; - SLNode* cigognier = importer.load(s->animManager(), - am, - dataPath + "erleb-AR/models/avenches/avenches-cigognier.gltf", - texPath, - nullptr, - true, // delete tex images after build - true, // only meshes - nullptr, // no replacement material - 0.4f); // 40% ambient reflection + SLNode* bern = importer.load(s->animManager(), + am, + dataPath + "erleb-AR/models/bern/bern-christoffel.gltf", + texPath, + nullptr, + false, + true, + nullptr, + 0.3f); // ambient factor - // Rotate to the true geographic rotation - cigognier->rotate(-36.52f, 0, 1, 0, TS_parent); + // Make city with hard edges and without shadow mapping + SLNode* Umg = bern->findChild("Umgebung-Swisstopo"); + Umg->setMeshMat(matVideoBkgd, true); + Umg->setDrawBitsRec(SL_DB_WITHEDGES, true); + Umg->castsShadows(false); - // Let the video shine through some objects - cigognier->findChild("Tmp-Sol-Pelouse")->setMeshMat(matVideoBkgdSM, true); - cigognier->findChild("Tmp-Souterrain")->setMeshMat(matVideoBkgd, true); + // Hide some objects + bern->findChild("Baldachin-Glas")->drawBits()->set(SL_DB_HIDDEN, true); + bern->findChild("Baldachin-Stahl")->drawBits()->set(SL_DB_HIDDEN, true); - // Add axis object a world origin + // Set the video background shader on the baldachin and the ground with shadow mapping + bern->findChild("Baldachin-Stahl")->setMeshMat(matVideoBkgdSM, true); + bern->findChild("Baldachin-Glas")->setMeshMat(matVideoBkgdSM, true); + bern->findChild("Chr-Alt-Stadtboden")->setMeshMat(matVideoBkgdSM, true); + bern->findChild("Chr-Neu-Stadtboden")->setMeshMat(matVideoBkgdSM, true); + + // Hide the new (last) version of the Christoffel tower + bern->findChild("Chr-Neu")->drawBits()->set(SL_DB_HIDDEN, true); + + // Create textures and material for water + SLGLTexture* cubemap = new SLGLTexture(am, + dataPath + "erleb-AR/models/bern/Sea1+X1024.jpg", + dataPath + "erleb-AR/models/bern/Sea1-X1024.jpg", + dataPath + "erleb-AR/models/bern/Sea1+Y1024.jpg", + dataPath + "erleb-AR/models/bern/Sea1-Y1024.jpg", + dataPath + "erleb-AR/models/bern/Sea1+Z1024.jpg", + dataPath + "erleb-AR/models/bern/Sea1-Z1024.jpg"); + // Material for water + SLMaterial* matWater = new SLMaterial(am, "water", SLCol4f::BLACK, SLCol4f::BLACK, 100, 0.1f, 0.9f, 1.5f); + matWater->translucency(1000); + matWater->transmissive(SLCol4f::WHITE); + matWater->addTexture(cubemap); + matWater->program(new SLGLProgramGeneric(am, + shaderPath + "Reflect.vert", + shaderPath + "Reflect.frag")); + bern->findChild("Chr-Wasser")->setMeshMat(matWater, true); + + // Add axis object a world origin (Loeb Ecke) SLNode* axis = new SLNode(new SLCoordAxis(am), "Axis Node"); axis->setDrawBitsRec(SL_DB_MESHWIRED, false); axis->rotate(-90, 1, 0, 0); axis->castsShadows(false); + // Bridge rotation animation + SLNode* bridge = bern->findChild("Chr-Alt-Tor"); + SLAnimation* bridgeAnim = s->animManager().createNodeAnimation("Gate animation", 8.0f, true, EC_inOutQuint, AL_pingPongLoop); + bridgeAnim->createNodeAnimTrackForRotation(bridge, 90, bridge->forwardOS()); + + // Gate translation animation + SLNode* gate = bern->findChild("Chr-Alt-Gatter"); + SLAnimation* gateAnim = s->animManager().createNodeAnimation("Gatter Animation", 8.0f, true, EC_inOutQuint, AL_pingPongLoop); + gateAnim->createNodeAnimTrackForTranslation(gate, SLVec3f(0.0f, -3.6f, 0.0f)); + SLNode* scene = new SLNode("Scene"); s->root3D(scene); scene->addChild(sunLight); scene->addChild(axis); - scene->addChild(cigognier); + scene->addChild(bern); scene->addChild(cam1); // initialize sensor stuff - AppDemo::devLoc.useOriginAltitude(false); - AppDemo::devLoc.nameLocations().push_back(SLLocation("Center of place, Origin", 46, 52, 53.245, 7, 2, 47.198, 450.9)); - AppDemo::devLoc.nameLocations().push_back(SLLocation("At the altar", 46, 52, 53.107, 7, 2, 47.498, 450.9 + 1.7)); - AppDemo::devLoc.nameLocations().push_back(SLLocation("Old AR viewer", 46, 52, 53.666, 7, 2, 48.316, 451.0 + 1.7)); - AppDemo::devLoc.nameLocations().push_back(SLLocation("Temple Entrance in hall", 46, 52, 54.007, 7, 2, 45.702, 453.0 + 1.7)); + AppDemo::devLoc.originLatLonAlt(46.94763, 7.44074, 542.2); // Loeb Ecken + AppDemo::devLoc.defaultLatLonAlt(46.94841, 7.43970, 542.2 + 1.7); // Bahnhof Ausgang in Augenhöhe + + AppDemo::devLoc.nameLocations().push_back(SLLocation("Loeb Ecken, Origin", 46, 56, 51.451, 7, 26, 26.676, 542.2)); + AppDemo::devLoc.nameLocations().push_back(SLLocation("Milchgässli, Velomarkierung, (N)", 46, 56, 54.197, 7, 26, 23.366, 541.2 + 1.7)); + AppDemo::devLoc.nameLocations().push_back(SLLocation("Spitalgasse (E)", 46, 56, 51.703, 7, 26, 27.565, 542.1 + 1.7)); + AppDemo::devLoc.nameLocations().push_back(SLLocation("Tramhaltestelle UBS, eckiger Schachtd. (S)", 46, 56, 50.366, 7, 26, 24.544, 542.3 + 1.7)); + AppDemo::devLoc.nameLocations().push_back(SLLocation("Ecke Schauplatz-Christoffelgasse (S)", 46, 56, 50.139, 7, 26, 27.225, 542.1 + 1.7)); + AppDemo::devLoc.nameLocations().push_back(SLLocation("Bubenbergplatz (S)", 46, 56, 50.304, 7, 26, 22.113, 542.4 + 1.7)); + AppDemo::devLoc.nameLocations().push_back(SLLocation("Heiliggeistkirche (Dole, N-W)", 46, 56, 53.500, 7, 26, 25.499, 541.6 + 1.7)); AppDemo::devLoc.originLatLonAlt(AppDemo::devLoc.nameLocations()[0].posWGS84LatLonAlt); AppDemo::devLoc.activeNamedLocation(1); // This sets the location 1 as defaultENU - AppDemo::devLoc.locMaxDistanceM(1000.0f); // Max. allowed distance from origin - AppDemo::devLoc.improveOrigin(false); // No auto improvement from + AppDemo::devLoc.locMaxDistanceM(1000.0f); // Max. Distanz. zum Loeb Ecken + AppDemo::devLoc.improveOrigin(false); // Keine autom. Verbesserung vom Origin + AppDemo::devLoc.useOriginAltitude(true); AppDemo::devLoc.hasOrigin(true); AppDemo::devLoc.offsetMode(LOM_twoFingerY); AppDemo::devRot.zeroYawAtStart(false); AppDemo::devRot.offsetMode(ROM_oneFingerX); // This loads the DEM file and overwrites the altitude of originLatLonAlt and defaultLatLonAlt - SLstring tif = dataPath + "erleb-AR/models/avenches/DTM-Aventicum-WGS84.tif"; + SLstring tif = dataPath + "erleb-AR/models/bern/DEM-Bern-2600_1199-WGS84.tif"; AppDemo::devLoc.loadGeoTiff(tif); #if defined(SL_OS_MACIOS) || defined(SL_OS_ANDROID) @@ -5140,39 +5304,38 @@ resolution shadows near the camera and lower resolution shadows further away."); SLVec3f pos_f((SLfloat)pos_d.x, (SLfloat)pos_d.y, (SLfloat)pos_d.z); cam1->translation(pos_f); cam1->focalDist(pos_f.length()); - cam1->lookAt(0, cam1->translationWS().y, 0); + cam1->lookAt(SLVec3f::ZERO); cam1->camAnim(SLCamAnim::CA_turntableYUp); #endif sv->doWaitOnIdle(false); // for constant video feed sv->camera(cam1); } - else if (sceneID == SID_ErlebARAventicumTheatre) //............................................ + else if (sceneID == SID_ErlebARBielBFH) //..................................................... { - s->name("Aventicum Theatre AR"); - s->info("Augmented Reality for Aventicum Theatre"); + s->name("Biel-BFH AR"); + s->info("Augmented Reality at Biel-BFH"); // Create video texture on global pointer updated in AppDemoVideo videoTexture = new SLGLTexture(am, texPath + "LiveVideoError.png", GL_LINEAR, GL_LINEAR); - videoTexture->texType(TT_videoBkgd); - // Create see through video background material without shadow mapping - SLMaterial* matVideoBkgd = new SLMaterial(am, "matVideoBkgd", videoTexture); - matVideoBkgd->reflectionModel(RM_Custom); - - // Create see through video background material with shadow mapping - SLMaterial* matVideoBkgdSM = new SLMaterial(am, "matVideoBkgdSM", videoTexture); - matVideoBkgdSM->reflectionModel(RM_Custom); - matVideoBkgdSM->ambient(SLCol4f(0.6f, 0.6f, 0.6f)); - matVideoBkgdSM->getsShadows(true); + // Define shader that shows on all pixels the video background + SLGLProgram* spVideoBackground = new SLGLProgramGeneric(am, + shaderPath + "PerPixTmBackground.vert", + shaderPath + "PerPixTmBackground.frag"); + SLMaterial* matVideoBkgd = new SLMaterial(am, + "matVideoBkgd", + videoTexture, + nullptr, + nullptr, + nullptr, + spVideoBackground); - // Setup the camera SLCamera* cam1 = new SLCamera("Camera 1"); - cam1->translation(0, 50, -150); - cam1->lookAt(0, 0, 0); + cam1->translation(0, 2, 0); + cam1->lookAt(-10, 2, 0); cam1->clipNear(1); - cam1->clipFar(300); - cam1->focalDist(150); + cam1->clipFar(1000); cam1->setInitialState(); cam1->devRotLoc(&AppDemo::devRot, &AppDemo::devLoc); cam1->background().texture(videoTexture); @@ -5181,76 +5344,65 @@ resolution shadows near the camera and lower resolution shadows further away."); CVCapture::instance()->videoType(VT_MAIN); // Create directional light for the sunlight - SLLightDirect* sunLight = new SLLightDirect(am, s, 1.0f); + SLLightDirect* sunLight = new SLLightDirect(am, s, 5.0f); sunLight->powers(1.0f, 1.0f, 1.0f); sunLight->attenuation(1, 0, 0); - sunLight->translation(0, 1, 0); - sunLight->doSunPowerAdaptation(true); sunLight->createsShadows(true); - sunLight->createShadowMapAutoSize(cam1, SLVec2i(2048, 2048), 4); - sunLight->shadowMap()->cascadesFactor(3.0); - // sunLight->createShadowMap(-80, 100, SLVec2f(130, 130), SLVec2i(4096, 4096)); + sunLight->createShadowMap(-100, 150, SLVec2f(150, 150), SLVec2i(4096, 4096)); sunLight->doSmoothShadows(true); sunLight->castsShadows(false); - sunLight->shadowMinBias(0.001f); - sunLight->shadowMaxBias(0.001f); // Let the sun be rotated by time and location AppDemo::devLoc.sunLightNode(sunLight); SLAssimpImporter importer; - SLNode* theatre = importer.load(s->animManager(), - am, - dataPath + "erleb-AR/models/avenches/avenches-theater.gltf", - texPath, - nullptr, - true, // delete tex images after build - true, // only meshes - nullptr, // no replacement material - 0.4f); // 40% ambient reflection + SLNode* bfh = importer.load(s->animManager(), + am, + dataPath + "erleb-AR/models/biel/Biel-BFH-Rolex.gltf", + texPath); - // Rotate to the true geographic rotation - theatre->rotate(-36.7f, 0, 1, 0, TS_parent); + bfh->setMeshMat(matVideoBkgd, true); - // Let the video shine through some objects - theatre->findChild("Tht-Rasen")->setMeshMat(matVideoBkgdSM, true); - theatre->findChild("Tht-Untergrund")->setMeshMat(matVideoBkgd, true); - theatre->findChild("Tht-Boden")->setMeshMat(matVideoBkgdSM, true); - theatre->findChild("Tht-Boden")->setDrawBitsRec(SL_DB_WITHEDGES, true); + // Make terrain a video shine trough + // bfh->findChild("Terrain")->setMeshMat(matVideoBkgd, true); + + /* Make buildings transparent + SLNode* buildings = bfh->findChild("Buildings"); + SLNode* roofs = bfh->findChild("Roofs"); + auto updateTranspFnc = [](SLMaterial* m) {m->kt(0.5f);}; + buildings->updateMeshMat(updateTranspFnc, true); + roofs->updateMeshMat(updateTranspFnc, true); + + // Set ambient on all child nodes + bfh->updateMeshMat([](SLMaterial* m) { m->ambient(SLCol4f(.2f, .2f, .2f)); }, true); + */ // Add axis object a world origin SLNode* axis = new SLNode(new SLCoordAxis(am), "Axis Node"); - axis->setDrawBitsRec(SL_DB_MESHWIRED, false); + axis->scale(2); axis->rotate(-90, 1, 0, 0); - axis->castsShadows(false); SLNode* scene = new SLNode("Scene"); s->root3D(scene); scene->addChild(sunLight); scene->addChild(axis); - scene->addChild(theatre); + scene->addChild(bfh); scene->addChild(cam1); // initialize sensor stuff - // https://map.geo.admin.ch/?lang=de&topic=ech&bgLayer=ch.swisstopo.swissimage&layers=ch.swisstopo.zeitreihen,ch.bfs.gebaeude_wohnungs_register,ch.bav.haltestellen-oev,ch.swisstopo.swisstlm3d-wanderwege&layers_opacity=1,1,1,0.8&layers_visibility=false,false,false,false&layers_timestamp=18641231,,,&E=2570281&N=1192204&zoom=13&crosshair=marker - AppDemo::devLoc.useOriginAltitude(false); - AppDemo::devLoc.nameLocations().push_back(SLLocation("Center of theatre, Origin", 46, 52, 49.041, 7, 2, 55.543, 454.9)); - AppDemo::devLoc.nameLocations().push_back(SLLocation("On the stage", 46, 52, 49.221, 7, 2, 55.206, 455.5 + 1.7)); - AppDemo::devLoc.nameLocations().push_back(SLLocation("At the tree (N-E)", 46, 52, 50.791, 7, 2, 55.960, 455.5 + 1.7)); - AppDemo::devLoc.nameLocations().push_back(SLLocation("Over the entrance (S)", 46, 52, 48.162, 7, 2, 56.097, 464.0 + 1.7)); - AppDemo::devLoc.nameLocations().push_back(SLLocation("At the 3rd tree (S-W)", 46, 52, 48.140, 7, 2, 51.506, 455.0 + 1.7)); - AppDemo::devLoc.originLatLonAlt(AppDemo::devLoc.nameLocations()[0].posWGS84LatLonAlt); - AppDemo::devLoc.activeNamedLocation(1); // This sets the location 1 as defaultENU - AppDemo::devLoc.locMaxDistanceM(1000.0f); // Max. Distanz. zum Nullpunkt - AppDemo::devLoc.improveOrigin(false); // Keine autom. Verbesserung vom Origin + AppDemo::devLoc.originLatLonAlt(47.14271, 7.24337, 488.2); // Ecke Giosa + AppDemo::devLoc.defaultLatLonAlt(47.14260, 7.24310, 488.7 + 1.7); // auf Parkplatz + AppDemo::devLoc.locMaxDistanceM(1000.0f); + AppDemo::devLoc.improveOrigin(false); + AppDemo::devLoc.useOriginAltitude(true); AppDemo::devLoc.hasOrigin(true); AppDemo::devLoc.offsetMode(LOM_twoFingerY); AppDemo::devRot.zeroYawAtStart(false); AppDemo::devRot.offsetMode(ROM_oneFingerX); // This loads the DEM file and overwrites the altitude of originLatLonAlt and defaultLatLonAlt - SLstring tif = dataPath + "erleb-AR/models/avenches/DTM-Aventicum-WGS84.tif"; + SLstring tif = dataPath + "erleb-AR/models/biel/DEM_Biel-BFH_WGS84.tif"; AppDemo::devLoc.loadGeoTiff(tif); #if defined(SL_OS_MACIOS) || defined(SL_OS_ANDROID) @@ -5270,11 +5422,12 @@ resolution shadows near the camera and lower resolution shadows further away."); sv->doWaitOnIdle(false); // for constant video feed sv->camera(cam1); + sv->drawBits()->on(SL_DB_ONLYEDGES); } - else if (sceneID == SID_ErlebARSutzKirchrain18) //............................................. + else if (sceneID == SID_ErlebARAugustaRauricaTmp) //........................................... { - s->name("Sutz, Kirchrain 18"); - s->info("Augmented Reality for Sutz, Kirchrain 18"); + s->name("Augusta Raurica Temple AR"); + s->info(s->name()); // Create video texture on global pointer updated in AppDemoVideo videoTexture = new SLGLTexture(am, texPath + "LiveVideoError.png", GL_LINEAR, GL_LINEAR); @@ -5290,57 +5443,76 @@ resolution shadows near the camera and lower resolution shadows further away."); matVideoBkgdSM->ambient(SLCol4f(0.6f, 0.6f, 0.6f)); matVideoBkgdSM->getsShadows(true); - // Create directional light for the sunlight - SLLightDirect* sunLight = new SLLightDirect(am, s, 5.0f); - sunLight->powers(1.0f, 1.0f, 1.0f); - sunLight->attenuation(1, 0, 0); - sunLight->translation(0, 10, 0); - sunLight->lookAt(10, 0, 10); - sunLight->doSunPowerAdaptation(true); - sunLight->createsShadows(true); - sunLight->createShadowMap(-100, 150, SLVec2f(150, 150), SLVec2i(4096, 4096)); - sunLight->doSmoothShadows(true); - sunLight->castsShadows(false); - - // Setup the camera + // Set the camera SLCamera* cam1 = new SLCamera("Camera 1"); cam1->translation(0, 50, -150); cam1->lookAt(0, 0, 0); cam1->clipNear(1); - cam1->clipFar(300); + cam1->clipFar(400); cam1->focalDist(150); - cam1->setInitialState(); cam1->devRotLoc(&AppDemo::devRot, &AppDemo::devLoc); cam1->background().texture(videoTexture); // Turn on main video CVCapture::instance()->videoType(VT_MAIN); - // Let the sun be rotated by time and location - AppDemo::devLoc.sunLightNode(sunLight); + string shdDir = shaderPath; + string texDir = texPath; + string datDir = dataPath + "erleb-AR/models/augst/"; + + // Create directional light for the sunlight + SLLightDirect* sunLight = new SLLightDirect(am, s, 1.0f); + sunLight->translate(-42, 10, 13); + sunLight->powers(1.0f, 1.5f, 1.0f); + sunLight->attenuation(1, 0, 0); + sunLight->doSunPowerAdaptation(true); + sunLight->createsShadows(true); + sunLight->createShadowMapAutoSize(cam1, SLVec2i(2048, 2048), 4); + sunLight->shadowMap()->cascadesFactor(3.0); + // sunLight->createShadowMap(-100, 250, SLVec2f(210, 180), SLVec2i(4096, 4096)); + sunLight->doSmoothShadows(true); + sunLight->castsShadows(false); + sunLight->shadowMinBias(0.001f); + sunLight->shadowMaxBias(0.001f); + AppDemo::devLoc.sunLightNode(sunLight); // Let the sun be rotated by time and location - // Import main model - SLAssimpImporter importer; - SLNode* sutzK18 = importer.load(s->animManager(), - am, - dataPath + "erleb-AR/models/sutz/Sutz-Kirchrain18.gltf", - texPath, - nullptr, - true, // delete tex images after build - true, // only meshes - nullptr, // no replacement material - 0.4f); // 40% ambient reflection + // Load main model + SLAssimpImporter importer; //(LV_diagnostic); + SLNode* thtAndTmp = importer.load(s->animManager(), + am, + datDir + "augst-thtL2-tmpL1.gltf", + texDir, + nullptr, + true, // delete tex images after build + true, // only meshes + nullptr, // no replacement material + 0.4f); // 40% ambient reflection // Rotate to the true geographic rotation - // Nothing to do here because the model is north up + thtAndTmp->rotate(16.7f, 0, 1, 0, TS_parent); - // Let the video shine through some objects - sutzK18->findChild("Terrain")->setMeshMat(matVideoBkgdSM, true); + // Let the video shine through on some objects without shadow mapping + SLNode* tmpUnderground = thtAndTmp->findChild("TmpUnderground"); + if (tmpUnderground) tmpUnderground->setMeshMat(matVideoBkgd, true); + SLNode* thtUnderground = thtAndTmp->findChild("ThtUnderground"); + if (thtUnderground) thtUnderground->setMeshMat(matVideoBkgd, true); - // Make buildings transparent with edges - SLNode* buildings = sutzK18->findChild("Buildings"); - buildings->setMeshMat(matVideoBkgd, true); - buildings->setDrawBitsRec(SL_DB_WITHEDGES, true); + // Let the video shine through on some objects with shadow mapping + SLNode* tmpFloor = thtAndTmp->findChild("TmpFloor"); + if (tmpFloor) tmpFloor->setMeshMat(matVideoBkgdSM, true); + + SLNode* terrain = thtAndTmp->findChild("Terrain"); + if (terrain) + { + terrain->setMeshMat(matVideoBkgdSM, true); + terrain->castsShadows(false); + } + SLNode* thtFrontTerrain = thtAndTmp->findChild("ThtFrontTerrain"); + if (thtFrontTerrain) + { + thtFrontTerrain->setMeshMat(matVideoBkgdSM, true); + thtFrontTerrain->castsShadows(false); + } // Add axis object a world origin SLNode* axis = new SLNode(new SLCoordAxis(am), "Axis Node"); @@ -5348,34 +5520,36 @@ resolution shadows near the camera and lower resolution shadows further away."); axis->rotate(-90, 1, 0, 0); axis->castsShadows(false); + // Set some ambient light + thtAndTmp->updateMeshMat([](SLMaterial* m) + { m->ambient(SLCol4f(.25f, .25f, .25f)); }, + true); SLNode* scene = new SLNode("Scene"); s->root3D(scene); scene->addChild(sunLight); scene->addChild(axis); - scene->addChild(sutzK18); + scene->addChild(thtAndTmp); scene->addChild(cam1); // initialize sensor stuff - // Go to https://map.geo.admin.ch and choose your origin and default point AppDemo::devLoc.useOriginAltitude(false); - AppDemo::devLoc.originLatLonAlt(47.10600, 7.21772, 434.4f); // Corner Carport - AppDemo::devLoc.defaultLatLonAlt(47.10598, 7.21757, 433.9f + 1.7); // In the street - - AppDemo::devLoc.nameLocations().push_back(SLLocation("Corner Carport, Origin", 47, 6, 21.609, 7, 13, 3.788, 434.4)); - AppDemo::devLoc.nameLocations().push_back(SLLocation("Einfahrt (Dolendeckel)", 47, 6, 21.639, 7, 13, 2.764, 433.6 + 1.7)); - AppDemo::devLoc.nameLocations().push_back(SLLocation("Elektrokasten, Brunnenweg", 47, 6, 21.044, 7, 13, 4.920, 438.4 + 1.7)); - AppDemo::devLoc.nameLocations().push_back(SLLocation("Sitzbänkli am See", 47, 6, 24.537, 7, 13, 2.766, 431.2 + 1.7)); + AppDemo::devLoc.nameLocations().push_back(SLLocation("Center of theatre, Origin", 47, 31, 59.461, 7, 43, 19.446, 282.6)); + AppDemo::devLoc.nameLocations().push_back(SLLocation("Treppe Tempel", 47, 31, 58.933, 7, 43, 16.799, 290.5 + 1.7)); + AppDemo::devLoc.nameLocations().push_back(SLLocation("Abzweigung (Dolendeckel)", 47, 31, 57.969, 7, 43, 17.946, 286.5 + 1.7)); + AppDemo::devLoc.nameLocations().push_back(SLLocation("Marker bei Tempel", 47, 31, 59.235, 7, 43, 15.161, 293.1 + 1.7)); + AppDemo::devLoc.nameLocations().push_back(SLLocation("Theater 1. Rang Zugang Ost", 47, 31, 59.698, 7, 43, 20.518, 291.0 + 1.7)); + AppDemo::devLoc.nameLocations().push_back(SLLocation("Theater 1. Rang Nord", 47, 32, 0.216, 7, 43, 19.173, 291.0 + 1.7)); AppDemo::devLoc.originLatLonAlt(AppDemo::devLoc.nameLocations()[0].posWGS84LatLonAlt); AppDemo::devLoc.activeNamedLocation(1); // This sets the location 1 as defaultENU - AppDemo::devLoc.locMaxDistanceM(1000.0f); // Max. Distanz. zum Nullpunkt - AppDemo::devLoc.improveOrigin(false); // Keine autom. Verbesserung vom Origin + AppDemo::devLoc.locMaxDistanceM(1000.0f); // Max. allowed distance to origin + AppDemo::devLoc.improveOrigin(false); // No autom. origin improvement AppDemo::devLoc.hasOrigin(true); AppDemo::devLoc.offsetMode(LOM_twoFingerY); AppDemo::devRot.zeroYawAtStart(false); AppDemo::devRot.offsetMode(ROM_oneFingerX); // This loads the DEM file and overwrites the altitude of originLatLonAlt and defaultLatLonAlt - SLstring tif = dataPath + "erleb-AR/models/sutz/Sutz-Kirchrain18-DEM-WGS84.tif"; + SLstring tif = datDir + "DTM-Theater-Tempel-WGS84.tif"; AppDemo::devLoc.loadGeoTiff(tif); #if defined(SL_OS_MACIOS) || defined(SL_OS_ANDROID) @@ -5396,1419 +5570,1247 @@ resolution shadows near the camera and lower resolution shadows further away."); sv->doWaitOnIdle(false); // for constant video feed sv->camera(cam1); } - else if (sceneID == SID_RTMuttenzerBox) //..................................................... + else if (sceneID == SID_ErlebARAugustaRauricaTht) //........................................... { - s->name("Muttenzer Box"); - s->info("Muttenzer Box with environment mapped reflective sphere and transparent " - "refractive glass sphere. Try ray tracing for real reflections and soft shadows."); - - // Create reflection & glass shaders - SLGLProgram* sp1 = new SLGLProgramGeneric(am, shaderPath + "Reflect.vert", shaderPath + "Reflect.frag"); - SLGLProgram* sp2 = new SLGLProgramGeneric(am, shaderPath + "RefractReflect.vert", shaderPath + "RefractReflect.frag"); - - // Create cube mapping texture - SLGLTexture* tex1 = new SLGLTexture(am, - texPath + "MuttenzerBox+X0512_C.png", - texPath + "MuttenzerBox-X0512_C.png", - texPath + "MuttenzerBox+Y0512_C.png", - texPath + "MuttenzerBox-Y0512_C.png", - texPath + "MuttenzerBox+Z0512_C.png", - texPath + "MuttenzerBox-Z0512_C.png"); - - SLCol4f lightEmisRGB(7.0f, 7.0f, 7.0f); - SLCol4f grayRGB(0.75f, 0.75f, 0.75f); - SLCol4f redRGB(0.75f, 0.25f, 0.25f); - SLCol4f blueRGB(0.25f, 0.25f, 0.75f); - SLCol4f blackRGB(0.00f, 0.00f, 0.00f); - - // create materials - SLMaterial* cream = new SLMaterial(am, "cream", grayRGB, SLCol4f::BLACK, 0); - SLMaterial* red = new SLMaterial(am, "red", redRGB, SLCol4f::BLACK, 0); - SLMaterial* blue = new SLMaterial(am, "blue", blueRGB, SLCol4f::BLACK, 0); - - // Material for mirror sphere - SLMaterial* refl = new SLMaterial(am, "refl", blackRGB, SLCol4f::WHITE, 1000, 1.0f); - refl->addTexture(tex1); - refl->program(sp1); - - // Material for glass sphere - SLMaterial* refr = new SLMaterial(am, "refr", blackRGB, blackRGB, 100, 0.05f, 0.95f, 1.5f); - refr->translucency(1000); - refr->transmissive(SLCol4f::WHITE); - refr->addTexture(tex1); - refr->program(sp2); - - SLNode* sphere1 = new SLNode(new SLSphere(am, 0.5f, 32, 32, "Sphere1", refl)); - sphere1->translate(-0.65f, -0.75f, -0.55f, TS_object); - - SLNode* sphere2 = new SLNode(new SLSphere(am, 0.45f, 32, 32, "Sphere2", refr)); - sphere2->translate(0.73f, -0.8f, 0.10f, TS_object); + s->name("Augusta Raurica Theater AR"); + s->info(s->name()); - SLNode* balls = new SLNode; - balls->addChild(sphere1); - balls->addChild(sphere2); + // Create video texture on global pointer updated in AppDemoVideo + videoTexture = new SLGLTexture(am, texPath + "LiveVideoError.png", GL_LINEAR, GL_LINEAR); + videoTexture->texType(TT_videoBkgd); - // Rectangular light - SLLightRect* lightRect = new SLLightRect(am, s, 1, 0.65f); - lightRect->rotate(90, -1.0f, 0.0f, 0.0f); - lightRect->translate(0.0f, -0.25f, 1.18f, TS_object); - lightRect->spotCutOffDEG(90); - lightRect->spotExponent(1.0); - lightRect->ambientColor(SLCol4f::WHITE); - lightRect->ambientPower(0.25f); - lightRect->diffuseColor(lightEmisRGB); - lightRect->attenuation(0, 0, 1); - lightRect->samplesXY(11, 7); - lightRect->createsShadows(true); - lightRect->createShadowMap(); + // Create see through video background material without shadow mapping + SLMaterial* matVideoBkgd = new SLMaterial(am, "matVideoBkgd", videoTexture); + matVideoBkgd->reflectionModel(RM_Custom); - SLLight::globalAmbient.set(lightEmisRGB * 0.01f); + // Create see through video background material with shadow mapping + SLMaterial* matVideoBkgdSM = new SLMaterial(am, "matVideoBkgdSM", videoTexture); + matVideoBkgdSM->reflectionModel(RM_Custom); + matVideoBkgdSM->ambient(SLCol4f(0.6f, 0.6f, 0.6f)); + matVideoBkgdSM->getsShadows(true); - // create camera - SLCamera* cam1 = new SLCamera(); - cam1->translation(0.0f, 0.40f, 6.35f); - cam1->lookAt(0.0f, -0.05f, 0.0f); - cam1->fov(27); - cam1->focalDist(cam1->translationOS().length()); - cam1->background().colors(SLCol4f(0.0f, 0.0f, 0.0f)); - cam1->setInitialState(); + // Setup the camera + SLCamera* cam1 = new SLCamera("Camera 1"); + cam1->translation(0, 50, -150); + cam1->lookAt(0, 0, 0); + cam1->clipNear(1); + cam1->clipFar(400); + cam1->focalDist(150); cam1->devRotLoc(&AppDemo::devRot, &AppDemo::devLoc); + cam1->background().texture(videoTexture); - // assemble scene - SLNode* scene = new SLNode; - s->root3D(scene); - scene->addChild(cam1); - scene->addChild(lightRect); - - // create wall polygons - SLfloat pL = -1.48f, pR = 1.48f; // left/right - SLfloat pB = -1.25f, pT = 1.19f; // bottom/top - SLfloat pN = 1.79f, pF = -1.55f; // near/far - - // bottom plane - SLNode* b = new SLNode(new SLRectangle(am, SLVec2f(pL, -pN), SLVec2f(pR, -pF), 6, 6, "bottom", cream)); - b->rotate(90, -1, 0, 0); - b->translate(0, 0, pB, TS_object); - scene->addChild(b); - - // top plane - SLNode* t = new SLNode(new SLRectangle(am, SLVec2f(pL, pF), SLVec2f(pR, pN), 6, 6, "top", cream)); - t->rotate(90, 1, 0, 0); - t->translate(0, 0, -pT, TS_object); - scene->addChild(t); - - // far plane - SLNode* f = new SLNode(new SLRectangle(am, SLVec2f(pL, pB), SLVec2f(pR, pT), 6, 6, "far", cream)); - f->translate(0, 0, pF, TS_object); - scene->addChild(f); - - // left plane - SLNode* l = new SLNode(new SLRectangle(am, SLVec2f(-pN, pB), SLVec2f(-pF, pT), 6, 6, "left", red)); - l->rotate(90, 0, 1, 0); - l->translate(0, 0, pL, TS_object); - scene->addChild(l); - - // right plane - SLNode* r = new SLNode(new SLRectangle(am, SLVec2f(pF, pB), SLVec2f(pN, pT), 6, 6, "right", blue)); - r->rotate(90, 0, -1, 0); - r->translate(0, 0, -pR, TS_object); - scene->addChild(r); - - scene->addChild(balls); - - sv->camera(cam1); - } - else if (sceneID == SID_RTSpheres) //.......................................................... - { - s->name("Ray tracing Spheres"); - s->info("Classic ray tracing scene with transparent and reflective spheres. Be patient on mobile devices."); - - // define materials - SLMaterial* matGla = new SLMaterial(am, "Glass", SLCol4f(0.0f, 0.0f, 0.0f), SLCol4f(0.5f, 0.5f, 0.5f), 100, 0.4f, 0.6f, 1.5f); - SLMaterial* matRed = new SLMaterial(am, "Red", SLCol4f(0.5f, 0.0f, 0.0f), SLCol4f(0.5f, 0.5f, 0.5f), 100, 0.5f, 0.0f, 1.0f); - SLMaterial* matYel = new SLMaterial(am, "Floor", SLCol4f(0.8f, 0.6f, 0.2f), SLCol4f(0.8f, 0.8f, 0.8f), 100, 0.5f, 0.0f, 1.0f); + // Turn on main video + CVCapture::instance()->videoType(VT_MAIN); - SLCamera* cam1 = new SLCamera(); - cam1->translation(0, 0.1f, 2.5f); - cam1->lookAt(0, 0, 0); - cam1->focalDist(cam1->translationOS().length()); - cam1->background().colors(SLCol4f(0.1f, 0.4f, 0.8f)); - cam1->setInitialState(); - cam1->devRotLoc(&AppDemo::devRot, &AppDemo::devLoc); + string shdDir = shaderPath; + string texDir = texPath; + string datDir = dataPath + "erleb-AR/models/augst/"; - SLNode* rect = new SLNode(new SLRectangle(am, SLVec2f(-3, -3), SLVec2f(5, 4), 20, 20, "Floor", matYel)); - rect->rotate(90, -1, 0, 0); - rect->translate(0, -1, -0.5f, TS_object); + // Create directional light for the sunlight + SLLightDirect* sunLight = new SLLightDirect(am, s, 1.0f); + sunLight->translate(-42, 10, 13); + sunLight->powers(1.0f, 1.5f, 1.0f); + sunLight->attenuation(1, 0, 0); + sunLight->doSunPowerAdaptation(true); + sunLight->createsShadows(true); + sunLight->createShadowMapAutoSize(cam1, SLVec2i(2048, 2048), 4); + sunLight->shadowMap()->cascadesFactor(3.0); + // sunLight->createShadowMap(-100, 250, SLVec2f(210, 180), SLVec2i(4096, 4096)); + sunLight->doSmoothShadows(true); + sunLight->castsShadows(false); + sunLight->shadowMinBias(0.001f); + sunLight->shadowMaxBias(0.001f); + AppDemo::devLoc.sunLightNode(sunLight); // Let the sun be rotated by time and location - SLLightSpot* light1 = new SLLightSpot(am, s, 2, 2, 2, 0.1f); - light1->powers(1, 7, 7); - light1->attenuation(0, 0, 1); + // Load main model + SLAssimpImporter importer; //(LV_diagnostic); + SLNode* thtAndTmp = importer.load(s->animManager(), + am, + datDir + "augst-thtL1-tmpL2.gltf", + texDir, + nullptr, + true, // delete tex images after build + true, // only meshes + nullptr, // no replacement material + 0.4f); // 40% ambient reflection - SLLightSpot* light2 = new SLLightSpot(am, s, 2, 2, -2, 0.1f); - light2->powers(1, 7, 7); - light2->attenuation(0, 0, 1); + // Rotate to the true geographic rotation + thtAndTmp->rotate(16.7f, 0, 1, 0, TS_parent); - SLNode* scene = new SLNode; - sv->camera(cam1); - scene->addChild(light1); - scene->addChild(light2); - scene->addChild(SphereGroupRT(am, 3, 0, 0, 0, 1, 30, matGla, matRed)); - scene->addChild(rect); - scene->addChild(cam1); + // Let the video shine through on some objects without shadow mapping + SLNode* tmpUnderground = thtAndTmp->findChild("TmpUnderground"); + if (tmpUnderground) tmpUnderground->setMeshMat(matVideoBkgd, true); + SLNode* thtUnderground = thtAndTmp->findChild("ThtUnderground"); + if (thtUnderground) thtUnderground->setMeshMat(matVideoBkgd, true); - s->root3D(scene); - } - else if (sceneID == SID_RTSoftShadows) //...................................................... - { - s->name("Ray tracing soft shadows"); - s->info("Ray tracing with soft shadow light sampling. Each light source is sampled 64x per pixel. Be patient on mobile devices."); + // Let the video shine through on some objects with shadow mapping + SLNode* tmpFloor = thtAndTmp->findChild("TmpFloor"); + if (tmpFloor) tmpFloor->setMeshMat(matVideoBkgdSM, true); - // Create root node - SLNode* scene = new SLNode; - s->root3D(scene); + SLNode* terrain = thtAndTmp->findChild("Terrain"); + if (terrain) + { + terrain->setMeshMat(matVideoBkgdSM, true); + terrain->castsShadows(false); + } + SLNode* thtFrontTerrain = thtAndTmp->findChild("ThtFrontTerrain"); + if (thtFrontTerrain) + { + thtFrontTerrain->setMeshMat(matVideoBkgdSM, true); + thtFrontTerrain->castsShadows(false); + } - // define materials - SLCol4f spec(0.8f, 0.8f, 0.8f); - SLMaterial* matBlk = new SLMaterial(am, "Glass", SLCol4f(0.0f, 0.0f, 0.0f), SLCol4f(0.5f, 0.5f, 0.5f), 100, 0.5f, 0.5f, 1.5f); - SLMaterial* matRed = new SLMaterial(am, "Red", SLCol4f(0.5f, 0.0f, 0.0f), SLCol4f(0.5f, 0.5f, 0.5f), 100, 0.5f, 0.0f, 1.0f); - SLMaterial* matYel = new SLMaterial(am, "Floor", SLCol4f(0.8f, 0.6f, 0.2f), SLCol4f(0.8f, 0.8f, 0.8f), 100, 0.0f, 0.0f, 1.0f); + // Add axis object a world origin + SLNode* axis = new SLNode(new SLCoordAxis(am), "Axis Node"); + axis->setDrawBitsRec(SL_DB_MESHWIRED, false); + axis->rotate(-90, 1, 0, 0); + axis->castsShadows(false); - SLCamera* cam1 = new SLCamera; - cam1->translation(0, 0.1f, 4); - cam1->lookAt(0, 0, 0); - cam1->focalDist(cam1->translationOS().length()); - cam1->background().colors(SLCol4f(0.1f, 0.4f, 0.8f)); - cam1->setInitialState(); - cam1->devRotLoc(&AppDemo::devRot, &AppDemo::devLoc); + // Set some ambient light + thtAndTmp->updateMeshMat([](SLMaterial* m) + { m->ambient(SLCol4f(.25f, .25f, .25f)); }, + true); + SLNode* scene = new SLNode("Scene"); + s->root3D(scene); + scene->addChild(sunLight); + scene->addChild(axis); + scene->addChild(thtAndTmp); scene->addChild(cam1); - SLNode* rect = new SLNode(new SLRectangle(am, SLVec2f(-5, -5), SLVec2f(5, 5), 1, 1, "Rect", matYel)); - rect->rotate(90, -1, 0, 0); - rect->translate(0, 0, -0.5f); - rect->castsShadows(false); - scene->addChild(rect); - - SLLightSpot* light1 = new SLLightSpot(am, s, 2, 2, 2, 0.3f); - light1->samples(8, 8); - light1->attenuation(0, 0, 1); - light1->createsShadows(true); - light1->createShadowMap(); - scene->addChild(light1); + // initialize sensor stuff + AppDemo::devLoc.useOriginAltitude(false); + AppDemo::devLoc.nameLocations().push_back(SLLocation("Center of theatre, Origin", 47, 31, 59.461, 7, 43, 19.446, 282.6)); + AppDemo::devLoc.nameLocations().push_back(SLLocation("Treppe Tempel", 47, 31, 58.933, 7, 43, 16.799, 290.5 + 1.7)); + AppDemo::devLoc.nameLocations().push_back(SLLocation("Abzweigung (Dolendeckel)", 47, 31, 57.969, 7, 43, 17.946, 286.5 + 1.7)); + AppDemo::devLoc.nameLocations().push_back(SLLocation("Marker bei Tempel", 47, 31, 59.235, 7, 43, 15.161, 293.1 + 1.7)); + AppDemo::devLoc.nameLocations().push_back(SLLocation("Theater 1. Rang Zugang Ost", 47, 31, 59.698, 7, 43, 20.518, 291.0 + 1.7)); + AppDemo::devLoc.nameLocations().push_back(SLLocation("Theater 1. Rang Nord", 47, 32, 0.216, 7, 43, 19.173, 291.0 + 1.7)); + AppDemo::devLoc.originLatLonAlt(AppDemo::devLoc.nameLocations()[0].posWGS84LatLonAlt); + AppDemo::devLoc.activeNamedLocation(1); // This sets the location 1 as defaultENU + AppDemo::devLoc.locMaxDistanceM(1000.0f); // Max. allowed distance to origin + AppDemo::devLoc.improveOrigin(false); // No autom. origin improvement + AppDemo::devLoc.hasOrigin(true); + AppDemo::devLoc.offsetMode(LOM_twoFingerY); + AppDemo::devRot.zeroYawAtStart(false); + AppDemo::devRot.offsetMode(ROM_oneFingerX); - SLLightSpot* light2 = new SLLightSpot(am, s, 2, 2, -2, 0.3f); - light2->samples(8, 8); - light2->attenuation(0, 0, 1); - light2->createsShadows(true); - light2->createShadowMap(); - scene->addChild(light2); + // This loads the DEM file and overwrites the altitude of originLatLonAlt and defaultLatLonAlt + SLstring tif = datDir + "DTM-Theater-Tempel-WGS84.tif"; + AppDemo::devLoc.loadGeoTiff(tif); - scene->addChild(SphereGroupRT(am, 1, 0, 0, 0, 1, 32, matBlk, matRed)); +#if defined(SL_OS_MACIOS) || defined(SL_OS_ANDROID) + AppDemo::devLoc.isUsed(true); + AppDemo::devRot.isUsed(true); + cam1->camAnim(SLCamAnim::CA_deviceRotLocYUp); +#else + AppDemo::devLoc.isUsed(false); + AppDemo::devRot.isUsed(false); + SLVec3d pos_d = AppDemo::devLoc.defaultENU() - AppDemo::devLoc.originENU(); + SLVec3f pos_f((SLfloat)pos_d.x, (SLfloat)pos_d.y, (SLfloat)pos_d.z); + cam1->translation(pos_f); + cam1->focalDist(pos_f.length()); + cam1->lookAt(SLVec3f::ZERO); + cam1->camAnim(SLCamAnim::CA_turntableYUp); +#endif + sv->doWaitOnIdle(false); // for constant video feed sv->camera(cam1); } - else if (sceneID == SID_RTDoF) //.............................................................. + else if (sceneID == SID_ErlebARAugustaRauricaTmpTht) //........................................ { - s->name("Ray tracing depth of field"); - - // Create root node - SLNode* scene = new SLNode; - s->root3D(scene); + s->name("Augusta Raurica AR Temple and Theater"); + s->info(s->name()); - // Create textures and materials - SLGLTexture* texC = new SLGLTexture(am, texPath + "Checkerboard0512_C.png", SL_ANISOTROPY_MAX, GL_LINEAR); - SLMaterial* mT = new SLMaterial(am, "mT", texC); - mT->kr(0.5f); - SLMaterial* mW = new SLMaterial(am, "mW", SLCol4f::WHITE); - SLMaterial* mB = new SLMaterial(am, "mB", SLCol4f::GRAY); - SLMaterial* mY = new SLMaterial(am, "mY", SLCol4f::YELLOW); - SLMaterial* mR = new SLMaterial(am, "mR", SLCol4f::RED); - SLMaterial* mG = new SLMaterial(am, "mG", SLCol4f::GREEN); - SLMaterial* mM = new SLMaterial(am, "mM", SLCol4f::MAGENTA); + // Create video texture on global pointer updated in AppDemoVideo + videoTexture = new SLGLTexture(am, texPath + "LiveVideoError.png", GL_LINEAR, GL_LINEAR); + videoTexture->texType(TT_videoBkgd); -#ifndef SL_GLES - SLuint numSamples = 10; -#else - SLuint numSamples = 4; -#endif + // Create see through video background material without shadow mapping + SLMaterial* matVideoBkgd = new SLMaterial(am, "matVideoBkgd", videoTexture); + matVideoBkgd->reflectionModel(RM_Custom); - stringstream ss; - ss << "Ray tracing with depth of field blur. Each pixel is sampled " << numSamples * numSamples << "x from a lens. Be patient on mobile devices."; - s->info(ss.str()); + // Create see through video background material with shadow mapping + SLMaterial* matVideoBkgdSM = new SLMaterial(am, "matVideoBkgdSM", videoTexture); + matVideoBkgdSM->reflectionModel(RM_Custom); + matVideoBkgdSM->ambient(SLCol4f(0.6f, 0.6f, 0.6f)); + matVideoBkgdSM->getsShadows(true); + // Setup the camera SLCamera* cam1 = new SLCamera("Camera 1"); - cam1->translation(0, 2, 7); + cam1->translation(0, 50, -150); cam1->lookAt(0, 0, 0); - cam1->focalDist(cam1->translationOS().length()); - cam1->clipFar(80); - cam1->lensDiameter(0.4f); - cam1->lensSamples()->samples(numSamples, numSamples); - cam1->background().colors(SLCol4f(0.1f, 0.4f, 0.8f)); - cam1->setInitialState(); - cam1->fogIsOn(true); - cam1->fogMode(FM_exp); - cam1->fogDensity(0.04f); - scene->addChild(cam1); + cam1->clipNear(1); + cam1->clipFar(400); + cam1->focalDist(150); + cam1->devRotLoc(&AppDemo::devRot, &AppDemo::devLoc); + cam1->background().texture(videoTexture); - SLuint res = 36; - SLNode* rect = new SLNode(new SLRectangle(am, - SLVec2f(-40, -10), - SLVec2f(40, 70), - SLVec2f(0, 0), - SLVec2f(4, 4), - 2, - 2, - "Rect", - mT)); - rect->rotate(90, -1, 0, 0); - rect->translate(0, 0, -0.5f, TS_object); - scene->addChild(rect); + // Turn on main video + CVCapture::instance()->videoType(VT_MAIN); - SLLightSpot* light1 = new SLLightSpot(am, s, 2, 2, 0, 0.1f); - light1->ambiDiffPowers(0.1f, 1); - light1->attenuation(1, 0, 0); - scene->addChild(light1); + string shdDir = shaderPath; + string texDir = texPath; + string datDir = dataPath + "erleb-AR/models/augst/"; - SLNode* balls = new SLNode; - SLNode* sp; - sp = new SLNode(new SLSphere(am, 0.5f, res, res, "S1", mW)); - sp->translate(2.0, 0, -4, TS_object); - balls->addChild(sp); - sp = new SLNode(new SLSphere(am, 0.5f, res, res, "S2", mB)); - sp->translate(1.5, 0, -3, TS_object); - balls->addChild(sp); - sp = new SLNode(new SLSphere(am, 0.5f, res, res, "S3", mY)); - sp->translate(1.0, 0, -2, TS_object); - balls->addChild(sp); - sp = new SLNode(new SLSphere(am, 0.5f, res, res, "S4", mR)); - sp->translate(0.5, 0, -1, TS_object); - balls->addChild(sp); - sp = new SLNode(new SLSphere(am, 0.5f, res, res, "S5", mG)); - sp->translate(0.0, 0, 0, TS_object); - balls->addChild(sp); - sp = new SLNode(new SLSphere(am, 0.5f, res, res, "S6", mM)); - sp->translate(-0.5, 0, 1, TS_object); - balls->addChild(sp); - sp = new SLNode(new SLSphere(am, 0.5f, res, res, "S7", mW)); - sp->translate(-1.0, 0, 2, TS_object); - balls->addChild(sp); - scene->addChild(balls); + // Create directional light for the sunlight + SLLightDirect* sunLight = new SLLightDirect(am, s, 1.0f); + sunLight->translate(-42, 10, 13); + sunLight->powers(1.0f, 1.5f, 1.0f); + sunLight->attenuation(1, 0, 0); + sunLight->doSunPowerAdaptation(true); + sunLight->createsShadows(true); + sunLight->createShadowMapAutoSize(cam1, SLVec2i(2048, 2048), 4); + sunLight->shadowMap()->cascadesFactor(3.0); + // Old stanard single map shadow map + // sunLight->createShadowMap(-100, 250, SLVec2f(210, 180), SLVec2i(4096, 4096)); + sunLight->doSmoothShadows(true); + sunLight->castsShadows(false); + sunLight->shadowMinBias(0.001f); + sunLight->shadowMaxBias(0.001f); + AppDemo::devLoc.sunLightNode(sunLight); // Let the sun be rotated by time and location - sv->camera(cam1); - } - else if (sceneID == SID_RTLens) //............................................................. - { - s->name("Ray tracing lens test"); - s->info("Ray tracing lens test scene."); + // Load main model + SLAssimpImporter importer; //(LV_diagnostic); + SLNode* thtAndTmp = importer.load(s->animManager(), + am, + datDir + "augst-thtL1L2-tmpL1L2.gltf", + texDir, + nullptr, + true, // delete tex images after build + true, // only meshes + nullptr, // no replacement material + 0.4f); // 40% ambient reflection - // Create root node - SLNode* scene = new SLNode; - s->root3D(scene); + // Rotate to the true geographic rotation + thtAndTmp->rotate(16.7f, 0, 1, 0, TS_parent); - // Create textures and materials - SLGLTexture* texC = new SLGLTexture(am, texPath + "VisionExample.jpg"); - // SLGLTexture* texC = new SLGLTexture(am, texPath + "Checkerboard0512_C.png"); + // Let the video shine through on some objects without shadow mapping + SLNode* tmpUnderground = thtAndTmp->findChild("TmpUnderground"); + if (tmpUnderground) tmpUnderground->setMeshMat(matVideoBkgd, true); + SLNode* thtUnderground = thtAndTmp->findChild("ThtUnderground"); + if (thtUnderground) thtUnderground->setMeshMat(matVideoBkgd, true); - SLMaterial* mT = new SLMaterial(am, "mT", texC, nullptr, nullptr, nullptr); - mT->kr(0.5f); + // Let the video shine through on some objects with shadow mapping + SLNode* tmpFloor = thtAndTmp->findChild("TmpFloor"); + if (tmpFloor) tmpFloor->setMeshMat(matVideoBkgdSM, true); - // Glass material - // name, ambient, specular, shininess, kr(reflectivity), kt(transparency), kn(refraction) - SLMaterial* matLens = new SLMaterial(am, "lens", SLCol4f(0.0f, 0.0f, 0.0f), SLCol4f(0.5f, 0.5f, 0.5f), 100, 0.5f, 0.5f, 1.5f); - // SLGLShaderProg* sp1 = new SLGLShaderProgGeneric("RefractReflect.vert", "RefractReflect.frag"); - // matLens->shaderProg(sp1); + SLNode* terrain = thtAndTmp->findChild("Terrain"); + if (terrain) + { + terrain->setMeshMat(matVideoBkgdSM, true); + terrain->castsShadows(false); + } + SLNode* thtFrontTerrain = thtAndTmp->findChild("ThtFrontTerrain"); + if (thtFrontTerrain) + { + thtFrontTerrain->setMeshMat(matVideoBkgdSM, true); + thtFrontTerrain->castsShadows(false); + } -#ifndef APP_USES_GLES - SLuint numSamples = 10; -#else - SLuint numSamples = 6; -#endif + // Add axis object a world origin + SLNode* axis = new SLNode(new SLCoordAxis(am), "Axis Node"); + axis->setDrawBitsRec(SL_DB_MESHWIRED, false); + axis->rotate(-90, 1, 0, 0); + axis->castsShadows(false); - // Scene - SLCamera* cam1 = new SLCamera; - cam1->translation(0, 8, 0); - cam1->lookAt(0, 0, 0); - cam1->focalDist(cam1->translationOS().length()); - cam1->lensDiameter(0.4f); - cam1->lensSamples()->samples(numSamples, numSamples); - cam1->background().colors(SLCol4f(0.1f, 0.4f, 0.8f)); - cam1->setInitialState(); - cam1->devRotLoc(&AppDemo::devRot, &AppDemo::devLoc); + // Set some ambient light + thtAndTmp->updateMeshMat([](SLMaterial* m) + { m->ambient(SLCol4f(.25f, .25f, .25f)); }, + true); + SLNode* scene = new SLNode("Scene"); + s->root3D(scene); + scene->addChild(sunLight); + scene->addChild(axis); + scene->addChild(thtAndTmp); scene->addChild(cam1); - // Plane - // SLNode* rect = new SLNode(new SLRectangle(SLVec2f(-20, -20), SLVec2f(20, 20), 50, 20, "Rect", mT)); - // rect->translate(0, 0, 0, TS_Object); - // rect->rotate(90, -1, 0, 0); - - SLLightSpot* light1 = new SLLightSpot(am, s, 1, 6, 1, 0.1f); - light1->attenuation(0, 0, 1); - scene->addChild(light1); - - SLuint res = 20; - SLNode* rect = new SLNode(new SLRectangle(am, SLVec2f(-5, -5), SLVec2f(5, 5), res, res, "Rect", mT)); - rect->rotate(90, -1, 0, 0); - rect->translate(0, 0, -0.0f, TS_object); - scene->addChild(rect); + // initialize sensor stuff + AppDemo::devLoc.useOriginAltitude(false); + AppDemo::devLoc.nameLocations().push_back(SLLocation("Center of theatre, Origin", 47, 31, 59.461, 7, 43, 19.446, 282.6)); + AppDemo::devLoc.nameLocations().push_back(SLLocation("Treppe Tempel", 47, 31, 58.933, 7, 43, 16.799, 290.5 + 1.7)); + AppDemo::devLoc.nameLocations().push_back(SLLocation("Abzweigung (Dolendeckel)", 47, 31, 57.969, 7, 43, 17.946, 286.5 + 1.7)); + AppDemo::devLoc.nameLocations().push_back(SLLocation("Marker bei Tempel", 47, 31, 59.235, 7, 43, 15.161, 293.1 + 1.7)); + AppDemo::devLoc.nameLocations().push_back(SLLocation("Theater 1. Rang Zugang Ost", 47, 31, 59.698, 7, 43, 20.518, 291.0 + 1.7)); + AppDemo::devLoc.nameLocations().push_back(SLLocation("Theater 1. Rang Nord", 47, 32, 0.216, 7, 43, 19.173, 291.0 + 1.7)); + AppDemo::devLoc.originLatLonAlt(AppDemo::devLoc.nameLocations()[0].posWGS84LatLonAlt); + AppDemo::devLoc.activeNamedLocation(1); // This sets the location 1 as defaultENU + AppDemo::devLoc.locMaxDistanceM(1000.0f); // Max. allowed distance to origin + AppDemo::devLoc.improveOrigin(false); // No autom. origin improvement + AppDemo::devLoc.hasOrigin(true); + AppDemo::devLoc.offsetMode(LOM_twoFingerY); + AppDemo::devRot.zeroYawAtStart(false); + AppDemo::devRot.offsetMode(ROM_oneFingerX); - // Lens from eye prescription card - // SLNode* lensA = new SLNode(new SLLens(s, 0.50f, -0.50f, 4.0f, 0.0f, 32, 32, "presbyopic", matLens)); // Weitsichtig - // lensA->translate(-2, 1, -2); - // scene->addChild(lensA); + // Level of Detail switch for Temple and Theater + SLNode* tmpAltar = thtAndTmp->findChild("TmpAltar"); + SLNode* tmpL1 = thtAndTmp->findChild("Tmp-L1"); + SLNode* tmpL2 = thtAndTmp->findChild("Tmp-L2"); + SLNode* thtL1 = thtAndTmp->findChild("Tht-L1"); + SLNode* thtL2 = thtAndTmp->findChild("Tht-L2"); + thtL1->drawBits()->set(SL_DB_HIDDEN, false); + thtL2->drawBits()->set(SL_DB_HIDDEN, true); + tmpL1->drawBits()->set(SL_DB_HIDDEN, false); + tmpL2->drawBits()->set(SL_DB_HIDDEN, true); - // SLNode* lensB = new SLNode(new SLLens(s, -0.65f, -0.10f, 4.0f, 0.0f, 32, 32, "myopic", matLens)); // Kurzsichtig - // lensB->translate(2, 1, -2); - // scene->addChild(lensB); + // Add level of detail switch callback lambda + cam1->onCamUpdateCB([=](SLSceneView* sv) + { + SLVec3f posCam = sv->camera()->updateAndGetWM().translation(); + SLVec3f posAlt = tmpAltar->updateAndGetWM().translation(); + SLVec3f distCamAlt = posCam - posAlt; + float tmpDist = distCamAlt.length(); + float thtDist = posCam.length(); + + // If the temple is closer than the theater activate level 1 and deactivate level 2 + if (tmpDist < thtDist) + { + thtL1->drawBits()->set(SL_DB_HIDDEN, true); + thtL2->drawBits()->set(SL_DB_HIDDEN, false); + tmpL1->drawBits()->set(SL_DB_HIDDEN, false); + tmpL2->drawBits()->set(SL_DB_HIDDEN, true); + } + else + { + thtL1->drawBits()->set(SL_DB_HIDDEN, false); + thtL2->drawBits()->set(SL_DB_HIDDEN, true); + tmpL1->drawBits()->set(SL_DB_HIDDEN, true); + tmpL2->drawBits()->set(SL_DB_HIDDEN, false); + } }); - // Lens with radius - // SLNode* lensC = new SLNode(new SLLens(s, 5.0, 4.0, 4.0f, 0.0f, 32, 32, "presbyopic", matLens)); // Weitsichtig - // lensC->translate(-2, 1, 2); - // scene->addChild(lensC); + // This loads the DEM file and overwrites the altitude of originLatLonAlt and defaultLatLonAlt + SLstring tif = datDir + "DTM-Theater-Tempel-WGS84.tif"; + AppDemo::devLoc.loadGeoTiff(tif); - SLNode* lensD = new SLNode(new SLLens(am, - -15.0f, - -15.0f, - 1.0f, - 0.1f, - res, - res, - "myopic", - matLens)); // Kurzsichtig - lensD->translate(0, 6, 0); - scene->addChild(lensD); +#if defined(SL_OS_MACIOS) || defined(SL_OS_ANDROID) + AppDemo::devLoc.isUsed(true); + AppDemo::devRot.isUsed(true); + cam1->camAnim(SLCamAnim::CA_deviceRotLocYUp); +#else + AppDemo::devLoc.isUsed(false); + AppDemo::devRot.isUsed(false); + SLVec3d pos_d = AppDemo::devLoc.defaultENU() - AppDemo::devLoc.originENU(); + SLVec3f pos_f((SLfloat)pos_d.x, (SLfloat)pos_d.y, (SLfloat)pos_d.z); + cam1->translation(pos_f); + cam1->focalDist(pos_f.length()); + cam1->lookAt(SLVec3f::ZERO); + cam1->camAnim(SLCamAnim::CA_turntableYUp); +#endif + sv->doWaitOnIdle(false); // for constant video feed sv->camera(cam1); } - else if (sceneID == SID_RTTest) //............................................................. + else if (sceneID == SID_ErlebARAventicumAmphiteatre) //........................................ { - // Set scene name and info string - s->name("Ray tracing test"); - s->info("RT Test Scene"); + s->name("Aventicum Amphitheatre AR (AO)"); + s->info("Augmented Reality for Aventicum Amphitheatre (AO)"); - // Create a camera node + // Create video texture on global pointer updated in AppDemoVideo + videoTexture = new SLGLTexture(am, texPath + "LiveVideoError.png", GL_LINEAR, GL_LINEAR); + videoTexture->texType(TT_videoBkgd); + + // Create see through video background material without shadow mapping + SLMaterial* matVideoBkgd = new SLMaterial(am, "matVideoBkgd", videoTexture); + matVideoBkgd->reflectionModel(RM_Custom); + + // Create see through video background material with shadow mapping + SLMaterial* matVideoBkgdSM = new SLMaterial(am, "matVideoBkgdSM", videoTexture); + matVideoBkgdSM->reflectionModel(RM_Custom); + matVideoBkgdSM->ambient(SLCol4f(0.6f, 0.6f, 0.6f)); + matVideoBkgdSM->getsShadows(true); + + // Setup the camera SLCamera* cam1 = new SLCamera("Camera 1"); - cam1->translation(0, 0, 5); + cam1->translation(0, 50, -150); cam1->lookAt(0, 0, 0); - cam1->background().colors(SLCol4f(0.5f, 0.5f, 0.5f)); + cam1->clipNear(1); + cam1->clipFar(400); + cam1->focalDist(150); cam1->setInitialState(); cam1->devRotLoc(&AppDemo::devRot, &AppDemo::devLoc); + cam1->background().texture(videoTexture); - // Create a light source node - SLLightSpot* light1 = new SLLightSpot(am, s, 0.3f); - light1->translation(5, 5, 5); - light1->lookAt(0, 0, 0); - light1->name("light node"); + // Turn on main video + CVCapture::instance()->videoType(VT_MAIN); - // Material for glass sphere - SLMaterial* matBox1 = new SLMaterial(am, "matBox1", SLCol4f(0.0f, 0.0f, 0.0f), SLCol4f(0.5f, 0.5f, 0.5f), 100, 0.0f, 0.9f, 1.5f); - SLMesh* boxMesh1 = new SLBox(am, -0.8f, -1, 0.02f, 1.2f, 1, 1, "boxMesh1", matBox1); - SLNode* boxNode1 = new SLNode(boxMesh1, "BoxNode1"); + // Create directional light for the sunlight + SLLightDirect* sunLight = new SLLightDirect(am, s, 1.0f); + sunLight->powers(1.0f, 1.5f, 1.0f); + sunLight->attenuation(1, 0, 0); + sunLight->translation(0, 1, 0); + sunLight->doSunPowerAdaptation(true); + sunLight->createsShadows(true); + sunLight->createShadowMapAutoSize(cam1, SLVec2i(2048, 2048), 4); + sunLight->shadowMap()->cascadesFactor(3.0); + // sunLight->createShadowMap(-70, 70, SLVec2f(140, 100), SLVec2i(4096, 4096)); + sunLight->doSmoothShadows(true); + sunLight->castsShadows(false); + sunLight->shadowMinBias(0.001f); + sunLight->shadowMaxBias(0.003f); - SLMaterial* matBox2 = new SLMaterial(am, "matBox2", SLCol4f(0.0f, 0.0f, 0.0f), SLCol4f(0.5f, 0.5f, 0.5f), 100, 0.0f, 0.9f, 1.3f); - SLMesh* boxMesh2 = new SLBox(am, -1.2f, -1, -1, 0.8f, 1, -0.02f, "BoxMesh2", matBox2); - SLNode* boxNode2 = new SLNode(boxMesh2, "BoxNode2"); + // Let the sun be rotated by time and location + AppDemo::devLoc.sunLightNode(sunLight); - // Create a scene group and add all nodes - SLNode* scene = new SLNode("scene node"); + SLAssimpImporter importer; + SLNode* amphiTheatre = importer.load(s->animManager(), + am, + dataPath + "erleb-AR/models/avenches/avenches-amphitheater.gltf", + texPath, + nullptr, + false, // delete tex images after build + true, // only meshes + nullptr, // no replacement material + 0.4f); // 40% ambient reflection + + // Rotate to the true geographic rotation + amphiTheatre->rotate(13.25f, 0, 1, 0, TS_parent); + + // Let the video shine through some objects + amphiTheatre->findChild("Tht-Aussen-Untergrund")->setMeshMat(matVideoBkgd, true); + amphiTheatre->findChild("Tht-Eingang-Ost-Boden")->setMeshMat(matVideoBkgdSM, true); + amphiTheatre->findChild("Tht-Arenaboden")->setMeshMat(matVideoBkgdSM, true); + // amphiTheatre->findChild("Tht-Aussen-Terrain")->setMeshMat(matVideoBkgdSM, true); + + // Add axis object a world origin + SLNode* axis = new SLNode(new SLCoordAxis(am), "Axis Node"); + axis->setDrawBitsRec(SL_DB_MESHWIRED, false); + axis->rotate(-90, 1, 0, 0); + axis->castsShadows(false); + + SLNode* scene = new SLNode("Scene"); s->root3D(scene); - scene->addChild(light1); + scene->addChild(sunLight); + scene->addChild(axis); + scene->addChild(amphiTheatre); scene->addChild(cam1); - scene->addChild(boxNode1); - scene->addChild(boxNode2); + // initialize sensor stuff + AppDemo::devLoc.useOriginAltitude(false); + AppDemo::devLoc.nameLocations().push_back(SLLocation("Arena Centre, Origin", 46, 52, 51.685, 7, 2, 33.458, 461.4)); + AppDemo::devLoc.nameLocations().push_back(SLLocation("Entrance East, Manhole Cover", 46, 52, 52.344, 7, 2, 37.600, 461.4 + 1.7)); + AppDemo::devLoc.nameLocations().push_back(SLLocation("Arena, Sewer Cover West", 46, 52, 51.484, 7, 2, 32.307, 461.3 + 1.7)); + AppDemo::devLoc.nameLocations().push_back(SLLocation("Arena, Sewer Cover East", 46, 52, 51.870, 7, 2, 34.595, 461.1 + 1.7)); + AppDemo::devLoc.nameLocations().push_back(SLLocation("Stand South, Sewer Cover", 46, 52, 50.635, 7, 2, 34.099, 471.7 + 1.7)); + AppDemo::devLoc.nameLocations().push_back(SLLocation("Stand West, Sewer Cover", 46, 52, 51.889, 7, 2, 31.567, 471.7 + 1.7)); + AppDemo::devLoc.originLatLonAlt(AppDemo::devLoc.nameLocations()[0].posWGS84LatLonAlt); + AppDemo::devLoc.activeNamedLocation(1); // This sets the location 1 as defaultENU + AppDemo::devLoc.locMaxDistanceM(1000.0f); // Max. Distanz. zum Nullpunkt + AppDemo::devLoc.improveOrigin(false); // Keine autom. Verbesserung vom Origin + AppDemo::devLoc.hasOrigin(true); + AppDemo::devLoc.offsetMode(LOM_twoFingerY); + AppDemo::devRot.zeroYawAtStart(false); + AppDemo::devRot.offsetMode(ROM_oneFingerX); + + // This loads the DEM file and overwrites the altitude of originLatLonAlt and defaultLatLonAlt + SLstring tif = dataPath + "erleb-AR/models/avenches/DTM-Aventicum-WGS84.tif"; + AppDemo::devLoc.loadGeoTiff(tif); + +#if defined(SL_OS_MACIOS) || defined(SL_OS_ANDROID) + AppDemo::devLoc.isUsed(true); + AppDemo::devRot.isUsed(true); + cam1->camAnim(SLCamAnim::CA_deviceRotLocYUp); +#else + AppDemo::devLoc.isUsed(false); + AppDemo::devRot.isUsed(false); + SLVec3d pos_d = AppDemo::devLoc.defaultENU() - AppDemo::devLoc.originENU(); + SLVec3f pos_f((SLfloat)pos_d.x, (SLfloat)pos_d.y, (SLfloat)pos_d.z); + cam1->translation(pos_f); + cam1->focalDist(pos_f.length()); + cam1->lookAt(SLVec3f::ZERO); + cam1->camAnim(SLCamAnim::CA_turntableYUp); +#endif + + sv->doWaitOnIdle(false); // for constant video feed sv->camera(cam1); } - - else if (sceneID == SID_ParticleSystem_First) //............................................... + else if (sceneID == SID_ErlebARAventicumCigognier) //.......................................... { - // Set scene name and info string - s->name("First particle system"); - s->info("First scene with a particle system"); + s->name("Aventicum Cigognier AR (AO)"); + s->info("Augmented Reality for Aventicum Cigognier Temple"); - // Create a scene group node - SLNode* scene = new SLNode("scene node"); - s->root3D(scene); + // Create video texture on global pointer updated in AppDemoVideo + videoTexture = new SLGLTexture(am, texPath + "LiveVideoError.png", GL_LINEAR, GL_LINEAR); + videoTexture->texType(TT_videoBkgd); - // Create and add camera - SLCamera* cam1 = new SLCamera("Camera 1"); - cam1->translation(0, 1.5f, 4); - cam1->lookAt(0, 1.5f, 0); - scene->addChild(cam1); + // Create see through video background material without shadow mapping + SLMaterial* matVideoBkgd = new SLMaterial(am, "matVideoBkgd", videoTexture); + matVideoBkgd->reflectionModel(RM_Custom); - // 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 see through video background material with shadow mapping + SLMaterial* matVideoBkgdSM = new SLMaterial(am, "matVideoBkgdSM", videoTexture); + matVideoBkgdSM->reflectionModel(RM_Custom); + matVideoBkgdSM->ambient(SLCol4f(0.6f, 0.6f, 0.6f)); + matVideoBkgdSM->getsShadows(true); - // Create a light source node - SLLightSpot* light1 = new SLLightSpot(am, s, 0.3f); - light1->translation(5, 5, 5); - light1->name("light node"); - scene->addChild(light1); + // Setup the camera + SLCamera* cam1 = new SLCamera("Camera 1"); + cam1->translation(0, 50, -150); + cam1->lookAt(0, 0, 0); + cam1->clipNear(1); + cam1->clipFar(400); + cam1->focalDist(150); + cam1->setInitialState(); + cam1->devRotLoc(&AppDemo::devRot, &AppDemo::devLoc); + cam1->background().texture(videoTexture); - // Create meshes and nodes - SLParticleSystem* ps = new SLParticleSystem(am, - 50, - SLVec3f(0.04f, 0.4f, 0.1f), - SLVec3f(-0.11f, 0.7f, -0.1f), - 4.0f, - texC, - "Particle System", - texFlipbook); + // Turn on main video + CVCapture::instance()->videoType(VT_MAIN); - SLNode* pSNode = new SLNode(ps, "Particle system node"); - scene->addChild(pSNode); + // Create directional light for the sunlight + SLLightDirect* sunLight = new SLLightDirect(am, s, 1.0f); + sunLight->powers(1.0f, 1.0f, 1.0f); + sunLight->attenuation(1, 0, 0); + sunLight->translation(0, 10, 0); + sunLight->lookAt(10, 0, 10); + sunLight->doSunPowerAdaptation(true); + sunLight->createsShadows(true); + sunLight->createShadowMapAutoSize(cam1, SLVec2i(2048, 2048), 4); + sunLight->shadowMap()->cascadesFactor(3.0); + // sunLight->createShadowMap(-70, 120, SLVec2f(150, 150), SLVec2i(2048, 2048)); + sunLight->doSmoothShadows(true); + sunLight->castsShadows(false); + sunLight->shadowMinBias(0.001f); + sunLight->shadowMaxBias(0.003f); - // 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)); - sv->camera(cam1); - sv->doWaitOnIdle(false); - } - else if (sceneID == SID_ParticleSystem_Demo) //................................................ - { - // 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."); + // Let the sun be rotated by time and location + AppDemo::devLoc.sunLightNode(sunLight); - // Create a scene group node - SLNode* scene = new SLNode("scene node"); - s->root3D(scene); + SLAssimpImporter importer; + SLNode* cigognier = importer.load(s->animManager(), + am, + dataPath + "erleb-AR/models/avenches/avenches-cigognier.gltf", + texPath, + nullptr, + true, // delete tex images after build + true, // only meshes + nullptr, // no replacement material + 0.4f); // 40% ambient reflection - // 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"); + // Rotate to the true geographic rotation + cigognier->rotate(-36.52f, 0, 1, 0, TS_parent); - // 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); + // Let the video shine through some objects + cigognier->findChild("Tmp-Sol-Pelouse")->setMeshMat(matVideoBkgdSM, true); + cigognier->findChild("Tmp-Souterrain")->setMeshMat(matVideoBkgd, true); - // 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) //........................................... - { - // 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"); + // Add axis object a world origin + SLNode* axis = new SLNode(new SLCoordAxis(am), "Axis Node"); + axis->setDrawBitsRec(SL_DB_MESHWIRED, false); + axis->rotate(-90, 1, 0, 0); + axis->castsShadows(false); - // Create a scene group node - SLNode* scene = new SLNode("scene node"); + SLNode* scene = new SLNode("Scene"); 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(sunLight); + scene->addChild(axis); + scene->addChild(cigognier); scene->addChild(cam1); - sv->camera(cam1); - - // 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 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); + // initialize sensor stuff + AppDemo::devLoc.useOriginAltitude(false); + AppDemo::devLoc.nameLocations().push_back(SLLocation("Center of place, Origin", 46, 52, 53.245, 7, 2, 47.198, 450.9)); + AppDemo::devLoc.nameLocations().push_back(SLLocation("At the altar", 46, 52, 53.107, 7, 2, 47.498, 450.9 + 1.7)); + AppDemo::devLoc.nameLocations().push_back(SLLocation("Old AR viewer", 46, 52, 53.666, 7, 2, 48.316, 451.0 + 1.7)); + AppDemo::devLoc.nameLocations().push_back(SLLocation("Temple Entrance in hall", 46, 52, 54.007, 7, 2, 45.702, 453.0 + 1.7)); + AppDemo::devLoc.originLatLonAlt(AppDemo::devLoc.nameLocations()[0].posWGS84LatLonAlt); + AppDemo::devLoc.activeNamedLocation(1); // This sets the location 1 as defaultENU + AppDemo::devLoc.locMaxDistanceM(1000.0f); // Max. allowed distance from origin + AppDemo::devLoc.improveOrigin(false); // No auto improvement from + AppDemo::devLoc.hasOrigin(true); + AppDemo::devLoc.offsetMode(LOM_twoFingerY); + AppDemo::devRot.zeroYawAtStart(false); + AppDemo::devRot.offsetMode(ROM_oneFingerX); - SLMesh* pSMesh = ps; - SLNode* pSNode = new SLNode(pSMesh, "Particle system node fire2"); - pSNode->translate(3.0f, -0.8f, 0.0f, TS_object); + // This loads the DEM file and overwrites the altitude of originLatLonAlt and defaultLatLonAlt + SLstring tif = dataPath + "erleb-AR/models/avenches/DTM-Aventicum-WGS84.tif"; + AppDemo::devLoc.loadGeoTiff(tif); - scene->addChild(pSNode); +#if defined(SL_OS_MACIOS) || defined(SL_OS_ANDROID) + AppDemo::devLoc.isUsed(true); + AppDemo::devRot.isUsed(true); + cam1->camAnim(SLCamAnim::CA_deviceRotLocYUp); +#else + AppDemo::devLoc.isUsed(false); + AppDemo::devRot.isUsed(false); + SLVec3d pos_d = AppDemo::devLoc.defaultENU() - AppDemo::devLoc.originENU(); + SLVec3f pos_f((SLfloat)pos_d.x, (SLfloat)pos_d.y, (SLfloat)pos_d.z); + cam1->translation(pos_f); + cam1->focalDist(pos_f.length()); + cam1->lookAt(0, cam1->translationWS().y, 0); + cam1->camAnim(SLCamAnim::CA_turntableYUp); +#endif - // 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); + sv->doWaitOnIdle(false); // for constant video feed + sv->camera(cam1); } - else if (sceneID == SID_ParticleSystem_Fountain) //............................................ + else if (sceneID == SID_ErlebARAventicumTheatre) //............................................ { - // 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"); + s->name("Aventicum Theatre AR"); + s->info("Augmented Reality for Aventicum Theatre"); - // Create a scene group node - SLNode* scene = new SLNode("scene node"); - s->root3D(scene); + // Create video texture on global pointer updated in AppDemoVideo + videoTexture = new SLGLTexture(am, texPath + "LiveVideoError.png", GL_LINEAR, GL_LINEAR); + videoTexture->texType(TT_videoBkgd); - // 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 see through video background material without shadow mapping + SLMaterial* matVideoBkgd = new SLMaterial(am, "matVideoBkgd", videoTexture); + matVideoBkgd->reflectionModel(RM_Custom); - // 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 see through video background material with shadow mapping + SLMaterial* matVideoBkgdSM = new SLMaterial(am, "matVideoBkgdSM", videoTexture); + matVideoBkgdSM->reflectionModel(RM_Custom); + matVideoBkgdSM->ambient(SLCol4f(0.6f, 0.6f, 0.6f)); + matVideoBkgdSM->getsShadows(true); - // 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); + // Setup the camera + SLCamera* cam1 = new SLCamera("Camera 1"); + cam1->translation(0, 50, -150); + cam1->lookAt(0, 0, 0); + cam1->clipNear(1); + cam1->clipFar(300); + cam1->focalDist(150); + cam1->setInitialState(); + cam1->devRotLoc(&AppDemo::devRot, &AppDemo::devLoc); + cam1->background().texture(videoTexture); - // 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); + // Turn on main video + CVCapture::instance()->videoType(VT_MAIN); - // 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) //................................................. - { - // 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 directional light for the sunlight + SLLightDirect* sunLight = new SLLightDirect(am, s, 1.0f); + sunLight->powers(1.0f, 1.0f, 1.0f); + sunLight->attenuation(1, 0, 0); + sunLight->translation(0, 1, 0); - // Create a scene group node - SLNode* scene = new SLNode("scene node"); - s->root3D(scene); + sunLight->doSunPowerAdaptation(true); + sunLight->createsShadows(true); + sunLight->createShadowMapAutoSize(cam1, SLVec2i(2048, 2048), 4); + sunLight->shadowMap()->cascadesFactor(3.0); + // sunLight->createShadowMap(-80, 100, SLVec2f(130, 130), SLVec2i(4096, 4096)); + sunLight->doSmoothShadows(true); + sunLight->castsShadows(false); + sunLight->shadowMinBias(0.001f); + sunLight->shadowMaxBias(0.001f); - // 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"); + // Let the sun be rotated by time and location + AppDemo::devLoc.sunLightNode(sunLight); - // 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); + SLAssimpImporter importer; + SLNode* theatre = importer.load(s->animManager(), + am, + dataPath + "erleb-AR/models/avenches/avenches-theater.gltf", + texPath, + nullptr, + true, // delete tex images after build + true, // only meshes + nullptr, // no replacement material + 0.4f); // 40% ambient reflection - 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)); + // Rotate to the true geographic rotation + theatre->rotate(-36.7f, 0, 1, 0, TS_parent); - SLMesh* pSMesh = ps; - SLNode* pSNode = new SLNode(pSMesh, "Particle Sun node"); - scene->addChild(pSNode); + // Let the video shine through some objects + theatre->findChild("Tht-Rasen")->setMeshMat(matVideoBkgdSM, true); + theatre->findChild("Tht-Untergrund")->setMeshMat(matVideoBkgd, true); + theatre->findChild("Tht-Boden")->setMeshMat(matVideoBkgdSM, true); + theatre->findChild("Tht-Boden")->setDrawBitsRec(SL_DB_WITHEDGES, true); - // 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) //.......................................... - { - // 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"); + // Add axis object a world origin + SLNode* axis = new SLNode(new SLCoordAxis(am), "Axis Node"); + axis->setDrawBitsRec(SL_DB_MESHWIRED, false); + axis->rotate(-90, 1, 0, 0); + axis->castsShadows(false); - // Create a scene group node - SLNode* scene = new SLNode("scene node"); + SLNode* scene = new SLNode("Scene"); s->root3D(scene); + scene->addChild(sunLight); + scene->addChild(axis); + scene->addChild(theatre); + scene->addChild(cam1); - // 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, - 1000, - SLVec3f(0.0f, 0.0f, 0.0f), - SLVec3f(0.0f, 0.0f, 0.0f), - 4.0f, - texC, - "Ring of fire Particle System", - texFlipbook); + // initialize sensor stuff + // https://map.geo.admin.ch/?lang=de&topic=ech&bgLayer=ch.swisstopo.swissimage&layers=ch.swisstopo.zeitreihen,ch.bfs.gebaeude_wohnungs_register,ch.bav.haltestellen-oev,ch.swisstopo.swisstlm3d-wanderwege&layers_opacity=1,1,1,0.8&layers_visibility=false,false,false,false&layers_timestamp=18641231,,,&E=2570281&N=1192204&zoom=13&crosshair=marker + AppDemo::devLoc.useOriginAltitude(false); + AppDemo::devLoc.nameLocations().push_back(SLLocation("Center of theatre, Origin", 46, 52, 49.041, 7, 2, 55.543, 454.9)); + AppDemo::devLoc.nameLocations().push_back(SLLocation("On the stage", 46, 52, 49.221, 7, 2, 55.206, 455.5 + 1.7)); + AppDemo::devLoc.nameLocations().push_back(SLLocation("At the tree (N-E)", 46, 52, 50.791, 7, 2, 55.960, 455.5 + 1.7)); + AppDemo::devLoc.nameLocations().push_back(SLLocation("Over the entrance (S)", 46, 52, 48.162, 7, 2, 56.097, 464.0 + 1.7)); + AppDemo::devLoc.nameLocations().push_back(SLLocation("At the 3rd tree (S-W)", 46, 52, 48.140, 7, 2, 51.506, 455.0 + 1.7)); + AppDemo::devLoc.originLatLonAlt(AppDemo::devLoc.nameLocations()[0].posWGS84LatLonAlt); + AppDemo::devLoc.activeNamedLocation(1); // This sets the location 1 as defaultENU + AppDemo::devLoc.locMaxDistanceM(1000.0f); // Max. Distanz. zum Nullpunkt + AppDemo::devLoc.improveOrigin(false); // Keine autom. Verbesserung vom Origin + AppDemo::devLoc.hasOrigin(true); + AppDemo::devLoc.offsetMode(LOM_twoFingerY); + AppDemo::devRot.zeroYawAtStart(false); + AppDemo::devRot.offsetMode(ROM_oneFingerX); - 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)); + // This loads the DEM file and overwrites the altitude of originLatLonAlt and defaultLatLonAlt + SLstring tif = dataPath + "erleb-AR/models/avenches/DTM-Aventicum-WGS84.tif"; + AppDemo::devLoc.loadGeoTiff(tif); - SLMesh* pSMesh = ps; - SLNode* pSNode = new SLNode(pSMesh, "Particle Ring Fire node"); - pSNode->rotate(90, 1, 0, 0); - scene->addChild(pSNode); +#if defined(SL_OS_MACIOS) || defined(SL_OS_ANDROID) + AppDemo::devLoc.isUsed(true); + AppDemo::devRot.isUsed(true); + cam1->camAnim(SLCamAnim::CA_deviceRotLocYUp); +#else + AppDemo::devLoc.isUsed(false); + AppDemo::devRot.isUsed(false); + SLVec3d pos_d = AppDemo::devLoc.defaultENU() - AppDemo::devLoc.originENU(); + SLVec3f pos_f((SLfloat)pos_d.x, (SLfloat)pos_d.y, (SLfloat)pos_d.z); + cam1->translation(pos_f); + cam1->focalDist(pos_f.length()); + cam1->lookAt(SLVec3f::ZERO); + cam1->camAnim(SLCamAnim::CA_turntableYUp); +#endif - // 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); + sv->doWaitOnIdle(false); // for constant video feed + sv->camera(cam1); } - else if (sceneID == SID_ParticleSystem_FireComplex) //......................................... + else if (sceneID == SID_ErlebARSutzKirchrain18) //............................................. { - // 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"); + s->name("Sutz, Kirchrain 18"); + s->info("Augmented Reality for Sutz, Kirchrain 18"); + + // Create video texture on global pointer updated in AppDemoVideo + videoTexture = new SLGLTexture(am, texPath + "LiveVideoError.png", GL_LINEAR, GL_LINEAR); + videoTexture->texType(TT_videoBkgd); + + // Create see through video background material without shadow mapping + SLMaterial* matVideoBkgd = new SLMaterial(am, "matVideoBkgd", videoTexture); + matVideoBkgd->reflectionModel(RM_Custom); - // Create a scene group node - SLNode* scene = new SLNode("scene node"); - s->root3D(scene); + // Create see through video background material with shadow mapping + SLMaterial* matVideoBkgdSM = new SLMaterial(am, "matVideoBkgdSM", videoTexture); + matVideoBkgdSM->reflectionModel(RM_Custom); + matVideoBkgdSM->ambient(SLCol4f(0.6f, 0.6f, 0.6f)); + matVideoBkgdSM->getsShadows(true); - // Create and add camera + // Create directional light for the sunlight + SLLightDirect* sunLight = new SLLightDirect(am, s, 5.0f); + sunLight->powers(1.0f, 1.0f, 1.0f); + sunLight->attenuation(1, 0, 0); + sunLight->translation(0, 10, 0); + sunLight->lookAt(10, 0, 10); + sunLight->doSunPowerAdaptation(true); + sunLight->createsShadows(true); + sunLight->createShadowMap(-100, 150, SLVec2f(150, 150), SLVec2i(4096, 4096)); + sunLight->doSmoothShadows(true); + sunLight->castsShadows(false); + + // Setup the 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->translation(0, 50, -150); + cam1->lookAt(0, 0, 0); + cam1->clipNear(1); + cam1->clipFar(300); + cam1->focalDist(150); cam1->setInitialState(); - scene->addChild(cam1); - sv->camera(cam1); + cam1->devRotLoc(&AppDemo::devRot, &AppDemo::devLoc); + cam1->background().texture(videoTexture); - // 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"); + // Turn on main video + CVCapture::instance()->videoType(VT_MAIN); - SLNode* complexFire = createComplexFire(am, - s, - true, - texTorchSmk, - texFireFlm, - 8, - 8, - texCircle, - texSmokeB, - texSmokeW); - scene->addChild(complexFire); + // Let the sun be rotated by time and location + AppDemo::devLoc.sunLightNode(sunLight); - // Room around - { - // Room parent node - SLNode* room = new SLNode("Room"); - scene->addChild(room); + // Import main model + SLAssimpImporter importer; + SLNode* sutzK18 = importer.load(s->animManager(), + am, + dataPath + "erleb-AR/models/sutz/Sutz-Kirchrain18.gltf", + texPath, + nullptr, + true, // delete tex images after build + true, // only meshes + nullptr, // no replacement material + 0.4f); // 40% ambient reflection - // 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); + // Rotate to the true geographic rotation + // Nothing to do here because the model is north up - // 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 + // Let the video shine through some objects + sutzK18->findChild("Terrain")->setMeshMat(matVideoBkgdSM, true); - // 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); + // Make buildings transparent with edges + SLNode* buildings = sutzK18->findChild("Buildings"); + buildings->setMeshMat(matVideoBkgd, true); + buildings->setDrawBitsRec(SL_DB_WITHEDGES, true); - // 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); + // Add axis object a world origin + SLNode* axis = new SLNode(new SLCoordAxis(am), "Axis Node"); + axis->setDrawBitsRec(SL_DB_MESHWIRED, false); + axis->rotate(-90, 1, 0, 0); + axis->castsShadows(false); - // 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); + SLNode* scene = new SLNode("Scene"); + s->root3D(scene); + scene->addChild(sunLight); + scene->addChild(axis); + scene->addChild(sutzK18); + scene->addChild(cam1); - // 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); + // initialize sensor stuff + // Go to https://map.geo.admin.ch and choose your origin and default point + AppDemo::devLoc.useOriginAltitude(false); + AppDemo::devLoc.originLatLonAlt(47.10600, 7.21772, 434.4f); // Corner Carport + AppDemo::devLoc.defaultLatLonAlt(47.10598, 7.21757, 433.9f + 1.7); // In the street - // 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); - } + AppDemo::devLoc.nameLocations().push_back(SLLocation("Corner Carport, Origin", 47, 6, 21.609, 7, 13, 3.788, 434.4)); + AppDemo::devLoc.nameLocations().push_back(SLLocation("Einfahrt (Dolendeckel)", 47, 6, 21.639, 7, 13, 2.764, 433.6 + 1.7)); + AppDemo::devLoc.nameLocations().push_back(SLLocation("Elektrokasten, Brunnenweg", 47, 6, 21.044, 7, 13, 4.920, 438.4 + 1.7)); + AppDemo::devLoc.nameLocations().push_back(SLLocation("Sitzbänkli am See", 47, 6, 24.537, 7, 13, 2.766, 431.2 + 1.7)); + AppDemo::devLoc.originLatLonAlt(AppDemo::devLoc.nameLocations()[0].posWGS84LatLonAlt); + AppDemo::devLoc.activeNamedLocation(1); // This sets the location 1 as defaultENU + AppDemo::devLoc.locMaxDistanceM(1000.0f); // Max. Distanz. zum Nullpunkt + AppDemo::devLoc.improveOrigin(false); // Keine autom. Verbesserung vom Origin + AppDemo::devLoc.hasOrigin(true); + AppDemo::devLoc.offsetMode(LOM_twoFingerY); + AppDemo::devRot.zeroYawAtStart(false); + AppDemo::devRot.offsetMode(ROM_oneFingerX); - // 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); + // This loads the DEM file and overwrites the altitude of originLatLonAlt and defaultLatLonAlt + SLstring tif = dataPath + "erleb-AR/models/sutz/Sutz-Kirchrain18-DEM-WGS84.tif"; + AppDemo::devLoc.loadGeoTiff(tif); - // 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); +#if defined(SL_OS_MACIOS) || defined(SL_OS_ANDROID) + AppDemo::devLoc.isUsed(true); + AppDemo::devRot.isUsed(true); + cam1->camAnim(SLCamAnim::CA_deviceRotLocYUp); +#else + AppDemo::devLoc.isUsed(false); + AppDemo::devRot.isUsed(false); + SLVec3d pos_d = AppDemo::devLoc.defaultENU() - AppDemo::devLoc.originENU(); + SLVec3f pos_f((SLfloat)pos_d.x, (SLfloat)pos_d.y, (SLfloat)pos_d.z); + cam1->translation(pos_f); + cam1->focalDist(pos_f.length()); + cam1->lookAt(SLVec3f::ZERO); + cam1->camAnim(SLCamAnim::CA_turntableYUp); +#endif - // 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); + sv->doWaitOnIdle(false); // for constant video feed + sv->camera(cam1); + } + else if (sceneID == SID_RTMuttenzerBox) //..................................................... + { + s->name("Muttenzer Box"); + s->info("Muttenzer Box with environment mapped reflective sphere and transparent " + "refractive glass sphere. Try ray tracing for real reflections and soft shadows."); - // 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); + // Create reflection & glass shaders + SLGLProgram* sp1 = new SLGLProgramGeneric(am, shaderPath + "Reflect.vert", shaderPath + "Reflect.frag"); + SLGLProgram* sp2 = new SLGLProgramGeneric(am, shaderPath + "RefractReflect.vert", shaderPath + "RefractReflect.frag"); - // 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); - } + // Create cube mapping texture + SLGLTexture* tex1 = new SLGLTexture(am, + texPath + "MuttenzerBox+X0512_C.png", + texPath + "MuttenzerBox-X0512_C.png", + texPath + "MuttenzerBox+Y0512_C.png", + texPath + "MuttenzerBox-Y0512_C.png", + texPath + "MuttenzerBox+Z0512_C.png", + texPath + "MuttenzerBox-Z0512_C.png"); - else if (sceneID == SID_Benchmark1_LargeModel) //.............................................. - { - SLstring largeFile = modelPath + "PLY/xyzrgb_dragon/xyzrgb_dragon.ply"; + SLCol4f lightEmisRGB(7.0f, 7.0f, 7.0f); + SLCol4f grayRGB(0.75f, 0.75f, 0.75f); + SLCol4f redRGB(0.75f, 0.25f, 0.25f); + SLCol4f blueRGB(0.25f, 0.25f, 0.75f); + SLCol4f blackRGB(0.00f, 0.00f, 0.00f); + + // create materials + SLMaterial* cream = new SLMaterial(am, "cream", grayRGB, SLCol4f::BLACK, 0); + SLMaterial* red = new SLMaterial(am, "red", redRGB, SLCol4f::BLACK, 0); + SLMaterial* blue = new SLMaterial(am, "blue", blueRGB, SLCol4f::BLACK, 0); - if (SLFileStorage::exists(largeFile, IOK_model)) - { - s->name("Large Model Benchmark Scene"); - s->info("Large Model with 7.2 mio. triangles."); + // Material for mirror sphere + SLMaterial* refl = new SLMaterial(am, "refl", blackRGB, SLCol4f::WHITE, 1000, 1.0f); + refl->addTexture(tex1); + refl->program(sp1); - // Material for glass - SLMaterial* diffuseMat = new SLMaterial(am, "diffuseMat", SLCol4f::WHITE, SLCol4f::WHITE); + // Material for glass sphere + SLMaterial* refr = new SLMaterial(am, "refr", blackRGB, blackRGB, 100, 0.05f, 0.95f, 1.5f); + refr->translucency(1000); + refr->transmissive(SLCol4f::WHITE); + refr->addTexture(tex1); + refr->program(sp2); - SLCamera* cam1 = new SLCamera("Camera 1"); - cam1->translation(0, 0, 220); - cam1->lookAt(0, 0, 0); - cam1->clipNear(1); - cam1->clipFar(10000); - cam1->focalDist(220); - cam1->background().colors(SLCol4f(0.7f, 0.7f, 0.7f), SLCol4f(0.2f, 0.2f, 0.2f)); - cam1->setInitialState(); - cam1->devRotLoc(&AppDemo::devRot, &AppDemo::devLoc); + SLNode* sphere1 = new SLNode(new SLSphere(am, 0.5f, 32, 32, "Sphere1", refl)); + sphere1->translate(-0.65f, -0.75f, -0.55f, TS_object); - SLLightSpot* light1 = new SLLightSpot(am, s, 200, 200, 200, 1); - light1->powers(0.1f, 1.0f, 1.0f); - light1->attenuation(1, 0, 0); + SLNode* sphere2 = new SLNode(new SLSphere(am, 0.45f, 32, 32, "Sphere2", refr)); + sphere2->translate(0.73f, -0.8f, 0.10f, TS_object); - SLAssimpImporter importer; - gDragonModel = importer.load(s->animManager(), - am, - largeFile, - texPath, - nullptr, - false, // delete tex images after build - true, - diffuseMat, - 0.2f, - false, - nullptr, - SLProcess_Triangulate | SLProcess_JoinIdenticalVertices); + SLNode* balls = new SLNode; + balls->addChild(sphere1); + balls->addChild(sphere2); - SLNode* scene = new SLNode("Scene"); - s->root3D(scene); - scene->addChild(light1); - scene->addChild(gDragonModel); - scene->addChild(cam1); + // Rectangular light + SLLightRect* lightRect = new SLLightRect(am, s, 1, 0.65f); + lightRect->rotate(90, -1.0f, 0.0f, 0.0f); + lightRect->translate(0.0f, -0.25f, 1.18f, TS_object); + lightRect->spotCutOffDEG(90); + lightRect->spotExponent(1.0); + lightRect->ambientColor(SLCol4f::WHITE); + lightRect->ambientPower(0.25f); + lightRect->diffuseColor(lightEmisRGB); + lightRect->attenuation(0, 0, 1); + lightRect->samplesXY(11, 7); + lightRect->createsShadows(true); + lightRect->createShadowMap(); - sv->camera(cam1); - } - } - else if (sceneID == SID_Benchmark2_MassiveNodes) //............................................ - { - s->name("Massive Data Benchmark Scene"); - s->info(s->name()); + SLLight::globalAmbient.set(lightEmisRGB * 0.01f); - SLCamera* cam1 = new SLCamera("Camera 1"); - cam1->clipNear(0.1f); - cam1->clipFar(100); - cam1->translation(0, 0, 50); - cam1->lookAt(0, 0, 0); - cam1->focalDist(50); - cam1->background().colors(SLCol4f(0.1f, 0.1f, 0.1f)); + // create camera + SLCamera* cam1 = new SLCamera(); + cam1->translation(0.0f, 0.40f, 6.35f); + cam1->lookAt(0.0f, -0.05f, 0.0f); + cam1->fov(27); + cam1->focalDist(cam1->translationOS().length()); + cam1->background().colors(SLCol4f(0.0f, 0.0f, 0.0f)); cam1->setInitialState(); + cam1->devRotLoc(&AppDemo::devRot, &AppDemo::devLoc); - SLLightSpot* light1 = new SLLightSpot(am, s, 15, 15, 15, 0.3f); - light1->powers(0.2f, 0.8f, 1.0f); - light1->attenuation(1, 0, 0); - + // assemble scene SLNode* scene = new SLNode; s->root3D(scene); scene->addChild(cam1); - scene->addChild(light1); + scene->addChild(lightRect); - // Generate NUM_MAT materials - const int NUM_MAT = 20; - SLVMaterial mat; - for (int i = 0; i < NUM_MAT; ++i) - { - SLGLTexture* texC = new SLGLTexture(am, texPath + "earth2048_C_Q95.jpg"); - SLstring matName = "mat-" + std::to_string(i); - mat.push_back(new SLMaterial(am, matName.c_str(), texC)); - SLCol4f color; - color.hsva2rgba(SLVec4f(Utils::TWOPI * (float)i / (float)NUM_MAT, 1.0f, 1.0f)); - mat[i]->diffuse(color); - } + // create wall polygons + SLfloat pL = -1.48f, pR = 1.48f; // left/right + SLfloat pB = -1.25f, pT = 1.19f; // bottom/top + SLfloat pN = 1.79f, pF = -1.55f; // near/far - // create a 3D array of spheres - SLint halfSize = 10; - SLuint n = 0; - for (SLint iZ = -halfSize; iZ <= halfSize; ++iZ) - { - for (SLint iY = -halfSize; iY <= halfSize; ++iY) - { - for (SLint iX = -halfSize; iX <= halfSize; ++iX) - { - // Choose a random material index - SLuint res = 36; - SLint iMat = (SLint)Utils::random(0, NUM_MAT - 1); - SLstring nodeName = "earth-" + std::to_string(n); + // bottom plane + SLNode* b = new SLNode(new SLRectangle(am, SLVec2f(pL, -pN), SLVec2f(pR, -pF), 6, 6, "bottom", cream)); + b->rotate(90, -1, 0, 0); + b->translate(0, 0, pB, TS_object); + scene->addChild(b); - // Create a new sphere and node and translate it - SLSphere* earth = new SLSphere(am, 0.3f, res, res, nodeName, mat[iMat]); - SLNode* sphere = new SLNode(earth); - sphere->translate(float(iX), float(iY), float(iZ), TS_object); - scene->addChild(sphere); - // SL_LOG("Earth: %000d (Mat: %00d)", n, iMat); - n++; - } - } - } + // top plane + SLNode* t = new SLNode(new SLRectangle(am, SLVec2f(pL, pF), SLVec2f(pR, pN), 6, 6, "top", cream)); + t->rotate(90, 1, 0, 0); + t->translate(0, 0, -pT, TS_object); + scene->addChild(t); + + // far plane + SLNode* f = new SLNode(new SLRectangle(am, SLVec2f(pL, pB), SLVec2f(pR, pT), 6, 6, "far", cream)); + f->translate(0, 0, pF, TS_object); + scene->addChild(f); + + // left plane + SLNode* l = new SLNode(new SLRectangle(am, SLVec2f(-pN, pB), SLVec2f(-pF, pT), 6, 6, "left", red)); + l->rotate(90, 0, 1, 0); + l->translate(0, 0, pL, TS_object); + scene->addChild(l); + + // right plane + SLNode* r = new SLNode(new SLRectangle(am, SLVec2f(pF, pB), SLVec2f(pN, pT), 6, 6, "right", blue)); + r->rotate(90, 0, -1, 0); + r->translate(0, 0, -pR, TS_object); + scene->addChild(r); + + scene->addChild(balls); sv->camera(cam1); - sv->doWaitOnIdle(false); } - else if (sceneID == SID_Benchmark3_NodeAnimations) //.......................................... + else if (sceneID == SID_RTSpheres) //.......................................................... { - s->name("Massive Node Animation Benchmark Scene"); - s->info(s->name()); - - SLCamera* cam1 = new SLCamera("Camera 1"); - cam1->clipNear(0.1f); - cam1->clipFar(100); - cam1->translation(0, 2.5f, 20); - cam1->focalDist(20); - cam1->lookAt(0, 2.5f, 0); - cam1->background().colors(SLCol4f(0.1f, 0.1f, 0.1f)); - cam1->setInitialState(); + s->name("Ray tracing Spheres"); + s->info("Classic ray tracing scene with transparent and reflective spheres. Be patient on mobile devices."); - SLLightSpot* light1 = new SLLightSpot(am, s, 15, 15, 15, 0.3f); - light1->powers(0.2f, 0.8f, 1.0f); - light1->attenuation(1, 0, 0); + // define materials + SLMaterial* matGla = new SLMaterial(am, "Glass", SLCol4f(0.0f, 0.0f, 0.0f), SLCol4f(0.5f, 0.5f, 0.5f), 100, 0.4f, 0.6f, 1.5f); + SLMaterial* matRed = new SLMaterial(am, "Red", SLCol4f(0.5f, 0.0f, 0.0f), SLCol4f(0.5f, 0.5f, 0.5f), 100, 0.5f, 0.0f, 1.0f); + SLMaterial* matYel = new SLMaterial(am, "Floor", SLCol4f(0.8f, 0.6f, 0.2f), SLCol4f(0.8f, 0.8f, 0.8f), 100, 0.5f, 0.0f, 1.0f); - SLNode* scene = new SLNode; - s->root3D(scene); - scene->addChild(cam1); - scene->addChild(light1); + SLCamera* cam1 = new SLCamera(); + cam1->translation(0, 0.1f, 2.5f); + cam1->lookAt(0, 0, 0); + cam1->focalDist(cam1->translationOS().length()); + cam1->background().colors(SLCol4f(0.1f, 0.4f, 0.8f)); + cam1->setInitialState(); + cam1->devRotLoc(&AppDemo::devRot, &AppDemo::devLoc); - // Generate NUM_MAT materials - const int NUM_MAT = 20; - SLVMaterial mat; - for (int i = 0; i < NUM_MAT; ++i) - { - SLGLTexture* texC = new SLGLTexture(am, texPath + "earth2048_C_Q95.jpg"); // color map - SLstring matName = "mat-" + std::to_string(i); - mat.push_back(new SLMaterial(am, matName.c_str(), texC)); - SLCol4f color; - color.hsva2rgba(SLVec4f(Utils::TWOPI * (float)i / (float)NUM_MAT, 1.0f, 1.0f)); - mat[i]->diffuse(color); - } + SLNode* rect = new SLNode(new SLRectangle(am, SLVec2f(-3, -3), SLVec2f(5, 4), 20, 20, "Floor", matYel)); + rect->rotate(90, -1, 0, 0); + rect->translate(0, -1, -0.5f, TS_object); - // create rotating sphere group - SLint maxDepth = 5; + SLLightSpot* light1 = new SLLightSpot(am, s, 2, 2, 2, 0.1f); + light1->powers(1, 7, 7); + light1->attenuation(0, 0, 1); - SLint resolution = 18; - scene->addChild(RotatingSphereGroup(am, - s, - maxDepth, - 0, - 0, - 0, - 1, - resolution, - mat)); + SLLightSpot* light2 = new SLLightSpot(am, s, 2, 2, -2, 0.1f); + light2->powers(1, 7, 7); + light2->attenuation(0, 0, 1); + SLNode* scene = new SLNode; sv->camera(cam1); - sv->doWaitOnIdle(false); + scene->addChild(light1); + scene->addChild(light2); + scene->addChild(SphereGroupRT(am, 3, 0, 0, 0, 1, 30, matGla, matRed)); + scene->addChild(rect); + scene->addChild(cam1); + + s->root3D(scene); } - else if (sceneID == SID_Benchmark4_SkinnedAnimations) //....................................... + else if (sceneID == SID_RTSoftShadows) //...................................................... { - SLint size = 20; - SLint numAstroboys = size * size; - SLchar name[512]; - snprintf(name, sizeof(name), "Massive Skinned Animation Benchmark w. %d individual Astroboys", numAstroboys); - s->name(name); - s->info(s->name()); - - // Create materials - SLMaterial* m1 = new SLMaterial(am, "m1", SLCol4f::GRAY); - m1->specular(SLCol4f::BLACK); + s->name("Ray tracing soft shadows"); + s->info("Ray tracing with soft shadow light sampling. Each light source is sampled 64x per pixel. Be patient on mobile devices."); - // Define a light - SLLightSpot* light1 = new SLLightSpot(am, s, 100, 40, 100, 1); - light1->powers(0.1f, 1.0f, 1.0f); - light1->attenuation(1, 0, 0); + // Create root node + SLNode* scene = new SLNode; + s->root3D(scene); + + // define materials + SLCol4f spec(0.8f, 0.8f, 0.8f); + SLMaterial* matBlk = new SLMaterial(am, "Glass", SLCol4f(0.0f, 0.0f, 0.0f), SLCol4f(0.5f, 0.5f, 0.5f), 100, 0.5f, 0.5f, 1.5f); + SLMaterial* matRed = new SLMaterial(am, "Red", SLCol4f(0.5f, 0.0f, 0.0f), SLCol4f(0.5f, 0.5f, 0.5f), 100, 0.5f, 0.0f, 1.0f); + SLMaterial* matYel = new SLMaterial(am, "Floor", SLCol4f(0.8f, 0.6f, 0.2f), SLCol4f(0.8f, 0.8f, 0.8f), 100, 0.0f, 0.0f, 1.0f); - // Define camera SLCamera* cam1 = new SLCamera; - cam1->translation(0, 30, 0); + cam1->translation(0, 0.1f, 4); cam1->lookAt(0, 0, 0); cam1->focalDist(cam1->translationOS().length()); cam1->background().colors(SLCol4f(0.1f, 0.4f, 0.8f)); cam1->setInitialState(); cam1->devRotLoc(&AppDemo::devRot, &AppDemo::devLoc); + scene->addChild(cam1); - // Floor rectangle - SLNode* rect = new SLNode(new SLRectangle(am, - SLVec2f(-20, -20), - SLVec2f(20, 20), - SLVec2f(0, 0), - SLVec2f(50, 50), - 50, - 50, - "Floor", - m1)); + SLNode* rect = new SLNode(new SLRectangle(am, SLVec2f(-5, -5), SLVec2f(5, 5), 1, 1, "Rect", matYel)); rect->rotate(90, -1, 0, 0); - - SLAssimpImporter importer; - - // Assemble scene - SLNode* scene = new SLNode("scene group"); - s->root3D(scene); - scene->addChild(light1); + rect->translate(0, 0, -0.5f); + rect->castsShadows(false); scene->addChild(rect); - scene->addChild(cam1); - - // create army with individual astroboys - SLfloat offset = 1.0f; - SLfloat z = (float)(size - 1) * offset * 0.5f; - for (SLint iZ = 0; iZ < size; ++iZ) - { - SLfloat x = -(float)(size - 1) * offset * 0.5f; + SLLightSpot* light1 = new SLLightSpot(am, s, 2, 2, 2, 0.3f); + light1->samples(8, 8); + light1->attenuation(0, 0, 1); + light1->createsShadows(true); + light1->createShadowMap(); + scene->addChild(light1); - for (SLint iX = 0; iX < size; ++iX) - { - SLNode* astroboy = importer.load(s->animManager(), - am, - modelPath + "DAE/AstroBoy/AstroBoy.dae", - texPath); + SLLightSpot* light2 = new SLLightSpot(am, s, 2, 2, -2, 0.3f); + light2->samples(8, 8); + light2->attenuation(0, 0, 1); + light2->createsShadows(true); + light2->createShadowMap(); + scene->addChild(light2); - s->animManager().lastAnimPlayback()->playForward(); - s->animManager().lastAnimPlayback()->playbackRate(Utils::random(0.5f, 1.5f)); - astroboy->translate(x, 0, z, TS_object); - scene->addChild(astroboy); - x += offset; - } - z -= offset; - } + scene->addChild(SphereGroupRT(am, 1, 0, 0, 0, 1, 32, matBlk, matRed)); sv->camera(cam1); } - else if (sceneID == SID_Benchmark5_ColumnsNoLOD || - sceneID == SID_Benchmark6_ColumnsLOD) //.............................................. + else if (sceneID == SID_RTDoF) //.............................................................. { - SLstring modelFile = modelPath + "GLTF/CorinthianColumn/Corinthian-Column-Round-LOD.gltf"; - SLstring texCFile = modelPath + "GLTF/CorinthianColumn/PavementSlateSquare2_2K_DIF.jpg"; - SLstring texNFile = modelPath + "GLTF/CorinthianColumn/PavementSlateSquare2_2K_NRM.jpg"; - - if (SLFileStorage::exists(modelFile, IOK_model) && - SLFileStorage::exists(texCFile, IOK_image) && - SLFileStorage::exists(texNFile, IOK_image)) - { - SLchar name[512]; - SLint size; - if (sceneID == SID_Benchmark5_ColumnsNoLOD) - { - size = 25; - snprintf(name, sizeof(name), "%d corinthian columns without LOD", size * size); - s->name(name); - } - else - { - size = 50; - snprintf(name, sizeof(name), "%d corinthian columns with LOD", size * size); - s->name(name); - } - s->info(s->name() + " with cascaded shadow mapping. In the Day-Time dialogue you can change the sun angle."); - - // Create ground material - SLGLTexture* texFloorDif = new SLGLTexture(am, texCFile, SL_ANISOTROPY_MAX, GL_LINEAR); - SLGLTexture* texFloorNrm = new SLGLTexture(am, texNFile, SL_ANISOTROPY_MAX, GL_LINEAR); - SLMaterial* matFloor = new SLMaterial(am, "matFloor", texFloorDif, texFloorNrm); - - // Define camera - SLCamera* cam1 = new SLCamera; - cam1->translation(0, 1.7f, 20); - cam1->lookAt(0, 1.7f, 0); - cam1->focalDist(cam1->translationOS().length()); - cam1->clipFar(600); - cam1->background().colors(SLCol4f(0.1f, 0.4f, 0.8f)); - cam1->setInitialState(); - - // Create directional light for the sunlight - SLLightDirect* sunLight = new SLLightDirect(am, s, 1.0f); - sunLight->powers(0.25f, 1.0f, 1.0f); - sunLight->attenuation(1, 0, 0); - sunLight->translation(0, 1.7f, 0); - sunLight->lookAt(-1, 0, -1); - sunLight->doSunPowerAdaptation(true); - - // Add cascaded shadow mapping - sunLight->createsShadows(true); - sunLight->createShadowMapAutoSize(cam1); - sunLight->doSmoothShadows(true); - sunLight->castsShadows(false); - sunLight->shadowMinBias(0.003f); - sunLight->shadowMaxBias(0.012f); - - // Let the sun be rotated by time and location - AppDemo::devLoc.sunLightNode(sunLight); - AppDemo::devLoc.originLatLonAlt(47.14271, 7.24337, 488.2); // Ecke Giosa - AppDemo::devLoc.defaultLatLonAlt(47.14260, 7.24310, 488.7 + 1.7); // auf Parkplatz + s->name("Ray tracing depth of field"); - // Floor rectangle - SLNode* rect = new SLNode(new SLRectangle(am, - SLVec2f(-200, -200), - SLVec2f(200, 200), - SLVec2f(0, 0), - SLVec2f(50, 50), - 50, - 50, - "Floor", - matFloor)); - rect->rotate(90, -1, 0, 0); - rect->castsShadows(false); + // Create root node + SLNode* scene = new SLNode; + s->root3D(scene); - // Load the corinthian column - SLAssimpImporter importer; - SLNode* columnLOD = importer.load(s->animManager(), - am, - modelFile, - texPath, - nullptr, - false, // delete tex images after build - true, // only meshes - nullptr, // no replacement material - 1.0f); // 40% ambient reflection + // Create textures and materials + SLGLTexture* texC = new SLGLTexture(am, texPath + "Checkerboard0512_C.png", SL_ANISOTROPY_MAX, GL_LINEAR); + SLMaterial* mT = new SLMaterial(am, "mT", texC); + mT->kr(0.5f); + SLMaterial* mW = new SLMaterial(am, "mW", SLCol4f::WHITE); + SLMaterial* mB = new SLMaterial(am, "mB", SLCol4f::GRAY); + SLMaterial* mY = new SLMaterial(am, "mY", SLCol4f::YELLOW); + SLMaterial* mR = new SLMaterial(am, "mR", SLCol4f::RED); + SLMaterial* mG = new SLMaterial(am, "mG", SLCol4f::GREEN); + SLMaterial* mM = new SLMaterial(am, "mM", SLCol4f::MAGENTA); - SLNode* columnL0 = columnLOD->findChild("Corinthian-Column-Round-L0"); - SLNode* columnL1 = columnLOD->findChild("Corinthian-Column-Round-L1"); - SLNode* columnL2 = columnLOD->findChild("Corinthian-Column-Round-L2"); - SLNode* columnL3 = columnLOD->findChild("Corinthian-Column-Round-L3"); +#ifndef SL_GLES + SLuint numSamples = 10; +#else + SLuint numSamples = 4; +#endif - // Assemble scene - SLNode* scene = new SLNode("Scene"); - s->root3D(scene); - scene->addChild(sunLight); - scene->addChild(rect); - scene->addChild(cam1); + stringstream ss; + ss << "Ray tracing with depth of field blur. Each pixel is sampled " << numSamples * numSamples << "x from a lens. Be patient on mobile devices."; + s->info(ss.str()); - // create loads of pillars - SLint numColumns = size * size; - SLfloat offset = 5.0f; - SLfloat z = (float)(size - 1) * offset * 0.5f; + SLCamera* cam1 = new SLCamera("Camera 1"); + cam1->translation(0, 2, 7); + cam1->lookAt(0, 0, 0); + cam1->focalDist(cam1->translationOS().length()); + cam1->clipFar(80); + cam1->lensDiameter(0.4f); + cam1->lensSamples()->samples(numSamples, numSamples); + cam1->background().colors(SLCol4f(0.1f, 0.4f, 0.8f)); + cam1->setInitialState(); + cam1->fogIsOn(true); + cam1->fogMode(FM_exp); + cam1->fogDensity(0.04f); + scene->addChild(cam1); - for (SLint iZ = 0; iZ < size; ++iZ) - { - SLfloat x = -(float)(size - 1) * offset * 0.5f; + SLuint res = 36; + SLNode* rect = new SLNode(new SLRectangle(am, + SLVec2f(-40, -10), + SLVec2f(40, 70), + SLVec2f(0, 0), + SLVec2f(4, 4), + 2, + 2, + "Rect", + mT)); + rect->rotate(90, -1, 0, 0); + rect->translate(0, 0, -0.5f, TS_object); + scene->addChild(rect); - for (SLint iX = 0; iX < size; ++iX) - { - SLint iZX = iZ * size + iX; + SLLightSpot* light1 = new SLLightSpot(am, s, 2, 2, 0, 0.1f); + light1->ambiDiffPowers(0.1f, 1); + light1->attenuation(1, 0, 0); + scene->addChild(light1); - if (sceneID == SID_Benchmark5_ColumnsNoLOD) - { - // Without just the level 0 node - string strNode = "Node" + std::to_string(iZX); - SLNode* column = new SLNode(columnL1->mesh(), strNode + "-L0"); - column->translate(x, 0, z, TS_object); - scene->addChild(column); - } - else - { - // With LOD parent node and 3 levels - string strLOD = "LOD" + std::to_string(iZX); - SLNodeLOD* lod_group = new SLNodeLOD(strLOD); - lod_group->translate(x, 0, z, TS_object); - lod_group->addChildLOD(new SLNode(columnL1->mesh(), strLOD + "-L0"), 0.1f, 3); - lod_group->addChildLOD(new SLNode(columnL2->mesh(), strLOD + "-L1"), 0.01f, 3); - lod_group->addChildLOD(new SLNode(columnL3->mesh(), strLOD + "-L2"), 0.0001f, 3); - scene->addChild(lod_group); - } - x += offset; - } - z -= offset; - } + SLNode* balls = new SLNode; + SLNode* sp; + sp = new SLNode(new SLSphere(am, 0.5f, res, res, "S1", mW)); + sp->translate(2.0, 0, -4, TS_object); + balls->addChild(sp); + sp = new SLNode(new SLSphere(am, 0.5f, res, res, "S2", mB)); + sp->translate(1.5, 0, -3, TS_object); + balls->addChild(sp); + sp = new SLNode(new SLSphere(am, 0.5f, res, res, "S3", mY)); + sp->translate(1.0, 0, -2, TS_object); + balls->addChild(sp); + sp = new SLNode(new SLSphere(am, 0.5f, res, res, "S4", mR)); + sp->translate(0.5, 0, -1, TS_object); + balls->addChild(sp); + sp = new SLNode(new SLSphere(am, 0.5f, res, res, "S5", mG)); + sp->translate(0.0, 0, 0, TS_object); + balls->addChild(sp); + sp = new SLNode(new SLSphere(am, 0.5f, res, res, "S6", mM)); + sp->translate(-0.5, 0, 1, TS_object); + balls->addChild(sp); + sp = new SLNode(new SLSphere(am, 0.5f, res, res, "S7", mW)); + sp->translate(-1.0, 0, 2, TS_object); + balls->addChild(sp); + scene->addChild(balls); - // Set active camera & the root pointer - sv->camera(cam1); - sv->doWaitOnIdle(false); - } + sv->camera(cam1); } - else if (sceneID == SID_Benchmark7_JansUniverse) //............................................ + else if (sceneID == SID_RTLens) //............................................................. { - s->name("Jan's Universe Test Scene"); - s->info(s->name()); - - SLCamera* cam1 = new SLCamera("Camera 1"); - cam1->clipNear(0.1f); - cam1->clipFar(1000); - cam1->translation(0, 0, 75); - cam1->focalDist(75); - cam1->lookAt(0, 0, 0); - cam1->background().colors(SLCol4f(0.3f, 0.3f, 0.3f)); - cam1->setInitialState(); + s->name("Ray tracing lens test"); + s->info("Ray tracing lens test scene."); - // Root scene node + // Create root node SLNode* scene = new SLNode; s->root3D(scene); - scene->addChild(cam1); - // Generate NUM_MAT cook-torrance materials -#ifndef SL_GLES - const int NUM_MAT_MESH = 100; - SLuint const levels = 6; - SLuint const childCount = 8; -#else - const int NUM_MAT_MESH = 20; - SLuint const levels = 6; - SLuint const childCount = 8; -#endif - SLVMaterial materials(NUM_MAT_MESH); - for (int i = 0; i < NUM_MAT_MESH; ++i) - { - /* - SLGLProgram* spTex = new SLGLProgramGeneric(am, - shaderPath + "PerPixCookTm.vert", - shaderPath + "PerPixCookTm.frag");*/ - SLstring matName = "mat-" + std::to_string(i); - materials[i] = new SLMaterial(am, - matName.c_str(), - nullptr, - new SLGLTexture(am, texPath + "rusty-metal_2048_C.jpg"), - nullptr, // new SLGLTexture(am, texPath + "rusty-metal_2048_N.jpg"), - new SLGLTexture(am, texPath + "rusty-metal_2048_M.jpg"), - new SLGLTexture(am, texPath + "rusty-metal_2048_R.jpg"), - nullptr, - nullptr); - SLCol4f color; - color.hsva2rgba(SLVec4f(Utils::TWOPI * (float)i / (float)NUM_MAT_MESH, 1.0f, 1.0f)); - materials[i]->diffuse(color); - } + // Create textures and materials + SLGLTexture* texC = new SLGLTexture(am, texPath + "VisionExample.jpg"); + // SLGLTexture* texC = new SLGLTexture(am, texPath + "Checkerboard0512_C.png"); - // Generate NUM_MESH sphere meshes - SLVMesh meshes(NUM_MAT_MESH); - for (int i = 0; i < NUM_MAT_MESH; ++i) - { - SLstring meshName = "mesh-" + std::to_string(i); - meshes[i] = new SLSphere(am, 1.0f, 32, 32, meshName.c_str(), materials[i % NUM_MAT_MESH]); - } + SLMaterial* mT = new SLMaterial(am, "mT", texC, nullptr, nullptr, nullptr); + mT->kr(0.5f); - // Create universe - generateUniverse(am, s, scene, 0, levels, childCount, materials, meshes); + // Glass material + // name, ambient, specular, shininess, kr(reflectivity), kt(transparency), kn(refraction) + SLMaterial* matLens = new SLMaterial(am, "lens", SLCol4f(0.0f, 0.0f, 0.0f), SLCol4f(0.5f, 0.5f, 0.5f), 100, 0.5f, 0.5f, 1.5f); + // SLGLShaderProg* sp1 = new SLGLShaderProgGeneric("RefractReflect.vert", "RefractReflect.frag"); + // matLens->shaderProg(sp1); - sv->camera(cam1); - sv->doWaitOnIdle(false); - } - else if (sceneID == SID_Benchmark8_ParticleSystemFireComplex) //............................... - { - s->name("Fire Complex Test Scene"); - s->info(s->name()); +#ifndef APP_USES_GLES + SLuint numSamples = 10; +#else + SLuint numSamples = 6; +#endif - SLCamera* cam1 = new SLCamera("Camera 1"); - cam1->clipNear(0.1f); - cam1->clipFar(1000); - cam1->translation(0, 10, 40); - cam1->focalDist(100); + // Scene + SLCamera* cam1 = new SLCamera; + cam1->translation(0, 8, 0); cam1->lookAt(0, 0, 0); - cam1->background().colors(SLCol4f(0.3f, 0.3f, 0.3f)); + cam1->focalDist(cam1->translationOS().length()); + cam1->lensDiameter(0.4f); + cam1->lensSamples()->samples(numSamples, numSamples); + cam1->background().colors(SLCol4f(0.1f, 0.4f, 0.8f)); cam1->setInitialState(); + cam1->devRotLoc(&AppDemo::devRot, &AppDemo::devLoc); + scene->addChild(cam1); - // Root scene node - SLNode* root = new SLNode; - s->root3D(root); - root->addChild(cam1); - const int NUM_NODES = 250; + // Plane + // SLNode* rect = new SLNode(new SLRectangle(SLVec2f(-20, -20), SLVec2f(20, 20), 50, 20, "Rect", mT)); + // rect->translate(0, 0, 0, TS_Object); + // rect->rotate(90, -1, 0, 0); - // Create textures and materials - SLGLTexture* texFireCld = new SLGLTexture(am, texPath + "ParticleFirecloudTransparent_C.png"); - SLGLTexture* texFireFlm = new SLGLTexture(am, texPath + "ParticleFlames_00_8x4_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"); + SLLightSpot* light1 = new SLLightSpot(am, s, 1, 6, 1, 0.1f); + light1->attenuation(0, 0, 1); + scene->addChild(light1); - SLVNode nodes(NUM_NODES); - for (int i = 0; i < NUM_NODES; ++i) - { - SLNode* fireComplex = createComplexFire(am, - s, - false, - texFireCld, - texFireFlm, - 8, - 4, - texCircle, - texSmokeB, - texSmokeW); - fireComplex->translate(-20.0f + (float)(i % 20) * 2, - 0.0f, - -(float)((i - (float)(i % 20)) / 20) * 4, - TS_object); - root->addChild(fireComplex); - } + SLuint res = 20; + SLNode* rect = new SLNode(new SLRectangle(am, SLVec2f(-5, -5), SLVec2f(5, 5), res, res, "Rect", mT)); + rect->rotate(90, -1, 0, 0); + rect->translate(0, 0, -0.0f, TS_object); + scene->addChild(rect); + + // Lens from eye prescription card + // SLNode* lensA = new SLNode(new SLLens(s, 0.50f, -0.50f, 4.0f, 0.0f, 32, 32, "presbyopic", matLens)); // Weitsichtig + // lensA->translate(-2, 1, -2); + // scene->addChild(lensA); + + // SLNode* lensB = new SLNode(new SLLens(s, -0.65f, -0.10f, 4.0f, 0.0f, 32, 32, "myopic", matLens)); // Kurzsichtig + // lensB->translate(2, 1, -2); + // scene->addChild(lensB); + + // Lens with radius + // SLNode* lensC = new SLNode(new SLLens(s, 5.0, 4.0, 4.0f, 0.0f, 32, 32, "presbyopic", matLens)); // Weitsichtig + // lensC->translate(-2, 1, 2); + // scene->addChild(lensC); + + SLNode* lensD = new SLNode(new SLLens(am, + -15.0f, + -15.0f, + 1.0f, + 0.1f, + res, + res, + "myopic", + matLens)); // Kurzsichtig + lensD->translate(0, 6, 0); + scene->addChild(lensD); sv->camera(cam1); - sv->doWaitOnIdle(false); } - else if (sceneID == SID_Benchmark9_ParticleSystemManyParticles) //............................. + else if (sceneID == SID_RTTest) //............................................................. { - s->name("Particle System number Scene"); - s->info(s->name()); + // Set scene name and info string + s->name("Ray tracing test"); + s->info("RT Test Scene"); + // Create a camera node SLCamera* cam1 = new SLCamera("Camera 1"); - cam1->clipNear(0.1f); - cam1->clipFar(1000); - cam1->translation(0, 0, 400); - cam1->focalDist(400); + cam1->translation(0, 0, 5); cam1->lookAt(0, 0, 0); - cam1->background().colors(SLCol4f(0.3f, 0.3f, 0.3f)); + cam1->background().colors(SLCol4f(0.5f, 0.5f, 0.5f)); cam1->setInitialState(); + cam1->devRotLoc(&AppDemo::devRot, &AppDemo::devLoc); - // Root scene node - SLNode* root = new SLNode; - root->addChild(cam1); + // Create a light source node + SLLightSpot* light1 = new SLLightSpot(am, s, 0.3f); + light1->translation(5, 5, 5); + light1->lookAt(0, 0, 0); + light1->name("light node"); - // 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"); + // Material for glass sphere + SLMaterial* matBox1 = new SLMaterial(am, "matBox1", SLCol4f(0.0f, 0.0f, 0.0f), SLCol4f(0.5f, 0.5f, 0.5f), 100, 0.0f, 0.9f, 1.5f); + SLMesh* boxMesh1 = new SLBox(am, -0.8f, -1, 0.02f, 1.2f, 1, 1, "boxMesh1", matBox1); + SLNode* boxNode1 = new SLNode(boxMesh1, "BoxNode1"); - // Create meshes and nodes - SLParticleSystem* ps = new SLParticleSystem(am, - 1000000, - SLVec3f(-10.0f, -10.0f, -10.0f), - SLVec3f(10.0f, 10.0f, 10.0f), - 4.0f, - texC, - "Particle System", - texFlipbook); - ps->doAlphaOverLT(false); - ps->doSizeOverLT(false); - ps->doRotation(false); - ps->doShape(true); - ps->shapeType(ST_Box); - ps->shapeScale(100.0f, 100.0f, 100.0f); - ps->doDirectionSpeed(true); - ps->doBlendBrightness(true); - ps->doColor(true); - ps->color(SLCol4f(0.875f, 0.156f, 0.875f, 1.0f)); - ps->speed(0.0f); - SLMesh* pSMesh = ps; - SLNode* pSNode = new SLNode(pSMesh, "Particle system node"); - root->addChild(pSNode); + SLMaterial* matBox2 = new SLMaterial(am, "matBox2", SLCol4f(0.0f, 0.0f, 0.0f), SLCol4f(0.5f, 0.5f, 0.5f), 100, 0.0f, 0.9f, 1.3f); + SLMesh* boxMesh2 = new SLBox(am, -1.2f, -1, -1, 0.8f, 1, -0.02f, "BoxMesh2", matBox2); + SLNode* boxNode2 = new SLNode(boxMesh2, "BoxNode2"); + + // Create a scene group and add all nodes + SLNode* scene = new SLNode("scene node"); + s->root3D(scene); + scene->addChild(light1); + scene->addChild(cam1); + scene->addChild(boxNode1); + scene->addChild(boxNode2); sv->camera(cam1); - sv->doWaitOnIdle(false); - s->root3D(root); } //////////////////////////////////////////////////////////////////////////// diff --git a/modules/sl/source/SLEnums.h b/modules/sl/source/SLEnums.h index a892a639..a4177eef 100644 --- a/modules/sl/source/SLEnums.h +++ b/modules/sl/source/SLEnums.h @@ -174,6 +174,8 @@ enum SLSceneID SID_ParticleSystem_RingOfFire, SID_ParticleSystem_FireComplex, + SID_MaxNoBenchmarks, + SID_Benchmark1_LargeModel, SID_Benchmark2_MassiveNodes, SID_Benchmark3_NodeAnimations, @@ -184,7 +186,7 @@ enum SLSceneID SID_Benchmark8_ParticleSystemFireComplex, SID_Benchmark9_ParticleSystemManyParticles, - SID_Maximal, + SID_MaxPublicAssets, // These scenes are not part of the public data SID_ErlebARBernChristoffel, @@ -195,7 +197,9 @@ enum SLSceneID SID_ErlebARAventicumAmphiteatre, SID_ErlebARAventicumCigognier, SID_ErlebARAventicumTheatre, - SID_ErlebARSutzKirchrain18 + SID_ErlebARSutzKirchrain18, + + SID_MaxAll }; //----------------------------------------------------------------------------- //! Mouse button codes diff --git a/modules/sl/source/gl/SLGLVertexArray.cpp b/modules/sl/source/gl/SLGLVertexArray.cpp index 2a8cab0b..a8ad18ad 100644 --- a/modules/sl/source/gl/SLGLVertexArray.cpp +++ b/modules/sl/source/gl/SLGLVertexArray.cpp @@ -285,6 +285,7 @@ void SLGLVertexArray::generateTF(SLuint numVertices, _vbo.bindAndEnableAttrib(divisor); } + // ??? if (_externalVbo != nullptr) { _externalVbo->bindAndEnableAttrib(_externalDivisor); @@ -317,7 +318,10 @@ void SLGLVertexArray::generateTF(SLuint numVertices, glGenBuffers(1, &_idVBOIndices); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _idVBOIndices); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, tmBufSize, tmpBuf, GL_STATIC_DRAW); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, + tmBufSize, + tmpBuf, + GL_STATIC_DRAW); SLGLVertexBuffer::totalBufferCount++; SLGLVertexBuffer::totalBufferSize += tmBufSize; delete[] tmpBuf; @@ -463,7 +467,7 @@ void SLGLVertexArray::drawArrayAs(SLGLPrimitiveType primitiveType, GET_GL_ERROR; } //----------------------------------------------------------------------------- -/*! +/*! ??? * * @param primitiveType * @param countInstance @@ -471,7 +475,7 @@ void SLGLVertexArray::drawArrayAs(SLGLPrimitiveType primitiveType, * @param indexOffset */ void SLGLVertexArray::drawElementsInstanced(SLGLPrimitiveType primitiveType, - SLuint countInstance, + SLuint countInstance, SLuint numIndexes, SLuint indexOffset) { @@ -495,7 +499,7 @@ void SLGLVertexArray::drawElementsInstanced(SLGLPrimitiveType primitiveType, (GLsizei)numIndexes, _indexDataType, (void*)(size_t)(indexOffset * (SLuint)indexTypeSize), - (GLsizei )countInstance); + (GLsizei)countInstance); //////////////////////////////////////////////////////// GET_GL_ERROR; diff --git a/modules/sl/source/gl/SLGLVertexArray.h b/modules/sl/source/gl/SLGLVertexArray.h index d324a3eb..956c7b20 100644 --- a/modules/sl/source/gl/SLGLVertexArray.h +++ b/modules/sl/source/gl/SLGLVertexArray.h @@ -216,7 +216,7 @@ class SLGLVertexArray SLuint _tfoID; //! OpenGL id of transform feedback object SLuint _numVertices; //! NO. of vertices in array SLGLVertexBuffer _vbo; //! Vertex buffer object for float attributes - SLGLVertexBuffer* _externalVbo; //! Vertex buffer object that has been created outside of this VAO + SLGLVertexBuffer* _externalVbo; //! Vertex buffer object that has been created outside of this VAO ??? What for? SLuint _externalDivisor; //! VBO attrib divisor for the external VBO SLuint _idVBOIndices; //! OpenGL id of index vbo size_t _numIndicesElements; //! NO. of vertex indices in array for triangles, lines or points diff --git a/modules/sl/source/mesh/SLParticleSystem.cpp b/modules/sl/source/mesh/SLParticleSystem.cpp index 121a6eca..6da55f05 100644 --- a/modules/sl/source/mesh/SLParticleSystem.cpp +++ b/modules/sl/source/mesh/SLParticleSystem.cpp @@ -38,7 +38,8 @@ SLParticleSystem::SLParticleSystem(SLAssetManager* assetMgr, _drawInstanced = !SLGLState::instance()->glHasGeometryShaders() || drawInstanced; _primitive = PT_points; - P.push_back(SLVec3f(0, 0, 0)); // Trick SL project because it wants mesh to have vertex + // Trick SL project because it wants mesh to have vertex + P.push_back(SLVec3f(0, 0, 0)); I32.push_back(0); if (amount > UINT_MAX) // Need to change for number of floats @@ -94,7 +95,8 @@ SLVec3f SLParticleSystem::getPointOnSphere(float radius, SLVec3f randomXs) //! Function which return the direction towards the exterior of a sphere SLVec3f SLParticleSystem::getDirectionSphere(SLVec3f position) { - return (position - SLVec3f(0.0f, 0.0f, 0.0f)).normalized(); // Get unit vector center to position + // return a unit vector from center to position + return (position - SLVec3f(0.0f, 0.0f, 0.0f)).normalized(); } //----------------------------------------------------------------------------- //! Function which return a position in a box @@ -157,7 +159,8 @@ SLVec3f SLParticleSystem::getPointOnBox(SLVec3f boxScale) //! Function which return the direction towards the exterior of a box SLVec3f SLParticleSystem::getDirectionBox(SLVec3f position) { - return (position - SLVec3f(0.0f, 0.0f, 0.0f)).normalized(); // Get unit vector center to position + // return a unit vector from center to position + return (position - SLVec3f(0.0f, 0.0f, 0.0f)).normalized(); } //----------------------------------------------------------------------------- //! Function which return a position in the cone define in the particle system @@ -167,7 +170,8 @@ SLVec3f SLParticleSystem::getPointInCone() float radius = _shapeRadius; if (!_doShapeSpawnBase) // Spawn inside volume { - y = Utils::random(0.0f, _shapeHeight); // NEED TO HAVE MORE value near 1 when we have smaller base that top + // NEED TO HAVE MORE value near 1 when we have smaller base that top + y = Utils::random(0.0f, _shapeHeight); radius = _shapeRadius + tan(_shapeAngle * DEG2RAD) * y; } float r = radius * sqrt(random(0.0f, 1.0f)); @@ -185,7 +189,8 @@ SLVec3f SLParticleSystem::getPointOnCone() float radius = _shapeRadius; if (!_doShapeSpawnBase) // Spawn inside volume { - y = Utils::random(0.0f, _shapeHeight); // NEED TO HAVE MORE value near 1 when we have smaller base that top + // NEED TO HAVE MORE value near 1 when we have smaller base that top + y = Utils::random(0.0f, _shapeHeight); radius = _shapeRadius + tan(_shapeAngle * DEG2RAD) * y; } float r = radius; @@ -199,15 +204,18 @@ SLVec3f SLParticleSystem::getPointOnCone() //! Function which return a direction following the cone shape SLVec3f SLParticleSystem::getDirectionCone(SLVec3f position) { - float maxRadius = _shapeRadius + tan(_shapeAngle * DEG2RAD) * _shapeHeight; // Calculate max radius - float percentX = position.x / maxRadius; // Calculate at which percent our x is, to know how much we need to adapt our angle - float percentZ = position.z / maxRadius; // Calculate at which percent our z is, to know how much we need to adapt our angle - float newX = position.x + tan(_shapeAngle * percentX * DEG2RAD) * _shapeHeight; - float newZ = position.z + tan(_shapeAngle * percentZ * DEG2RAD) * _shapeHeight; + float maxRadius = _shapeRadius + tan(_shapeAngle * DEG2RAD) * _shapeHeight; + + // Calculate at which percent our x is, to know how much we need to adapt our angle + float percentX = position.x / maxRadius; + // Calculate at which percent our z is, to know how much we need to adapt our angle + float percentZ = position.z / maxRadius; + float newX = position.x + tan(_shapeAngle * percentX * DEG2RAD) * _shapeHeight; + float newZ = position.z + tan(_shapeAngle * percentZ * DEG2RAD) * _shapeHeight; return SLVec3f(newX, _shapeHeight, newZ).normalize(); } //----------------------------------------------------------------------------- -//! Function which return a position in the pyramid define in the particle system +//! Function which returns a position in the pyramid that define the PS SLVec3f SLParticleSystem::getPointInPyramid() { float y = 0.0f; @@ -223,7 +231,7 @@ SLVec3f SLParticleSystem::getPointInPyramid() return SLVec3f(x, y, z); } //----------------------------------------------------------------------------- -//! Function which return a position on the pyramid define in the particle system +//! Function which return a position on the pyramid that defines the PS SLVec3f SLParticleSystem::getPointOnPyramid() { float y = 0.0f; @@ -234,12 +242,11 @@ SLVec3f SLParticleSystem::getPointOnPyramid() radius = _shapeWidth + tan(_shapeAngle * DEG2RAD) * y; } - // int temp = Utils::random(0, 5); int temp = Utils::random(0, 3); - float x = 0.0f; - float z = 0.0f; - if (temp == 0) - { // LEFT + float x = 0.0f, z = 0.0f; + + if (temp == 0) // LEFT + { x = -radius; z = Utils::random(-radius, radius); } @@ -258,22 +265,6 @@ SLVec3f SLParticleSystem::getPointOnPyramid() x = Utils::random(-radius, radius); z = -radius; } - // Comments to have top and bottom not filled - /* else if (temp == 4) //TOP - { - y = _heightPyramid; - radius = _shapeWidth + tan(_anglePyramid * DEG2RAD) * y; - - x = Utils::random(-radius, radius); - z = Utils::random(-radius, radius); - } - else if (temp == 5) // BOTTOM - { - y = 0.0f; - radius = _shapeWidth; - x = Utils::random(-radius, radius); - z = Utils::random(-radius, radius); - }*/ return SLVec3f(x, y, z); } @@ -299,12 +290,8 @@ void SLParticleSystem::generate() default_random_engine generator(seed); normal_distribution distribution(0.0f, 1.0f); - SLVVec3f tempP; - SLVVec3f tempV; - SLVfloat tempST; - SLVVec3f tempInitV; - SLVfloat tempR; - SLVfloat tempAngulareVelo; + SLVVec3f tempP, tempV, tempInitV; + SLVfloat tempR, tempST, tempAngulareVelo; SLVuint tempTexNum; SLVVec3f tempInitP; @@ -313,23 +300,30 @@ void SLParticleSystem::generate() else _primitive = PT_points; - deleteDataGpu(); + SLMesh::deleteDataGpu(); // Initialize the drawing: if (_doFlipBookTexture) { - SLMaterial* mDraw = new SLMaterial(_assetManager, "Drawing-Material", this, _textureFlipbook); + SLMaterial* mDraw = new SLMaterial(_assetManager, + "Drawing-Material", + this, + _textureFlipbook); mat(mDraw); } else { - SLMaterial* mDraw = new SLMaterial(_assetManager, "Drawing-Material", this, _textureFirst); + SLMaterial* mDraw = new SLMaterial(_assetManager, + "Drawing-Material", + this, + _textureFirst); mat(mDraw); } tempP.resize(_amount); tempV.resize(_amount); tempST.resize(_amount); + if (_doAcceleration || _doGravity) tempInitV.resize(_amount); if (_doRotation) @@ -478,16 +472,21 @@ void SLParticleSystem::generate() AT_angularVelo, &tempAngulareVelo); if (_doFlipBookTexture) - _vao2.setAttrib(AT_texNum, AT_texNum, &tempTexNum); + _vao2.setAttrib(AT_texNum, + AT_texNum, + &tempTexNum); if (_doShape) - _vao2.setAttrib(AT_initialPosition, AT_initialPosition, &tempInitP); + _vao2.setAttrib(AT_initialPosition, + AT_initialPosition, + &tempInitP); _vao2.generateTF((SLuint)tempP.size()); if (_drawInstanced) { P.clear(); I32.clear(); - /* Generate for billboard (for drawing without geometry shader)*/ + + // Generate for billboard (for drawing instanced without geometry shader) P.push_back(SLVec3f(-1, -1, 0)); P.push_back(SLVec3f(1, -1, 0)); P.push_back(SLVec3f(1, 1, 0)); @@ -501,17 +500,15 @@ void SLParticleSystem::generate() 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); + _renderVao1.generate((SLuint)P.size()); + _renderVao2.deleteGL(); _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()); } @@ -760,7 +757,8 @@ void SLParticleSystem::draw(SLSceneView* sv, SLNode* node, SLuint instances) // Rotation if (_doRotation && !_doRotRange) - spTF->uniform1f("u_angularVelo", _angularVelocityConst * DEG2RAD); + spTF->uniform1f("u_angularVelo", + _angularVelocityConst * DEG2RAD); ////////////////////// // Draw call to update @@ -771,20 +769,14 @@ void SLParticleSystem::draw(SLSceneView* sv, SLNode* node, SLuint instances) _vao1.beginTF(_vao2.tfoID()); _vao1.drawArrayAs(PT_points); _vao1.endTF(); - if (_drawInstanced) - _vao = _renderVao1; - else - _vao = _vao2; + _vao = _drawInstanced ? _renderVao1 : _vao2; // ??? Is this correct? } else { _vao2.beginTF(_vao1.tfoID()); _vao2.drawArrayAs(PT_points); _vao2.endTF(); - if (_drawInstanced) - _vao = _renderVao2; - else - _vao = _vao1; + _vao = _drawInstanced ? _renderVao2 : _vao1; // ??? Is this correct? } _updateTime.set(GlobalTimer::timeMS() - _startUpdateTimeMS); } @@ -809,7 +801,8 @@ void SLParticleSystem::draw(SLSceneView* sv, SLNode* node, SLuint instances) { if (_billboardType == BT_Vertical) { - SLMat4f vMat = stateGL->viewMatrix; // Just view matrix because world space is enabled + // Just view matrix because world space is enabled + SLMat4f vMat = stateGL->viewMatrix; vMat.m(0, 1.0f); vMat.m(1, 0.0f); @@ -821,12 +814,12 @@ void SLParticleSystem::draw(SLSceneView* sv, SLNode* node, SLuint instances) spD->uniformMatrix4fv("u_vYawPMatrix", 1, - (SLfloat*)&vMat); // TO change for custom shader generation + (SLfloat*)&vMat); } else { - - SLMat4f vMat = stateGL->viewMatrix; // Just view matrix because world space is enabled + // Just view matrix because world space is enabled + SLMat4f vMat = stateGL->viewMatrix; std::cout << "vMat" << std::endl; vMat.m(0, 1.0f); vMat.m(1, 0.0f); @@ -839,13 +832,16 @@ void SLParticleSystem::draw(SLSceneView* sv, SLNode* node, SLuint instances) 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); + + spD->uniformMatrix4fv("u_vOmvMatrix", + 1, + (SLfloat*)&vMat); } } else { - SLMat4f mvMat = stateGL->viewMatrix * stateGL->modelMatrix; // Model-View Matrix + // Build Model-View Matrix + SLMat4f mvMat = stateGL->viewMatrix * stateGL->modelMatrix; if (_billboardType == BT_Vertical) { @@ -859,13 +855,13 @@ void SLParticleSystem::draw(SLSceneView* sv, SLNode* node, SLuint instances) spD->uniformMatrix4fv("u_vYawPMatrix", 1, - (SLfloat*)&mvMat); // TO change for custom shader generation + (SLfloat*)&mvMat); } else { spD->uniformMatrix4fv("u_vOmvMatrix", 1, - (SLfloat*)&mvMat); // TO change for custom shader generation + (SLfloat*)&mvMat); } } @@ -921,7 +917,6 @@ void SLParticleSystem::draw(SLSceneView* sv, SLNode* node, SLuint instances) spD->uniform1f("u_scale", _scale); spD->uniform1f("u_radiusW", _radiusW); spD->uniform1f("u_radiusH", _radiusH); - spD->uniform1f("u_oneOverGamma", 1.0f); // Check wireframe @@ -934,12 +929,12 @@ void SLParticleSystem::draw(SLSceneView* sv, SLNode* node, SLuint instances) if (_doColor && _doBlendBrightness) stateGL->blendFunc(GL_SRC_ALPHA, GL_ONE); - /////////////////////// + /////////////////////////// if (_drawInstanced) - SLMesh::draw(sv, node, 2 * _amount); //2 triangles per particle + SLMesh::draw(sv, node, 2 * _amount); // 2 triangles per particle else SLMesh::draw(sv, node); - /////////////////////// + /////////////////////////// if (_doColor && _doBlendBrightness) stateGL->blendFunc(GL_SRC_ALPHA, @@ -970,24 +965,12 @@ void SLParticleSystem::changeTexture() } } //----------------------------------------------------------------------------- -//! deleteData deletes all mesh data and VAOs -void SLParticleSystem::deleteData() -{ - return; -} -//----------------------------------------------------------------------------- -//! deleteData deletes all mesh data and VAOs -void SLParticleSystem::deleteDataGpu() -{ - SLMesh::deleteDataGpu(); -} -//----------------------------------------------------------------------------- /*! SLParticleSystem::buildAABB builds the passed axis-aligned bounding box in OS and updates the min & max points in WS with the passed WM of the node. Take into account features like acceleration, gravity, shape, velocity. SLMesh::buildAABB builds the passed axis-aligned bounding box in OS and updates the min& max points in WS with the passed WM of the node. - Todo: Can ben enhance furthermore the acceleration doesn't work wll for the moments + Todo: Can ben enhance furthermore the acceleration doesn't work well for the moment. The negative value for the acceleration are not take into account and also acceleration which goes against the velocity. To adapt the acceleration to exactly the same as the gravity not enough time to do it. Need to adapt more diff --git a/modules/sl/source/mesh/SLParticleSystem.h b/modules/sl/source/mesh/SLParticleSystem.h index fceffcc2..0a45be26 100644 --- a/modules/sl/source/mesh/SLParticleSystem.h +++ b/modules/sl/source/mesh/SLParticleSystem.h @@ -46,8 +46,6 @@ class SLParticleSystem : public SLMesh const bool drawInstanced = false); void draw(SLSceneView* sv, SLNode* node, SLuint instances = 1); - void deleteData(); - void deleteDataGpu(); void buildAABB(SLAABBox& aabb, const SLMat4f& wmNode); void generate(); void generateBernsteinPAlpha(); @@ -310,8 +308,8 @@ class SLParticleSystem : public SLMesh // Color SLCol4f _color = SLCol4f(0.66f, 0.0f, 0.66f, 0.2f); //!< Color for particle - SLfloat _colorArr[256 * 3]; //!< Color values of color gradient widget - SLVColorLUTPoint _colorPoints; //! Color gradient points + SLfloat _colorArr[256 * 3]; //!< Color values of color gradient widget + SLVColorLUTPoint _colorPoints; //! Color gradient points // Int (but boolean) to switch buffer int _drawBuf = 0; //!< Boolean to switch buffer @@ -357,11 +355,10 @@ class SLParticleSystem : public SLMesh SLGLTexture* _textureFlipbook; //!< Flipbook texture with e.g. multiple flames at subsequent frames // VAOs - 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 + SLGLVertexArray _vao1; //!< 1. Vertex Array Object for swapping between updating/drawing + SLGLVertexArray _vao2; //!< 2. Vertex Array Object for swapping between updating/drawing + SLGLVertexArray _renderVao1; //!< 1. Vertex Array Object for swapping between updating/drawing + SLGLVertexArray _renderVao2; //!< 2. Vertex Array Object for swapping between updating/drawing // Boolean for generation/resume SLbool _isVisibleInFrustum = true; //!< Boolean to set time since node not visible From a2f2590d1bb75d46663330bfcd39b5052463cb92 Mon Sep 17 00:00:00 2001 From: Marcus Hudritsch Date: Sun, 10 Dec 2023 17:06:05 +0100 Subject: [PATCH 080/108] Update AppDemoMainEmscripten.cpp --- apps/app_demo_slproject/emscripten/AppDemoMainEmscripten.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/app_demo_slproject/emscripten/AppDemoMainEmscripten.cpp b/apps/app_demo_slproject/emscripten/AppDemoMainEmscripten.cpp index 8e8cd2e4..89596d84 100644 --- a/apps/app_demo_slproject/emscripten/AppDemoMainEmscripten.cpp +++ b/apps/app_demo_slproject/emscripten/AppDemoMainEmscripten.cpp @@ -330,7 +330,7 @@ EM_BOOL emOnKeyPressed(int eventType, (SLSceneID)(AppDemo::sceneID - 1)); SL_LOG("Loading SceneID: %d", AppDemo::sceneID); } - else if (key == K_right && sv && AppDemo::sceneID < SID_Maximal - 1) + else if (key == K_right && sv && AppDemo::sceneID < SID_MaxNoBenchmarks - 1) { appDemoLoadScene(AppDemo::assetManager, AppDemo::scene, From 05642d81019a28c00a6eb89fa17bbcf907ca9857 Mon Sep 17 00:00:00 2001 From: Marcus Hudritsch Date: Mon, 11 Dec 2023 08:27:41 +0100 Subject: [PATCH 081/108] Some Warnings removed in Windows --- modules/sl/source/gl/SLGLVertexArray.cpp | 35 ++++++++++++++---------- modules/sl/source/gl/SLGLVertexArray.h | 4 +-- 2 files changed, 22 insertions(+), 17 deletions(-) diff --git a/modules/sl/source/gl/SLGLVertexArray.cpp b/modules/sl/source/gl/SLGLVertexArray.cpp index a8ad18ad..bde0cdae 100644 --- a/modules/sl/source/gl/SLGLVertexArray.cpp +++ b/modules/sl/source/gl/SLGLVertexArray.cpp @@ -49,7 +49,8 @@ void SLGLVertexArray::deleteGL() glDeleteBuffers(1, &_idVBOIndices); _idVBOIndices = 0; SLGLVertexBuffer::totalBufferCount--; - SLGLVertexBuffer::totalBufferSize -= _numIndicesElements * (SLuint)SLGLVertexBuffer::sizeOfType(_indexDataType); + SLGLVertexBuffer::totalBufferSize -= (SLuint)_numIndicesElements * + (SLuint)SLGLVertexBuffer::sizeOfType(_indexDataType); } } //----------------------------------------------------------------------------- @@ -220,9 +221,10 @@ void SLGLVertexArray::generate(SLuint numVertices, _numIndicesEdges && _indexDataEdges) { // create temp. buffer with both index arrays - SLuint typeSize = SLGLVertexBuffer::sizeOfType(_indexDataType); - SLuint tmBufSize = (_numIndicesElements + _numIndicesEdges) * (SLuint)typeSize; - SLubyte* tmpBuf = new SLubyte[tmBufSize]; + SLuint typeSize = SLGLVertexBuffer::sizeOfType(_indexDataType); + SLuint tmBufSize = (SLuint)(_numIndicesElements + _numIndicesEdges) * + (SLuint)typeSize; + SLubyte* tmpBuf = new SLubyte[tmBufSize]; memcpy(tmpBuf, _indexDataElements, _numIndicesElements * (SLuint)typeSize); @@ -247,7 +249,8 @@ void SLGLVertexArray::generate(SLuint numVertices, _indexDataElements, GL_STATIC_DRAW); SLGLVertexBuffer::totalBufferCount++; - SLGLVertexBuffer::totalBufferSize += _numIndicesElements * (SLuint)typeSize; + SLGLVertexBuffer::totalBufferSize += (SLuint)_numIndicesElements * + (SLuint)typeSize; } glBindVertexArray(0); @@ -306,9 +309,10 @@ void SLGLVertexArray::generateTF(SLuint numVertices, _numIndicesEdges && _indexDataEdges) { // create temp. buffer with both index arrays - SLuint typeSize = SLGLVertexBuffer::sizeOfType(_indexDataType); - SLuint tmBufSize = (_numIndicesElements + _numIndicesEdges) * (SLuint)typeSize; - SLubyte* tmpBuf = new SLubyte[tmBufSize]; + SLuint typeSize = (SLuint)SLGLVertexBuffer::sizeOfType(_indexDataType); + SLuint tmBufSize = (SLuint)(_numIndicesElements + _numIndicesEdges) * + (SLuint)typeSize; + SLubyte* tmpBuf = new SLubyte[tmBufSize]; memcpy(tmpBuf, _indexDataElements, _numIndicesElements * (SLuint)typeSize); @@ -336,7 +340,8 @@ void SLGLVertexArray::generateTF(SLuint numVertices, _indexDataElements, GL_STATIC_DRAW); SLGLVertexBuffer::totalBufferCount++; - SLGLVertexBuffer::totalBufferSize += _numIndicesElements * (SLuint)typeSize; + SLGLVertexBuffer::totalBufferSize += (SLuint)_numIndicesElements * + (SLuint)typeSize; } glBindVertexArray(0); @@ -390,7 +395,7 @@ void SLGLVertexArray::drawElementsAs(SLGLPrimitiveType primitiveType, // Do the draw call with indices if (numIndexes == 0) - numIndexes = _numIndicesElements; + numIndexes = (SLuint)_numIndicesElements; SLuint indexTypeSize = SLGLVertexBuffer::sizeOfType(_indexDataType); @@ -490,7 +495,7 @@ void SLGLVertexArray::drawElementsInstanced(SLGLPrimitiveType primitiveType, // Do the draw call with indices if (numIndexes == 0) - numIndexes = _numIndicesElements; + numIndexes = (SLuint)_numIndicesElements; SLuint indexTypeSize = SLGLVertexBuffer::sizeOfType(_indexDataType); @@ -552,11 +557,11 @@ void SLGLVertexArray::drawEdges(SLCol4f color, glLineWidth(lineWidth); #endif - ////////////////////////////////////////////// + ////////////////////////////////////////////////////// drawElementsAs(PT_lines, - _numIndicesEdges, - _numIndicesElements); - ////////////////////////////////////////////// + (SLuint)_numIndicesEdges, + (SLuint)_numIndicesElements); + ////////////////////////////////////////////////////// #ifndef SL_GLES if (lineWidth != 1.0f) diff --git a/modules/sl/source/gl/SLGLVertexArray.h b/modules/sl/source/gl/SLGLVertexArray.h index 956c7b20..239019f7 100644 --- a/modules/sl/source/gl/SLGLVertexArray.h +++ b/modules/sl/source/gl/SLGLVertexArray.h @@ -202,8 +202,8 @@ class SLGLVertexArray // Some getters SLuint numVertices() const { return _numVertices; } - SLuint numIndicesElements() const { return _numIndicesElements; } - SLuint numIndicesEdges() const { return _numIndicesEdges; } + SLuint numIndicesElements() const { return (SLuint)_numIndicesElements; } + SLuint numIndicesEdges() const { return (SLuint)_numIndicesEdges; } SLGLVertexBuffer* vbo() { return &_vbo; } // Some statistics From 4cff6bcc3c76d145df10535206e8d03e93f7ac87 Mon Sep 17 00:00:00 2001 From: Marcus Hudritsch Date: Mon, 11 Dec 2023 16:15:53 +0100 Subject: [PATCH 082/108] Minor fixes in SLGLVertexArray for instanced rendering --- modules/sl/source/gl/SLGLVertexArray.cpp | 1 + modules/sl/source/gl/SLGLVertexArray.h | 2 +- modules/sl/source/gl/SLGLVertexBuffer.cpp | 2 ++ modules/sl/source/mesh/SLParticleSystem.cpp | 6 +++--- modules/sl/source/mesh/SLParticleSystem.h | 8 ++++---- 5 files changed, 11 insertions(+), 8 deletions(-) diff --git a/modules/sl/source/gl/SLGLVertexArray.cpp b/modules/sl/source/gl/SLGLVertexArray.cpp index bde0cdae..84e578bc 100644 --- a/modules/sl/source/gl/SLGLVertexArray.cpp +++ b/modules/sl/source/gl/SLGLVertexArray.cpp @@ -508,6 +508,7 @@ void SLGLVertexArray::drawElementsInstanced(SLGLPrimitiveType primitiveType, //////////////////////////////////////////////////////// GET_GL_ERROR; + // Update statistics totalDrawCalls++; switch (primitiveType) diff --git a/modules/sl/source/gl/SLGLVertexArray.h b/modules/sl/source/gl/SLGLVertexArray.h index 239019f7..56981145 100644 --- a/modules/sl/source/gl/SLGLVertexArray.h +++ b/modules/sl/source/gl/SLGLVertexArray.h @@ -216,7 +216,7 @@ class SLGLVertexArray SLuint _tfoID; //! OpenGL id of transform feedback object SLuint _numVertices; //! NO. of vertices in array SLGLVertexBuffer _vbo; //! Vertex buffer object for float attributes - SLGLVertexBuffer* _externalVbo; //! Vertex buffer object that has been created outside of this VAO ??? What for? + SLGLVertexBuffer* _externalVbo; //! Vertex buffer object that has been created outside of this VAO SLuint _externalDivisor; //! VBO attrib divisor for the external VBO SLuint _idVBOIndices; //! OpenGL id of index vbo size_t _numIndicesElements; //! NO. of vertex indices in array for triangles, lines or points diff --git a/modules/sl/source/gl/SLGLVertexBuffer.cpp b/modules/sl/source/gl/SLGLVertexBuffer.cpp index 7b0fc4e3..49e23504 100644 --- a/modules/sl/source/gl/SLGLVertexBuffer.cpp +++ b/modules/sl/source/gl/SLGLVertexBuffer.cpp @@ -385,7 +385,9 @@ void SLGLVertexBuffer::bindAndEnableAttrib(SLuint divisor) const ////////////////////////////////////// // Associate VBO to Attribute location ////////////////////////////////////// + glBindBuffer(GL_ARRAY_BUFFER, _id); + if (_outputIsInterleaved) // Copy attribute data interleaved { for (auto a : _attribs) diff --git a/modules/sl/source/mesh/SLParticleSystem.cpp b/modules/sl/source/mesh/SLParticleSystem.cpp index 6da55f05..c91aa657 100644 --- a/modules/sl/source/mesh/SLParticleSystem.cpp +++ b/modules/sl/source/mesh/SLParticleSystem.cpp @@ -38,7 +38,7 @@ SLParticleSystem::SLParticleSystem(SLAssetManager* assetMgr, _drawInstanced = !SLGLState::instance()->glHasGeometryShaders() || drawInstanced; _primitive = PT_points; - // Trick SL project because it wants mesh to have vertex + // Trick SL project because it wants the mesh to have vertex P.push_back(SLVec3f(0, 0, 0)); I32.push_back(0); @@ -769,14 +769,14 @@ void SLParticleSystem::draw(SLSceneView* sv, SLNode* node, SLuint instances) _vao1.beginTF(_vao2.tfoID()); _vao1.drawArrayAs(PT_points); _vao1.endTF(); - _vao = _drawInstanced ? _renderVao1 : _vao2; // ??? Is this correct? + _vao = _drawInstanced ? _renderVao2 : _vao2; } else { _vao2.beginTF(_vao1.tfoID()); _vao2.drawArrayAs(PT_points); _vao2.endTF(); - _vao = _drawInstanced ? _renderVao2 : _vao1; // ??? Is this correct? + _vao = _drawInstanced ? _renderVao1 : _vao1; } _updateTime.set(GlobalTimer::timeMS() - _startUpdateTimeMS); } diff --git a/modules/sl/source/mesh/SLParticleSystem.h b/modules/sl/source/mesh/SLParticleSystem.h index 0a45be26..1ab23daa 100644 --- a/modules/sl/source/mesh/SLParticleSystem.h +++ b/modules/sl/source/mesh/SLParticleSystem.h @@ -355,10 +355,10 @@ class SLParticleSystem : public SLMesh SLGLTexture* _textureFlipbook; //!< Flipbook texture with e.g. multiple flames at subsequent frames // VAOs - SLGLVertexArray _vao1; //!< 1. Vertex Array Object for swapping between updating/drawing - SLGLVertexArray _vao2; //!< 2. Vertex Array Object for swapping between updating/drawing - SLGLVertexArray _renderVao1; //!< 1. Vertex Array Object for swapping between updating/drawing - SLGLVertexArray _renderVao2; //!< 2. Vertex Array Object for swapping between updating/drawing + SLGLVertexArray _vao1; //!< 1. VAO for swapping between updating/drawing + SLGLVertexArray _vao2; //!< 2. VAO for swapping between updating/drawing + SLGLVertexArray _renderVao1; //!< 1. VAO for instanced rendering with vao1 + SLGLVertexArray _renderVao2; //!< 2. VAO for instanced rendering with vao2 // Boolean for generation/resume SLbool _isVisibleInFrustum = true; //!< Boolean to set time since node not visible From 85e20d8c79513e5324de55d3ee7f3cb1f6bcf851 Mon Sep 17 00:00:00 2001 From: Marcus Hudritsch Date: Mon, 11 Dec 2023 18:43:36 +0100 Subject: [PATCH 083/108] Clarified shader code for particles --- modules/sl/source/gl/SLGLEnums.h | 53 ++++++++++--------- modules/sl/source/gl/SLGLProgramGenerated.cpp | 33 ++++++------ modules/sl/source/mesh/SLParticleSystem.cpp | 4 +- 3 files changed, 46 insertions(+), 44 deletions(-) diff --git a/modules/sl/source/gl/SLGLEnums.h b/modules/sl/source/gl/SLGLEnums.h index 01c436d4..67297c90 100644 --- a/modules/sl/source/gl/SLGLEnums.h +++ b/modules/sl/source/gl/SLGLEnums.h @@ -19,7 +19,7 @@ enum SLGLBufferType { BT_float = GL_FLOAT, //!< float vertex attributes - BT_int = GL_INT, //!< int 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) @@ -55,33 +55,34 @@ enum SLGLAttributeType SLVVfloat Jw; //!< 2D Vector of per vertex joint weights (opt.) layout (location = 7) */ - AT_position = 0, //!< Vertex position as a 2, 3 or 4 component vectors - AT_normal, //!< Vertex normal as a 3 component vector - AT_uv1, //!< Vertex 1st texture coordinate as 2 component vector - AT_uv2, //!< Vertex 2nd texture coordinate as 2 component vector - AT_color, //!< Vertex color as 3 or 4 component vector - AT_tangent, //!< Vertex tangent as a 4 component vector (see SLMesh) - AT_jointIndex, //!< Vertex joint id for vertex skinning - AT_jointWeight, //!< Vertex joint weight for vertex skinning + AT_position = 0, //!< Vertex position as a 2, 3 or 4 component vectors + AT_normal, //!< Vertex normal as a 3 component vector + AT_uv1, //!< Vertex 1st texture coordinate as 2 component vector + AT_uv2, //!< Vertex 2nd texture coordinate as 2 component vector + AT_color, //!< Vertex color as 3 or 4 component vector + AT_tangent, //!< Vertex tangent as a 4 component vector (see SLMesh) + AT_jointIndex, //!< Vertex joint id for vertex skinning + AT_jointWeight, //!< Vertex joint weight for vertex skinning - AT_custom0, //!< Custom vertex attribute 0 - AT_custom1, //!< Custom vertex attribute 1 - AT_custom2, //!< Custom vertex attribute 2 - AT_custom3, //!< Custom vertex attribute 3 - AT_custom4, //!< Custom vertex attribute 4 - AT_custom5, //!< Custom vertex attribute 5 - AT_custom6, //!< Custom vertex attribute 6 - AT_custom7, //!< Custom vertex attribute 7 - AT_custom8, //!< Custom vertex attribute 8 - AT_custom9, //!< Custom vertex attribute 9 + AT_custom0, //!< Custom vertex attribute 0 + AT_custom1, //!< Custom vertex attribute 1 + AT_custom2, //!< Custom vertex attribute 2 + AT_custom3, //!< Custom vertex attribute 3 + AT_custom4, //!< Custom vertex attribute 4 + AT_custom5, //!< Custom vertex attribute 5 + AT_custom6, //!< Custom vertex attribute 6 + AT_custom7, //!< Custom vertex attribute 7 + AT_custom8, //!< Custom vertex attribute 8 + AT_custom9, //!< Custom vertex attribute 9 - AT_velocity = 1, //!< Vertex velocity 3 component vectors - AT_startTime = 2, //!< Vertex start time float - AT_initialVelocity = 3, //!< Vertex initial velocity 3 component vectors - AT_rotation = 4, //!< Vertex rotation float - AT_angularVelo = 5, //!< Vertex angulare velocity for rotation float - AT_texNum = 6, //!< Vertex texture number int - AT_initialPosition = 7 //!< Vertex initial position 3 component vectors + AT_velocity = 1, //!< Vertex velocity 3 component vectors + AT_startTime = 2, //!< Vertex start time float + AT_initialVelocity = 3, //!< Vertex initial velocity 3 component vectors + AT_rotation = 4, //!< Vertex rotation float + AT_angularVelo = 5, //!< Vertex angular velocity for rotation float + AT_texNum = 6, //!< Vertex texture number int + AT_initialPosition = 7, //!< Vertex initial position 3 component vectors + AT_instancePosition = 8 //!< Vertex instance position for instanced particle drawing }; //----------------------------------------------------------------------------- //! Enumeration for buffer usage types also supported by OpenGL ES diff --git a/modules/sl/source/gl/SLGLProgramGenerated.cpp b/modules/sl/source/gl/SLGLProgramGenerated.cpp index e80e0de1..d1954a92 100644 --- a/modules/sl/source/gl/SLGLProgramGenerated.cpp +++ b/modules/sl/source/gl/SLGLProgramGenerated.cpp @@ -44,8 +44,8 @@ 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_PS_a_InstPos = R"( +layout (location = 8) in vec3 a_instancePos; // Particle instance triangle vertex 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"( @@ -62,7 +62,7 @@ uniform mat4 u_mMatrix; // Model matrix (object to world tra uniform mat4 u_vMatrix; // View matrix (world to camera transform) uniform mat4 u_pMatrix; // Projection matrix (camera to normalize device coords.))"; const string vertInput_u_matrix_vOmv = R"( -uniform mat4 u_vOmvMatrix; // view or modelview matrix)"; +uniform mat4 u_vOmvMatrix; // view or modelview matrix)"; //----------------------------------------------------------------------------- const string vertInput_u_skinning = R"( @@ -167,9 +167,9 @@ const string vertOutput_PS_struct_End = R"( } vert; )"; const string vertOutput_PS_instanced_transparency = R"( -out float transparency; // transparency of a particle )"; +out float transparency; // transparency of a particle )"; const string fragInput_PS_instanced_transparency = R"( -in float transparency; // transparency of a particle )"; +in float transparency; // transparency of a particle )"; const string fragMain_PS_instanced_v_doColorOverLT = R"( vec4 color = vec4(colorByAge(v_age/u_tTL), 1.0); // Particle color)"; @@ -203,7 +203,7 @@ out float tf_rotation; // To transform feedback)"; const string vertOutput_PS_tf_r_angularVelo = R"( out float tf_angularVelo; // To transform feedback)"; const string vertOutput_PS_tf_texNum = R"( -flat out uint tf_texNum; // To transform feedback)"; +flat out uint tf_texNum; // To transform feedback)"; const string vertOutput_PS_tf_initP = R"( out vec3 tf_initialPosition; // To transform feedback)"; @@ -350,7 +350,7 @@ const string vertMain_PS_v_texNum = R"( 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));)"; + v_texCoord = 0.5 * (a_instancePos.xy + vec2(1.0));)"; const string vertMain_PS_v_age = R"( v_age = age;)"; const string vertMain_PS_v_tC_flipbook = R"( @@ -358,12 +358,12 @@ const string vertMain_PS_v_tC_flipbook = R"( float actC = float(actCI); float actR = floor(float(int(a_texNum) - actCI) / float(u_col)); - vec2 p = 0.5 * (a_positionP.xy + vec2(1.0)); + vec2 p = 0.5 * (a_instancePos.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_position = R"( - vec3 position = a_positionP; + vec3 position = a_instancePos; )"; const string vertMain_PS_instanced_v_s = R"( float size = age / u_tTL;)"; @@ -410,7 +410,7 @@ const string vertMain_PS_instanced_EndAll = R"( } )"; const string vertMain_PS_instanced_EndAll_VertBillboard = R"( - //gl_Position = vec4(a_position + a_positionP, 1); + //gl_Position = vec4(a_position + a_instancePos, 1); gl_Position = u_pMatrix * u_vYawPMatrix * vec4(a_position + position, 1.0); } )"; @@ -535,9 +535,9 @@ 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)"; +out vec2 v_texCoord; // Texture coordinate at vertex)"; const string vertOutput_PS_v_tC = R"( -out vec2 v_texCoord; // Texture coordinate at vertex)"; +out vec2 v_texCoord; // Texture coordinate at vertex)"; //----------------------------------------------------------------------------- const string geomMain_PS_v_s = R"( float scale = u_scale;)"; @@ -2192,10 +2192,11 @@ void SLGLProgramGenerated::buildPerPixParticleInstanced(SLMaterial* mat) 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 + vertCode += vertInput_PS_a_InstPos; // instance position + 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_a_texNum; // per particle texture number diff --git a/modules/sl/source/mesh/SLParticleSystem.cpp b/modules/sl/source/mesh/SLParticleSystem.cpp index c91aa657..2019bda8 100644 --- a/modules/sl/source/mesh/SLParticleSystem.cpp +++ b/modules/sl/source/mesh/SLParticleSystem.cpp @@ -500,13 +500,13 @@ void SLParticleSystem::generate() I32.push_back(0); _renderVao1.deleteGL(); - _renderVao1.setAttrib(AT_custom0, AT_custom0, &P); + _renderVao1.setAttrib(AT_custom0, AT_instancePosition, &P); _renderVao1.setIndices(&I32); _renderVao1.setExternalVBO(_vao1.vbo(), 2); _renderVao1.generate((SLuint)P.size()); _renderVao2.deleteGL(); - _renderVao2.setAttrib(AT_custom0, AT_custom0, &P); + _renderVao2.setAttrib(AT_custom0, AT_instancePosition, &P); _renderVao2.setIndices(&I32); _renderVao2.setExternalVBO(_vao2.vbo(), 2); _renderVao2.generate((SLuint)P.size()); From 9a36b27cb04ae340433789a0afe4b69f3870dbfb Mon Sep 17 00:00:00 2001 From: Marcus Hudritsch Date: Tue, 12 Dec 2023 11:51:02 +0100 Subject: [PATCH 084/108] Improved Docs --- apps/app_demo_slproject/source/AppDemoGui.cpp | 4 +- docs/pages/ExampleParticles.md | 23 ++ docs/pages/ExampleScenes.md | 3 +- modules/sl/source/gl/SLGLState.h | 37 ++-- modules/sl/source/gl/SLGLVertexArray.cpp | 34 +-- modules/sl/source/gl/SLGLVertexArray.h | 6 +- modules/sl/source/gl/SLGLVertexBuffer.cpp | 205 ++++-------------- modules/sl/source/gl/SLGLVertexBuffer.h | 2 +- modules/sl/source/mesh/SLParticleSystem.cpp | 62 +++--- modules/sl/source/mesh/SLParticleSystem.h | 128 +++++------ 10 files changed, 212 insertions(+), 292 deletions(-) create mode 100644 docs/pages/ExampleParticles.md diff --git a/apps/app_demo_slproject/source/AppDemoGui.cpp b/apps/app_demo_slproject/source/AppDemoGui.cpp index 16dd74a9..d97a23d8 100644 --- a/apps/app_demo_slproject/source/AppDemoGui.cpp +++ b/apps/app_demo_slproject/source/AppDemoGui.cpp @@ -3705,10 +3705,10 @@ void AppDemoGui::buildProperties(SLScene* s, SLSceneView* sv) if (SLGLState::instance()->glHasGeometryShaders()) { - bool drawInstanced = ps->drawInstanced(); + bool drawInstanced = ps->doInstancedDrawing(); if (ImGui::Checkbox("Instanced draw", &drawInstanced)) { - ps->drawInstanced(drawInstanced); + ps->doInstancedDrawing(drawInstanced); ps->isGenerated(false); } } diff --git a/docs/pages/ExampleParticles.md b/docs/pages/ExampleParticles.md new file mode 100644 index 00000000..df280a1c --- /dev/null +++ b/docs/pages/ExampleParticles.md @@ -0,0 +1,23 @@ +\page example-particles Particle Systems +The SLParticleSystem is the most complex derivative of the SLMesh class and allows the creation of highly +configurable particle systems (PS). All its parameters can be modified in the _Properties_ tab (see menu Info > Properties). +The implementation uses for rendering geometry shaders on systems that have OpenGL >= 4.1 or OpenGLES >= 3.2. All other +systems (mainly iOS and Emscripten with WebGL) use instanced rendering. The consecutive motion update is done with OpenGL +feedback buffers. For the torch fires in the following example scene we use one PS for the glow effect and one +for the flames. The center fire uses one PS for the glow, one for the flames, one for the sparks, one for the black, +and one for the white smoke. + +\htmlonly + +\endhtmlonly + +General help: +
    +
  • Click and drag the left mouse button to rotate the scene.
  • +
  • Click and drag the middle mouse button to move sidewards/up-down.
  • +
  • Roll the mouse-wheel to move forward/backward.
  • +
  • Double click with the left mouse button to select an object.
  • +
  • Click the right mouse button to open the context menu.
  • +
  • Open and dock additional windows from the menu Infos.
  • +
  • See more example scenes over the menu File > Load Test Scene
  • +
\ No newline at end of file diff --git a/docs/pages/ExampleScenes.md b/docs/pages/ExampleScenes.md index 6a4669af..85324803 100644 --- a/docs/pages/ExampleScenes.md +++ b/docs/pages/ExampleScenes.md @@ -13,4 +13,5 @@ See also \ref emscripten for more background information. - \subpage example-node-animations - \subpage example-skinned-animations - \subpage example-raytracing -- \subpage example-pathtracing \ No newline at end of file +- \subpage example-pathtracing +- \subpage example-particles \ No newline at end of file diff --git a/modules/sl/source/gl/SLGLState.h b/modules/sl/source/gl/SLGLState.h index 9ec4670d..4a3b67ce 100644 --- a/modules/sl/source/gl/SLGLState.h +++ b/modules/sl/source/gl/SLGLState.h @@ -136,7 +136,8 @@ class SLGLState SLbool glIsES3() const { return _glIsES3; } SLint glMaxTexUnits() const { return _glMaxTexUnits; } SLint glMaxTexSize() const { return _glMaxTexSize; } - SLbool glHasGeometryShaders() const { return (_glIsES3 && _glVersionNOf > 3.1f) || (!glIsES() && _glVersionNOf >= 4.1f); } + SLbool glHasGeometryShaders() const { return (_glIsES3 && _glVersionNOf >= 3.2f) || + (!glIsES() && _glVersionNOf >= 4.1f); } SLbool hasExtension(const SLstring& e) { return _glExtensions.find(e) != string::npos; } SLVec4i viewport() { return _viewport; } SLMat4f viewportMatrix() @@ -157,25 +158,25 @@ class SLGLState SLstring getSLVersionNO(); private: - SLGLState(); //!< private onetime constructor - ~SLGLState(); //!< destruction in ~SLScene + SLGLState(); //!< private onetime constructor + ~SLGLState(); //!< destruction in ~SLScene static SLGLState* _instance; //!< global singleton object - SLbool _isInitialized; //!< flag for first init - - SLstring _glVersion; //!< OpenGL Version string - SLstring _glVersionNO; //!< OpenGL Version number string - SLfloat _glVersionNOf; //!< OpenGL Version number as float - SLstring _glVendor; //!< OpenGL Vendor string - SLstring _glRenderer; //!< OpenGL Renderer string - SLstring _glSLVersion; //!< GLSL Version string - SLstring _glSLVersionNO; //!< GLSL Version number string - SLstring _glExtensions; //!< OpenGL extensions string - SLbool _glIsES2; //!< Flag if OpenGL ES2 - SLbool _glIsES3; //!< Flag if OpenGL ES3 - SLint _glMaxTexUnits; //!< glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &_glMaxTexUnits); - SLint _glMaxTexSize; //!< glGetIntegerv(GL_MAX_TEXTURE_SIZE, &_glMaxTexSize); + SLbool _isInitialized; //!< flag for first init + + SLstring _glVersion; //!< OpenGL Version string + SLstring _glVersionNO; //!< OpenGL Version number string + SLfloat _glVersionNOf; //!< OpenGL Version number as float + SLstring _glVendor; //!< OpenGL Vendor string + SLstring _glRenderer; //!< OpenGL Renderer string + SLstring _glSLVersion; //!< GLSL Version string + SLstring _glSLVersionNO; //!< GLSL Version number string + SLstring _glExtensions; //!< OpenGL extensions string + SLbool _glIsES2; //!< Flag if OpenGL ES2 + SLbool _glIsES3; //!< Flag if OpenGL ES3 + SLint _glMaxTexUnits; //!< glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &_glMaxTexUnits); + SLint _glMaxTexSize; //!< glGetIntegerv(GL_MAX_TEXTURE_SIZE, &_glMaxTexSize); // read/write states SLbool _blend; //!< blending default false; @@ -204,7 +205,7 @@ class SLGLState GLboolean _colorMaskB; //!< current color mask for B GLboolean _colorMaskA; //!< current color mask for A - SLVstring errors; //!< vector for errors collected in getGLError + SLVstring errors; //!< vector for errors collected in getGLError SLMaterial* _currentMaterial; }; diff --git a/modules/sl/source/gl/SLGLVertexArray.cpp b/modules/sl/source/gl/SLGLVertexArray.cpp index 84e578bc..ae7168eb 100644 --- a/modules/sl/source/gl/SLGLVertexArray.cpp +++ b/modules/sl/source/gl/SLGLVertexArray.cpp @@ -29,7 +29,7 @@ SLGLVertexArray::SLGLVertexArray() _numVertices = 0; _indexDataElements = nullptr; _indexDataEdges = nullptr; - _externalVbo = nullptr; + _instanceVbo = nullptr; } //----------------------------------------------------------------------------- /*! Deletes the OpenGL objects for the vertex array and the vertex buffer. @@ -89,10 +89,15 @@ void SLGLVertexArray::setAttrib(SLGLAttributeType type, _vbo.attribs().push_back(va); } //----------------------------------------------------------------------------- -void SLGLVertexArray::setExternalVBO(SLGLVertexBuffer* vbo, SLuint divisor) +/*! Assignment of the additional VBO for instanced drawing. The passed vbo + * contains the positions for the instanced drawing done in + * drawElementsInstanced. This used e.g. for SLParticleSystems on systems + * without geometry shaders. + */ +void SLGLVertexArray::setInstanceVBO(SLGLVertexBuffer* vbo, SLuint divisor) { - _externalVbo = vbo; - _externalDivisor = divisor; + _instanceVbo = vbo; + _instanceDivisor = divisor; } //----------------------------------------------------------------------------- /*! Defines the vertex indices for the element drawing. Without indices vertex @@ -208,9 +213,9 @@ void SLGLVertexArray::generate(SLuint numVertices, _vbo.bindAndEnableAttrib(divisor); } - if (_externalVbo != nullptr) + if (_instanceVbo != nullptr) { - _externalVbo->bindAndEnableAttrib(_externalDivisor); + _instanceVbo->bindAndEnableAttrib(_instanceDivisor); } ///////////////////////////////////////////////////////////////// @@ -289,9 +294,9 @@ void SLGLVertexArray::generateTF(SLuint numVertices, } // ??? - if (_externalVbo != nullptr) + if (_instanceVbo != nullptr) { - _externalVbo->bindAndEnableAttrib(_externalDivisor); + _instanceVbo->bindAndEnableAttrib(_instanceDivisor); } /////////////////////////////// @@ -472,15 +477,12 @@ void SLGLVertexArray::drawArrayAs(SLGLPrimitiveType primitiveType, GET_GL_ERROR; } //----------------------------------------------------------------------------- -/*! ??? - * - * @param primitiveType - * @param countInstance - * @param numIndexes - * @param indexOffset +/*! Wrapper around glDrawElementsInstanced using a second VBO (_instanceVbo) + * that contains all positions for the instances. This used e.g. for + * SLParticleSystems on systems without geometry shaders. */ void SLGLVertexArray::drawElementsInstanced(SLGLPrimitiveType primitiveType, - SLuint countInstance, + SLuint countInstances, SLuint numIndexes, SLuint indexOffset) { @@ -504,7 +506,7 @@ void SLGLVertexArray::drawElementsInstanced(SLGLPrimitiveType primitiveType, (GLsizei)numIndexes, _indexDataType, (void*)(size_t)(indexOffset * (SLuint)indexTypeSize), - (GLsizei)countInstance); + (GLsizei)countInstances); //////////////////////////////////////////////////////// GET_GL_ERROR; diff --git a/modules/sl/source/gl/SLGLVertexArray.h b/modules/sl/source/gl/SLGLVertexArray.h index 56981145..40286d5f 100644 --- a/modules/sl/source/gl/SLGLVertexArray.h +++ b/modules/sl/source/gl/SLGLVertexArray.h @@ -136,7 +136,7 @@ class SLGLVertexArray } //! Attach a VBO that has been created outside of this VAO - void setExternalVBO(SLGLVertexBuffer* vbo, SLuint divisor = 0); + void setInstanceVBO(SLGLVertexBuffer* vbo, SLuint divisor = 0); //! Updates a specific vertex attribute in the VBO void updateAttrib(SLGLAttributeType type, @@ -216,14 +216,14 @@ class SLGLVertexArray SLuint _tfoID; //! OpenGL id of transform feedback object SLuint _numVertices; //! NO. of vertices in array SLGLVertexBuffer _vbo; //! Vertex buffer object for float attributes - SLGLVertexBuffer* _externalVbo; //! Vertex buffer object that has been created outside of this VAO - SLuint _externalDivisor; //! VBO attrib divisor for the external VBO SLuint _idVBOIndices; //! OpenGL id of index vbo size_t _numIndicesElements; //! NO. of vertex indices in array for triangles, lines or points void* _indexDataElements; //! Pointer to index data for elements size_t _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) + SLGLVertexBuffer* _instanceVbo; //! Vertex buffer object containing the positions for instanced drawing + SLuint _instanceDivisor; //! instanceVBO divisor number }; //----------------------------------------------------------------------------- diff --git a/modules/sl/source/gl/SLGLVertexBuffer.cpp b/modules/sl/source/gl/SLGLVertexBuffer.cpp index 49e23504..d8f287c8 100644 --- a/modules/sl/source/gl/SLGLVertexBuffer.cpp +++ b/modules/sl/source/gl/SLGLVertexBuffer.cpp @@ -18,11 +18,11 @@ SLuint SLGLVertexBuffer::totalBufferCount = 0; //! Constructor initializing with default values SLGLVertexBuffer::SLGLVertexBuffer() { - _id = 0; - _numVertices = 0; - _sizeBytes = 0; + _id = 0; + _numVertices = 0; + _sizeBytes = 0; _outputIsInterleaved = false; - _usage = BU_stream; + _usage = BU_stream; } //----------------------------------------------------------------------------- /*! Deletes the OpenGL objects for the vertex array and the vertex buffer. @@ -79,9 +79,9 @@ void SLGLVertexBuffer::updateAttrib(SLGLAttributeType type, _attribs[(SLuint)index].dataPointer = dataPointer; - //////////////////////////////////////////// - // copy sub-data into existing buffer object - //////////////////////////////////////////// + /////////////////////////////////////////////// + // copy sub-data into existing buffer object // + /////////////////////////////////////////////// glBindBuffer(GL_ARRAY_BUFFER, _id); glBufferSubData(GL_ARRAY_BUFFER, @@ -119,7 +119,6 @@ sequential vertex buffer.\n\n \n |<---------- strideBytes=32 ----------->|
*/ -/* void SLGLVertexBuffer::generate(SLuint numVertices, SLGLBufferUsage usage, SLbool outputInterleaved) @@ -129,136 +128,8 @@ void SLGLVertexBuffer::generate(SLuint numVertices, // if buffers exist delete them first deleteGL(); - _numVertices = numVertices; - _usage = usage; - _outputInterleaved = 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 - SLbool 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) - { - _outputInterleaved = 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 (_outputInterleaved) - _attribs[i].offsetBytes = _strideBytes; - else - _attribs[i].offsetBytes = _sizeBytes; - _attribs[i].bufferSizeBytes = elementSizeBytes * _numVertices; - _sizeBytes += _attribs[i].bufferSizeBytes; - if (_outputInterleaved) _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 - { - 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) - { - 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]; - } - } - - // 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) - { - // Copies the attributes data at the right offset into the VBO - glBufferSubData(GL_ARRAY_BUFFER, - a.offsetBytes, - a.bufferSizeBytes, - a.dataPointer); - } - } - } - } - - 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; + _numVertices = numVertices; + _usage = usage; _outputIsInterleaved = outputInterleaved; // Generate the vertex buffer object @@ -296,7 +167,8 @@ void SLGLVertexBuffer::generate(SLuint numVertices, for (SLuint i = 0; i < _attribs.size(); ++i) { - SLuint elementSizeBytes = (SLuint)_attribs[i].elementSize * sizeOfType(_attribs[i].dataType); + SLuint elementSizeBytes = (SLuint)_attribs[i].elementSize * + sizeOfType(_attribs[i].dataType); _attribs[i].offsetBytes = _strideBytes; _attribs[i].bufferSizeBytes = elementSizeBytes * _numVertices; _sizeBytes += _attribs[i].bufferSizeBytes; @@ -307,7 +179,8 @@ void SLGLVertexBuffer::generate(SLuint numVertices, { for (SLuint i = 0; i < _attribs.size(); ++i) { - SLuint elementSizeBytes = (SLuint)_attribs[i].elementSize * sizeOfType(_attribs[i].dataType); + SLuint elementSizeBytes = (SLuint)_attribs[i].elementSize * + sizeOfType(_attribs[i].dataType); if (_outputIsInterleaved) _attribs[i].offsetBytes = _strideBytes; else @@ -325,9 +198,12 @@ void SLGLVertexBuffer::generate(SLuint numVertices, if (_inputIsInterleaved) { // generate the interleaved VBO buffer on the GPU - glBufferData(GL_ARRAY_BUFFER, _sizeBytes, _attribs[0].dataPointer, _usage); + glBufferData(GL_ARRAY_BUFFER, + _sizeBytes, + _attribs[0].dataPointer, + _usage); } - else // input is in separate attribute data block + else // input is in separate attribute data block { if (_outputIsInterleaved) // Copy attribute data interleaved { @@ -335,7 +211,8 @@ void SLGLVertexBuffer::generate(SLuint numVertices, data.resize(_sizeBytes); for (auto a : _attribs) { - SLuint elementSizeBytes = (SLuint)a.elementSize * sizeOfType(a.dataType); + SLuint elementSizeBytes = (SLuint)a.elementSize * + sizeOfType(a.dataType); // Copy attributes interleaved for (SLuint v = 0; v < _numVertices; ++v) @@ -347,13 +224,19 @@ void SLGLVertexBuffer::generate(SLuint numVertices, } // generate the interleaved VBO buffer on the GPU - glBufferData(GL_ARRAY_BUFFER, _sizeBytes, &data[0], _usage); + 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); + glBufferData(GL_ARRAY_BUFFER, + _sizeBytes, + nullptr, + _usage); for (auto a : _attribs) { @@ -374,17 +257,19 @@ void SLGLVertexBuffer::generate(SLuint numVertices, 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(SLuint divisor) const + * 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. + * For VAO (Vertex Array Objects) this method is only used once at the creation + * of the VAO. The instanceDivisor number is only used for instanced drawing. + * For all other usage it is by default 0. + */ +void SLGLVertexBuffer::bindAndEnableAttrib(SLuint instanceDivisor) const { - ////////////////////////////////////// - // Associate VBO to Attribute location - ////////////////////////////////////// + ///////////////////////////////////////// + // Associate VBO to Attribute location // + ///////////////////////////////////////// glBindBuffer(GL_ARRAY_BUFFER, _id); @@ -414,8 +299,11 @@ void SLGLVertexBuffer::bindAndEnableAttrib(SLuint divisor) const // 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); + + // Special setting for instanced drawing + if (instanceDivisor > 0) + glVertexAttribDivisor((SLuint)a.location, + instanceDivisor); } } } @@ -446,8 +334,11 @@ void SLGLVertexBuffer::bindAndEnableAttrib(SLuint divisor) const // 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); + + // Special setting for instanced drawing + if (instanceDivisor > 0) + glVertexAttribDivisor((SLuint)a.location, + instanceDivisor); } } } diff --git a/modules/sl/source/gl/SLGLVertexBuffer.h b/modules/sl/source/gl/SLGLVertexBuffer.h index d2ae1292..ec974316 100644 --- a/modules/sl/source/gl/SLGLVertexBuffer.h +++ b/modules/sl/source/gl/SLGLVertexBuffer.h @@ -86,7 +86,7 @@ class SLGLVertexBuffer SLGLBufferUsage usage = BU_static, SLbool outputInterleaved = true); - //! Binds & enables the vertex attribute for OpenGL < 3.0 + //! Binds & enables the vertex attribute for OpenGL < 3.0 and during VAO creation void bindAndEnableAttrib(SLuint divisor = 0) const; //! disables the vertex attribute for OpenGL < 3.0 diff --git a/modules/sl/source/mesh/SLParticleSystem.cpp b/modules/sl/source/mesh/SLParticleSystem.cpp index 2019bda8..5f682af2 100644 --- a/modules/sl/source/mesh/SLParticleSystem.cpp +++ b/modules/sl/source/mesh/SLParticleSystem.cpp @@ -30,12 +30,12 @@ SLParticleSystem::SLParticleSystem(SLAssetManager* assetMgr, SLGLTexture* texC, const SLstring& name, SLGLTexture* texFlipbook, - const bool drawInstanced) : SLMesh(assetMgr, name) + const bool doInstancedDrawing) : SLMesh(assetMgr, name) { assert(!name.empty()); _assetManager = assetMgr; - _drawInstanced = !SLGLState::instance()->glHasGeometryShaders() || drawInstanced; + _doInstancedDrawing = !SLGLState::instance()->glHasGeometryShaders() || doInstancedDrawing; _primitive = PT_points; // Trick SL project because it wants the mesh to have vertex @@ -295,7 +295,7 @@ void SLParticleSystem::generate() SLVuint tempTexNum; SLVVec3f tempInitP; - if (_drawInstanced) + if (_doInstancedDrawing) _primitive = PT_triangles; else _primitive = PT_points; @@ -481,7 +481,7 @@ void SLParticleSystem::generate() &tempInitP); _vao2.generateTF((SLuint)tempP.size()); - if (_drawInstanced) + if (_doInstancedDrawing) { P.clear(); I32.clear(); @@ -499,17 +499,17 @@ void SLParticleSystem::generate() I32.push_back(3); I32.push_back(0); - _renderVao1.deleteGL(); - _renderVao1.setAttrib(AT_custom0, AT_instancePosition, &P); - _renderVao1.setIndices(&I32); - _renderVao1.setExternalVBO(_vao1.vbo(), 2); - _renderVao1.generate((SLuint)P.size()); - - _renderVao2.deleteGL(); - _renderVao2.setAttrib(AT_custom0, AT_instancePosition, &P); - _renderVao2.setIndices(&I32); - _renderVao2.setExternalVBO(_vao2.vbo(), 2); - _renderVao2.generate((SLuint)P.size()); + _instanceVao1.deleteGL(); + _instanceVao1.setAttrib(AT_custom0, AT_instancePosition, &P); + _instanceVao1.setIndices(&I32); + _instanceVao1.setInstanceVBO(_vao1.vbo(), 2); + _instanceVao1.generate((SLuint)P.size()); + + _instanceVao2.deleteGL(); + _instanceVao2.setAttrib(AT_custom0, AT_instancePosition, &P); + _instanceVao2.setIndices(&I32); + _instanceVao2.setInstanceVBO(_vao2.vbo(), 2); + _instanceVao2.generate((SLuint)P.size()); } _isGenerated = true; @@ -591,23 +591,23 @@ void SLParticleSystem::pauseOrResume() */ void SLParticleSystem::draw(SLSceneView* sv, SLNode* node, SLuint instances) { - ///////////////////////////////////// - // Init particles vector and init VAO - ///////////////////////////////////// + //////////////////////////////////////// + // Init particles vector and init VAO // + //////////////////////////////////////// if (!_isGenerated) generate(); - //////////////////// - // Generate programs - //////////////////// + /////////////////////// + // Generate programs // + /////////////////////// if (!_mat->program() || !_mat->programTF()) - _mat->generateProgramPS(_drawInstanced); + _mat->generateProgramPS(_doInstancedDrawing); - //////////////////////////////////////////////// - // Calculate time and paused and frustum culling - //////////////////////////////////////////////// + /////////////////////////////////////////////////// + // Calculate time and paused and frustum culling // + /////////////////////////////////////////////////// float difTime = 0.0f; float deltaTime = GlobalTimer::timeS() - _startUpdateTimeS; // Actual delta time @@ -769,21 +769,21 @@ void SLParticleSystem::draw(SLSceneView* sv, SLNode* node, SLuint instances) _vao1.beginTF(_vao2.tfoID()); _vao1.drawArrayAs(PT_points); _vao1.endTF(); - _vao = _drawInstanced ? _renderVao2 : _vao2; + _vao = _doInstancedDrawing ? _instanceVao2 : _vao2; } else { _vao2.beginTF(_vao1.tfoID()); _vao2.drawArrayAs(PT_points); _vao2.endTF(); - _vao = _drawInstanced ? _renderVao1 : _vao1; + _vao = _doInstancedDrawing ? _instanceVao1 : _vao1; } _updateTime.set(GlobalTimer::timeMS() - _startUpdateTimeMS); } - ////////// - // DRAWING - ////////// + ///////////// + // DRAWING // + ///////////// // Give uniform for drawing and find for linking vao vbo SLGLProgram* spD = _mat->program(); @@ -930,7 +930,7 @@ void SLParticleSystem::draw(SLSceneView* sv, SLNode* node, SLuint instances) stateGL->blendFunc(GL_SRC_ALPHA, GL_ONE); /////////////////////////// - if (_drawInstanced) + if (_doInstancedDrawing) SLMesh::draw(sv, node, 2 * _amount); // 2 triangles per particle else SLMesh::draw(sv, node); diff --git a/modules/sl/source/mesh/SLParticleSystem.h b/modules/sl/source/mesh/SLParticleSystem.h index 1ab23daa..b29b64b1 100644 --- a/modules/sl/source/mesh/SLParticleSystem.h +++ b/modules/sl/source/mesh/SLParticleSystem.h @@ -22,11 +22,11 @@ * An OpenGL transform feedback buffer is used to update the particle positions * on the GPU and a geometry shader is used the create two triangles per * particle vertex and orient them as a billboard to the viewer. Geometry - * shaders are only supported under OpenGL >= 4.0 and OpenGL ES >= 3.2. This is + * shaders are only supported under OpenGL >= 4.0 and OpenGLES >= 3.2. This is * the case on most desktop systems and on Android SDK > 24 but not on iOS which * has only OpenGL ES 3.0\n. * For all systems that don't support geometry shaders we use an alternative - * with instanced drawing\n. + * with instanced drawing (mainly for iOS and Emscripten with WebGL)\n. * The particle system supports many options of which many can be turned on the * do* methods. All options can also be modified in the UI when the mesh is * selected. See the different demo scenes in the app_demo_slproject under the @@ -41,9 +41,9 @@ class SLParticleSystem : public SLMesh const SLVec3f& velocityRandomEnd, const SLfloat& timeToLive, SLGLTexture* texC, - const SLstring& name = "Particle system", - SLGLTexture* texFlipbook = nullptr, - const bool drawInstanced = false); + const SLstring& name = "Particle System", + SLGLTexture* texFlipbook = nullptr, + const bool doInstancedDrawing = false); void draw(SLSceneView* sv, SLNode* node, SLuint instances = 1); void buildAABB(SLAABBox& aabb, const SLMat4f& wmNode); @@ -57,7 +57,6 @@ class SLParticleSystem : public SLMesh // Getters - SLbool drawInstanced() { return _drawInstanced; } SLVec3f acceleration() { return _acceleration; } SLfloat accelerationConst() { return _accelerationConst; } SLint amount() { return _amount; } @@ -70,58 +69,61 @@ class SLParticleSystem : public SLMesh SLBillboardType billboardType() { return _billboardType; } SLCol4f color() { return _color; } SLVColorLUTPoint& colorPoints() { return _colorPoints; } - SLbool doDirectionSpeed() { return _doDirectionSpeed; } - SLbool doSpeedRange() { return _doSpeedRange; } - SLbool doAcc() { return _doAcceleration; } - SLbool doAccDiffDir() { return _doAccDiffDir; } - SLbool doAlphaOverLT() { return _doAlphaOverLT; } - SLbool doAlphaOverLTCurve() { return _doAlphaOverLTCurve; } - SLbool doBlendBrightness() { return _doBlendBrightness; } - SLbool doCounterGap() { return _doCounterGap; } - SLbool doColor() { return _doColor; } - SLbool doColorOverLT() { return _doColorOverLT; } - SLbool doGravity() { return _doGravity; } - SLbool doFlipBookTexture() { return _doFlipBookTexture; } - SLbool doRotation() { return _doRotation; } - SLbool doRotRange() { return _doRotRange; } - SLbool doSizeOverLT() { return _doSizeOverLT; } - SLbool doSizeOverLTCurve() { return _doSizeOverLTCurve; } - SLbool doShape() { return _doShape; } - SLbool doShapeSurface() { return _doShapeSurface; } - SLbool doShapeOverride() { return _doShapeOverride; } - SLbool doShapeSpawnBase() { return _doShapeSpawnBase; } - SLbool doWorldSpace() { return _doWorldSpace; } - SLVec3f direction() { return _direction; } - AvgFloat& drawTime() { return _drawTime; } - SLVec3f emitterPos() const { return _emitterPos; } - SLVec3f gravity() { return _gravity; } - SLint flipbookColumns() { return _flipbookColumns; } - SLint flipbookRows() { return _flipbookRows; } - int frameRateFB() { return _flipbookFPS; } - SLbool isGenerated() { return _isGenerated; } - SLbool isPaused() { return _isPaused; } - SLfloat radiusW() { return _radiusW; } - SLfloat radiusH() { return _radiusH; } - SLfloat scale() { return _scale; } - SLShapeType shapeType() { return _shapeType; } - SLfloat shapeAngle() { return _shapeAngle; } - SLfloat shapeHeight() { return _shapeHeight; } - SLfloat shapeRadius() { return _shapeRadius; } - SLVec3f shapeScale() { return _shapeScale; } - SLfloat shapeWidth() { return _shapeWidth; } - SLfloat speed() { return _speed; } - SLVec2f speedRange() { return _speedRange; } - SLGLTexture* textureFirst() { return _textureFirst; } - SLGLTexture* textureFlipbook() { return _textureFlipbook; } - SLfloat timeToLive() { return _timeToLive; } - AvgFloat& updateTime() { return _updateTime; } - SLint velocityType() { return _velocityType; } - SLVec3f velocityConst() { return _velocityConst; } - SLVec3f velocityRndMin() { return _velocityRndMin; } - SLVec3f velocityRndMax() { return _velocityRndMax; } + + SLbool doInstancedDrawing() { return _doInstancedDrawing; } + SLbool doAcc() { return _doAcceleration; } + SLbool doAccDiffDir() { return _doAccDiffDir; } + SLbool doAlphaOverLT() { return _doAlphaOverLT; } + SLbool doAlphaOverLTCurve() { return _doAlphaOverLTCurve; } + SLbool doBlendBrightness() { return _doBlendBrightness; } + SLbool doCounterGap() { return _doCounterGap; } + SLbool doColor() { return _doColor; } + SLbool doColorOverLT() { return _doColorOverLT; } + SLbool doDirectionSpeed() { return _doDirectionSpeed; } + SLbool doGravity() { return _doGravity; } + SLbool doFlipBookTexture() { return _doFlipBookTexture; } + SLbool doRotation() { return _doRotation; } + SLbool doRotRange() { return _doRotRange; } + SLbool doSizeOverLT() { return _doSizeOverLT; } + SLbool doSizeOverLTCurve() { return _doSizeOverLTCurve; } + SLbool doShape() { return _doShape; } + SLbool doShapeSurface() { return _doShapeSurface; } + SLbool doShapeOverride() { return _doShapeOverride; } + SLbool doShapeSpawnBase() { return _doShapeSpawnBase; } + SLbool doSpeedRange() { return _doSpeedRange; } + SLbool doWorldSpace() { return _doWorldSpace; } + + SLVec3f direction() { return _direction; } + AvgFloat& drawTime() { return _drawTime; } + SLVec3f emitterPos() const { return _emitterPos; } + SLVec3f gravity() { return _gravity; } + SLint flipbookColumns() { return _flipbookColumns; } + SLint flipbookRows() { return _flipbookRows; } + int frameRateFB() { return _flipbookFPS; } + SLbool isGenerated() { return _isGenerated; } + SLbool isPaused() { return _isPaused; } + SLfloat radiusW() { return _radiusW; } + SLfloat radiusH() { return _radiusH; } + SLfloat scale() { return _scale; } + SLShapeType shapeType() { return _shapeType; } + SLfloat shapeAngle() { return _shapeAngle; } + SLfloat shapeHeight() { return _shapeHeight; } + SLfloat shapeRadius() { return _shapeRadius; } + SLVec3f shapeScale() { return _shapeScale; } + SLfloat shapeWidth() { return _shapeWidth; } + SLfloat speed() { return _speed; } + SLVec2f speedRange() { return _speedRange; } + SLGLTexture* textureFirst() { return _textureFirst; } + SLGLTexture* textureFlipbook() { return _textureFlipbook; } + SLfloat timeToLive() { return _timeToLive; } + AvgFloat& updateTime() { return _updateTime; } + SLint velocityType() { return _velocityType; } + SLVec3f velocityConst() { return _velocityConst; } + SLVec3f velocityRndMin() { return _velocityRndMin; } + SLVec3f velocityRndMax() { return _velocityRndMax; } // Setters - void drawInstanced(bool instanced) { _drawInstanced = instanced; } + void doInstancedDrawing(bool instanced) { _doInstancedDrawing = instanced; } void amount(SLint i) { _amount = i; } void accConst(SLfloat f) { _accelerationConst = f; } void acceleration(SLVec3f v) { _acceleration = v; } @@ -308,8 +310,8 @@ class SLParticleSystem : public SLMesh // Color SLCol4f _color = SLCol4f(0.66f, 0.0f, 0.66f, 0.2f); //!< Color for particle - SLfloat _colorArr[256 * 3]; //!< Color values of color gradient widget - SLVColorLUTPoint _colorPoints; //! Color gradient points + SLfloat _colorArr[256 * 3]; //!< Color values of color gradient widget + SLVColorLUTPoint _colorPoints; //! Color gradient points // Int (but boolean) to switch buffer int _drawBuf = 0; //!< Boolean to switch buffer @@ -355,10 +357,10 @@ class SLParticleSystem : public SLMesh SLGLTexture* _textureFlipbook; //!< Flipbook texture with e.g. multiple flames at subsequent frames // VAOs - SLGLVertexArray _vao1; //!< 1. VAO for swapping between updating/drawing - SLGLVertexArray _vao2; //!< 2. VAO for swapping between updating/drawing - SLGLVertexArray _renderVao1; //!< 1. VAO for instanced rendering with vao1 - SLGLVertexArray _renderVao2; //!< 2. VAO for instanced rendering with vao2 + SLGLVertexArray _vao1; //!< 1. VAO for swapping between updating/drawing + SLGLVertexArray _vao2; //!< 2. VAO for swapping between updating/drawing + SLGLVertexArray _instanceVao1; //!< 1. VAO for instanced rendering with vao1 + SLGLVertexArray _instanceVao2; //!< 2. VAO for instanced rendering with vao2 // Boolean for generation/resume SLbool _isVisibleInFrustum = true; //!< Boolean to set time since node not visible @@ -387,7 +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 _drawInstanced = false; //!< Boolean for instanced rendering + SLbool _doInstancedDrawing = false; //!< Boolean for instanced rendering }; //----------------------------------------------------------------------------- #endif From 920bb354b39bd7ae7b22877602849f529847f8c5 Mon Sep 17 00:00:00 2001 From: Marcus Hudritsch Date: Tue, 12 Dec 2023 13:52:58 +0100 Subject: [PATCH 085/108] More docs --- docs/Doxyfile | 1 + docs/pages/OnPaint.md | 65 +++++++++++-------------------- modules/sl/source/SLSceneView.cpp | 23 ++++++----- modules/sl/source/node/SLNode.cpp | 17 ++++---- 4 files changed, 43 insertions(+), 63 deletions(-) diff --git a/docs/Doxyfile b/docs/Doxyfile index a435f3f7..45f4e276 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -921,6 +921,7 @@ INPUT = ../modules/sl/source \ pages/ExampleLevelOfDetail.md \ pages/ExampleNodeAnimations.md \ pages/ExamplePathtracing.md \ + pages/ExampleParticles.md \ pages/ExampleRaytracing.md \ pages/ExampleRevolver.md \ pages/ExampleScenes.md \ diff --git a/docs/pages/OnPaint.md b/docs/pages/OnPaint.md index a32abfb1..2ce87fb7 100644 --- a/docs/pages/OnPaint.md +++ b/docs/pages/OnPaint.md @@ -19,51 +19,30 @@ timer involved. * 3) SLAnimManager::update: Updates all animations. * 4) SLNode::updateAABBRec: Updates the axis aligned bounding boxes of all nodes that have changed during animation. * **SLInterface.h: slPaintAllViews()**: - * SLSceneView::onPaint called for every sceneview: - * SLSceneView::draw3DGL - * SLCamera::camUpdate updates any camera animation (smooth transitions) - * All buffers are cleared (color, depth and Oculus frame buffer) - * **Camera Settings**: - * SLCamera::setProjection sets the projection - (perspective, orthographic or one of the stereo projections) and viewport. - On stereo the projection for the left eye is set. - * SLCamera::setView applies the view transform. - * **Frustum Culling**: + * **SLSceneView::onPaint** called for every sceneview: + * **SLSceneView::draw3DGL** + * 1) Render all shadow maps from the light position and direction. + * 2) Do camera update (smooth transitions from keyboard entries). + * 3) Clear color and depth buffers. + * 4) Set Viewports with SLCamera::setViewport depending on its projection + * 5) Render Background with SLCamera::background: single color, smooth gradient or video image. + * 6) Set SLCamera::setProjection and SLCamera::setView. + * 7) Frustum Culling * SLCamera::setFrustumPlanes set the cameras frustum planes according to the current projection and view transform. - * SLNode::cullRec called on the root node: - * All nodes are checked if they are visible. - * All visible nodes without transparencies (opaque) are added to the _opaqueNodes vector. - * All visible nodes with transparencies are added to the _blendNodes vector. - * SLSceneView::draw3DGLAll: - * Blending is turned off and the depth test on - * SLSceneView::draw3DGLNodes is called for every node in the _opaqueNodes vector: - * The view matrix is applied to the modelview matrix - * The nodes world matrix is applied to the modelview matrix - * SLMesh::draw is called on all meshes of the node: - * 1) Apply the drawing bits - * 2) Apply the uniform variables to the shader - * 2a) Activate a shader program if it is not yet in use and apply all its material parameters. - * 2b) Pass the modelview and modelview-projection matrix to the shader. - * 2c) If needed build and pass the inverse modelview and the normal matrix. - * 2d) If the mesh has a skeleton and HW skinning is applied pass the joint matrices. - * 3) Build the vertex attribute object once - * 4) Finally do the draw call - * 5) Draw optional normals & tangents - * 6) Draw optional acceleration structure - * 7) Draw optional the selected node - * SLSceneView::draw3DGLLines: for every node in the _opaqueNodes vector: - * The view matrix is applied to the modelview matrix - * If the drawbit for viewing the AABBs is set SLAABBox::drawWS draws it. - * If the drawbit for viewing the axis is set SLAABBox::drawAxisWS draws it. - * SLSceneView::draw3DGLLines: for every node in the _blendNodes vector the same as above. - * Blending is turned on and the depth test off. - * The nodes of the _blendNotes get sorted by depth for correct transparency blending. - * SLSceneView::draw3DGLNodes is called for every node in the _blendNodes vector (same as above). - * Blending is turned off and the depth test on again. - * SLSceneView::draw3DGLAll: - * ... (the same but for the right eye of stereo projections) - * SLSceneView::draw2DGL is called for all 2D drawing + * SLNode::cull3DRec gets called on the root node: + * All nodes that are checked as visible get assigned to their meshes material nodesVisible3D vector. + * 8) Skybox Drawing: If a skybox is set, this gets drawn first around the camera with depth-buffer turned off. + * 9) Draw all visible nodes with **SLSceneView::draw3DGLAll**: + * a) Draw nodes with meshes with opaque materials and all helper lines sorted by material. + * b) Draw remaining opaque nodes without meshes. + * c) Draw nodes with meshes with blended materials sorted by material and sorted back to front. + * d) Draw remaining blended nodes (SLText, needs redesign). + * e) Draw helpers in overlay mode (not depth buffered). + * f) Draw visualization lines of animation curves. + * g) Turn blending off again for correct anaglyph stereo modes. + * 10) Draw right eye for stereo projection. + * **SLSceneView::draw2DGL** is called for all 2D drawing * 1) The orthographic projection and viewport in screen space is set. * 2) A pseudo culling step for all nodes on the _root2D scene. * 3) SLSceneView::draw2DGLNodes: diff --git a/modules/sl/source/SLSceneView.cpp b/modules/sl/source/SLSceneView.cpp index a39b574e..32b9ee3e 100644 --- a/modules/sl/source/SLSceneView.cpp +++ b/modules/sl/source/SLSceneView.cpp @@ -651,7 +651,8 @@ SLbool SLSceneView::draw3DGL(SLfloat elapsedTimeMS) // Update camera animation separately (smooth transition on key movement) // todo: ghm1: this is currently only necessary for walking animation (which is somehow always enabled) - // A problem is also, that it only updates the current camera. This is maybe not what we want for sensor rotated camera. + // A problem is also, that it only updates the current camera. + // This is maybe not what we want for sensor rotated camera. SLbool camUpdated = _camera->camUpdate(this, elapsedTimeMS); @@ -686,7 +687,8 @@ SLbool SLSceneView::draw3DGL(SLfloat elapsedTimeMS) // Render solid color, gradient or textured background from active camera if (!_s->skybox()) - _camera->background().render(_viewportRect.width, _viewportRect.height); + _camera->background().render(_viewportRect.width, + _viewportRect.height); // Change state (only when changed) stateGL->multiSample(_doMultiSampling); @@ -825,13 +827,14 @@ void SLSceneView::draw3DGLAll() // e) Draw helpers in overlay mode (not depth buffered) for (auto material : _visibleMaterials3D) draw3DGLLinesOverlay(material->nodesVisible3D()); + draw3DGLLinesOverlay(_nodesOverdrawn); draw3DGLLinesOverlay(_nodesOpaque3D); // f) Draw visualization lines of animation curves _s->animManager().drawVisuals(this); - // Turn blending off again for correct anaglyph stereo modes + // g) Turn blending off again for correct anaglyph stereo modes SLGLState* stateGL = SLGLState::instance(); stateGL->blend(false); stateGL->depthMask(true); @@ -1085,8 +1088,8 @@ void SLSceneView::draw2DGL() stateGL->depthMask(false); // Freeze depth buffer for blending stateGL->depthTest(false); // Disable depth testing _camera->selectRect().drawGL(SLCol4f::WHITE); - stateGL->depthMask(true); // enable depth buffer writing - stateGL->depthTest(true); // enable depth testing + stateGL->depthMask(true); // enable depth buffer writing + stateGL->depthTest(true); // enable depth testing stateGL->viewMatrix = prevViewMat; } @@ -1100,8 +1103,8 @@ void SLSceneView::draw2DGL() stateGL->depthMask(false); // Freeze depth buffer for blending stateGL->depthTest(false); // Disable depth testing _camera->deselectRect().drawGL(SLCol4f::MAGENTA); - stateGL->depthMask(true); // enable depth buffer writing - stateGL->depthTest(true); // enable depth testing + stateGL->depthMask(true); // enable depth buffer writing + stateGL->depthTest(true); // enable depth testing stateGL->viewMatrix = prevViewMat; } } @@ -2049,14 +2052,14 @@ void SLSceneView::saveFrameBufferAsImage(SLstring pathFilename, CVMat rgbImg = CVMat(fbH, fbW, CV_8UC3, (void*)buffer.data(), stride); cv::cvtColor(rgbImg, rgbImg, cv::COLOR_BGR2RGB); #else - CVMat rgbImg = CVMat(fbH, + CVMat rgbImg = CVMat(fbH, fbW, CV_8UC4, (void*)buffer.data(), stride); cv::cvtColor(rgbImg, rgbImg, cv::COLOR_RGBA2RGB); - nrChannels = 3; - stride = nrChannels * fbW; + nrChannels = 3; + stride = nrChannels * fbW; #endif cv::flip(rgbImg, rgbImg, 0); diff --git a/modules/sl/source/node/SLNode.cpp b/modules/sl/source/node/SLNode.cpp index ad232a42..4fa501fa 100644 --- a/modules/sl/source/node/SLNode.cpp +++ b/modules/sl/source/node/SLNode.cpp @@ -364,20 +364,19 @@ bool SLNode::removeChild(SLNode* child) return false; } //----------------------------------------------------------------------------- +//! Initializer function to call SLNode::cull3DRec recursively void SLNode::cullChildren3D(SLSceneView* sv) { for (auto* child : _children) child->cull3DRec(sv); } //----------------------------------------------------------------------------- -/*! -Does the view frustum culling by checking whether the AABB is inside the 3D -cameras view frustum. The check is done in world space. If a AABB is visible -the nodes children are checked recursively. -If a node is visible its mesh material is added to the -SLSceneview::_visibleMaterials3D set and the node to the -SLMaterials::nodesVisible3D vector. -See also SLSceneView::draw3DGLAll for more details. +/*! Does the view frustum culling by checking whether the AABB is inside the 3D + * cameras view frustum. The check is done in world space. If a AABB is visible + * the nodes children are checked recursively. If a node is visible its mesh + * material is added to the SLSceneview::_visibleMaterials3D set and the node + * to the SLMaterials::nodesVisible3D vector. See also SLSceneView::draw3DGLAll + * for more details. */ void SLNode::cull3DRec(SLSceneView* sv) { @@ -407,9 +406,7 @@ void SLNode::cull3DRec(SLSceneView* sv) cullChildren3D(sv); if (this->drawBit(SL_DB_OVERDRAW)) - { sv->nodesOverdrawn().push_back(this); - } else { // All nodes with meshes get rendered sorted by their material From 446d4550c75dd184ce0716295448bf97f39d0ee9 Mon Sep 17 00:00:00 2001 From: Marcus Hudritsch Date: Tue, 12 Dec 2023 15:37:02 +0100 Subject: [PATCH 086/108] Fixed point size problem for iOS and Emscripten --- data/shaders/ColorUniform.vert | 1 + modules/math/source/SLQuat4.h | 7 ++++--- modules/sl/source/gl/SLGLOculus.cpp | 1 - modules/sl/source/gl/SLGLTextureIBL.cpp | 2 +- modules/sl/source/gl/SLGLVertexArray.cpp | 17 +++++++++-------- modules/sl/source/gl/SLGLVertexArrayExt.cpp | 8 ++++---- modules/sl/source/mesh/SLParticleSystem.cpp | 12 +++++++++--- modules/sl/source/node/SLLightDirect.cpp | 5 ++--- modules/sl/source/node/SLLightRect.cpp | 1 - modules/sl/source/node/SLLightSpot.cpp | 1 - 10 files changed, 30 insertions(+), 25 deletions(-) diff --git a/data/shaders/ColorUniform.vert b/data/shaders/ColorUniform.vert index 80515b6a..4e5adcc1 100644 --- a/data/shaders/ColorUniform.vert +++ b/data/shaders/ColorUniform.vert @@ -21,6 +21,7 @@ out vec4 v_color; // Resulting color per vertex //----------------------------------------------------------------------------- void main(void) { + gl_PointSize = 1.0; v_color = u_matDiff; gl_Position = u_pMatrix * u_vMatrix * u_mMatrix * a_position; } diff --git a/modules/math/source/SLQuat4.h b/modules/math/source/SLQuat4.h index fffff149..27aea3c6 100644 --- a/modules/math/source/SLQuat4.h +++ b/modules/math/source/SLQuat4.h @@ -20,7 +20,6 @@ /*!Quaternion class for angle-axis rotation representation. For rotations quaternions must have unit length. See http://en.wikipedia.org/wiki/Quaternion Quaternions can be interpolated at low cost with the method lerp or slerp. -@todo Add more doxygen documentation. */ // clang-format off template @@ -737,11 +736,13 @@ inline void SLQuat4::lerp(const SLQuat4& q1, } //----------------------------------------------------------------------------- -//! Spherical linear interpolation +/*! Spherical linear interpolation +@todo clean up the code below and find a working algorithm or check the + original shoemake implementation for errors. + */ template inline SLQuat4 SLQuat4::slerp(const SLQuat4& q2, const T t) const { - /// @todo clean up the code below and find a working algorithm (or check the original shoemake implementation for errors) // Not 100% slerp, uses lerp in case of close angle! note the todo above this line! SLfloat factor = t; // calc cosine theta diff --git a/modules/sl/source/gl/SLGLOculus.cpp b/modules/sl/source/gl/SLGLOculus.cpp index b5e9f1c8..94ff33ca 100644 --- a/modules/sl/source/gl/SLGLOculus.cpp +++ b/modules/sl/source/gl/SLGLOculus.cpp @@ -117,7 +117,6 @@ void SLGLOculus::renderDistortion(SLint width, */ const SLVec3f& SLGLOculus::viewAdjust(SLEyeType eye) { - //@todo find a nicer way to store this (SLEye has a -1 for left and +1 for right eye) if (eye == ET_left) return _viewAdjust[0]; else diff --git a/modules/sl/source/gl/SLGLTextureIBL.cpp b/modules/sl/source/gl/SLGLTextureIBL.cpp index 1eb4af3a..232c687d 100644 --- a/modules/sl/source/gl/SLGLTextureIBL.cpp +++ b/modules/sl/source/gl/SLGLTextureIBL.cpp @@ -105,7 +105,7 @@ SLGLTextureIBL::~SLGLTextureIBL() Build the texture into a cube map, rendering the texture 6 times and capturing each time one side of the cube (except for the BRDF LUT texture, which is completely generated by calculations directly with the shader). - @todo: Priority 1: These frame buffer ops do not work properly on iOS & Android. + @todo Priority 1: These frame buffer ops do not work properly on iOS & Android. This is probably the most difficult OpenGL code in the project. */ void SLGLTextureIBL::build(SLint texUnit) diff --git a/modules/sl/source/gl/SLGLVertexArray.cpp b/modules/sl/source/gl/SLGLVertexArray.cpp index ae7168eb..c981a8c4 100644 --- a/modules/sl/source/gl/SLGLVertexArray.cpp +++ b/modules/sl/source/gl/SLGLVertexArray.cpp @@ -238,8 +238,12 @@ void SLGLVertexArray::generate(SLuint numVertices, _numIndicesEdges * (SLuint)typeSize); glGenBuffers(1, &_idVBOIndices); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _idVBOIndices); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, tmBufSize, tmpBuf, GL_STATIC_DRAW); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, + _idVBOIndices); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, + tmBufSize, + tmpBuf, + GL_STATIC_DRAW); SLGLVertexBuffer::totalBufferCount++; SLGLVertexBuffer::totalBufferSize += tmBufSize; delete[] tmpBuf; @@ -434,9 +438,6 @@ void SLGLVertexArray::drawElementsAs(SLGLPrimitiveType primitiveType, /*! * Draws the vertex attributes as a specified primitive type as the vertices * are defined in the attribute arrays. - * @param primitiveType - * @param firstVertex - * @param countVertices */ void SLGLVertexArray::drawArrayAs(SLGLPrimitiveType primitiveType, SLint firstVertex, @@ -555,7 +556,7 @@ void SLGLVertexArray::drawEdges(SLCol4f color, // Set uniform color glUniform4fv(sp->getUniformLocation("u_matDiff"), 1, (SLfloat*)&color); -#ifndef SL_GLES +#if not defined(SL_GLES) && not defined(SL_EMSCRIPTEN) if (lineWidth != 1.0f) glLineWidth(lineWidth); #endif @@ -566,9 +567,9 @@ void SLGLVertexArray::drawEdges(SLCol4f color, (SLuint)_numIndicesElements); ////////////////////////////////////////////////////// -#ifndef SL_GLES +#if not defined(SL_GLES) && not defined(SL_EMSCRIPTEN) if (lineWidth != 1.0f) - glPointSize(1.0f); + glLineWidth(1.0f); #endif GET_GL_ERROR; diff --git a/modules/sl/source/gl/SLGLVertexArrayExt.cpp b/modules/sl/source/gl/SLGLVertexArrayExt.cpp index 290e794f..0f78302b 100644 --- a/modules/sl/source/gl/SLGLVertexArrayExt.cpp +++ b/modules/sl/source/gl/SLGLVertexArrayExt.cpp @@ -67,7 +67,7 @@ void SLGLVertexArrayExt::drawArrayAsColored(SLGLPrimitiveType primitiveType, // Set uniform color glUniform4fv(sp->getUniformLocation("u_matDiff"), 1, (SLfloat*)&color); -#ifndef SL_GLES +#if not defined(SL_GLES) && not defined(SL_EMSCRIPTEN) if (pointSize != 1.0f) if (primitiveType == PT_points) glPointSize(pointSize); @@ -79,7 +79,7 @@ void SLGLVertexArrayExt::drawArrayAsColored(SLGLPrimitiveType primitiveType, (SLsizei)countVertices); ////////////////////////////////////// -#ifndef SL_GLES +#if not defined(SL_GLES) && not defined(SL_EMSCRIPTEN) if (pointSize != 1.0f) if (primitiveType == PT_points) glPointSize(1.0f); @@ -114,7 +114,7 @@ void SLGLVertexArrayExt::drawElementAsColored(SLGLPrimitiveType primitiveType, // Set uniform color glUniform4fv(sp->getUniformLocation("u_matDiff"), 1, (SLfloat*)&color); -#ifndef SL_GLES +#if not defined(SL_GLES) && not defined(SL_EMSCRIPTEN) if (pointSize != 1.0f) if (primitiveType == PT_points) glPointSize(pointSize); @@ -126,7 +126,7 @@ void SLGLVertexArrayExt::drawElementAsColored(SLGLPrimitiveType primitiveType, countVertices); //////////////////////////////// -#ifndef SL_GLES +#if not defined(SL_GLES) && not defined(SL_EMSCRIPTEN) if (pointSize != 1.0f) if (primitiveType == PT_points) glPointSize(1.0f); diff --git a/modules/sl/source/mesh/SLParticleSystem.cpp b/modules/sl/source/mesh/SLParticleSystem.cpp index 5f682af2..742efbc9 100644 --- a/modules/sl/source/mesh/SLParticleSystem.cpp +++ b/modules/sl/source/mesh/SLParticleSystem.cpp @@ -588,6 +588,8 @@ void SLParticleSystem::pauseOrResume() * have been culled by the frustum culling or if they have been resumed by the * user. After I update the particle in the update pass, then and finally I * draw them. + @todo The assignment of TF buffer to a rendering buffer causes most probably + a warning on Emscripten-WebGL */ void SLParticleSystem::draw(SLSceneView* sv, SLNode* node, SLuint instances) { @@ -760,15 +762,17 @@ void SLParticleSystem::draw(SLSceneView* sv, SLNode* node, SLuint instances) spTF->uniform1f("u_angularVelo", _angularVelocityConst * DEG2RAD); - ////////////////////// - // Draw call to update - ////////////////////// + ///////////////////////// + // Draw call to update // + ///////////////////////// if (_drawBuf == 0) { _vao1.beginTF(_vao2.tfoID()); _vao1.drawArrayAs(PT_points); _vao1.endTF(); + + // @todo The assignment of TF buffer to a rendering buffer causes most probably a warning on Emscripten-WebGL _vao = _doInstancedDrawing ? _instanceVao2 : _vao2; } else @@ -776,6 +780,8 @@ void SLParticleSystem::draw(SLSceneView* sv, SLNode* node, SLuint instances) _vao2.beginTF(_vao1.tfoID()); _vao2.drawArrayAs(PT_points); _vao2.endTF(); + + // @todo The assignment of TF buffer to a rendering buffer causes most probably a warning on Emscripten-WebGL _vao = _doInstancedDrawing ? _instanceVao1 : _vao1; } _updateTime.set(GlobalTimer::timeMS() - _startUpdateTimeMS); diff --git a/modules/sl/source/node/SLLightDirect.cpp b/modules/sl/source/node/SLLightDirect.cpp index 9693c55a..c9093c3a 100644 --- a/modules/sl/source/node/SLLightDirect.cpp +++ b/modules/sl/source/node/SLLightDirect.cpp @@ -100,9 +100,8 @@ SLLightDirect::~SLLightDirect() } //----------------------------------------------------------------------------- /*! -SLLightDirect::init sets the light id, the light states & creates an -emissive mat. -@todo properly remove this function and find a clean way to init lights in a scene + * SLLightDirect::init sets the light id, the light states & creates an emissive + * material. */ void SLLightDirect::init(SLScene* s) { diff --git a/modules/sl/source/node/SLLightRect.cpp b/modules/sl/source/node/SLLightRect.cpp index 3b26c11a..9dcbd722 100644 --- a/modules/sl/source/node/SLLightRect.cpp +++ b/modules/sl/source/node/SLLightRect.cpp @@ -56,7 +56,6 @@ SLLightRect::~SLLightRect() /*! SLLightRect::init sets the light id, the light states & creates an emissive mat. -@todo properly remove this function and find a clean way to init lights in a scene */ void SLLightRect::init(SLScene* s) { diff --git a/modules/sl/source/node/SLLightSpot.cpp b/modules/sl/source/node/SLLightSpot.cpp index 8701f7d4..d74fa6b7 100644 --- a/modules/sl/source/node/SLLightSpot.cpp +++ b/modules/sl/source/node/SLLightSpot.cpp @@ -110,7 +110,6 @@ SLLightSpot::~SLLightSpot() //----------------------------------------------------------------------------- /*! SLLightSpot::init sets the light id, the light states & creates an emissive mat. -@todo properly remove this function and find a clean way to init lights in a scene */ void SLLightSpot::init(SLScene* s) { From 64d90d919c267a5c637d76246ab3a4472574376c Mon Sep 17 00:00:00 2001 From: Marcus Hudritsch Date: Tue, 12 Dec 2023 16:22:26 +0100 Subject: [PATCH 087/108] Renamed webgpu-demo to AppDemoWebGPU --- apps/CMakeLists.txt | 2 +- .../AppDemoWebGPU.cpp} | 152 ++++++++++-------- .../CMakeLists.txt | 8 +- .../metal_layer.mm | 0 apps/source/AppDemo.cpp | 2 +- docs/Doxyfile | 1 + 6 files changed, 89 insertions(+), 76 deletions(-) rename apps/{webgpu/webgpu_demo.cpp => app_demo_webgpu/AppDemoWebGPU.cpp} (95%) rename apps/{webgpu => app_demo_webgpu}/CMakeLists.txt (89%) rename apps/{webgpu => app_demo_webgpu}/metal_layer.mm (100%) diff --git a/apps/CMakeLists.txt b/apps/CMakeLists.txt index 5f9a5172..2bcefcfc 100644 --- a/apps/CMakeLists.txt +++ b/apps/CMakeLists.txt @@ -11,6 +11,6 @@ else() add_subdirectory(app_demo_slproject) if (SL_BUILD_WEBGPU_DEMO AND NOT ("${SYSTEM_NAME_UPPER}" MATCHES "EMSCRIPTEN")) - add_subdirectory(webgpu) + add_subdirectory(app_demo_webgpu) endif () endif() \ No newline at end of file diff --git a/apps/webgpu/webgpu_demo.cpp b/apps/app_demo_webgpu/AppDemoWebGPU.cpp similarity index 95% rename from apps/webgpu/webgpu_demo.cpp rename to apps/app_demo_webgpu/AppDemoWebGPU.cpp index 07db8f2f..a5864da0 100644 --- a/apps/webgpu/webgpu_demo.cpp +++ b/apps/app_demo_webgpu/AppDemoWebGPU.cpp @@ -1,5 +1,59 @@ -/* +//############################################################################# +// File: app_demo_webgpu.cpp +// Date: Summer 2023 +// Codestyle: https://github.com/cpvrlab/SLProject/wiki/SLProject-Coding-Style +// Authors: Marino von Wattenwyl +// License: This software is provided under the GNU General Public License +// Please visit: http://opensource.org/licenses/GPL-3.0 +//############################################################################# + +////////////////////////////////////////////////////////////////////////////// +// Please read the documentation for the WebGpuDemoApp struct further below // +////////////////////////////////////////////////////////////////////////////// +#if defined(_WIN32) +# define SYSTEM_WINDOWS +# define GLFW_EXPOSE_NATIVE_WIN32 +# define WIN32_LEAN_AND_MEAN +# include +#elif defined(__linux__) +# define SYSTEM_LINUX +# define GLFW_EXPOSE_NATIVE_X11 +#elif defined(__APPLE__) +# ifndef SYSTEM_DARWIN +# define SYSTEM_DARWIN +# endif +# define GLFW_EXPOSE_NATIVE_COCOA +#endif +//----------------------------------------------------------------------------- +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#ifdef SYSTEM_DARWIN +extern "C" void* createMetalLayer(void* window); +#endif +//----------------------------------------------------------------------------- +#define WEBGPU_DEMO_LOG(msg) std::cout << (msg) << std::endl +//----------------------------------------------------------------------------- +#define WEBGPU_DEMO_CHECK(condition, errorMsg) \ + if (!(condition)) \ + { \ + std::cerr << (errorMsg) << std::endl; \ + std::exit(1); \ + } +//----------------------------------------------------------------------------- +//! Application Struct WebGpuDemoApp with all global variables and documentation +/*! -- Overview WebGPU is a graphics API standard developed by the World Wide Web Consortium (W3C). It is an abstraction layer @@ -10,13 +64,13 @@ compute functionality is available. The primary target for WebGPU was the Web as a replacement for the old WebGL API. This means that the specification is written for a JavaScript API. However, Google and Mozilla have decided to provide their in-browser implementations as native libraries, so we can use WebGPU in native apps written in C, C++ or Rust. The implementers -have agreed on a common interface for their libraries in form of a header called `webgpu.h` +have agreed on a common interface for their libraries in form of a header called `app_demo_webgpu.h` (https://github.com/webgpu-native/webgpu-headers/). There are currently three implementations of this header: - wgpu-native: Mozilla's implementation for Firefox, written in Rust - Dawn: Google's implementation for Chromium, written in C++ - - Emscripten: Translates the webgpu.h calls to JavaScript calls in the browser + - Emscripten: Translates the app_demo_webgpu.h calls to JavaScript calls in the browser WebGPU uses its own shader language called WGSL (WebGPU Shader Language). This is the only shader language supported in the browsers even though the native implementations also support SPIR-V. @@ -189,48 +243,8 @@ Here's a list of things I've noticed are handled differently from Vulkan (as of most of the time a nice error message with a human-readable error message including the labels of the problematic objects, suggests fixes and even generates a stack trace. I'm not sure what the overhead of this validation is, but I excpect there to be an option to turn it off in the future. - */ - -#if defined(_WIN32) -# define SYSTEM_WINDOWS -# define GLFW_EXPOSE_NATIVE_WIN32 -# define WIN32_LEAN_AND_MEAN -# include -#elif defined(__linux__) -# define SYSTEM_LINUX -# define GLFW_EXPOSE_NATIVE_X11 -#elif defined(__APPLE__) -# ifndef SYSTEM_DARWIN -# define SYSTEM_DARWIN -# endif -# define GLFW_EXPOSE_NATIVE_COCOA -#endif - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#define WEBGPU_DEMO_LOG(msg) std::cout << (msg) << std::endl - -#define WEBGPU_DEMO_CHECK(condition, errorMsg) \ - if (!(condition)) \ - { \ - std::cerr << (errorMsg) << std::endl; \ - std::exit(1); \ - } - -struct App +struct WebGpuDemoApp { GLFWwindow* window = nullptr; int surfaceWidth = 0; @@ -268,7 +282,7 @@ struct App float camRotY = 0.0f; float camZ = 2.0f; }; - +//----------------------------------------------------------------------------- struct VertexData { float positionX; @@ -280,21 +294,17 @@ struct VertexData float uvX; float uvY; }; - +//----------------------------------------------------------------------------- struct alignas(16) ShaderUniformData { float projectionMatrix[16]; float viewMatrix[16]; float modelMatrix[16]; }; - +//----------------------------------------------------------------------------- static_assert(sizeof(ShaderUniformData) % 16 == 0, "uniform data size must be a multiple of 16"); - -#ifdef SYSTEM_DARWIN -extern "C" void* createMetalLayer(void* window); -#endif - -void reconfigureSurface(App& app) +//----------------------------------------------------------------------------- +void reconfigureSurface(WebGpuDemoApp& app) { // Get the window size from the GLFW window. glfwGetWindowSize(app.window, &app.surfaceWidth, &app.surfaceHeight); @@ -325,8 +335,8 @@ void reconfigureSurface(App& app) WEBGPU_DEMO_CHECK(app.depthTextureView, "[WebGPU] Failed to re-create depth texture view"); WEBGPU_DEMO_LOG("[WebGPU] Depth texture view re-created"); } - -void onPaint(App& app) +//----------------------------------------------------------------------------- +void onPaint(WebGpuDemoApp& app) { if (app.surfaceWidth == 0 || app.surfaceHeight == 0) return; @@ -472,15 +482,15 @@ void onPaint(App& app) wgpuTextureViewRelease(view); wgpuTextureRelease(surfaceTexture.texture); } - +//----------------------------------------------------------------------------- void onResize(GLFWwindow* window, int width, int height) { - App& app = *((App*)glfwGetWindowUserPointer(window)); + WebGpuDemoApp& app = *((WebGpuDemoApp*)glfwGetWindowUserPointer(window)); reconfigureSurface(app); onPaint(app); } - -void initGLFW(App& app) +//----------------------------------------------------------------------------- +void initGLFW(WebGpuDemoApp& app) { // === Initialize GLFW === @@ -499,7 +509,7 @@ void initGLFW(App& app) glfwSetWindowUserPointer(app.window, &app); glfwSetFramebufferSizeCallback(app.window, onResize); } - +//----------------------------------------------------------------------------- void handleAdapterRequest(WGPURequestAdapterStatus status, WGPUAdapter adapter, char const* message, @@ -512,7 +522,7 @@ void handleAdapterRequest(WGPURequestAdapterStatus status, WGPUAdapter* outAdapter = (WGPUAdapter*)userdata; *outAdapter = adapter; } - +//----------------------------------------------------------------------------- void handleDeviceRequest(WGPURequestDeviceStatus status, WGPUDevice device, char const* message, @@ -525,8 +535,8 @@ void handleDeviceRequest(WGPURequestDeviceStatus status, WGPUDevice* outDevice = (WGPUDevice*)userdata; *outDevice = device; } - -void initWebGPU(App& app) +//----------------------------------------------------------------------------- +void initWebGPU(WebGpuDemoApp& app) { // === Create a WebGPU instance === // The instance is the root interface to WebGPU through which we create all other WebGPU resources. @@ -1102,8 +1112,8 @@ void initWebGPU(App& app) WEBGPU_DEMO_CHECK(app.pipeline, "[WebGPU] Failed to create render pipeline"); WEBGPU_DEMO_LOG("[WebGPU] Render pipeline created"); } - -void deinitWebGPU(App& app) +//----------------------------------------------------------------------------- +void deinitWebGPU(WebGpuDemoApp& app) { // === Release all WebGPU resources === @@ -1133,8 +1143,8 @@ void deinitWebGPU(App& app) WEBGPU_DEMO_LOG("[WebGPU] Resources released"); } - -void deinitGLFW(App& app) +//----------------------------------------------------------------------------- +void deinitGLFW(WebGpuDemoApp& app) { // === Destroy the window and terminate GLFW === @@ -1142,10 +1152,11 @@ void deinitGLFW(App& app) glfwTerminate(); WEBGPU_DEMO_LOG("[GLFW] Window closed and terminated"); } - +//----------------------------------------------------------------------------- int main(int argc, const char* argv[]) { - App app; + WebGpuDemoApp app; + initGLFW(app); initWebGPU(app); @@ -1179,4 +1190,5 @@ int main(int argc, const char* argv[]) deinitGLFW(app); return 0; -} \ No newline at end of file +} +//----------------------------------------------------------------------------- \ No newline at end of file diff --git a/apps/webgpu/CMakeLists.txt b/apps/app_demo_webgpu/CMakeLists.txt similarity index 89% rename from apps/webgpu/CMakeLists.txt rename to apps/app_demo_webgpu/CMakeLists.txt index 564ea90b..b67242bd 100644 --- a/apps/webgpu/CMakeLists.txt +++ b/apps/app_demo_webgpu/CMakeLists.txt @@ -60,11 +60,11 @@ if (SYSTEM_NAME_UPPER MATCHES "DARWIN") set(TARGET_SPECIFIC_SOURCES metal_layer.mm) endif () -add_executable(webgpu-demo webgpu_demo.cpp ${TARGET_SPECIFIC_SOURCES}) -target_link_libraries(webgpu-demo PRIVATE wgpu sl_cv sl_math ${glfw_LIBS}) +add_executable(app-demo-webgpu AppDemoWebGPU.cpp ${TARGET_SPECIFIC_SOURCES}) +target_link_libraries(app-demo-webgpu PRIVATE wgpu sl_cv sl_math ${glfw_LIBS}) if (SYSTEM_NAME_UPPER MATCHES "LINUX") - target_link_libraries(webgpu-demo PRIVATE "X11") + target_link_libraries(app-demo-webgpu PRIVATE "X11") elseif (SYSTEM_NAME_UPPER MATCHES "DARWIN") - target_link_libraries(webgpu-demo PRIVATE "-framework Cocoa" "-framework IOKit") + target_link_libraries(app-demo-webgpu PRIVATE "-framework Cocoa" "-framework IOKit") endif () \ No newline at end of file diff --git a/apps/webgpu/metal_layer.mm b/apps/app_demo_webgpu/metal_layer.mm similarity index 100% rename from apps/webgpu/metal_layer.mm rename to apps/app_demo_webgpu/metal_layer.mm diff --git a/apps/source/AppDemo.cpp b/apps/source/AppDemo.cpp index 14d8a1ae..0ca8807e 100644 --- a/apps/source/AppDemo.cpp +++ b/apps/source/AppDemo.cpp @@ -29,7 +29,7 @@ SLDeviceRotation AppDemo::devRot; SLDeviceLocation AppDemo::devLoc; SLstring AppDemo::name = "SLProjectApp"; SLstring AppDemo::appTag = "SLProject"; -SLstring AppDemo::version = "4.1.000"; +SLstring AppDemo::version = "4.1.001"; #ifdef _DEBUG SLstring AppDemo::configuration = "Debug"; #else diff --git a/docs/Doxyfile b/docs/Doxyfile index 45f4e276..25867ef9 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -914,6 +914,7 @@ INPUT = ../modules/sl/source \ ../apps/app_demo_slproject/include \ ../apps/app_demo_slproject/glfw \ ../apps/app_demo_slproject/source \ + ../apps/app_demo_webgpu \ pages/Introduction.md \ pages/ExampleScenes.md \ pages/ExampleGlTF.md \ From 84849aa6aa26bd34cc7777ebc07fb25ae795aed2 Mon Sep 17 00:00:00 2001 From: Marcus Hudritsch Date: Tue, 12 Dec 2023 17:44:17 +0100 Subject: [PATCH 088/108] Update AppDemoWebGPU.cpp --- apps/app_demo_webgpu/AppDemoWebGPU.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/app_demo_webgpu/AppDemoWebGPU.cpp b/apps/app_demo_webgpu/AppDemoWebGPU.cpp index a5864da0..6c57dda0 100644 --- a/apps/app_demo_webgpu/AppDemoWebGPU.cpp +++ b/apps/app_demo_webgpu/AppDemoWebGPU.cpp @@ -54,7 +54,8 @@ extern "C" void* createMetalLayer(void* window); //----------------------------------------------------------------------------- //! Application Struct WebGpuDemoApp with all global variables and documentation /*! --- Overview + +**Overview** WebGPU is a graphics API standard developed by the World Wide Web Consortium (W3C). It is an abstraction layer over modern graphics APIs like Vulkan, D3D12 and Metal and thus uses modern concepts such as queues, command buffers @@ -84,7 +85,7 @@ downloaded from CMake. Since wgpu-native uses the same interface header as Dawn, link Dawn instead of wgpu-native, but we would have to build and distribute it ourselves. --- Usage +**Usage** WebGPU follows a stateless design as opposed to OpenGL, where much state has to be set globally before making a draw call. To use WebGPU, we create objects with a call to wgpu*Create*. This function generally takes the parent @@ -214,7 +215,7 @@ WebGPU concepts: wgpuCommandEncoderBeginRenderPass. --- WebGPU vs. Vulkan +**WebGPU vs. Vulkan** Here's a list of things I've noticed are handled differently from Vulkan (as of 2023-11-26): From 4c5c6bf3c6c8d7a51b78d99e3d32a45ffb3c792e Mon Sep 17 00:00:00 2001 From: Marcus Hudritsch Date: Tue, 12 Dec 2023 18:03:40 +0100 Subject: [PATCH 089/108] Update AppDemoWebGPU.cpp --- apps/app_demo_webgpu/AppDemoWebGPU.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/app_demo_webgpu/AppDemoWebGPU.cpp b/apps/app_demo_webgpu/AppDemoWebGPU.cpp index 6c57dda0..af21d421 100644 --- a/apps/app_demo_webgpu/AppDemoWebGPU.cpp +++ b/apps/app_demo_webgpu/AppDemoWebGPU.cpp @@ -55,7 +55,7 @@ extern "C" void* createMetalLayer(void* window); //! Application Struct WebGpuDemoApp with all global variables and documentation /*! -**Overview** +Overview: WebGPU is a graphics API standard developed by the World Wide Web Consortium (W3C). It is an abstraction layer over modern graphics APIs like Vulkan, D3D12 and Metal and thus uses modern concepts such as queues, command buffers @@ -85,7 +85,7 @@ downloaded from CMake. Since wgpu-native uses the same interface header as Dawn, link Dawn instead of wgpu-native, but we would have to build and distribute it ourselves. -**Usage** +Usage: WebGPU follows a stateless design as opposed to OpenGL, where much state has to be set globally before making a draw call. To use WebGPU, we create objects with a call to wgpu*Create*. This function generally takes the parent @@ -215,7 +215,7 @@ WebGPU concepts: wgpuCommandEncoderBeginRenderPass. -**WebGPU vs. Vulkan** +WebGPU vs. Vulkan: Here's a list of things I've noticed are handled differently from Vulkan (as of 2023-11-26): From e42fd46688d22cc3a653c60c583a548bbdb74857 Mon Sep 17 00:00:00 2001 From: Marino von Wattenwyl Date: Mon, 18 Dec 2023 13:50:00 +0100 Subject: [PATCH 090/108] [WebGPU Demo] Minor tweaks --- apps/app_demo_webgpu/AppDemoWebGPU.cpp | 29 +++++++++++++------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/apps/app_demo_webgpu/AppDemoWebGPU.cpp b/apps/app_demo_webgpu/AppDemoWebGPU.cpp index af21d421..076838e0 100644 --- a/apps/app_demo_webgpu/AppDemoWebGPU.cpp +++ b/apps/app_demo_webgpu/AppDemoWebGPU.cpp @@ -1,5 +1,5 @@ //############################################################################# -// File: app_demo_webgpu.cpp +// File: AppDemoWebGPU.cpp // Date: Summer 2023 // Codestyle: https://github.com/cpvrlab/SLProject/wiki/SLProject-Coding-Style // Authors: Marino von Wattenwyl @@ -8,7 +8,7 @@ //############################################################################# ////////////////////////////////////////////////////////////////////////////// -// Please read the documentation for the WebGpuDemoApp struct further below // +// Please read the documentation for the WebGPUDemoApp struct further below // ////////////////////////////////////////////////////////////////////////////// #if defined(_WIN32) @@ -39,6 +39,7 @@ #include #include +//----------------------------------------------------------------------------- #ifdef SYSTEM_DARWIN extern "C" void* createMetalLayer(void* window); #endif @@ -52,7 +53,7 @@ extern "C" void* createMetalLayer(void* window); std::exit(1); \ } //----------------------------------------------------------------------------- -//! Application Struct WebGpuDemoApp with all global variables and documentation +//! Application struct WebGPUDemoApp with all global variables and documentation /*! Overview: @@ -65,13 +66,13 @@ compute functionality is available. The primary target for WebGPU was the Web as a replacement for the old WebGL API. This means that the specification is written for a JavaScript API. However, Google and Mozilla have decided to provide their in-browser implementations as native libraries, so we can use WebGPU in native apps written in C, C++ or Rust. The implementers -have agreed on a common interface for their libraries in form of a header called `app_demo_webgpu.h` +have agreed on a common interface for their libraries in form of a header called `webgpu.h` (https://github.com/webgpu-native/webgpu-headers/). There are currently three implementations of this header: - wgpu-native: Mozilla's implementation for Firefox, written in Rust - Dawn: Google's implementation for Chromium, written in C++ - - Emscripten: Translates the app_demo_webgpu.h calls to JavaScript calls in the browser + - Emscripten: Translates webgpu.h calls to calls to the WebGPU JavaScript API in the browser WebGPU uses its own shader language called WGSL (WebGPU Shader Language). This is the only shader language supported in the browsers even though the native implementations also support SPIR-V. @@ -245,7 +246,7 @@ Here's a list of things I've noticed are handled differently from Vulkan (as of problematic objects, suggests fixes and even generates a stack trace. I'm not sure what the overhead of this validation is, but I excpect there to be an option to turn it off in the future. */ -struct WebGpuDemoApp +struct WebGPUDemoApp { GLFWwindow* window = nullptr; int surfaceWidth = 0; @@ -305,7 +306,7 @@ struct alignas(16) ShaderUniformData //----------------------------------------------------------------------------- static_assert(sizeof(ShaderUniformData) % 16 == 0, "uniform data size must be a multiple of 16"); //----------------------------------------------------------------------------- -void reconfigureSurface(WebGpuDemoApp& app) +void reconfigureSurface(WebGPUDemoApp& app) { // Get the window size from the GLFW window. glfwGetWindowSize(app.window, &app.surfaceWidth, &app.surfaceHeight); @@ -337,7 +338,7 @@ void reconfigureSurface(WebGpuDemoApp& app) WEBGPU_DEMO_LOG("[WebGPU] Depth texture view re-created"); } //----------------------------------------------------------------------------- -void onPaint(WebGpuDemoApp& app) +void onPaint(WebGPUDemoApp& app) { if (app.surfaceWidth == 0 || app.surfaceHeight == 0) return; @@ -486,12 +487,12 @@ void onPaint(WebGpuDemoApp& app) //----------------------------------------------------------------------------- void onResize(GLFWwindow* window, int width, int height) { - WebGpuDemoApp& app = *((WebGpuDemoApp*)glfwGetWindowUserPointer(window)); + WebGPUDemoApp& app = *((WebGPUDemoApp*)glfwGetWindowUserPointer(window)); reconfigureSurface(app); onPaint(app); } //----------------------------------------------------------------------------- -void initGLFW(WebGpuDemoApp& app) +void initGLFW(WebGPUDemoApp& app) { // === Initialize GLFW === @@ -537,7 +538,7 @@ void handleDeviceRequest(WGPURequestDeviceStatus status, *outDevice = device; } //----------------------------------------------------------------------------- -void initWebGPU(WebGpuDemoApp& app) +void initWebGPU(WebGPUDemoApp& app) { // === Create a WebGPU instance === // The instance is the root interface to WebGPU through which we create all other WebGPU resources. @@ -1114,7 +1115,7 @@ void initWebGPU(WebGpuDemoApp& app) WEBGPU_DEMO_LOG("[WebGPU] Render pipeline created"); } //----------------------------------------------------------------------------- -void deinitWebGPU(WebGpuDemoApp& app) +void deinitWebGPU(WebGPUDemoApp& app) { // === Release all WebGPU resources === @@ -1145,7 +1146,7 @@ void deinitWebGPU(WebGpuDemoApp& app) WEBGPU_DEMO_LOG("[WebGPU] Resources released"); } //----------------------------------------------------------------------------- -void deinitGLFW(WebGpuDemoApp& app) +void deinitGLFW(WebGPUDemoApp& app) { // === Destroy the window and terminate GLFW === @@ -1156,7 +1157,7 @@ void deinitGLFW(WebGpuDemoApp& app) //----------------------------------------------------------------------------- int main(int argc, const char* argv[]) { - WebGpuDemoApp app; + WebGPUDemoApp app; initGLFW(app); initWebGPU(app); From 5ccb7b158658bb61f4d0df6defeea6fa308a5380 Mon Sep 17 00:00:00 2001 From: Marcus Hudritsch Date: Tue, 19 Dec 2023 08:01:59 +0100 Subject: [PATCH 091/108] Update AppDemoWebGPU.cpp --- apps/app_demo_webgpu/AppDemoWebGPU.cpp | 52 ++++++++++++++++++++------ 1 file changed, 41 insertions(+), 11 deletions(-) diff --git a/apps/app_demo_webgpu/AppDemoWebGPU.cpp b/apps/app_demo_webgpu/AppDemoWebGPU.cpp index 076838e0..45a072f3 100644 --- a/apps/app_demo_webgpu/AppDemoWebGPU.cpp +++ b/apps/app_demo_webgpu/AppDemoWebGPU.cpp @@ -541,7 +541,8 @@ void handleDeviceRequest(WGPURequestDeviceStatus status, void initWebGPU(WebGPUDemoApp& app) { // === Create a WebGPU instance === - // The instance is the root interface to WebGPU through which we create all other WebGPU resources. + // The instance is the root interface to WebGPU through which we create + // all other WebGPU resources. app.instance = wgpuCreateInstance(nullptr); WEBGPU_DEMO_CHECK(app.instance, "[WebGPU] Failed to create instance"); @@ -591,7 +592,10 @@ void initWebGPU(WebGPUDemoApp& app) deviceDesc.requiredLimits = &requiredLimits; deviceDesc.defaultQueue.label = "Demo Queue"; - wgpuAdapterRequestDevice(app.adapter, &deviceDesc, handleDeviceRequest, &app.device); + wgpuAdapterRequestDevice(app.adapter, + &deviceDesc, + handleDeviceRequest, + &app.device); // === Acquire a WebGPU queue === // The queue is where the commands for the GPU are submitted to. @@ -633,10 +637,14 @@ void initWebGPU(WebGPUDemoApp& app) // Query the surface capabilities from the adapter. WGPUSurfaceCapabilities surfaceCapabilities; - wgpuSurfaceGetCapabilities(app.surface, app.adapter, &surfaceCapabilities); + wgpuSurfaceGetCapabilities(app.surface, + app.adapter, + &surfaceCapabilities); // Get the window size from the GLFW window. - glfwGetFramebufferSize(app.window, &app.surfaceWidth, &app.surfaceHeight); + glfwGetFramebufferSize(app.window, + &app.surfaceWidth, + &app.surfaceHeight); app.surfaceConfig = {}; app.surfaceConfig.device = app.device; @@ -706,7 +714,11 @@ void initWebGPU(WebGPUDemoApp& app) WEBGPU_DEMO_LOG("[WebGPU] Vertex buffer created"); // Upload the data to the GPU. - wgpuQueueWriteBuffer(app.queue, app.vertexBuffer, 0, vertexData.data(), app.vertexDataSize); + wgpuQueueWriteBuffer(app.queue, + app.vertexBuffer, + 0, + vertexData.data(), + app.vertexDataSize); // === Create the index buffer === @@ -734,7 +746,11 @@ void initWebGPU(WebGPUDemoApp& app) WEBGPU_DEMO_CHECK(app.indexBuffer, "[WebGPU] Failed to create index buffer"); WEBGPU_DEMO_LOG("[WebGPU] Index buffer created"); - wgpuQueueWriteBuffer(app.queue, app.indexBuffer, 0, indexData.data(), app.indexDataSize); + wgpuQueueWriteBuffer(app.queue, + app.indexBuffer, + 0, + indexData.data(), + app.indexDataSize); // === Create the uniform buffer === @@ -748,7 +764,11 @@ void initWebGPU(WebGPUDemoApp& app) WEBGPU_DEMO_LOG("[WebGPU] Uniform buffer created"); ShaderUniformData uniformData = {}; - wgpuQueueWriteBuffer(app.queue, app.uniformBuffer, 0, &uniformData, sizeof(ShaderUniformData)); + wgpuQueueWriteBuffer(app.queue, + app.uniformBuffer, + 0, + &uniformData, + sizeof(ShaderUniformData)); // === Create the depth texture === @@ -832,7 +852,8 @@ void initWebGPU(WebGPUDemoApp& app) for (unsigned mipLevel = 0; mipLevel < textureDesc.mipLevelCount; mipLevel++) { cv::Mat mipLevelImage; - cv::Size cvSize(static_cast(mipLevelSize.width), static_cast(mipLevelSize.height)); + cv::Size cvSize(static_cast(mipLevelSize.width), + static_cast(mipLevelSize.height)); cv::resize(image, mipLevelImage, cvSize); std::size_t mipLevelBytes = 4ull * mipLevelSize.width * mipLevelSize.height; @@ -842,7 +863,12 @@ void initWebGPU(WebGPUDemoApp& app) pixelDataLayout.rowsPerImage = mipLevelSize.height; // Upload the data to the GPU. - wgpuQueueWriteTexture(app.queue, &destination, mipLevelImage.data, mipLevelBytes, &pixelDataLayout, &mipLevelSize); + wgpuQueueWriteTexture(app.queue, + &destination, + mipLevelImage.data, + mipLevelBytes, + &pixelDataLayout, + &mipLevelSize); // Scale the image down for the next mip level. mipLevelSize.width /= 2; @@ -981,7 +1007,8 @@ void initWebGPU(WebGPUDemoApp& app) bindGroupLayoutDesc.entryCount = bindGroupLayoutEntries.size(); bindGroupLayoutDesc.entries = bindGroupLayoutEntries.data(); - app.bindGroupLayout = wgpuDeviceCreateBindGroupLayout(app.device, &bindGroupLayoutDesc); + app.bindGroupLayout = wgpuDeviceCreateBindGroupLayout(app.device, + &bindGroupLayoutDesc); WEBGPU_DEMO_CHECK(app.bindGroupLayout, "[WebGPU] Failed to create bind group layout"); WEBGPU_DEMO_LOG("[WebGPU] Bind group layout created"); @@ -1046,7 +1073,9 @@ void initWebGPU(WebGPUDemoApp& app) uvAttribute.offset = 6 * sizeof(float); uvAttribute.shaderLocation = 2; - std::vector vertexAttributes = {positionAttribute, normalAttribute, uvAttribute}; + std::vector vertexAttributes = {positionAttribute, + normalAttribute, + uvAttribute}; // Description of the vertex buffer layout for the vertex shader stage WGPUVertexBufferLayout vertexBufferLayout = {}; @@ -1185,6 +1214,7 @@ int main(int argc, const char* argv[]) lastCursorY = cursorY; onPaint(app); + glfwPollEvents(); } From 9653b91b2259cf8bc33e2b60103ba367f90ee22c Mon Sep 17 00:00:00 2001 From: Marino von Wattenwyl Date: Thu, 21 Dec 2023 10:04:32 +0100 Subject: [PATCH 092/108] [CI/CD] Use Emscripten version 3.1.50 --- .github/workflows/build-wasm-emscripten.yml | 2 ++ .github/workflows/deploy-wasm-emscripten.yml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/.github/workflows/build-wasm-emscripten.yml b/.github/workflows/build-wasm-emscripten.yml index 193da333..ff3cb78a 100644 --- a/.github/workflows/build-wasm-emscripten.yml +++ b/.github/workflows/build-wasm-emscripten.yml @@ -15,6 +15,8 @@ jobs: - name: Install Emscripten uses: mymindstorm/setup-emsdk@v11 + with: + version: 3.1.50 - name: Configure CMake run: emcmake cmake -B ${{ github.workspace }}/build -DSL_BUILD_WAI=OFF -DSL_BUILD_WITH_OPENSSL=OFF -DSL_DOWNLOAD_DATA=OFF diff --git a/.github/workflows/deploy-wasm-emscripten.yml b/.github/workflows/deploy-wasm-emscripten.yml index 9228fde5..63293152 100644 --- a/.github/workflows/deploy-wasm-emscripten.yml +++ b/.github/workflows/deploy-wasm-emscripten.yml @@ -16,6 +16,8 @@ jobs: - name: Install Emscripten uses: mymindstorm/setup-emsdk@v11 + with: + version: 3.1.50 - name: Configure CMake run: emcmake cmake -B ${{ github.workspace }}/build -DSL_BUILD_WAI=OFF -DSL_BUILD_WITH_OPENSSL=OFF -DSL_DOWNLOAD_DATA=OFF -DCMAKE_BUILD_TYPE=Release From 8d16228a88db5e66c83c5bcdd51ff44a1f41610c Mon Sep 17 00:00:00 2001 From: Marino von Wattenwyl Date: Thu, 21 Dec 2023 10:24:07 +0100 Subject: [PATCH 093/108] [CI/CD] Disable running building docs and deploying the Web version on every push to develop --- .github/workflows/build-docs.yml | 1 - .github/workflows/deploy-wasm-emscripten.yml | 1 - 2 files changed, 2 deletions(-) diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml index fb69d91b..e91f2c55 100644 --- a/.github/workflows/build-docs.yml +++ b/.github/workflows/build-docs.yml @@ -5,7 +5,6 @@ on: push: branches: - main - - develop jobs: build: diff --git a/.github/workflows/deploy-wasm-emscripten.yml b/.github/workflows/deploy-wasm-emscripten.yml index 63293152..60e96a91 100644 --- a/.github/workflows/deploy-wasm-emscripten.yml +++ b/.github/workflows/deploy-wasm-emscripten.yml @@ -5,7 +5,6 @@ on: push: branches: - main - - develop jobs: deploy: From 7becff98df4f6599feb51694b5cb6c48afc61e09 Mon Sep 17 00:00:00 2001 From: Marcus Hudritsch Date: Sun, 7 Jan 2024 13:51:29 +0100 Subject: [PATCH 094/108] Added Volume Rendering Online Example Page --- docs/Doxyfile | 1 + docs/pages/ExampleScenes.md | 3 ++- docs/pages/ExampleVolumeRendering.md | 19 +++++++++++++++++++ 3 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 docs/pages/ExampleVolumeRendering.md diff --git a/docs/Doxyfile b/docs/Doxyfile index 25867ef9..fcdefda8 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -928,6 +928,7 @@ INPUT = ../modules/sl/source \ pages/ExampleScenes.md \ pages/ExampleShadowMapping.md \ pages/ExampleSkinnedAnimations.md \ + pages/ExampleVolumeRendering.md \ pages/OnPaint.md \ pages/Emscripten.md \ pages/BlenderToSL.md \ diff --git a/docs/pages/ExampleScenes.md b/docs/pages/ExampleScenes.md index 85324803..f89caebb 100644 --- a/docs/pages/ExampleScenes.md +++ b/docs/pages/ExampleScenes.md @@ -14,4 +14,5 @@ See also \ref emscripten for more background information. - \subpage example-skinned-animations - \subpage example-raytracing - \subpage example-pathtracing -- \subpage example-particles \ No newline at end of file +- \subpage example-particles +- \subpage example-volume-rendering \ No newline at end of file diff --git a/docs/pages/ExampleVolumeRendering.md b/docs/pages/ExampleVolumeRendering.md new file mode 100644 index 00000000..8d5bbbc1 --- /dev/null +++ b/docs/pages/ExampleVolumeRendering.md @@ -0,0 +1,19 @@ +\page example-volume-rendering Volume Rendering +Volume Rendering of an MRI angiography dataset. In the material properties you can adjust the transfer function for +color and transparency points that transform the MRI intensities into color and transparency. Volume rendering is +realized with a special fragment shader that performs a ray cast through the MRI volume. + +\htmlonly + +\endhtmlonly + +General help: +
    +
  • Click and drag the left mouse button to rotate the scene.
  • +
  • Click and drag the middle mouse button to move sidewards/up-down.
  • +
  • Roll the mouse-wheel to move forward/backward.
  • +
  • Double click with the left mouse button to select an object.
  • +
  • Click the right mouse button to open the context menu.
  • +
  • Open and dock additional windows from the menu Infos.
  • +
  • See more example scenes over the menu File > Load Test Scene
  • +
\ No newline at end of file From 96765d9d91e942af05c5819894a05513721fcbfc Mon Sep 17 00:00:00 2001 From: Marino von Wattenwyl Date: Thu, 11 Jan 2024 15:58:34 +0100 Subject: [PATCH 095/108] [Emscripten] Change Emscripten version to 3.1.46 --- .github/workflows/build-wasm-emscripten.yml | 2 +- apps/app_demo_slproject/emscripten/AppDemoMainEmscripten.cpp | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-wasm-emscripten.yml b/.github/workflows/build-wasm-emscripten.yml index ff3cb78a..f95eff74 100644 --- a/.github/workflows/build-wasm-emscripten.yml +++ b/.github/workflows/build-wasm-emscripten.yml @@ -16,7 +16,7 @@ jobs: - name: Install Emscripten uses: mymindstorm/setup-emsdk@v11 with: - version: 3.1.50 + version: 3.1.46 - name: Configure CMake run: emcmake cmake -B ${{ github.workspace }}/build -DSL_BUILD_WAI=OFF -DSL_BUILD_WITH_OPENSSL=OFF -DSL_DOWNLOAD_DATA=OFF diff --git a/apps/app_demo_slproject/emscripten/AppDemoMainEmscripten.cpp b/apps/app_demo_slproject/emscripten/AppDemoMainEmscripten.cpp index 89596d84..5cedbba2 100644 --- a/apps/app_demo_slproject/emscripten/AppDemoMainEmscripten.cpp +++ b/apps/app_demo_slproject/emscripten/AppDemoMainEmscripten.cpp @@ -552,6 +552,7 @@ int main(void) emscripten_set_beforeunload_callback(nullptr, emOnUnload); // HACK: Fixes to make this able to run in an