From 660d9237e6e77fbefa8d4845132fc01e3b65eba8 Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Sat, 28 Feb 2015 13:07:16 -0800 Subject: [PATCH 01/43] First version of the Shader and the magical shader program --- libraries/gpu/src/gpu/Context.h | 20 +- libraries/gpu/src/gpu/GLBackend.h | 11 + libraries/gpu/src/gpu/GLBackendShader.cpp | 275 ++++++++++++++++++ libraries/gpu/src/gpu/Resource.h | 3 +- libraries/gpu/src/gpu/Shader.cpp | 67 +++++ libraries/gpu/src/gpu/Shader.h | 126 ++++++++ libraries/gpu/src/gpu/Texture.h | 4 +- .../model/src/model/SkyFromAtmosphere.slf | 108 +++++++ .../model/src/model/SkyFromAtmosphere.slv | 68 +++++ libraries/model/src/model/SkyFromSpace.slf | 114 ++++++++ libraries/model/src/model/SkyFromSpace.slv | 43 +++ libraries/model/src/model/Stage.cpp | 10 + libraries/model/src/model/Stage.h | 4 + 13 files changed, 845 insertions(+), 8 deletions(-) create mode 100755 libraries/gpu/src/gpu/GLBackendShader.cpp create mode 100755 libraries/gpu/src/gpu/Shader.cpp create mode 100755 libraries/gpu/src/gpu/Shader.h create mode 100755 libraries/model/src/model/SkyFromAtmosphere.slf create mode 100755 libraries/model/src/model/SkyFromAtmosphere.slv create mode 100755 libraries/model/src/model/SkyFromSpace.slf create mode 100755 libraries/model/src/model/SkyFromSpace.slv diff --git a/libraries/gpu/src/gpu/Context.h b/libraries/gpu/src/gpu/Context.h index 8955010f50..632c5f96de 100644 --- a/libraries/gpu/src/gpu/Context.h +++ b/libraries/gpu/src/gpu/Context.h @@ -15,6 +15,7 @@ #include "Resource.h" #include "Texture.h" +#include "Shader.h" namespace gpu { @@ -46,14 +47,15 @@ public: template< typename T > static void setGPUObject(const Buffer& buffer, T* bo) { - buffer.setGPUObject(reinterpret_cast(bo)); + // buffer.setGPUObject(reinterpret_cast(bo)); + buffer.setGPUObject(bo); } template< typename T > static T* getGPUObject(const Buffer& buffer) { return reinterpret_cast(buffer.getGPUObject()); } - void syncGPUObject(const Buffer& buffer); + //void syncGPUObject(const Buffer& buffer); template< typename T > static void setGPUObject(const Texture& texture, T* to) { @@ -64,7 +66,19 @@ public: return reinterpret_cast(texture.getGPUObject()); } - void syncGPUObject(const Texture& texture); + //void syncGPUObject(const Texture& texture); + + + template< typename T > + static void setGPUObject(const Shader& shader, T* so) { + shader.setGPUObject(reinterpret_cast(so)); + } + template< typename T > + static T* getGPUObject(const Shader& shader) { + return reinterpret_cast(shader.getGPUObject()); + } + + // void syncGPUObject(const Shader& shader); protected: diff --git a/libraries/gpu/src/gpu/GLBackend.h b/libraries/gpu/src/gpu/GLBackend.h index 49d139d727..161ce1dad6 100644 --- a/libraries/gpu/src/gpu/GLBackend.h +++ b/libraries/gpu/src/gpu/GLBackend.h @@ -60,6 +60,17 @@ public: static void syncGPUObject(const Texture& texture); static GLuint getTextureID(const TexturePointer& texture); + class GLShader : public GPUObject { + public: + GLuint _shader; + GLuint _program; + + GLShader(); + ~GLShader(); + }; + static GLShader* syncGPUObject(const Shader& shader); + static GLuint getShaderID(const ShaderPointer& shader); + static const int MAX_NUM_ATTRIBUTES = Stream::NUM_INPUT_SLOTS; static const int MAX_NUM_INPUT_BUFFERS = 16; diff --git a/libraries/gpu/src/gpu/GLBackendShader.cpp b/libraries/gpu/src/gpu/GLBackendShader.cpp new file mode 100755 index 0000000000..e37643e2f2 --- /dev/null +++ b/libraries/gpu/src/gpu/GLBackendShader.cpp @@ -0,0 +1,275 @@ +// +// GLBackendShader.cpp +// libraries/gpu/src/gpu +// +// Created by Sam Gateau on 2/28/2015. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#include "GLBackendShared.h" + + +GLBackend::GLShader::GLShader() : + _shader(0), + _program(0) +{} + +GLBackend::GLShader::~GLShader() { + if (_shader != 0) { + glDeleteShader(_shader); + } + if (_program != 0) { + glDeleteProgram(_program); + } +} + +bool compileShader(const Shader& shader, GLBackend::GLShader& object) { + // Any GLSLprogram ? normally yes... + const std::string& shaderSource = shader.getSource().getCode(); + if (shaderSource.empty()) { + qDebug() << "GLShader::compileShader - no GLSL shader source code ? so failed to create"; + return false; + } + + // Shader domain + const GLenum SHADER_DOMAINS[2] = { GL_VERTEX_SHADER, GL_FRAGMENT_SHADER }; + GLenum shaderDomain = SHADER_DOMAINS[shader.getType()]; + + // Create the shader object + GLuint glshader = glCreateShader(shaderDomain); + if (!glshader) { + qDebug() << "GLShader::compileShader - failed to create the gl shader & gl program object"; + return false; + } + + // Assign the source + const GLchar* srcstr = shaderSource.c_str(); + glShaderSource(glshader, 1, &srcstr, NULL); + + // Compile ! + glCompileShader(glshader); + + // check if shader compiled + GLint compiled = 0; + glGetShaderiv(glshader, GL_COMPILE_STATUS, &compiled); + + // if compilation fails + if (!compiled) + { + // save the source code to a temp file so we can debug easily + /* std::ofstream filestream; + filestream.open( "debugshader.glsl" ); + if ( filestream.is_open() ) + { + filestream << shaderSource->source; + filestream.close(); + } + */ + + GLint infoLength = 0; + glGetShaderiv(glshader, GL_INFO_LOG_LENGTH, &infoLength); + + char* temp = new char[infoLength] ; + glGetShaderInfoLog( glshader, infoLength, NULL, temp); + + qDebug() << "GLShader::compileShader - failed to compile the gl shader object:"; + qDebug() << temp; + + /* + filestream.open( "debugshader.glsl.info.txt" ); + if ( filestream.is_open() ) + { + filestream << String( temp ); + filestream.close(); + } + */ + delete[] temp; + + glDeleteShader( glshader); + return false; + } + + // so far so good, program is almost done, need to link: + GLuint glprogram = glCreateProgram(); + if (!glprogram) { + qDebug() << "GLShader::compileShader - failed to create the gl shader & gl program object"; + return false; + } + + glProgramParameteri(glprogram, GL_PROGRAM_SEPARABLE, GL_TRUE); + glAttachShader(glprogram, glshader); + glLinkProgram(glprogram); + + GLint linked = 0; + glGetProgramiv(glprogram, GL_LINK_STATUS, &linked); + + if (!linked) + { + /* + // save the source code to a temp file so we can debug easily + std::ofstream filestream; + filestream.open( "debugshader.glsl" ); + if ( filestream.is_open() ) + { + filestream << shaderSource->source; + filestream.close(); + } + */ + + GLint infoLength = 0; + glGetProgramiv( glprogram, GL_INFO_LOG_LENGTH, &infoLength ); + + char* temp = new char[infoLength] ; + glGetProgramInfoLog( glprogram, infoLength, NULL, temp); + + qDebug() << "GLShader::compileShader - failed to LINK the gl program object :"; + qDebug() << temp; + + /* + filestream.open( "debugshader.glsl.info.txt" ); + if ( filestream.is_open() ) + { + filestream << String( temp ); + filestream.close(); + } + */ + delete[] temp; + + glDeleteShader( glshader); + glDeleteProgram( glprogram); + return false; + } + + // So far so good, the shader is created successfully + object._shader = glshader; + object._program = glprogram; + + return true; +} + +bool compileProgram(const Shader& program, GLBackend::GLShader& object) { + if(!program.isProgram()) { + return false; + } + + // Let's go through every shaders and make sure they are ready to go + std::vector< GLuint > shaderObjects; + for (auto subShader : program.getShaders()) { + GLuint so = GLBackend::getShaderID(subShader); + if (!so) { + qDebug() << "GLShader::compileProgram - One of the shaders of the program is not compiled?"; + return false; + } + shaderObjects.push_back(so); + } + + // so far so good, program is almost done, need to link: + GLuint glprogram = glCreateProgram(); + if (!glprogram) { + qDebug() << "GLShader::compileProgram - failed to create the gl program object"; + return false; + } + + // glProgramParameteri(glprogram, GL_PROGRAM_, GL_TRUE); + // Create the program from the sub shaders + for (auto so : shaderObjects) { + glAttachShader(glprogram, so); + } + + // Link! + glLinkProgram(glprogram); + + GLint linked = 0; + glGetProgramiv(glprogram, GL_LINK_STATUS, &linked); + + if (!linked) + { + /* + // save the source code to a temp file so we can debug easily + std::ofstream filestream; + filestream.open( "debugshader.glsl" ); + if ( filestream.is_open() ) + { + filestream << shaderSource->source; + filestream.close(); + } + */ + + GLint infoLength = 0; + glGetProgramiv( glprogram, GL_INFO_LOG_LENGTH, &infoLength ); + + char* temp = new char[infoLength] ; + glGetProgramInfoLog( glprogram, infoLength, NULL, temp); + + qDebug() << "GLShader::compileProgram - failed to LINK the gl program object :"; + qDebug() << temp; + + /* + filestream.open( "debugshader.glsl.info.txt" ); + if ( filestream.is_open() ) + { + filestream << String( temp ); + filestream.close(); + } + */ + delete[] temp; + + glDeleteProgram( glprogram); + return false; + } + + // So far so good, the program is created successfully + object._shader = 0; + object._program = glprogram; + + return true; +} + +GLBackend::GLShader* GLBackend::syncGPUObject(const Shader& shader) { + GLShader* object = Backend::getGPUObject(shader); + + // If GPU object already created then good + if (object) { + return object; + } + + // need to have a gpu object? + + // GO through the process of allocating the correct storage and/or update the content + if (shader.isProgram()) { + GLShader tempObject; + if (compileProgram(shader, tempObject)) { + object = new GLShader(tempObject); + Backend::setGPUObject(shader, object); + } + } else if (shader.isDomain()) { + GLShader tempObject; + if (compileShader(shader, tempObject)) { + object = new GLShader(tempObject); + Backend::setGPUObject(shader, object); + } + } + + return object; +} + + + +GLuint GLBackend::getShaderID(const ShaderPointer& shader) { + if (!shader) { + return 0; + } + GLShader* object = GLBackend::syncGPUObject(*shader); + if (object) { + if (shader->isProgram()) { + return object->_program; + } else { + return object->_shader; + } + } else { + return 0; + } +} + diff --git a/libraries/gpu/src/gpu/Resource.h b/libraries/gpu/src/gpu/Resource.h index a75c3e8d7c..225e3fd927 100644 --- a/libraries/gpu/src/gpu/Resource.h +++ b/libraries/gpu/src/gpu/Resource.h @@ -140,12 +140,11 @@ protected: Sysmem* _sysmem = NULL; - mutable GPUObject* _gpuObject = NULL; // This shouldn't be used by anything else than the Backend class with the proper casting. + mutable GPUObject* _gpuObject = NULL; void setGPUObject(GPUObject* gpuObject) const { _gpuObject = gpuObject; } GPUObject* getGPUObject() const { return _gpuObject; } - friend class Backend; }; diff --git a/libraries/gpu/src/gpu/Shader.cpp b/libraries/gpu/src/gpu/Shader.cpp new file mode 100755 index 0000000000..87365e245e --- /dev/null +++ b/libraries/gpu/src/gpu/Shader.cpp @@ -0,0 +1,67 @@ +// +// Shader.cpp +// libraries/gpu/src/gpu +// +// Created by Sam Gateau on 2/27/2015. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "Shader.h" +#include +#include + +using namespace gpu; + +Shader::Shader(Type type, const Source& source): + _source(source), + _type(type) +{ +} + +Shader::Shader(Type type, Pointer& vertex, Pointer& pixel): + _type(type) +{ + _shaders.resize(2); + _shaders[VERTEX] = vertex; + _shaders[PIXEL] = pixel; +} + + +Shader::~Shader() +{ +} + +/* +Program::Program(): + _storage(), + _type(GRAPHICS) +{ +} + +Program::~Program() +{ +} +*/ + +Shader* Shader::createVertex(const Source& source) { + Shader* shader = new Shader(VERTEX, source); + return shader; +} + +Shader* Shader::createPixel(const Source& source) { + Shader* shader = new Shader(PIXEL, source); + return shader; +} + +Shader* Shader::createProgram(Pointer& vertexShader, Pointer& pixelShader) { + if (vertexShader && vertexShader->getType() == VERTEX) { + if (pixelShader && pixelShader->getType() == PIXEL) { + Shader* shader = new Shader(PROGRAM, vertexShader, pixelShader); + return shader; + } + } + return nullptr; +} diff --git a/libraries/gpu/src/gpu/Shader.h b/libraries/gpu/src/gpu/Shader.h new file mode 100755 index 0000000000..57177639ae --- /dev/null +++ b/libraries/gpu/src/gpu/Shader.h @@ -0,0 +1,126 @@ +// +// Shader.h +// libraries/gpu/src/gpu +// +// Created by Sam Gateau on 2/27/2015. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#ifndef hifi_gpu_Shader_h +#define hifi_gpu_Shader_h + +#include "Resource.h" +#include + +namespace gpu { + +class Shader { +public: + + typedef QSharedPointer< Shader > Pointer; + typedef std::vector< Pointer > Shaders; + + class Source { + public: + enum Language { + GLSL = 0, + }; + + Source() {} + Source(const std::string& code, Language lang = GLSL) : _code(code), _lang(lang) {} + Source(const Source& source) : _code(source._code), _lang(source._lang) {} + virtual ~Source() {} + + virtual const std::string& getCode() const { return _code; } + + protected: + std::string _code; + Language _lang = GLSL; + }; + + enum Type { + VERTEX = 0, + PIXEL, + GEOMETRY, + NUM_DOMAINS, + + PROGRAM, + }; + + static Shader* createVertex(const Source& source); + static Shader* createPixel(const Source& source); + + static Shader* createProgram(Pointer& vertexShader, Pointer& pixelShader); + + + ~Shader(); + + Type getType() const { return _type; } + bool isProgram() const { return getType() > NUM_DOMAINS; } + bool isDomain() const { return getType() < NUM_DOMAINS; } + + const Source& getSource() const { return _source; } + + const Shaders& getShaders() const { return _shaders; } + +protected: + Shader(Type type, const Source& source); + Shader(Type type, Pointer& vertex, Pointer& pixel); + + Shader(const Shader& shader); // deep copy of the sysmem shader + Shader& operator=(const Shader& shader); // deep copy of the sysmem texture + + // Source contains the actual source code or nothing if the shader is a program + Source _source; + + // if shader is composed of sub shaders, here they are + Shaders _shaders; + + // The type of the shader, the master key + Type _type; + + // This shouldn't be used by anything else than the Backend class with the proper casting. + mutable GPUObject* _gpuObject = NULL; + void setGPUObject(GPUObject* gpuObject) const { _gpuObject = gpuObject; } + GPUObject* getGPUObject() const { return _gpuObject; } + friend class Backend; +}; + +typedef Shader::Pointer ShaderPointer; +typedef std::vector< ShaderPointer > Shaders; + +/* +class Program { +public: + + enum Type { + GRAPHICS = 0, + + NUM_TYPES, + }; + + + Program(); + Program(const Program& program); // deep copy of the sysmem shader + Program& operator=(const Program& program); // deep copy of the sysmem texture + ~Program(); + +protected: + Shaders _shaders; + Type _type; + + // This shouldn't be used by anything else than the Backend class with the proper casting. + mutable GPUObject* _gpuObject = NULL; + void setGPUObject(GPUObject* gpuObject) const { _gpuObject = gpuObject; } + GPUObject* getGPUObject() const { return _gpuObject; } + friend class Backend; +}; + +typedef QSharedPointer ShaderPointer; +*/ +}; + + +#endif diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index 61923999b9..3eed52c686 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -207,12 +207,10 @@ protected: Size resize(Type type, const Element& texelFormat, uint16 width, uint16 height, uint16 depth, uint16 numSamples, uint16 numSlices); - mutable GPUObject* _gpuObject = NULL; - // This shouldn't be used by anything else than the Backend class with the proper casting. + mutable GPUObject* _gpuObject = NULL; void setGPUObject(GPUObject* gpuObject) const { _gpuObject = gpuObject; } GPUObject* getGPUObject() const { return _gpuObject; } - friend class Backend; }; diff --git a/libraries/model/src/model/SkyFromAtmosphere.slf b/libraries/model/src/model/SkyFromAtmosphere.slf new file mode 100755 index 0000000000..02036d0d7c --- /dev/null +++ b/libraries/model/src/model/SkyFromAtmosphere.slf @@ -0,0 +1,108 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> + +// +// For licensing information, see http://http.developer.nvidia.com/GPUGems/gpugems_app01.html: +// +// NVIDIA Statement on the Software +// +// The source code provided is freely distributable, so long as the NVIDIA header remains unaltered and user modifications are +// detailed. +// +// No Warranty +// +// THE SOFTWARE AND ANY OTHER MATERIALS PROVIDED BY NVIDIA ON THE ENCLOSED CD-ROM ARE PROVIDED "AS IS." NVIDIA DISCLAIMS ALL +// WARRANTIES, EXPRESS, IMPLIED OR STATUTORY, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF TITLE, MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// +// Limitation of Liability +// +// NVIDIA SHALL NOT BE LIABLE TO ANY USER, DEVELOPER, DEVELOPER'S CUSTOMERS, OR ANY OTHER PERSON OR ENTITY CLAIMING THROUGH OR +// UNDER DEVELOPER FOR ANY LOSS OF PROFITS, INCOME, SAVINGS, OR ANY OTHER CONSEQUENTIAL, INCIDENTAL, SPECIAL, PUNITIVE, DIRECT +// OR INDIRECT DAMAGES (WHETHER IN AN ACTION IN CONTRACT, TORT OR BASED ON A WARRANTY), EVEN IF NVIDIA HAS BEEN ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGES. THESE LIMITATIONS SHALL APPLY NOTWITHSTANDING ANY FAILURE OF THE ESSENTIAL PURPOSE OF ANY +// LIMITED REMEDY. IN NO EVENT SHALL NVIDIA'S AGGREGATE LIABILITY TO DEVELOPER OR ANY OTHER PERSON OR ENTITY CLAIMING THROUGH +// OR UNDER DEVELOPER EXCEED THE AMOUNT OF MONEY ACTUALLY PAID BY DEVELOPER TO NVIDIA FOR THE SOFTWARE OR ANY OTHER MATERIALS. +// + +// +// Atmospheric scattering fragment shader +// +// Author: Sean O'Neil +// +// Copyright (c) 2004 Sean O'Neil +// + +uniform vec3 v3CameraPos; // The camera's current position +uniform vec3 v3InvWavelength; // 1 / pow(wavelength, 4) for the red, green, and blue channels +uniform float fInnerRadius; // The inner (planetary) radius +uniform float fKrESun; // Kr * ESun +uniform float fKmESun; // Km * ESun +uniform float fKr4PI; // Kr * 4 * PI +uniform float fKm4PI; // Km * 4 * PI +uniform float fScale; // 1 / (fOuterRadius - fInnerRadius) +uniform float fScaleDepth; // The scale depth (i.e. the altitude at which the atmosphere's average density is found) +uniform float fScaleOverScaleDepth; // fScale / fScaleDepth + +const int nSamples = 2; +const float fSamples = 2.0; + +uniform vec3 v3LightPos; +uniform float g; +uniform float g2; + +varying vec3 position; + +float scale(float fCos) +{ + float x = 1.0 - fCos; + return fScaleDepth * exp(-0.00287 + x*(0.459 + x*(3.83 + x*(-6.80 + x*5.25)))); +} + +void main (void) +{ + // Get the ray from the camera to the vertex, and its length (which is the far point of the ray passing through the atmosphere) + vec3 v3Pos = position; + vec3 v3Ray = v3Pos - v3CameraPos; + float fFar = length(v3Ray); + v3Ray /= fFar; + + // Calculate the ray's starting position, then calculate its scattering offset + vec3 v3Start = v3CameraPos; + float fHeight = length(v3Start); + float fDepth = exp(fScaleOverScaleDepth * (fInnerRadius - fHeight)); + float fStartAngle = dot(v3Ray, v3Start) / fHeight; + float fStartOffset = fDepth * scale(fStartAngle); + + // Initialize the scattering loop variables + //gl_FrontColor = vec4(0.0, 0.0, 0.0, 0.0); + float fSampleLength = fFar / fSamples; + float fScaledLength = fSampleLength * fScale; + vec3 v3SampleRay = v3Ray * fSampleLength; + vec3 v3SamplePoint = v3Start + v3SampleRay * 0.5; + + // Now loop through the sample rays + vec3 v3FrontColor = vec3(0.0, 0.0, 0.0); + for(int i=0; i +<$VERSION_HEADER$> + +// +// For licensing information, see http://http.developer.nvidia.com/GPUGems/gpugems_app01.html: +// +// NVIDIA Statement on the Software +// +// The source code provided is freely distributable, so long as the NVIDIA header remains unaltered and user modifications are +// detailed. +// +// No Warranty +// +// THE SOFTWARE AND ANY OTHER MATERIALS PROVIDED BY NVIDIA ON THE ENCLOSED CD-ROM ARE PROVIDED "AS IS." NVIDIA DISCLAIMS ALL +// WARRANTIES, EXPRESS, IMPLIED OR STATUTORY, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF TITLE, MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// +// Limitation of Liability +// +// NVIDIA SHALL NOT BE LIABLE TO ANY USER, DEVELOPER, DEVELOPER'S CUSTOMERS, OR ANY OTHER PERSON OR ENTITY CLAIMING THROUGH OR +// UNDER DEVELOPER FOR ANY LOSS OF PROFITS, INCOME, SAVINGS, OR ANY OTHER CONSEQUENTIAL, INCIDENTAL, SPECIAL, PUNITIVE, DIRECT +// OR INDIRECT DAMAGES (WHETHER IN AN ACTION IN CONTRACT, TORT OR BASED ON A WARRANTY), EVEN IF NVIDIA HAS BEEN ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGES. THESE LIMITATIONS SHALL APPLY NOTWITHSTANDING ANY FAILURE OF THE ESSENTIAL PURPOSE OF ANY +// LIMITED REMEDY. IN NO EVENT SHALL NVIDIA'S AGGREGATE LIABILITY TO DEVELOPER OR ANY OTHER PERSON OR ENTITY CLAIMING THROUGH +// OR UNDER DEVELOPER EXCEED THE AMOUNT OF MONEY ACTUALLY PAID BY DEVELOPER TO NVIDIA FOR THE SOFTWARE OR ANY OTHER MATERIALS. +// + +// +// Atmospheric scattering vertex shader +// +// Author: Sean O'Neil +// +// Copyright (c) 2004 Sean O'Neil +// + +uniform vec3 v3CameraPos; // The camera's current position +uniform vec3 v3LightPos; // The direction vector to the light source +uniform vec3 v3InvWavelength; // 1 / pow(wavelength, 4) for the red, green, and blue channels +uniform float fOuterRadius; // The outer (atmosphere) radius +uniform float fInnerRadius; // The inner (planetary) radius +uniform float fKrESun; // Kr * ESun +uniform float fKmESun; // Km * ESun +uniform float fKr4PI; // Kr * 4 * PI +uniform float fKm4PI; // Km * 4 * PI +uniform float fScale; // 1 / (fOuterRadius - fInnerRadius) +uniform float fScaleDepth; // The scale depth (i.e. the altitude at which the atmosphere's average density is found) +uniform float fScaleOverScaleDepth; // fScale / fScaleDepth + + +const int nSamples = 2; +const float fSamples = 2.0; + +varying vec3 position; + + +float scale(float fCos) +{ + float x = 1.0 - fCos; + return fScaleDepth * exp(-0.00287 + x*(0.459 + x*(3.83 + x*(-6.80 + x*5.25)))); +} + +void main(void) +{ + // Get the ray from the camera to the vertex, and its length (which is the far point of the ray passing through the atmosphere) + position = gl_Vertex.xyz * fOuterRadius; + + gl_Position = gl_ModelViewProjectionMatrix * vec4(position, 1.0); +} diff --git a/libraries/model/src/model/SkyFromSpace.slf b/libraries/model/src/model/SkyFromSpace.slf new file mode 100755 index 0000000000..5f6ce80efa --- /dev/null +++ b/libraries/model/src/model/SkyFromSpace.slf @@ -0,0 +1,114 @@ +#version 120 + +// +// For licensing information, see http://http.developer.nvidia.com/GPUGems/gpugems_app01.html: +// +// NVIDIA Statement on the Software +// +// The source code provided is freely distributable, so long as the NVIDIA header remains unaltered and user modifications are +// detailed. +// +// No Warranty +// +// THE SOFTWARE AND ANY OTHER MATERIALS PROVIDED BY NVIDIA ON THE ENCLOSED CD-ROM ARE PROVIDED "AS IS." NVIDIA DISCLAIMS ALL +// WARRANTIES, EXPRESS, IMPLIED OR STATUTORY, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF TITLE, MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// +// Limitation of Liability +// +// NVIDIA SHALL NOT BE LIABLE TO ANY USER, DEVELOPER, DEVELOPER'S CUSTOMERS, OR ANY OTHER PERSON OR ENTITY CLAIMING THROUGH OR +// UNDER DEVELOPER FOR ANY LOSS OF PROFITS, INCOME, SAVINGS, OR ANY OTHER CONSEQUENTIAL, INCIDENTAL, SPECIAL, PUNITIVE, DIRECT +// OR INDIRECT DAMAGES (WHETHER IN AN ACTION IN CONTRACT, TORT OR BASED ON A WARRANTY), EVEN IF NVIDIA HAS BEEN ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGES. THESE LIMITATIONS SHALL APPLY NOTWITHSTANDING ANY FAILURE OF THE ESSENTIAL PURPOSE OF ANY +// LIMITED REMEDY. IN NO EVENT SHALL NVIDIA'S AGGREGATE LIABILITY TO DEVELOPER OR ANY OTHER PERSON OR ENTITY CLAIMING THROUGH +// OR UNDER DEVELOPER EXCEED THE AMOUNT OF MONEY ACTUALLY PAID BY DEVELOPER TO NVIDIA FOR THE SOFTWARE OR ANY OTHER MATERIALS. +// + +// +// Atmospheric scattering fragment shader +// +// Author: Sean O'Neil +// +// Copyright (c) 2004 Sean O'Neil +// + +uniform vec3 v3CameraPos; // The camera's current position +uniform vec3 v3LightPos; // The direction vector to the light source +uniform vec3 v3InvWavelength; // 1 / pow(wavelength, 4) for the red, green, and blue channels +uniform float fCameraHeight2; // fCameraHeight^2 +uniform float fOuterRadius; // The outer (atmosphere) radius +uniform float fOuterRadius2; // fOuterRadius^2 +uniform float fInnerRadius; // The inner (planetary) radius +uniform float fKrESun; // Kr * ESun +uniform float fKmESun; // Km * ESun +uniform float fKr4PI; // Kr * 4 * PI +uniform float fKm4PI; // Km * 4 * PI +uniform float fScale; // 1 / (fOuterRadius - fInnerRadius) +uniform float fScaleDepth; // The scale depth (i.e. the altitude at which the atmosphere's average density is found) +uniform float fScaleOverScaleDepth; // fScale / fScaleDepth + +uniform float g; +uniform float g2; + +const int nSamples = 2; +const float fSamples = 2.0; + +varying vec3 position; + +float scale(float fCos) +{ + float x = 1.0 - fCos; + return fScaleDepth * exp(-0.00287 + x*(0.459 + x*(3.83 + x*(-6.80 + x*5.25)))); +} + + +void main (void) +{ + // Get the ray from the camera to the vertex and its length (which is the far point of the ray passing through the atmosphere) + vec3 v3Pos = position; + vec3 v3Ray = v3Pos - v3CameraPos; + float fFar = length(v3Ray); + v3Ray /= fFar; + + // Calculate the closest intersection of the ray with the outer atmosphere (which is the near point of the ray passing through the atmosphere) + float B = 2.0 * dot(v3CameraPos, v3Ray); + float C = fCameraHeight2 - fOuterRadius2; + float fDet = max(0.0, B*B - 4.0 * C); + float fNear = 0.5 * (-B - sqrt(fDet)); + + // Calculate the ray's starting position, then calculate its scattering offset + vec3 v3Start = v3CameraPos + v3Ray * fNear; + fFar -= fNear; + float fStartAngle = dot(v3Ray, v3Start) / fOuterRadius; + float fStartDepth = exp(-1.0 / fScaleDepth); + float fStartOffset = fStartDepth * scale(fStartAngle); + + // Initialize the scattering loop variables + //gl_FrontColor = vec4(0.0, 0.0, 0.0, 0.0); + float fSampleLength = fFar / fSamples; + float fScaledLength = fSampleLength * fScale; + vec3 v3SampleRay = v3Ray * fSampleLength; + vec3 v3SamplePoint = v3Start + v3SampleRay * 0.5; + + // Now loop through the sample rays + vec3 v3FrontColor = vec3(0.0, 0.0, 0.0); + for(int i=0; i #include +#include "SkyFromAtmosphere_vert.h" +#include "SkyFromAtmosphere_frag.h" + using namespace model; @@ -150,6 +153,13 @@ SunSkyStage::SunSkyStage() : setDayTime(12.0f); // Begining of march setYearTime(60.0f); + + _skyShader = gpu::ShaderPointer( + gpu::Shader::createProgram( + gpu::ShaderPointer(gpu::Shader::createVertex(std::string(SkyFromAtmosphere_vert))), + gpu::ShaderPointer(gpu::Shader::createPixel(std::string(SkyFromAtmosphere_frag))) + ) + ); } SunSkyStage::~SunSkyStage() { diff --git a/libraries/model/src/model/Stage.h b/libraries/model/src/model/Stage.h index 762e2d9717..4de0edb96b 100644 --- a/libraries/model/src/model/Stage.h +++ b/libraries/model/src/model/Stage.h @@ -11,6 +11,8 @@ #ifndef hifi_model_Stage_h #define hifi_model_Stage_h +#include "gpu/Shader.h" + #include "Light.h" namespace model { @@ -143,6 +145,8 @@ public: protected: LightPointer _sunLight; + gpu::ShaderPointer _skyShader; + float _dayTime; int _yearTime; From cb737d64d3a858f45e6a2a58978e9ddd4e0fff07 Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Sun, 1 Mar 2015 15:20:32 -0800 Subject: [PATCH 02/43] Testing the gpu::Shader compilation and building steps, ready to be used for real --- libraries/gpu/src/gpu/GLBackend.cpp | 14 ++- libraries/gpu/src/gpu/GLBackend.h | 4 +- libraries/gpu/src/gpu/GLBackendShader.cpp | 120 ++++++++++----------- libraries/gpu/src/gpu/GLBackendTexture.cpp | 11 +- libraries/model/src/model/Stage.cpp | 4 + 5 files changed, 78 insertions(+), 75 deletions(-) diff --git a/libraries/gpu/src/gpu/GLBackend.cpp b/libraries/gpu/src/gpu/GLBackend.cpp index fd91e0df0c..64bcb006a5 100644 --- a/libraries/gpu/src/gpu/GLBackend.cpp +++ b/libraries/gpu/src/gpu/GLBackend.cpp @@ -1015,11 +1015,11 @@ GLBackend::GLBuffer::~GLBuffer() { } } -void GLBackend::syncGPUObject(const Buffer& buffer) { +GLBackend::GLBuffer* GLBackend::syncGPUObject(const Buffer& buffer) { GLBuffer* object = Backend::getGPUObject(buffer); if (object && (object->_stamp == buffer.getSysmem().getStamp())) { - return; + return object; } // need to have a gpu object? @@ -1040,12 +1040,18 @@ void GLBackend::syncGPUObject(const Buffer& buffer) { object->_size = buffer.getSysmem().getSize(); //} CHECK_GL_ERROR(); + + return object; } GLuint GLBackend::getBufferID(const Buffer& buffer) { - GLBackend::syncGPUObject(buffer); - return Backend::getGPUObject(buffer)->_buffer; + GLBuffer* bo = GLBackend::syncGPUObject(buffer); + if (bo) { + return bo->_buffer; + } else { + return 0; + } } diff --git a/libraries/gpu/src/gpu/GLBackend.h b/libraries/gpu/src/gpu/GLBackend.h index 161ce1dad6..81271851bb 100644 --- a/libraries/gpu/src/gpu/GLBackend.h +++ b/libraries/gpu/src/gpu/GLBackend.h @@ -44,7 +44,7 @@ public: GLBuffer(); ~GLBuffer(); }; - static void syncGPUObject(const Buffer& buffer); + static GLBuffer* syncGPUObject(const Buffer& buffer); static GLuint getBufferID(const Buffer& buffer); class GLTexture : public GPUObject { @@ -57,7 +57,7 @@ public: GLTexture(); ~GLTexture(); }; - static void syncGPUObject(const Texture& texture); + static GLTexture* syncGPUObject(const Texture& texture); static GLuint getTextureID(const TexturePointer& texture); class GLShader : public GPUObject { diff --git a/libraries/gpu/src/gpu/GLBackendShader.cpp b/libraries/gpu/src/gpu/GLBackendShader.cpp index e37643e2f2..4d84d33a5f 100755 --- a/libraries/gpu/src/gpu/GLBackendShader.cpp +++ b/libraries/gpu/src/gpu/GLBackendShader.cpp @@ -25,12 +25,12 @@ GLBackend::GLShader::~GLShader() { } } -bool compileShader(const Shader& shader, GLBackend::GLShader& object) { +GLBackend::GLShader* compileShader(const Shader& shader) { // Any GLSLprogram ? normally yes... const std::string& shaderSource = shader.getSource().getCode(); if (shaderSource.empty()) { qDebug() << "GLShader::compileShader - no GLSL shader source code ? so failed to create"; - return false; + return nullptr; } // Shader domain @@ -40,8 +40,8 @@ bool compileShader(const Shader& shader, GLBackend::GLShader& object) { // Create the shader object GLuint glshader = glCreateShader(shaderDomain); if (!glshader) { - qDebug() << "GLShader::compileShader - failed to create the gl shader & gl program object"; - return false; + qDebug() << "GLShader::compileShader - failed to create the gl shader object"; + return nullptr; } // Assign the source @@ -56,13 +56,11 @@ bool compileShader(const Shader& shader, GLBackend::GLShader& object) { glGetShaderiv(glshader, GL_COMPILE_STATUS, &compiled); // if compilation fails - if (!compiled) - { + if (!compiled) { // save the source code to a temp file so we can debug easily /* std::ofstream filestream; - filestream.open( "debugshader.glsl" ); - if ( filestream.is_open() ) - { + filestream.open("debugshader.glsl"); + if (filestream.is_open()) { filestream << shaderSource->source; filestream.close(); } @@ -72,30 +70,31 @@ bool compileShader(const Shader& shader, GLBackend::GLShader& object) { glGetShaderiv(glshader, GL_INFO_LOG_LENGTH, &infoLength); char* temp = new char[infoLength] ; - glGetShaderInfoLog( glshader, infoLength, NULL, temp); + glGetShaderInfoLog(glshader, infoLength, NULL, temp); qDebug() << "GLShader::compileShader - failed to compile the gl shader object:"; qDebug() << temp; /* - filestream.open( "debugshader.glsl.info.txt" ); - if ( filestream.is_open() ) - { - filestream << String( temp ); + filestream.open("debugshader.glsl.info.txt"); + if (filestream.is_open()) { + filestream << std::string(temp); filestream.close(); } */ delete[] temp; - glDeleteShader( glshader); - return false; + glDeleteShader(glshader); + return nullptr; } + GLuint glprogram = 0; +#ifdef SEPARATE_PROGRAM // so far so good, program is almost done, need to link: GLuint glprogram = glCreateProgram(); if (!glprogram) { qDebug() << "GLShader::compileShader - failed to create the gl shader & gl program object"; - return false; + return nullptr; } glProgramParameteri(glprogram, GL_PROGRAM_SEPARABLE, GL_TRUE); @@ -105,53 +104,52 @@ bool compileShader(const Shader& shader, GLBackend::GLShader& object) { GLint linked = 0; glGetProgramiv(glprogram, GL_LINK_STATUS, &linked); - if (!linked) - { + if (!linked) { /* // save the source code to a temp file so we can debug easily std::ofstream filestream; - filestream.open( "debugshader.glsl" ); - if ( filestream.is_open() ) - { + filestream.open("debugshader.glsl"); + if (filestream.is_open()) { filestream << shaderSource->source; filestream.close(); } */ GLint infoLength = 0; - glGetProgramiv( glprogram, GL_INFO_LOG_LENGTH, &infoLength ); + glGetProgramiv(glprogram, GL_INFO_LOG_LENGTH, &infoLength); char* temp = new char[infoLength] ; - glGetProgramInfoLog( glprogram, infoLength, NULL, temp); + glGetProgramInfoLog(glprogram, infoLength, NULL, temp); qDebug() << "GLShader::compileShader - failed to LINK the gl program object :"; qDebug() << temp; /* - filestream.open( "debugshader.glsl.info.txt" ); - if ( filestream.is_open() ) - { - filestream << String( temp ); + filestream.open("debugshader.glsl.info.txt"); + if (filestream.is_open()) { + filestream << String(temp); filestream.close(); } */ delete[] temp; - glDeleteShader( glshader); - glDeleteProgram( glprogram); - return false; + glDeleteShader(glshader); + glDeleteProgram(glprogram); + return nullptr; } +#endif // So far so good, the shader is created successfully - object._shader = glshader; - object._program = glprogram; + GLBackend::GLShader* object = new GLBackend::GLShader(); + object->_shader = glshader; + object->_program = glprogram; - return true; + return object; } -bool compileProgram(const Shader& program, GLBackend::GLShader& object) { +GLBackend::GLShader* compileProgram(const Shader& program) { if(!program.isProgram()) { - return false; + return nullptr; } // Let's go through every shaders and make sure they are ready to go @@ -160,7 +158,7 @@ bool compileProgram(const Shader& program, GLBackend::GLShader& object) { GLuint so = GLBackend::getShaderID(subShader); if (!so) { qDebug() << "GLShader::compileProgram - One of the shaders of the program is not compiled?"; - return false; + return nullptr; } shaderObjects.push_back(so); } @@ -169,7 +167,7 @@ bool compileProgram(const Shader& program, GLBackend::GLShader& object) { GLuint glprogram = glCreateProgram(); if (!glprogram) { qDebug() << "GLShader::compileProgram - failed to create the gl program object"; - return false; + return nullptr; } // glProgramParameteri(glprogram, GL_PROGRAM_, GL_TRUE); @@ -184,47 +182,45 @@ bool compileProgram(const Shader& program, GLBackend::GLShader& object) { GLint linked = 0; glGetProgramiv(glprogram, GL_LINK_STATUS, &linked); - if (!linked) - { + if (!linked) { /* // save the source code to a temp file so we can debug easily std::ofstream filestream; - filestream.open( "debugshader.glsl" ); - if ( filestream.is_open() ) - { + filestream.open("debugshader.glsl"); + if (filestream.is_open()) { filestream << shaderSource->source; filestream.close(); } */ GLint infoLength = 0; - glGetProgramiv( glprogram, GL_INFO_LOG_LENGTH, &infoLength ); + glGetProgramiv(glprogram, GL_INFO_LOG_LENGTH, &infoLength); char* temp = new char[infoLength] ; - glGetProgramInfoLog( glprogram, infoLength, NULL, temp); + glGetProgramInfoLog(glprogram, infoLength, NULL, temp); qDebug() << "GLShader::compileProgram - failed to LINK the gl program object :"; qDebug() << temp; /* - filestream.open( "debugshader.glsl.info.txt" ); - if ( filestream.is_open() ) - { - filestream << String( temp ); + filestream.open("debugshader.glsl.info.txt"); + if (filestream.is_open()) { + filestream << std::string(temp); filestream.close(); } */ delete[] temp; - glDeleteProgram( glprogram); - return false; + glDeleteProgram(glprogram); + return nullptr; } // So far so good, the program is created successfully - object._shader = 0; - object._program = glprogram; + GLBackend::GLShader* object = new GLBackend::GLShader(); + object->_shader = 0; + object->_program = glprogram; - return true; + return object; } GLBackend::GLShader* GLBackend::syncGPUObject(const Shader& shader) { @@ -234,20 +230,17 @@ GLBackend::GLShader* GLBackend::syncGPUObject(const Shader& shader) { if (object) { return object; } - // need to have a gpu object? - - // GO through the process of allocating the correct storage and/or update the content if (shader.isProgram()) { - GLShader tempObject; - if (compileProgram(shader, tempObject)) { - object = new GLShader(tempObject); + GLShader* tempObject = compileProgram(shader); + if (tempObject) { + object = tempObject; Backend::setGPUObject(shader, object); } } else if (shader.isDomain()) { - GLShader tempObject; - if (compileShader(shader, tempObject)) { - object = new GLShader(tempObject); + GLShader* tempObject = compileShader(shader); + if (tempObject) { + object = tempObject; Backend::setGPUObject(shader, object); } } @@ -256,7 +249,6 @@ GLBackend::GLShader* GLBackend::syncGPUObject(const Shader& shader) { } - GLuint GLBackend::getShaderID(const ShaderPointer& shader) { if (!shader) { return 0; diff --git a/libraries/gpu/src/gpu/GLBackendTexture.cpp b/libraries/gpu/src/gpu/GLBackendTexture.cpp index 0b2dffd6f7..6875abbc33 100755 --- a/libraries/gpu/src/gpu/GLBackendTexture.cpp +++ b/libraries/gpu/src/gpu/GLBackendTexture.cpp @@ -223,7 +223,7 @@ public: }; -void GLBackend::syncGPUObject(const Texture& texture) { +GLBackend::GLTexture* GLBackend::syncGPUObject(const Texture& texture) { GLTexture* object = Backend::getGPUObject(texture); // If GPU object already created and in sync @@ -232,14 +232,14 @@ void GLBackend::syncGPUObject(const Texture& texture) { // If gpu object info is in sync with sysmem version if (object->_contentStamp >= texture.getDataStamp()) { // Then all good, GPU object is ready to be used - return; + return object; } else { // Need to update the content of the GPU object from the source sysmem of the texture needUpdate = true; } } else if (!texture.isDefined()) { // NO texture definition yet so let's avoid thinking - return; + return nullptr; } // need to have a gpu object? @@ -320,6 +320,8 @@ void GLBackend::syncGPUObject(const Texture& texture) { qDebug() << "GLBackend::syncGPUObject(const Texture&) case for Texture Type " << texture.getType() << " not supported"; } CHECK_GL_ERROR(); + + return object; } @@ -328,8 +330,7 @@ GLuint GLBackend::getTextureID(const TexturePointer& texture) { if (!texture) { return 0; } - GLBackend::syncGPUObject(*texture); - GLTexture* object = Backend::getGPUObject(*texture); + GLTexture* object = GLBackend::syncGPUObject(*texture); if (object) { return object->_texture; } else { diff --git a/libraries/model/src/model/Stage.cpp b/libraries/model/src/model/Stage.cpp index 10dea9ab3f..9a68779caf 100644 --- a/libraries/model/src/model/Stage.cpp +++ b/libraries/model/src/model/Stage.cpp @@ -15,6 +15,7 @@ #include "SkyFromAtmosphere_vert.h" #include "SkyFromAtmosphere_frag.h" +#include "gpu/GLBackend.h" using namespace model; @@ -215,5 +216,8 @@ void SunSkyStage::updateGraphicsObject() const { double originAlt = _earthSunModel.getAltitude(); _sunLight->setPosition(Vec3(0.0f, originAlt, 0.0f)); + GLuint program = gpu::GLBackend::getShaderID(_skyShader); + + } From 3d558dae6435c0749bd84c51571bd0d94c627f54 Mon Sep 17 00:00:00 2001 From: Virendra Singh Date: Tue, 3 Mar 2015 01:51:35 +0530 Subject: [PATCH 03/43] MassProperties of a 3D mesh --- libraries/physics/src/MassProperties.cpp | 195 +++++++++++++++++++++++ libraries/physics/src/MassProperties.h | 60 +++++++ 2 files changed, 255 insertions(+) create mode 100644 libraries/physics/src/MassProperties.cpp create mode 100644 libraries/physics/src/MassProperties.h diff --git a/libraries/physics/src/MassProperties.cpp b/libraries/physics/src/MassProperties.cpp new file mode 100644 index 0000000000..ec4066ce9f --- /dev/null +++ b/libraries/physics/src/MassProperties.cpp @@ -0,0 +1,195 @@ +// +// MassProperties.cpp +// libraries/physics/src +// +// Created by Virendra Singh 2015.02.28 +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "MassProperties.h" +using namespace massproperties; + +Tetrahedron::Tetrahedron(Vertex p1, Vertex p2, Vertex p3, Vertex p4) :\ +_w(p1), +_x(p2), +_y(p3), +_z(p4){ + + computeVolumeAndInertia(); +} +Tetrahedron::~Tetrahedron(){ + +} + +Vertex Tetrahedron::getX(){ + return _x; +} + +Vertex Tetrahedron::getY(){ + return _y; +} +Vertex Tetrahedron::getZ(){ + return _z; +} + +Vertex Tetrahedron::getw(){ + return _w; +} + +Vertex Tetrahedron::getCentroid(){ + Vertex com; + com.x = (_x.x + _y.x + _z.x + _w.x) / 4.0f; + com.y = (_x.y + _y.y + _z.y + _w.y) / 4.0f; + com.z = (_x.z + _y.z + _z.z + _w.z) / 4.0f; + return com; +} + +vector Tetrahedron::getVolumeAndInertia(){ + return _volumeAndInertia; +} + +void Tetrahedron::computeVolumeAndInertia(){ + double A = glm::distance2(_w, _x); + double B = glm::distance2(_w, _y); + double C = glm::distance2(_x, _y); + double a = glm::distance2(_y, _z); + double b = glm::distance2(_x, _z); + double c = glm::distance2(_w, _z); + double squaredVol = (4 * a * b * c) - (a*glm::pow((b + c - A), 2.0)) - (b*glm::pow((c + a - B), 2.0)) - + (c*glm::pow((a + b - C), 2.0)) + ((a + b - C)*(a + c - B)*(b + c - A)); + + double volume = glm::sqrt(squaredVol);// volume of tetrahedron + _volumeAndInertia.push_back(volume); + + //centroid is used for calculating inertia tensor relative to center of mass. + // translatw the tetrahedron to its center of mass using parallel axis theorem + Vertex com = getCentroid(); + Vertex p0 = _w - com; + Vertex p1 = _x - com; + Vertex p2 = _y - com; + Vertex p3 = _z - com; + + //Calculate inertia tensor based on Tonon's Formulae given in the paper mentioned below. + //http://docsdrive.com/pdfs/sciencepublications/jmssp/2005/8-11.pdf + //Explicit exact formulas for the 3-D tetrahedron inertia tensor in terms of its vertex coordinates - F.Tonon + + double inertia_a = (volume * 6.0 / 60.0) * ( + p0.y*p0.y + p0.y*p1.y + p0.y*p2.y + p0.y*p3.y + + p1.y*p1.y + p1.y*p2.y + p1.y*p3.y + + p2.y*p2.y + p2.y*p3.y + + p3.y*p3.y + + p0.z*p0.z + p0.z*p1.z + p0.z*p2.z + p0.z*p3.z + + p1.z*p1.z + p1.z*p2.z + p1.z*p3.z + + p2.z*p2.z + p2.z*p3.z + + p3.z*p3.z); + _volumeAndInertia.push_back(inertia_a); + + double inertia_b = (volume * 6.0 / 60.0) * ( + p0.x*p0.x + p0.x*p1.x + p0.x*p2.x + p0.x*p3.x + + p1.x*p1.x + p1.x*p2.x + p1.x*p3.x + + p2.x*p2.x + p2.x*p3.x + + p3.x*p3.x + + p0.z*p0.z + p0.z*p1.z + p0.z*p2.z + p0.z*p3.z + + p1.z*p1.z + p1.z*p2.z + p1.z*p3.z + + p2.z*p2.z + p2.z*p3.z + + p3.z*p3.z); + _volumeAndInertia.push_back(inertia_b); + + double inertia_c = (volume * 6.0 / 60.0) * ( + p0.x*p0.x + p0.x*p1.x + p0.x*p2.x + p0.x*p3.x + + p1.x*p1.x + p1.x*p2.x + p1.x*p3.x + + p2.x*p2.x + p2.x*p3.x + + p3.x*p3.x + + p0.y*p0.y + p0.y*p1.y + p0.y*p2.y + p0.y*p3.y + + p1.y*p1.y + p1.y*p2.y + p1.y*p3.y + + p2.y*p2.y + p2.y*p3.y + + p3.y*p3.y); + _volumeAndInertia.push_back(inertia_c); + + double inertia_aa = (volume * 6.0 / 60.0) * (2.0 * (p0.y*p0.z + p1.y*p1.z + p2.y*p2.z + p3.y*p3.z) + + p0.y*p1.z + p0.y*p2.z + p0.y*p3.z + + p1.y*p0.z + p1.y*p2.z + p1.y*p3.z + + p2.y*p0.z + p2.y*p1.z + p2.y*p3.z + + p3.y*p0.z + p3.y*p1.z + p3.y*p2.z); + _volumeAndInertia.push_back(inertia_aa); + + double inertia_bb = (volume * 6.0 / 60.0) * (2.0 * (p0.x*p0.z + p1.x*p1.z + p2.x*p2.z + p3.x*p3.z) + + p0.x*p1.z + p0.x*p2.z + p0.x*p3.z + + p1.x*p0.z + p1.x*p2.z + p1.x*p3.z + + p2.x*p0.z + p2.x*p1.z + p2.x*p3.z + + p3.x*p0.z + p3.x*p1.z + p3.x*p2.z); + _volumeAndInertia.push_back(inertia_bb); + + double inertia_cc = (volume * 6.0 / 60.0) * (2.0 * (p0.x*p0.y + p1.x*p1.y + p2.x*p2.y + p3.x*p3.y) + + p0.x*p1.y + p0.x*p2.y + p0.x*p3.y + + p1.x*p0.y + p1.x*p2.y + p1.x*p3.y + + p2.x*p0.y + p2.x*p1.y + p2.x*p3.y + + p3.x*p0.y + p3.x*p1.y + p3.x*p2.y); + _volumeAndInertia.push_back(inertia_cc); +} + +//class to compute volume, mass, center of mass, and inertia tensor of a mesh. +//origin is the default reference point for generating the tetrahedron from each triangle of the mesh. We can provide another reference +//point by passing it as 3rd parameter to the constructor + +MassProperties::MassProperties(vector *vertices, Triangle *triangles, Vertex referencepoint = glm::vec3(0.0,0.0,0.0)):\ +_vertices(vertices), +_triangles(triangles), +_referencePoint(referencepoint), +_trianglesCount(0), +_tetrahedraCount(0), +_verticesCount(0){ + + if (_triangles){ + _trianglesCount = _triangles->size() / 3; + } + + if (_vertices){ + _verticesCount = _vertices->size(); + } + generateTetrahedra(); +} + +MassProperties::~MassProperties(){ + if (_vertices){ + _vertices->clear(); + } + if (_triangles){ + _triangles->clear(); + } + delete _vertices; + delete _triangles; +} + +void MassProperties::generateTetrahedra(){ + for (int i = 0; i < _trianglesCount * 3; i += 3){ + Vertex p1 = _vertices->at(_triangles->at(i)); + Vertex p2 = _vertices->at(_triangles->at(i + 1)); + Vertex p3 = _vertices->at(_triangles->at(i + 2)); + Tetrahedron t(_referencePoint, p1, p2, p3); + _tetrahedra.push_back(t); + } +} + +int MassProperties::getTriangleCount() const{ + return _trianglesCount; +} + +int MassProperties::getVerticesCount() const{ + return _verticesCount; +} + +int MassProperties::getTetrahedraCount() const{ + return _tetrahedra.size(); +} + +vector MassProperties::getTetrahedra() const{ + return _tetrahedra; +} +vector MassProperties::getVolumeAndInertia(){ + vector volumeAndInertia; + return volumeAndInertia; +} \ No newline at end of file diff --git a/libraries/physics/src/MassProperties.h b/libraries/physics/src/MassProperties.h new file mode 100644 index 0000000000..f5a2f0a0c7 --- /dev/null +++ b/libraries/physics/src/MassProperties.h @@ -0,0 +1,60 @@ +// +// MassProperties.h +// libraries/physics/src +// +// Created by Virendra Singh 2015.02.28 +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include +#include +#include +#include +using namespace std; +namespace massproperties{ + typedef glm::vec3 Vertex; + typedef vector Triangle; + + //Tetrahedron class containing the base triangle and the apex. + class Tetrahedron{ + private: + Vertex _w; //apex + Vertex _x; + Vertex _y; + Vertex _z; + vector _volumeAndInertia; + public: + Tetrahedron(Vertex p1, Vertex p2, Vertex p3, Vertex p4); + ~Tetrahedron(); + Vertex getX(); + Vertex getY(); + Vertex getZ(); + Vertex getw(); + Vertex getCentroid(); + void computeVolumeAndInertia(); + vector getVolumeAndInertia(); + }; + + class MassProperties{ + private: + int _trianglesCount; + int _tetrahedraCount; + int _verticesCount; + vector *_vertices; + Vertex _referencePoint; + Triangle *_triangles; + vector _tetrahedra; + void generateTetrahedra(); + public: + MassProperties(vector *vertices, Triangle *triangles, Vertex refewrencepoint); + ~MassProperties(); + int getTriangleCount() const; + int getVerticesCount() const; + int getTetrahedraCount() const; + vector getTetrahedra() const; + vector getVolumeAndInertia(); + }; +} \ No newline at end of file From 7a129235c26af553f0fe5991e493b32e5516f6e4 Mon Sep 17 00:00:00 2001 From: Virendra Singh Date: Wed, 4 Mar 2015 01:12:20 +0530 Subject: [PATCH 04/43] Mass properties unit tests --- libraries/physics/src/MassProperties.cpp | 279 ++++++++++++---------- libraries/physics/src/MassProperties.h | 89 +++---- tests/physics/src/MassPropertiesTests.cpp | 133 +++++++++++ tests/physics/src/MassPropertiesTests.h | 20 ++ tests/physics/src/main.cpp | 14 +- 5 files changed, 369 insertions(+), 166 deletions(-) create mode 100644 tests/physics/src/MassPropertiesTests.cpp create mode 100644 tests/physics/src/MassPropertiesTests.h diff --git a/libraries/physics/src/MassProperties.cpp b/libraries/physics/src/MassProperties.cpp index ec4066ce9f..4d2e668f95 100644 --- a/libraries/physics/src/MassProperties.cpp +++ b/libraries/physics/src/MassProperties.cpp @@ -12,123 +12,119 @@ #include "MassProperties.h" using namespace massproperties; -Tetrahedron::Tetrahedron(Vertex p1, Vertex p2, Vertex p3, Vertex p4) :\ +Tetrahedron::Tetrahedron(const Vertex p1, const Vertex p2, const Vertex p3, const Vertex p4) :\ _w(p1), _x(p2), _y(p3), _z(p4){ - - computeVolumeAndInertia(); + computeVolume(); + computeInertia(); } Tetrahedron::~Tetrahedron(){ } -Vertex Tetrahedron::getX(){ - return _x; +Vertex Tetrahedron::getX() const{ + return _x; } -Vertex Tetrahedron::getY(){ - return _y; +Vertex Tetrahedron::getY() const{ + return _y; } -Vertex Tetrahedron::getZ(){ - return _z; +Vertex Tetrahedron::getZ() const{ + return _z; } -Vertex Tetrahedron::getw(){ - return _w; +Vertex Tetrahedron::getw() const{ + return _w; } -Vertex Tetrahedron::getCentroid(){ - Vertex com; - com.x = (_x.x + _y.x + _z.x + _w.x) / 4.0f; - com.y = (_x.y + _y.y + _z.y + _w.y) / 4.0f; - com.z = (_x.z + _y.z + _z.z + _w.z) / 4.0f; - return com; +Vertex Tetrahedron::getCentroid() const{ + Vertex com; + com.x = (_x.x + _y.x + _z.x + _w.x) / 4.0f; + com.y = (_x.y + _y.y + _z.y + _w.y) / 4.0f; + com.z = (_x.z + _y.z + _z.z + _w.z) / 4.0f; + return com; } -vector Tetrahedron::getVolumeAndInertia(){ - return _volumeAndInertia; +vector Tetrahedron::getVolumeAndInertia() const{ + return _volumeAndInertia; } -void Tetrahedron::computeVolumeAndInertia(){ - double A = glm::distance2(_w, _x); - double B = glm::distance2(_w, _y); - double C = glm::distance2(_x, _y); - double a = glm::distance2(_y, _z); - double b = glm::distance2(_x, _z); - double c = glm::distance2(_w, _z); - double squaredVol = (4 * a * b * c) - (a*glm::pow((b + c - A), 2.0)) - (b*glm::pow((c + a - B), 2.0)) - - (c*glm::pow((a + b - C), 2.0)) + ((a + b - C)*(a + c - B)*(b + c - A)); +void Tetrahedron::computeVolume(){ + glm::mat4 tet = { glm::vec4(_x.x, _y.x, _z.x, _w.x), glm::vec4(_x.y, _y.y, _z.y, _w.y), glm::vec4(_x.z, _y.z, _z.z, _w.z), glm::vec4(1.0f, 1.0f, 1.0f, 1.0f) }; + _volume = glm::determinant(tet) / 6.0f; + _volumeAndInertia.push_back(_volume); + std::cout << "volume : " << _volume << std::endl; +} - double volume = glm::sqrt(squaredVol);// volume of tetrahedron - _volumeAndInertia.push_back(volume); +void Tetrahedron::computeInertia(){ + + //centroid is used for calculating inertia tensor relative to center of mass. + // translate the tetrahedron to its center of mass using P = P - centroid + Vertex com = getCentroid(); + Vertex p0 = _w - com; + Vertex p1 = _x - com; + Vertex p2 = _y - com; + Vertex p3 = _z - com; - //centroid is used for calculating inertia tensor relative to center of mass. - // translatw the tetrahedron to its center of mass using parallel axis theorem - Vertex com = getCentroid(); - Vertex p0 = _w - com; - Vertex p1 = _x - com; - Vertex p2 = _y - com; - Vertex p3 = _z - com; + //Calculate inertia tensor based on Tonon's Formulae given in the paper mentioned below. + //http://docsdrive.com/pdfs/sciencepublications/jmssp/2005/8-11.pdf + //Explicit exact formulas for the 3-D tetrahedron inertia tensor in terms of its vertex coordinates - F.Tonon - //Calculate inertia tensor based on Tonon's Formulae given in the paper mentioned below. - //http://docsdrive.com/pdfs/sciencepublications/jmssp/2005/8-11.pdf - //Explicit exact formulas for the 3-D tetrahedron inertia tensor in terms of its vertex coordinates - F.Tonon + double inertia_a = (_volume * 6.0 / 60.0) * ( + p0.y*p0.y + p0.y*p1.y + p0.y*p2.y + p0.y*p3.y + + p1.y*p1.y + p1.y*p2.y + p1.y*p3.y + + p2.y*p2.y + p2.y*p3.y + + p3.y*p3.y + + p0.z*p0.z + p0.z*p1.z + p0.z*p2.z + p0.z*p3.z + + p1.z*p1.z + p1.z*p2.z + p1.z*p3.z + + p2.z*p2.z + p2.z*p3.z + + p3.z*p3.z); + _volumeAndInertia.push_back(inertia_a); - double inertia_a = (volume * 6.0 / 60.0) * ( - p0.y*p0.y + p0.y*p1.y + p0.y*p2.y + p0.y*p3.y + - p1.y*p1.y + p1.y*p2.y + p1.y*p3.y + - p2.y*p2.y + p2.y*p3.y + - p3.y*p3.y + - p0.z*p0.z + p0.z*p1.z + p0.z*p2.z + p0.z*p3.z + - p1.z*p1.z + p1.z*p2.z + p1.z*p3.z + - p2.z*p2.z + p2.z*p3.z + - p3.z*p3.z); - _volumeAndInertia.push_back(inertia_a); + double inertia_b = (_volume * 6.0 / 60.0) * ( + p0.x*p0.x + p0.x*p1.x + p0.x*p2.x + p0.x*p3.x + + p1.x*p1.x + p1.x*p2.x + p1.x*p3.x + + p2.x*p2.x + p2.x*p3.x + + p3.x*p3.x + + p0.z*p0.z + p0.z*p1.z + p0.z*p2.z + p0.z*p3.z + + p1.z*p1.z + p1.z*p2.z + p1.z*p3.z + + p2.z*p2.z + p2.z*p3.z + + p3.z*p3.z); + _volumeAndInertia.push_back(inertia_b); - double inertia_b = (volume * 6.0 / 60.0) * ( - p0.x*p0.x + p0.x*p1.x + p0.x*p2.x + p0.x*p3.x + - p1.x*p1.x + p1.x*p2.x + p1.x*p3.x + - p2.x*p2.x + p2.x*p3.x + - p3.x*p3.x + - p0.z*p0.z + p0.z*p1.z + p0.z*p2.z + p0.z*p3.z + - p1.z*p1.z + p1.z*p2.z + p1.z*p3.z + - p2.z*p2.z + p2.z*p3.z + - p3.z*p3.z); - _volumeAndInertia.push_back(inertia_b); + double inertia_c = (_volume * 6.0 / 60.0) * ( + p0.x*p0.x + p0.x*p1.x + p0.x*p2.x + p0.x*p3.x + + p1.x*p1.x + p1.x*p2.x + p1.x*p3.x + + p2.x*p2.x + p2.x*p3.x + + p3.x*p3.x + + p0.y*p0.y + p0.y*p1.y + p0.y*p2.y + p0.y*p3.y + + p1.y*p1.y + p1.y*p2.y + p1.y*p3.y + + p2.y*p2.y + p2.y*p3.y + + p3.y*p3.y); + _volumeAndInertia.push_back(inertia_c); - double inertia_c = (volume * 6.0 / 60.0) * ( - p0.x*p0.x + p0.x*p1.x + p0.x*p2.x + p0.x*p3.x + - p1.x*p1.x + p1.x*p2.x + p1.x*p3.x + - p2.x*p2.x + p2.x*p3.x + - p3.x*p3.x + - p0.y*p0.y + p0.y*p1.y + p0.y*p2.y + p0.y*p3.y + - p1.y*p1.y + p1.y*p2.y + p1.y*p3.y + - p2.y*p2.y + p2.y*p3.y + - p3.y*p3.y); - _volumeAndInertia.push_back(inertia_c); + double inertia_aa = (_volume * 6.0 / 120.0) * (2.0 * (p0.y*p0.z + p1.y*p1.z + p2.y*p2.z + p3.y*p3.z) + + p0.y*p1.z + p0.y*p2.z + p0.y*p3.z + + p1.y*p0.z + p1.y*p2.z + p1.y*p3.z + + p2.y*p0.z + p2.y*p1.z + p2.y*p3.z + + p3.y*p0.z + p3.y*p1.z + p3.y*p2.z); + _volumeAndInertia.push_back(inertia_aa); - double inertia_aa = (volume * 6.0 / 60.0) * (2.0 * (p0.y*p0.z + p1.y*p1.z + p2.y*p2.z + p3.y*p3.z) + - p0.y*p1.z + p0.y*p2.z + p0.y*p3.z + - p1.y*p0.z + p1.y*p2.z + p1.y*p3.z + - p2.y*p0.z + p2.y*p1.z + p2.y*p3.z + - p3.y*p0.z + p3.y*p1.z + p3.y*p2.z); - _volumeAndInertia.push_back(inertia_aa); + double inertia_bb = (_volume * 6.0 / 120.0) * (2.0 * (p0.x*p0.z + p1.x*p1.z + p2.x*p2.z + p3.x*p3.z) + + p0.x*p1.z + p0.x*p2.z + p0.x*p3.z + + p1.x*p0.z + p1.x*p2.z + p1.x*p3.z + + p2.x*p0.z + p2.x*p1.z + p2.x*p3.z + + p3.x*p0.z + p3.x*p1.z + p3.x*p2.z); + _volumeAndInertia.push_back(inertia_bb); - double inertia_bb = (volume * 6.0 / 60.0) * (2.0 * (p0.x*p0.z + p1.x*p1.z + p2.x*p2.z + p3.x*p3.z) + - p0.x*p1.z + p0.x*p2.z + p0.x*p3.z + - p1.x*p0.z + p1.x*p2.z + p1.x*p3.z + - p2.x*p0.z + p2.x*p1.z + p2.x*p3.z + - p3.x*p0.z + p3.x*p1.z + p3.x*p2.z); - _volumeAndInertia.push_back(inertia_bb); - - double inertia_cc = (volume * 6.0 / 60.0) * (2.0 * (p0.x*p0.y + p1.x*p1.y + p2.x*p2.y + p3.x*p3.y) + - p0.x*p1.y + p0.x*p2.y + p0.x*p3.y + - p1.x*p0.y + p1.x*p2.y + p1.x*p3.y + - p2.x*p0.y + p2.x*p1.y + p2.x*p3.y + - p3.x*p0.y + p3.x*p1.y + p3.x*p2.y); - _volumeAndInertia.push_back(inertia_cc); + double inertia_cc = (_volume * 6.0 / 120.0) * (2.0 * (p0.x*p0.y + p1.x*p1.y + p2.x*p2.y + p3.x*p3.y) + + p0.x*p1.y + p0.x*p2.y + p0.x*p3.y + + p1.x*p0.y + p1.x*p2.y + p1.x*p3.y + + p2.x*p0.y + p2.x*p1.y + p2.x*p3.y + + p3.x*p0.y + p3.x*p1.y + p3.x*p2.y); + _volumeAndInertia.push_back(inertia_cc); } //class to compute volume, mass, center of mass, and inertia tensor of a mesh. @@ -141,55 +137,98 @@ _triangles(triangles), _referencePoint(referencepoint), _trianglesCount(0), _tetrahedraCount(0), -_verticesCount(0){ +_verticesCount(0), +_centerOfMass(glm::vec3(0.0, 0.0, 0.0)){ - if (_triangles){ - _trianglesCount = _triangles->size() / 3; - } + if (_triangles){ + _trianglesCount = _triangles->size() / 3; + } - if (_vertices){ - _verticesCount = _vertices->size(); - } - generateTetrahedra(); + if (_vertices){ + _verticesCount = _vertices->size(); + } + generateTetrahedra(); } MassProperties::~MassProperties(){ - if (_vertices){ - _vertices->clear(); - } - if (_triangles){ - _triangles->clear(); - } - delete _vertices; - delete _triangles; + if (_vertices){ + _vertices->clear(); + } + if (_triangles){ + _triangles->clear(); + } } -void MassProperties::generateTetrahedra(){ - for (int i = 0; i < _trianglesCount * 3; i += 3){ - Vertex p1 = _vertices->at(_triangles->at(i)); - Vertex p2 = _vertices->at(_triangles->at(i + 1)); - Vertex p3 = _vertices->at(_triangles->at(i + 2)); - Tetrahedron t(_referencePoint, p1, p2, p3); - _tetrahedra.push_back(t); - } +void MassProperties::generateTetrahedra() { + std::cout << "apex : " << _referencePoint.x << " " << _referencePoint.y << " " << _referencePoint.z << std::endl; + for (int i = 0; i < _trianglesCount * 3; i += 3){ + Vertex p1 = _vertices->at(_triangles->at(i)); + Vertex p2 = _vertices->at(_triangles->at(i + 1)); + Vertex p3 = _vertices->at(_triangles->at(i + 2)); + Tetrahedron t(_referencePoint, p1, p2, p3); + _tetrahedra.push_back(t); + } } int MassProperties::getTriangleCount() const{ - return _trianglesCount; + return _trianglesCount; } int MassProperties::getVerticesCount() const{ - return _verticesCount; + return _verticesCount; +} + +Vertex MassProperties::getCenterOfMass() const{ + return _centerOfMass; } int MassProperties::getTetrahedraCount() const{ - return _tetrahedra.size(); + return _tetrahedra.size(); } vector MassProperties::getTetrahedra() const{ - return _tetrahedra; + return _tetrahedra; } -vector MassProperties::getVolumeAndInertia(){ - vector volumeAndInertia; - return volumeAndInertia; + +vector MassProperties::getMassProperties(){ + vector volumeAndInertia; + double volume = 0.0; + double inertia_a = 0.0; + double inertia_b = 0.0; + double inertia_c = 0.0; + double inertia_aa = 0.0; + double inertia_bb = 0.0; + double inertia_cc = 0.0; + glm::vec3 centerOfMass; + + //Translate accumulated center of mass from each tetrahedron to mesh center of mass using parallel axis theorem + for each (Tetrahedron tet in _tetrahedra){ + vector tetMassProperties = tet.getVolumeAndInertia(); + volume += tetMassProperties.at(0); //volume + centerOfMass += tet.getCentroid() * (float)tetMassProperties.at(0); + } + + if (volume != 0){ + _centerOfMass = (centerOfMass / (float)volume); + } + + //Translate the moment of inertia from each tetrahedron to mesh center of mass using parallel axis theorem + for each (Tetrahedron tet in _tetrahedra){ + vector tetMassProperties = tet.getVolumeAndInertia(); + const double dist = glm::distance(_centerOfMass, tet.getCentroid()); + inertia_a += tetMassProperties.at(1) + (dist * dist * tetMassProperties.at(0)); + inertia_b += tetMassProperties.at(2) + (dist * dist * tetMassProperties.at(0)); + inertia_c += tetMassProperties.at(3) + (dist * dist * tetMassProperties.at(0)); + inertia_aa += tetMassProperties.at(4) + (dist * dist * tetMassProperties.at(0)); + inertia_bb += tetMassProperties.at(5) + (dist * dist * tetMassProperties.at(0)); + inertia_cc += tetMassProperties.at(6) + (dist * dist * tetMassProperties.at(0)); + } + volumeAndInertia.push_back(volume); + volumeAndInertia.push_back(inertia_a); + volumeAndInertia.push_back(inertia_b); + volumeAndInertia.push_back(inertia_c); + volumeAndInertia.push_back(inertia_aa); + volumeAndInertia.push_back(inertia_bb); + volumeAndInertia.push_back(inertia_cc); + return volumeAndInertia; } \ No newline at end of file diff --git a/libraries/physics/src/MassProperties.h b/libraries/physics/src/MassProperties.h index f5a2f0a0c7..240e0e53f7 100644 --- a/libraries/physics/src/MassProperties.h +++ b/libraries/physics/src/MassProperties.h @@ -8,6 +8,8 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#ifndef hifi_MassProperties_h +#define hifi_MassProperties_h #include #include @@ -15,46 +17,51 @@ #include using namespace std; namespace massproperties{ - typedef glm::vec3 Vertex; - typedef vector Triangle; + typedef glm::vec3 Vertex; + typedef vector Triangle; - //Tetrahedron class containing the base triangle and the apex. - class Tetrahedron{ - private: - Vertex _w; //apex - Vertex _x; - Vertex _y; - Vertex _z; - vector _volumeAndInertia; - public: - Tetrahedron(Vertex p1, Vertex p2, Vertex p3, Vertex p4); - ~Tetrahedron(); - Vertex getX(); - Vertex getY(); - Vertex getZ(); - Vertex getw(); - Vertex getCentroid(); - void computeVolumeAndInertia(); - vector getVolumeAndInertia(); - }; + //Tetrahedron class containing the base triangle and the apex. + class Tetrahedron{ + private: + Vertex _w; //apex + Vertex _x; + Vertex _y; + Vertex _z; + double _volume; + vector _volumeAndInertia; + void computeInertia(); + void computeVolume(); + public: + Tetrahedron(const Vertex p1, const Vertex p2, const Vertex p3, const Vertex p4); + ~Tetrahedron(); + Vertex getX() const; + Vertex getY() const; + Vertex getZ() const; + Vertex getw() const; + Vertex getCentroid() const; + vector getVolumeAndInertia() const; + }; - class MassProperties{ - private: - int _trianglesCount; - int _tetrahedraCount; - int _verticesCount; - vector *_vertices; - Vertex _referencePoint; - Triangle *_triangles; - vector _tetrahedra; - void generateTetrahedra(); - public: - MassProperties(vector *vertices, Triangle *triangles, Vertex refewrencepoint); - ~MassProperties(); - int getTriangleCount() const; - int getVerticesCount() const; - int getTetrahedraCount() const; - vector getTetrahedra() const; - vector getVolumeAndInertia(); - }; -} \ No newline at end of file + class MassProperties{ + private: + int _trianglesCount; + int _tetrahedraCount; + int _verticesCount; + vector *_vertices; + Vertex _referencePoint; + Vertex _centerOfMass; + Triangle *_triangles; + vector _tetrahedra; + void generateTetrahedra(); + public: + MassProperties(vector *vertices, Triangle *triangles, Vertex refewrencepoint); + ~MassProperties(); + int getTriangleCount() const; + int getVerticesCount() const; + int getTetrahedraCount() const; + Vertex getCenterOfMass() const; + vector getTetrahedra() const; + vector getMassProperties(); + }; +} +#endif // hifi_MassProperties_h \ No newline at end of file diff --git a/tests/physics/src/MassPropertiesTests.cpp b/tests/physics/src/MassPropertiesTests.cpp new file mode 100644 index 0000000000..83a8443cf9 --- /dev/null +++ b/tests/physics/src/MassPropertiesTests.cpp @@ -0,0 +1,133 @@ +// +// MassPropertiesTests.cpp +// tests/physics/src +// +// Created by Virendra Singh on 2015.03.02 +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include +#include +#include + +#include "MassPropertiesTests.h" + +void MassPropertiesTests::testWithTetrahedron(){ + glm::vec3 p0(8.33220, -11.86875, 0.93355); + glm::vec3 p1(0.75523, 5.00000, 16.37072); + glm::vec3 p2(52.61236, 5.00000, -5.38580); + glm::vec3 p3(2.00000, 5.00000, 3.00000); + glm::vec3 centroid(15.92492, 0.782813, 3.72962); + double volume = 1873.233236; + double inertia_a = 43520.33257; + double inertia_b = 194711.28938; + double inertia_c = 191168.76173; + double inertia_aa = 4417.66150; + double inertia_bb = -46343.16662; + double inertia_cc = 11996.20119; + massproperties::Tetrahedron tet(p0, p1, p2, p3); + glm::vec3 diff = centroid - tet.getCentroid(); + vector voumeAndInertia = tet.getVolumeAndInertia(); + std::cout << std::setprecision(12); + //test if centroid is correct + if (diff.x > epsilion || diff.y > epsilion || diff.z > epsilion){ + std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Centroid is incorrect : Expected = " << centroid.x << " " << + centroid.y << " " << centroid.z << ", actual = " << tet.getCentroid().x << " " << tet.getCentroid().y << + " " << tet.getCentroid().z << std::endl; + } + + //test if volume is correct + if (abs(volume - voumeAndInertia.at(0)) > epsilion){ + std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Volume is incorrect : Expected = " << volume << " " << + ", actual = " << voumeAndInertia.at(0) << std::endl; + } + + //test if moment of inertia with respect to x axis is correct + if (abs(inertia_a - (voumeAndInertia.at(1))) > epsilion){ + std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Moment of inertia with respect to x axis is incorrect : Expected = " << + inertia_a << " " << ", actual = " << (voumeAndInertia.at(1)) << std::endl; + } + + //test if moment of inertia with respect to y axis is correct + if (abs(inertia_b - (voumeAndInertia.at(2))) > epsilion){ + std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Moment of inertia with respect to y axis is incorrect : Expected = " << + inertia_b << " " << ", actual = " << (voumeAndInertia.at(2)) << std::endl; + } + + //test if moment of inertia with respect to z axis is correct + if (abs(inertia_c - (voumeAndInertia.at(3))) > epsilion){ + std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Moment of inertia with respect to z axis is incorrect : Expected = " << + inertia_c << " " << ", actual = " << (voumeAndInertia.at(3)) << std::endl; + } + + //test if product of inertia with respect to x axis is correct + if (abs(inertia_aa - (voumeAndInertia.at(4))) > epsilion){ + std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Product of inertia with respect to x axis is incorrect : Expected = " << + inertia_aa << " " << ", actual = " << (voumeAndInertia.at(4)) << std::endl; + } + + //test if product of inertia with respect to y axis is correct + if (abs(inertia_bb - (voumeAndInertia.at(5))) > epsilion){ + std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Product of inertia with respect to y axis is incorrect : Expected = " << + inertia_bb << " " << ", actual = " << (voumeAndInertia.at(5)) << std::endl; + } + + //test if product of inertia with respect to z axis is correct + if (abs(inertia_cc - (voumeAndInertia.at(6))) > epsilion){ + std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Product of inertia with respect to z axis is incorrect : Expected = " << + inertia_cc << " " << ", actual = " << (voumeAndInertia.at(6)) << std::endl; + } + +} + +void MassPropertiesTests::testWithUnitCube(){ + massproperties::Vertex p0(1.0, -1.0, -1.0); + massproperties::Vertex p1(1.0, -1.0, 1.0); + massproperties::Vertex p2(-1.0, -1.0, 1.0); + massproperties::Vertex p3(-1.0, -1.0, -1.0); + massproperties::Vertex p4(1.0, 1.0, -1.0); + massproperties::Vertex p5(1.0, 1.0, 1.0); + massproperties::Vertex p6(-1.0, 1.0, 1.0); + massproperties::Vertex p7(-1.0, 1.0, -1.0); + vector vertices; + vertices.push_back(p0); + vertices.push_back(p1); + vertices.push_back(p2); + vertices.push_back(p3); + vertices.push_back(p4); + vertices.push_back(p5); + vertices.push_back(p6); + vertices.push_back(p7); + std::cout << std::setprecision(5); + vector triangles = { 1 - 1, 2 - 1, 3 - 1, 1 - 1, 3 - 1, 4 - 1, 5 - 1, 8 - 1, 7 - 1, 5 - 1, 7 - 1, 6 - 1, 1 - 1, 5 - 1, 6 - 1, 1 - 1, + 6 - 1, 2 - 1, 2 - 1, 6 - 1, 7 - 1, 2 - 1, 7 - 1, 3 - 1, 3 - 1, 7 - 1, 8 - 1, 3 - 1, 8 - 1, 4 - 1, 5 - 1, 1 - 1, 4 - 1, 5 - 1, 4 - 1, 8 - 1 }; + glm::vec3 centerOfMass(0.0, 0.0, 0.0); + double volume =8.0; + double side = 2.0; + double inertia = (volume * side * side) / 6.0; //inertia of a unit cube is (mass * side * side) /6 + + //test with origin as reference point + massproperties::MassProperties massProp1(&vertices, &triangles, {}); + vector volumeAndInertia1 = massProp1.getMassProperties(); + if (abs(centerOfMass.x - massProp1.getCenterOfMass().x) > epsilion || abs(centerOfMass.y - massProp1.getCenterOfMass().y) > epsilion || + abs(centerOfMass.z - massProp1.getCenterOfMass().z) > epsilion){ + std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Center of mass is incorrect : Expected = " << centerOfMass.x << " " << + centerOfMass.y << " " << centerOfMass.z << ", actual = " << massProp1.getCenterOfMass().x << " " << massProp1.getCenterOfMass().y << + " " << massProp1.getCenterOfMass().z << std::endl; + } + + if (abs(inertia - (volumeAndInertia1.at(1))) > epsilion || abs(inertia - (volumeAndInertia1.at(2))) > epsilion || + abs(inertia - (volumeAndInertia1.at(3))) > epsilion){ + std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Moment is incorrect : Expected = " << inertia << " " << + inertia << " " << inertia << ", actual = " << (volumeAndInertia1.at(1)) << " " << (volumeAndInertia1.at(2)) << + " " << (volumeAndInertia1.at(3)) << std::endl; + } +} + +void MassPropertiesTests::runAllTests(){ + testWithTetrahedron(); + testWithUnitCube(); +} \ No newline at end of file diff --git a/tests/physics/src/MassPropertiesTests.h b/tests/physics/src/MassPropertiesTests.h new file mode 100644 index 0000000000..583ac25126 --- /dev/null +++ b/tests/physics/src/MassPropertiesTests.h @@ -0,0 +1,20 @@ +// +// MassPropertiesTests.h +// tests/physics/src +// +// Created by Virendra Singh on 2015.03.02 +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_MassPropertiesTests_h +#define hifi_MassPropertiesTests_h +#define epsilion 0.02 +namespace MassPropertiesTests{ + void testWithTetrahedron(); + void testWithUnitCube(); + void runAllTests(); +} +#endif // hifi_MassPropertiesTests_h \ No newline at end of file diff --git a/tests/physics/src/main.cpp b/tests/physics/src/main.cpp index bcf26f4115..eed58dbbdb 100644 --- a/tests/physics/src/main.cpp +++ b/tests/physics/src/main.cpp @@ -8,17 +8,21 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include #include "ShapeColliderTests.h" #include "VerletShapeTests.h" #include "ShapeInfoTests.h" #include "ShapeManagerTests.h" #include "BulletUtilTests.h" +#include "MassPropertiesTests.h" int main(int argc, char** argv) { - ShapeColliderTests::runAllTests(); - VerletShapeTests::runAllTests(); - ShapeInfoTests::runAllTests(); - ShapeManagerTests::runAllTests(); - BulletUtilTests::runAllTests(); + //ShapeColliderTests::runAllTests(); + //VerletShapeTests::runAllTests(); + //ShapeInfoTests::runAllTests(); + //ShapeManagerTests::runAllTests(); + // BulletUtilTests::runAllTests(); + MassPropertiesTests::runAllTests(); + getch(); return 0; } From 4627d2e7b5112984edd80a250a30a9cbcb131d5e Mon Sep 17 00:00:00 2001 From: Virendra Singh Date: Wed, 4 Mar 2015 03:52:59 +0530 Subject: [PATCH 05/43] Parallel axis theorem correction --- libraries/physics/src/MassProperties.cpp | 44 ++++---- tests/physics/src/MassPropertiesTests.cpp | 117 ++++++++++++++++++++-- tests/physics/src/MassPropertiesTests.h | 1 + tests/physics/src/main.cpp | 14 ++- 4 files changed, 141 insertions(+), 35 deletions(-) diff --git a/libraries/physics/src/MassProperties.cpp b/libraries/physics/src/MassProperties.cpp index 4d2e668f95..c6264ee220 100644 --- a/libraries/physics/src/MassProperties.cpp +++ b/libraries/physics/src/MassProperties.cpp @@ -52,10 +52,10 @@ vector Tetrahedron::getVolumeAndInertia() const{ } void Tetrahedron::computeVolume(){ - glm::mat4 tet = { glm::vec4(_x.x, _y.x, _z.x, _w.x), glm::vec4(_x.y, _y.y, _z.y, _w.y), glm::vec4(_x.z, _y.z, _z.z, _w.z), glm::vec4(1.0f, 1.0f, 1.0f, 1.0f) }; + glm::mat4 tet = { glm::vec4(_x.x, _y.x, _z.x, _w.x), glm::vec4(_x.y, _y.y, _z.y, _w.y), glm::vec4(_x.z, _y.z, _z.z, _w.z), + glm::vec4(1.0f, 1.0f, 1.0f, 1.0f) }; _volume = glm::determinant(tet) / 6.0f; _volumeAndInertia.push_back(_volume); - std::cout << "volume : " << _volume << std::endl; } void Tetrahedron::computeInertia(){ @@ -128,8 +128,8 @@ void Tetrahedron::computeInertia(){ } //class to compute volume, mass, center of mass, and inertia tensor of a mesh. -//origin is the default reference point for generating the tetrahedron from each triangle of the mesh. We can provide another reference -//point by passing it as 3rd parameter to the constructor +//origin is the default reference point for generating the tetrahedron from each triangle of the mesh. We can provide +//another reference point by passing it as 3rd parameter to the constructor MassProperties::MassProperties(vector *vertices, Triangle *triangles, Vertex referencepoint = glm::vec3(0.0,0.0,0.0)):\ _vertices(vertices), @@ -160,7 +160,6 @@ MassProperties::~MassProperties(){ } void MassProperties::generateTetrahedra() { - std::cout << "apex : " << _referencePoint.x << " " << _referencePoint.y << " " << _referencePoint.z << std::endl; for (int i = 0; i < _trianglesCount * 3; i += 3){ Vertex p1 = _vertices->at(_triangles->at(i)); Vertex p2 = _vertices->at(_triangles->at(i + 1)); @@ -200,6 +199,8 @@ vector MassProperties::getMassProperties(){ double inertia_bb = 0.0; double inertia_cc = 0.0; glm::vec3 centerOfMass; + glm::mat3 globalInertia(0.0); + glm::mat3 globalProductInertia(0.0); //Translate accumulated center of mass from each tetrahedron to mesh center of mass using parallel axis theorem for each (Tetrahedron tet in _tetrahedra){ @@ -215,20 +216,27 @@ vector MassProperties::getMassProperties(){ //Translate the moment of inertia from each tetrahedron to mesh center of mass using parallel axis theorem for each (Tetrahedron tet in _tetrahedra){ vector tetMassProperties = tet.getVolumeAndInertia(); - const double dist = glm::distance(_centerOfMass, tet.getCentroid()); - inertia_a += tetMassProperties.at(1) + (dist * dist * tetMassProperties.at(0)); - inertia_b += tetMassProperties.at(2) + (dist * dist * tetMassProperties.at(0)); - inertia_c += tetMassProperties.at(3) + (dist * dist * tetMassProperties.at(0)); - inertia_aa += tetMassProperties.at(4) + (dist * dist * tetMassProperties.at(0)); - inertia_bb += tetMassProperties.at(5) + (dist * dist * tetMassProperties.at(0)); - inertia_cc += tetMassProperties.at(6) + (dist * dist * tetMassProperties.at(0)); + glm::mat3 identity; + glm::vec3 diff = _centerOfMass - tet.getCentroid(); + float diffDot = glm::dot(diff, diff); + glm::mat3 outerDiff = glm::outerProduct(diff, diff); + + //3x3 of local inertia tensors of each tetrahedron. Inertia tensors are the diagonal elements + glm::mat3 localMomentInertia = { Vertex(tetMassProperties.at(1), 0.0f, 0.0f), Vertex(0.0f, tetMassProperties.at(2), 0.0f), + Vertex(0.0f, 0.0f, tetMassProperties.at(3)) }; + glm::mat3 localProductInertia = { Vertex(tetMassProperties.at(4), 0.0f, 0.0f), Vertex(0.0f, tetMassProperties.at(5), 0.0f), + Vertex(0.0f, 0.0f, tetMassProperties.at(6)) }; + + //Parallel axis theorem J = I * m[(R.R)*Identity - RxR] where x is outer cross product + globalInertia += localMomentInertia + (float)tetMassProperties.at(0) * ((diffDot*identity) - outerDiff); + globalProductInertia += localProductInertia + (float)tetMassProperties.at(0) * ((diffDot * identity) - outerDiff); } volumeAndInertia.push_back(volume); - volumeAndInertia.push_back(inertia_a); - volumeAndInertia.push_back(inertia_b); - volumeAndInertia.push_back(inertia_c); - volumeAndInertia.push_back(inertia_aa); - volumeAndInertia.push_back(inertia_bb); - volumeAndInertia.push_back(inertia_cc); + volumeAndInertia.push_back(globalInertia[0][0]); + volumeAndInertia.push_back(globalInertia[1][1]); + volumeAndInertia.push_back(globalInertia[2][2]); + volumeAndInertia.push_back(globalProductInertia[0][0]); + volumeAndInertia.push_back(globalProductInertia[1][1]); + volumeAndInertia.push_back(globalProductInertia[2][2]); return volumeAndInertia; } \ No newline at end of file diff --git a/tests/physics/src/MassPropertiesTests.cpp b/tests/physics/src/MassPropertiesTests.cpp index 83a8443cf9..f36a494b35 100644 --- a/tests/physics/src/MassPropertiesTests.cpp +++ b/tests/physics/src/MassPropertiesTests.cpp @@ -47,7 +47,7 @@ void MassPropertiesTests::testWithTetrahedron(){ //test if moment of inertia with respect to x axis is correct if (abs(inertia_a - (voumeAndInertia.at(1))) > epsilion){ - std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Moment of inertia with respect to x axis is incorrect : Expected = " << + std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Moment of inertia with respect to x axis is incorrect : Expected = " << inertia_a << " " << ", actual = " << (voumeAndInertia.at(1)) << std::endl; } @@ -83,7 +83,7 @@ void MassPropertiesTests::testWithTetrahedron(){ } -void MassPropertiesTests::testWithUnitCube(){ +void MassPropertiesTests::testWithCube(){ massproperties::Vertex p0(1.0, -1.0, -1.0); massproperties::Vertex p1(1.0, -1.0, 1.0); massproperties::Vertex p2(-1.0, -1.0, 1.0); @@ -101,11 +101,11 @@ void MassPropertiesTests::testWithUnitCube(){ vertices.push_back(p5); vertices.push_back(p6); vertices.push_back(p7); - std::cout << std::setprecision(5); - vector triangles = { 1 - 1, 2 - 1, 3 - 1, 1 - 1, 3 - 1, 4 - 1, 5 - 1, 8 - 1, 7 - 1, 5 - 1, 7 - 1, 6 - 1, 1 - 1, 5 - 1, 6 - 1, 1 - 1, - 6 - 1, 2 - 1, 2 - 1, 6 - 1, 7 - 1, 2 - 1, 7 - 1, 3 - 1, 3 - 1, 7 - 1, 8 - 1, 3 - 1, 8 - 1, 4 - 1, 5 - 1, 1 - 1, 4 - 1, 5 - 1, 4 - 1, 8 - 1 }; + std::cout << std::setprecision(10); + vector triangles = { 0, 1, 2, 0, 2, 3, 4, 7, 6, 4, 6, 5, 0, 4, 5, 0, 5, 1, 1, 5, 6, 1, 6, 2, 2, 6, + 7, 2, 7, 3, 4, 0, 3, 4, 3, 7 }; glm::vec3 centerOfMass(0.0, 0.0, 0.0); - double volume =8.0; + double volume = 8.0; double side = 2.0; double inertia = (volume * side * side) / 6.0; //inertia of a unit cube is (mass * side * side) /6 @@ -115,19 +115,118 @@ void MassPropertiesTests::testWithUnitCube(){ if (abs(centerOfMass.x - massProp1.getCenterOfMass().x) > epsilion || abs(centerOfMass.y - massProp1.getCenterOfMass().y) > epsilion || abs(centerOfMass.z - massProp1.getCenterOfMass().z) > epsilion){ std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Center of mass is incorrect : Expected = " << centerOfMass.x << " " << - centerOfMass.y << " " << centerOfMass.z << ", actual = " << massProp1.getCenterOfMass().x << " " << massProp1.getCenterOfMass().y << - " " << massProp1.getCenterOfMass().z << std::endl; + centerOfMass.y << " " << centerOfMass.z << ", actual = " << massProp1.getCenterOfMass().x << " " << + massProp1.getCenterOfMass().y << " " << massProp1.getCenterOfMass().z << std::endl; } - if (abs(inertia - (volumeAndInertia1.at(1))) > epsilion || abs(inertia - (volumeAndInertia1.at(2))) > epsilion || + if (abs(volume - volumeAndInertia1.at(0)) > epsilion){ + std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Volume is incorrect : Expected = " << volume << + ", actual = " << volumeAndInertia1.at(0) << std::endl; + } + + if (abs(inertia - (volumeAndInertia1.at(1))) > epsilion || abs(inertia - (volumeAndInertia1.at(2))) > epsilion || abs(inertia - (volumeAndInertia1.at(3))) > epsilion){ std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Moment is incorrect : Expected = " << inertia << " " << inertia << " " << inertia << ", actual = " << (volumeAndInertia1.at(1)) << " " << (volumeAndInertia1.at(2)) << " " << (volumeAndInertia1.at(3)) << std::endl; } + + //test with {2,2,2} as reference point + massproperties::MassProperties massProp2(&vertices, &triangles, { 2, 2, 2 }); + vector volumeAndInertia2 = massProp2.getMassProperties(); + if (abs(centerOfMass.x - massProp2.getCenterOfMass().x) > epsilion || abs(centerOfMass.y - massProp2.getCenterOfMass().y) > epsilion || + abs(centerOfMass.z - massProp2.getCenterOfMass().z) > epsilion){ + std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Center of mass is incorrect : Expected = " << centerOfMass.x << + " " << centerOfMass.y << " " << centerOfMass.z << ", actual = " << massProp2.getCenterOfMass().x << " " << + massProp2.getCenterOfMass().y << " " << massProp2.getCenterOfMass().z << std::endl; + } + + if (abs(volume - volumeAndInertia2.at(0)) > epsilion){ + std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Volume is incorrect : Expected = " << volume << + ", actual = " << volumeAndInertia2.at(0) << std::endl; + } + + if (abs(inertia - (volumeAndInertia2.at(1))) > epsilion || abs(inertia - (volumeAndInertia2.at(2))) > epsilion || + abs(inertia - (volumeAndInertia2.at(3))) > epsilion){ + std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Moment is incorrect : Expected = " << inertia << " " << + inertia << " " << inertia << ", actual = " << (volumeAndInertia2.at(1)) << " " << (volumeAndInertia2.at(2)) << + " " << (volumeAndInertia2.at(3)) << std::endl; + } } +void MassPropertiesTests::testWithUnitCube() +{ + massproperties::Vertex p0(0, 0, 1); + massproperties::Vertex p1(1, 0, 1); + massproperties::Vertex p2(0, 1, 1); + massproperties::Vertex p3(1, 1, 1); + massproperties::Vertex p4(0, 0, 0); + massproperties::Vertex p5(1, 0, 0); + massproperties::Vertex p6(0, 1, 0); + massproperties::Vertex p7(1, 1, 0); + vector vertices; + vertices.push_back(p0); + vertices.push_back(p1); + vertices.push_back(p2); + vertices.push_back(p3); + vertices.push_back(p4); + vertices.push_back(p5); + vertices.push_back(p6); + vertices.push_back(p7); + vector triangles = { 0, 1, 2, 1, 3, 2, 2, 3, 7, 2, 7, 6, 1, 7, 3, 1, 5, 7, 6, 7, 4, 7, 5, 4, 0, 4, 1, + 1, 4, 5, 2, 6, 4, 0, 2, 4 }; + glm::vec3 centerOfMass(0.5, 0.5, 0.5); + double volume = 1.0; + double side = 1.0; + double inertia = (volume * side * side) / 6.0; //inertia of a unit cube is (mass * side * side) /6 + std::cout << std::setprecision(10); + + //test with origin as reference point + massproperties::MassProperties massProp1(&vertices, &triangles, {}); + vector volumeAndInertia1 = massProp1.getMassProperties(); + if (abs(centerOfMass.x - massProp1.getCenterOfMass().x) > epsilion || abs(centerOfMass.y - massProp1.getCenterOfMass().y) > epsilion || + abs(centerOfMass.z - massProp1.getCenterOfMass().z) > epsilion){ + std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Center of mass is incorrect : Expected = " << centerOfMass.x << + " " << centerOfMass.y << " " << centerOfMass.z << ", actual = " << massProp1.getCenterOfMass().x << " " << + massProp1.getCenterOfMass().y << " " << massProp1.getCenterOfMass().z << std::endl; + } + + if (abs(volume - volumeAndInertia1.at(0)) > epsilion){ + std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Volume is incorrect : Expected = " << volume << + ", actual = " << volumeAndInertia1.at(0) << std::endl; + } + + if (abs(inertia - (volumeAndInertia1.at(1))) > epsilion || abs(inertia - (volumeAndInertia1.at(2))) > epsilion || + abs(inertia - (volumeAndInertia1.at(3))) > epsilion){ + std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Moment is incorrect : Expected = " << inertia << " " << + inertia << " " << inertia << ", actual = " << (volumeAndInertia1.at(1)) << " " << (volumeAndInertia1.at(2)) << + " " << (volumeAndInertia1.at(3)) << std::endl; + } + + //test with {2,1,2} as reference point + massproperties::MassProperties massProp2(&vertices, &triangles, { 2, 1, 2 }); + vector volumeAndInertia2 = massProp2.getMassProperties(); + if (abs(centerOfMass.x - massProp2.getCenterOfMass().x) > epsilion || abs(centerOfMass.y - massProp2.getCenterOfMass().y) > epsilion || + abs(centerOfMass.z - massProp2.getCenterOfMass().z) > epsilion){ + std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Center of mass is incorrect : Expected = " << centerOfMass.x << " " << + centerOfMass.y << " " << centerOfMass.z << ", actual = " << massProp2.getCenterOfMass().x << " " << + massProp2.getCenterOfMass().y << " " << massProp2.getCenterOfMass().z << std::endl; + } + + if (abs(volume - volumeAndInertia2.at(0)) > epsilion){ + std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Volume is incorrect : Expected = " << volume << + ", actual = " << volumeAndInertia2.at(0) << std::endl; + } + + if (abs(inertia - (volumeAndInertia2.at(1))) > epsilion || abs(inertia - (volumeAndInertia2.at(2))) > epsilion || + abs(inertia - (volumeAndInertia2.at(3))) > epsilion){ + std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Moment is incorrect : Expected = " << inertia << " " << + inertia << " " << inertia << ", actual = " << (volumeAndInertia2.at(1)) << " " << (volumeAndInertia2.at(2)) << + " " << (volumeAndInertia2.at(3)) << std::endl; + } +} void MassPropertiesTests::runAllTests(){ testWithTetrahedron(); testWithUnitCube(); + testWithCube(); } \ No newline at end of file diff --git a/tests/physics/src/MassPropertiesTests.h b/tests/physics/src/MassPropertiesTests.h index 583ac25126..21ab3ccb0a 100644 --- a/tests/physics/src/MassPropertiesTests.h +++ b/tests/physics/src/MassPropertiesTests.h @@ -15,6 +15,7 @@ namespace MassPropertiesTests{ void testWithTetrahedron(); void testWithUnitCube(); + void testWithCube(); void runAllTests(); } #endif // hifi_MassPropertiesTests_h \ No newline at end of file diff --git a/tests/physics/src/main.cpp b/tests/physics/src/main.cpp index eed58dbbdb..d42b5d1241 100644 --- a/tests/physics/src/main.cpp +++ b/tests/physics/src/main.cpp @@ -8,7 +8,6 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include #include "ShapeColliderTests.h" #include "VerletShapeTests.h" #include "ShapeInfoTests.h" @@ -17,12 +16,11 @@ #include "MassPropertiesTests.h" int main(int argc, char** argv) { - //ShapeColliderTests::runAllTests(); - //VerletShapeTests::runAllTests(); - //ShapeInfoTests::runAllTests(); - //ShapeManagerTests::runAllTests(); - // BulletUtilTests::runAllTests(); - MassPropertiesTests::runAllTests(); - getch(); + ShapeColliderTests::runAllTests(); + VerletShapeTests::runAllTests(); + ShapeInfoTests::runAllTests(); + ShapeManagerTests::runAllTests(); + BulletUtilTests::runAllTests(); + MassPropertiesTests::runAllTests(); return 0; } From 1facbbb844c8941883fb1ce26e9f7980dccbfad4 Mon Sep 17 00:00:00 2001 From: Virendra Singh Date: Wed, 4 Mar 2015 08:21:39 +0530 Subject: [PATCH 06/43] Removed MSVC specific for each blocks --- libraries/physics/src/MassProperties.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/physics/src/MassProperties.cpp b/libraries/physics/src/MassProperties.cpp index c6264ee220..deaa37587d 100644 --- a/libraries/physics/src/MassProperties.cpp +++ b/libraries/physics/src/MassProperties.cpp @@ -203,7 +203,7 @@ vector MassProperties::getMassProperties(){ glm::mat3 globalProductInertia(0.0); //Translate accumulated center of mass from each tetrahedron to mesh center of mass using parallel axis theorem - for each (Tetrahedron tet in _tetrahedra){ + for(Tetrahedron tet : _tetrahedra){ vector tetMassProperties = tet.getVolumeAndInertia(); volume += tetMassProperties.at(0); //volume centerOfMass += tet.getCentroid() * (float)tetMassProperties.at(0); @@ -214,7 +214,7 @@ vector MassProperties::getMassProperties(){ } //Translate the moment of inertia from each tetrahedron to mesh center of mass using parallel axis theorem - for each (Tetrahedron tet in _tetrahedra){ + for(Tetrahedron tet : _tetrahedra){ vector tetMassProperties = tet.getVolumeAndInertia(); glm::mat3 identity; glm::vec3 diff = _centerOfMass - tet.getCentroid(); From 4366525da46ae7e81df17d5ddf45a1a9604926ae Mon Sep 17 00:00:00 2001 From: Virendra Singh Date: Wed, 4 Mar 2015 08:51:44 +0530 Subject: [PATCH 07/43] Removed unused variables --- libraries/physics/src/MassProperties.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/libraries/physics/src/MassProperties.cpp b/libraries/physics/src/MassProperties.cpp index deaa37587d..e579f2d358 100644 --- a/libraries/physics/src/MassProperties.cpp +++ b/libraries/physics/src/MassProperties.cpp @@ -192,12 +192,6 @@ vector MassProperties::getTetrahedra() const{ vector MassProperties::getMassProperties(){ vector volumeAndInertia; double volume = 0.0; - double inertia_a = 0.0; - double inertia_b = 0.0; - double inertia_c = 0.0; - double inertia_aa = 0.0; - double inertia_bb = 0.0; - double inertia_cc = 0.0; glm::vec3 centerOfMass; glm::mat3 globalInertia(0.0); glm::mat3 globalProductInertia(0.0); From 42867bf98d089cdeb6e5c4a4f663a8c5fd5d7159 Mon Sep 17 00:00:00 2001 From: Virendra Singh Date: Fri, 6 Mar 2015 11:10:38 +0530 Subject: [PATCH 08/43] code revamp --- libraries/physics/src/MassProperties.cpp | 236 ------------------ libraries/physics/src/MassProperties.h | 67 ----- libraries/physics/src/MeshInfo.cpp | 160 ++++++++++++ libraries/physics/src/MeshInfo.h | 34 +++ tests/physics/src/MassPropertiesTests.cpp | 232 ----------------- tests/physics/src/MeshInfoTests.cpp | 198 +++++++++++++++ ...{MassPropertiesTests.h => MeshInfoTests.h} | 11 +- tests/physics/src/main.cpp | 4 +- 8 files changed, 399 insertions(+), 543 deletions(-) delete mode 100644 libraries/physics/src/MassProperties.cpp delete mode 100644 libraries/physics/src/MassProperties.h create mode 100644 libraries/physics/src/MeshInfo.cpp create mode 100644 libraries/physics/src/MeshInfo.h delete mode 100644 tests/physics/src/MassPropertiesTests.cpp create mode 100644 tests/physics/src/MeshInfoTests.cpp rename tests/physics/src/{MassPropertiesTests.h => MeshInfoTests.h} (66%) diff --git a/libraries/physics/src/MassProperties.cpp b/libraries/physics/src/MassProperties.cpp deleted file mode 100644 index e579f2d358..0000000000 --- a/libraries/physics/src/MassProperties.cpp +++ /dev/null @@ -1,236 +0,0 @@ -// -// MassProperties.cpp -// libraries/physics/src -// -// Created by Virendra Singh 2015.02.28 -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include "MassProperties.h" -using namespace massproperties; - -Tetrahedron::Tetrahedron(const Vertex p1, const Vertex p2, const Vertex p3, const Vertex p4) :\ -_w(p1), -_x(p2), -_y(p3), -_z(p4){ - computeVolume(); - computeInertia(); -} -Tetrahedron::~Tetrahedron(){ - -} - -Vertex Tetrahedron::getX() const{ - return _x; -} - -Vertex Tetrahedron::getY() const{ - return _y; -} -Vertex Tetrahedron::getZ() const{ - return _z; -} - -Vertex Tetrahedron::getw() const{ - return _w; -} - -Vertex Tetrahedron::getCentroid() const{ - Vertex com; - com.x = (_x.x + _y.x + _z.x + _w.x) / 4.0f; - com.y = (_x.y + _y.y + _z.y + _w.y) / 4.0f; - com.z = (_x.z + _y.z + _z.z + _w.z) / 4.0f; - return com; -} - -vector Tetrahedron::getVolumeAndInertia() const{ - return _volumeAndInertia; -} - -void Tetrahedron::computeVolume(){ - glm::mat4 tet = { glm::vec4(_x.x, _y.x, _z.x, _w.x), glm::vec4(_x.y, _y.y, _z.y, _w.y), glm::vec4(_x.z, _y.z, _z.z, _w.z), - glm::vec4(1.0f, 1.0f, 1.0f, 1.0f) }; - _volume = glm::determinant(tet) / 6.0f; - _volumeAndInertia.push_back(_volume); -} - -void Tetrahedron::computeInertia(){ - - //centroid is used for calculating inertia tensor relative to center of mass. - // translate the tetrahedron to its center of mass using P = P - centroid - Vertex com = getCentroid(); - Vertex p0 = _w - com; - Vertex p1 = _x - com; - Vertex p2 = _y - com; - Vertex p3 = _z - com; - - //Calculate inertia tensor based on Tonon's Formulae given in the paper mentioned below. - //http://docsdrive.com/pdfs/sciencepublications/jmssp/2005/8-11.pdf - //Explicit exact formulas for the 3-D tetrahedron inertia tensor in terms of its vertex coordinates - F.Tonon - - double inertia_a = (_volume * 6.0 / 60.0) * ( - p0.y*p0.y + p0.y*p1.y + p0.y*p2.y + p0.y*p3.y + - p1.y*p1.y + p1.y*p2.y + p1.y*p3.y + - p2.y*p2.y + p2.y*p3.y + - p3.y*p3.y + - p0.z*p0.z + p0.z*p1.z + p0.z*p2.z + p0.z*p3.z + - p1.z*p1.z + p1.z*p2.z + p1.z*p3.z + - p2.z*p2.z + p2.z*p3.z + - p3.z*p3.z); - _volumeAndInertia.push_back(inertia_a); - - double inertia_b = (_volume * 6.0 / 60.0) * ( - p0.x*p0.x + p0.x*p1.x + p0.x*p2.x + p0.x*p3.x + - p1.x*p1.x + p1.x*p2.x + p1.x*p3.x + - p2.x*p2.x + p2.x*p3.x + - p3.x*p3.x + - p0.z*p0.z + p0.z*p1.z + p0.z*p2.z + p0.z*p3.z + - p1.z*p1.z + p1.z*p2.z + p1.z*p3.z + - p2.z*p2.z + p2.z*p3.z + - p3.z*p3.z); - _volumeAndInertia.push_back(inertia_b); - - double inertia_c = (_volume * 6.0 / 60.0) * ( - p0.x*p0.x + p0.x*p1.x + p0.x*p2.x + p0.x*p3.x + - p1.x*p1.x + p1.x*p2.x + p1.x*p3.x + - p2.x*p2.x + p2.x*p3.x + - p3.x*p3.x + - p0.y*p0.y + p0.y*p1.y + p0.y*p2.y + p0.y*p3.y + - p1.y*p1.y + p1.y*p2.y + p1.y*p3.y + - p2.y*p2.y + p2.y*p3.y + - p3.y*p3.y); - _volumeAndInertia.push_back(inertia_c); - - double inertia_aa = (_volume * 6.0 / 120.0) * (2.0 * (p0.y*p0.z + p1.y*p1.z + p2.y*p2.z + p3.y*p3.z) + - p0.y*p1.z + p0.y*p2.z + p0.y*p3.z + - p1.y*p0.z + p1.y*p2.z + p1.y*p3.z + - p2.y*p0.z + p2.y*p1.z + p2.y*p3.z + - p3.y*p0.z + p3.y*p1.z + p3.y*p2.z); - _volumeAndInertia.push_back(inertia_aa); - - double inertia_bb = (_volume * 6.0 / 120.0) * (2.0 * (p0.x*p0.z + p1.x*p1.z + p2.x*p2.z + p3.x*p3.z) + - p0.x*p1.z + p0.x*p2.z + p0.x*p3.z + - p1.x*p0.z + p1.x*p2.z + p1.x*p3.z + - p2.x*p0.z + p2.x*p1.z + p2.x*p3.z + - p3.x*p0.z + p3.x*p1.z + p3.x*p2.z); - _volumeAndInertia.push_back(inertia_bb); - - double inertia_cc = (_volume * 6.0 / 120.0) * (2.0 * (p0.x*p0.y + p1.x*p1.y + p2.x*p2.y + p3.x*p3.y) + - p0.x*p1.y + p0.x*p2.y + p0.x*p3.y + - p1.x*p0.y + p1.x*p2.y + p1.x*p3.y + - p2.x*p0.y + p2.x*p1.y + p2.x*p3.y + - p3.x*p0.y + p3.x*p1.y + p3.x*p2.y); - _volumeAndInertia.push_back(inertia_cc); -} - -//class to compute volume, mass, center of mass, and inertia tensor of a mesh. -//origin is the default reference point for generating the tetrahedron from each triangle of the mesh. We can provide -//another reference point by passing it as 3rd parameter to the constructor - -MassProperties::MassProperties(vector *vertices, Triangle *triangles, Vertex referencepoint = glm::vec3(0.0,0.0,0.0)):\ -_vertices(vertices), -_triangles(triangles), -_referencePoint(referencepoint), -_trianglesCount(0), -_tetrahedraCount(0), -_verticesCount(0), -_centerOfMass(glm::vec3(0.0, 0.0, 0.0)){ - - if (_triangles){ - _trianglesCount = _triangles->size() / 3; - } - - if (_vertices){ - _verticesCount = _vertices->size(); - } - generateTetrahedra(); -} - -MassProperties::~MassProperties(){ - if (_vertices){ - _vertices->clear(); - } - if (_triangles){ - _triangles->clear(); - } -} - -void MassProperties::generateTetrahedra() { - for (int i = 0; i < _trianglesCount * 3; i += 3){ - Vertex p1 = _vertices->at(_triangles->at(i)); - Vertex p2 = _vertices->at(_triangles->at(i + 1)); - Vertex p3 = _vertices->at(_triangles->at(i + 2)); - Tetrahedron t(_referencePoint, p1, p2, p3); - _tetrahedra.push_back(t); - } -} - -int MassProperties::getTriangleCount() const{ - return _trianglesCount; -} - -int MassProperties::getVerticesCount() const{ - return _verticesCount; -} - -Vertex MassProperties::getCenterOfMass() const{ - return _centerOfMass; -} - -int MassProperties::getTetrahedraCount() const{ - return _tetrahedra.size(); -} - -vector MassProperties::getTetrahedra() const{ - return _tetrahedra; -} - -vector MassProperties::getMassProperties(){ - vector volumeAndInertia; - double volume = 0.0; - glm::vec3 centerOfMass; - glm::mat3 globalInertia(0.0); - glm::mat3 globalProductInertia(0.0); - - //Translate accumulated center of mass from each tetrahedron to mesh center of mass using parallel axis theorem - for(Tetrahedron tet : _tetrahedra){ - vector tetMassProperties = tet.getVolumeAndInertia(); - volume += tetMassProperties.at(0); //volume - centerOfMass += tet.getCentroid() * (float)tetMassProperties.at(0); - } - - if (volume != 0){ - _centerOfMass = (centerOfMass / (float)volume); - } - - //Translate the moment of inertia from each tetrahedron to mesh center of mass using parallel axis theorem - for(Tetrahedron tet : _tetrahedra){ - vector tetMassProperties = tet.getVolumeAndInertia(); - glm::mat3 identity; - glm::vec3 diff = _centerOfMass - tet.getCentroid(); - float diffDot = glm::dot(diff, diff); - glm::mat3 outerDiff = glm::outerProduct(diff, diff); - - //3x3 of local inertia tensors of each tetrahedron. Inertia tensors are the diagonal elements - glm::mat3 localMomentInertia = { Vertex(tetMassProperties.at(1), 0.0f, 0.0f), Vertex(0.0f, tetMassProperties.at(2), 0.0f), - Vertex(0.0f, 0.0f, tetMassProperties.at(3)) }; - glm::mat3 localProductInertia = { Vertex(tetMassProperties.at(4), 0.0f, 0.0f), Vertex(0.0f, tetMassProperties.at(5), 0.0f), - Vertex(0.0f, 0.0f, tetMassProperties.at(6)) }; - - //Parallel axis theorem J = I * m[(R.R)*Identity - RxR] where x is outer cross product - globalInertia += localMomentInertia + (float)tetMassProperties.at(0) * ((diffDot*identity) - outerDiff); - globalProductInertia += localProductInertia + (float)tetMassProperties.at(0) * ((diffDot * identity) - outerDiff); - } - volumeAndInertia.push_back(volume); - volumeAndInertia.push_back(globalInertia[0][0]); - volumeAndInertia.push_back(globalInertia[1][1]); - volumeAndInertia.push_back(globalInertia[2][2]); - volumeAndInertia.push_back(globalProductInertia[0][0]); - volumeAndInertia.push_back(globalProductInertia[1][1]); - volumeAndInertia.push_back(globalProductInertia[2][2]); - return volumeAndInertia; -} \ No newline at end of file diff --git a/libraries/physics/src/MassProperties.h b/libraries/physics/src/MassProperties.h deleted file mode 100644 index 240e0e53f7..0000000000 --- a/libraries/physics/src/MassProperties.h +++ /dev/null @@ -1,67 +0,0 @@ -// -// MassProperties.h -// libraries/physics/src -// -// Created by Virendra Singh 2015.02.28 -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// -#ifndef hifi_MassProperties_h -#define hifi_MassProperties_h - -#include -#include -#include -#include -using namespace std; -namespace massproperties{ - typedef glm::vec3 Vertex; - typedef vector Triangle; - - //Tetrahedron class containing the base triangle and the apex. - class Tetrahedron{ - private: - Vertex _w; //apex - Vertex _x; - Vertex _y; - Vertex _z; - double _volume; - vector _volumeAndInertia; - void computeInertia(); - void computeVolume(); - public: - Tetrahedron(const Vertex p1, const Vertex p2, const Vertex p3, const Vertex p4); - ~Tetrahedron(); - Vertex getX() const; - Vertex getY() const; - Vertex getZ() const; - Vertex getw() const; - Vertex getCentroid() const; - vector getVolumeAndInertia() const; - }; - - class MassProperties{ - private: - int _trianglesCount; - int _tetrahedraCount; - int _verticesCount; - vector *_vertices; - Vertex _referencePoint; - Vertex _centerOfMass; - Triangle *_triangles; - vector _tetrahedra; - void generateTetrahedra(); - public: - MassProperties(vector *vertices, Triangle *triangles, Vertex refewrencepoint); - ~MassProperties(); - int getTriangleCount() const; - int getVerticesCount() const; - int getTetrahedraCount() const; - Vertex getCenterOfMass() const; - vector getTetrahedra() const; - vector getMassProperties(); - }; -} -#endif // hifi_MassProperties_h \ No newline at end of file diff --git a/libraries/physics/src/MeshInfo.cpp b/libraries/physics/src/MeshInfo.cpp new file mode 100644 index 0000000000..b406cff269 --- /dev/null +++ b/libraries/physics/src/MeshInfo.cpp @@ -0,0 +1,160 @@ +// +// MeshInfo.cpp +// libraries/physics/src +// +// Created by Virendra Singh 2015.02.28 +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "MeshInfo.h" +#include df +using namespace meshinfo; + +//class to compute volume, mass, center of mass, and inertia tensor of a mesh. +//origin is the default reference point for generating the tetrahedron from each triangle of the mesh. + +MeshInfo::MeshInfo(vector *vertices, vector *triangles) :\ +_vertices(vertices), +_triangles(triangles), +_centerOfMass(Vertex(0.0, 0.0, 0.0)){ +} + +MeshInfo::~MeshInfo(){ + + _vertices = NULL; + _triangles = NULL; + +} + +inline Vertex MeshInfo::getCentroid(const Vertex p1, const Vertex p2, const Vertex p3, const Vertex p4) const{ + Vertex com; + com.x = (p1.x + p2.x + p3.x + p4.x) / 4.0f; + com.y = (p2.y + p2.y + p3.y + p4.y) / 4.0f; + com.z = (p2.z + p2.z + p3.z + p4.z) / 4.0f; + return com; +} + +inline float MeshInfo::getVolume(const Vertex p1, const Vertex p2, const Vertex p3, const Vertex p4) const{ + glm::mat4 tet = { glm::vec4(p1.x, p2.x, p3.x, p4.x), glm::vec4(p1.y, p2.y, p3.y, p4.y), glm::vec4(p1.z, p2.z, p3.z, p4.z), + glm::vec4(1.0f, 1.0f, 1.0f, 1.0f) }; + return glm::determinant(tet) / 6.0f; +} + +Vertex MeshInfo::getMeshCentroid() const{ + return _centerOfMass; +} + +vector MeshInfo::computeMassProperties(){ + vector volumeAndInertia = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 }; + Vertex p0(0.0, 0.0, 0.0); + float meshVolume = 0.0f; + glm::mat3 globalMomentOfInertia(0.0); + glm::mat3 globalProductOfInertia(0.0); + + //First we need need the center of mass of the mesh in order to translate the tetrahedron inertia to center of mass of the mesh. + for (int i = 0; i < _triangles->size(); i += 3){ + Vertex p1 = _vertices->at(_triangles->at(i)); + Vertex p2 = _vertices->at(_triangles->at(i + 1)); + Vertex p3 = _vertices->at(_triangles->at(i + 2)); + float volume = getVolume(p1, p2, p3, p0); + Vertex com = getCentroid(p0, p1, p2, p3); + //Translate accumulated center of mass from each tetrahedron to mesh's center of mass using parallel axis theorem + meshVolume += volume; + _centerOfMass += com * volume; + } + if (meshVolume == 0){ + return volumeAndInertia; + } + _centerOfMass = (_centerOfMass / (float)meshVolume); + + //Translate the moment of inertia from each tetrahedron to mesh's center of mass using parallel axis theorem + for (int i = 0; i < _triangles->size(); i += 3){ + Vertex p1 = _vertices->at(_triangles->at(i)); + Vertex p2 = _vertices->at(_triangles->at(i + 1)); + Vertex p3 = _vertices->at(_triangles->at(i + 2)); + float volume = getVolume(p1, p2, p3, p0); + Vertex com = getCentroid(p0, p1, p2, p3); + glm::mat3 identity; + Vertex diff = _centerOfMass - com; + float diffDot = glm::dot(diff, diff); + glm::mat3 outerDiff = glm::outerProduct(diff, diff); + //centroid is used for calculating inertia tensor relative to center of mass. + // translate the tetrahedron to its center of mass using P = P - centroid + p0 = p0 - com; + p1 = p1 - com; + p2 = p2 - com; + p3 = p3 - com; + + //Calculate inertia tensor based on Tonon's Formulae given in the paper mentioned below. + //http://docsdrive.com/pdfs/sciencepublications/jmssp/2005/8-11.pdf + //Explicit exact formulas for the 3-D tetrahedron inertia tensor in terms of its vertex coordinates - F.Tonon + + float inertia_a = (volume * 6.0 / 60.0) * ( + p0.y*p0.y + p0.y*p1.y + p0.y*p2.y + p0.y*p3.y + + p1.y*p1.y + p1.y*p2.y + p1.y*p3.y + + p2.y*p2.y + p2.y*p3.y + + p3.y*p3.y + + p0.z*p0.z + p0.z*p1.z + p0.z*p2.z + p0.z*p3.z + + p1.z*p1.z + p1.z*p2.z + p1.z*p3.z + + p2.z*p2.z + p2.z*p3.z + + p3.z*p3.z); + + float inertia_b = (volume * 6.0 / 60.0) * ( + p0.x*p0.x + p0.x*p1.x + p0.x*p2.x + p0.x*p3.x + + p1.x*p1.x + p1.x*p2.x + p1.x*p3.x + + p2.x*p2.x + p2.x*p3.x + + p3.x*p3.x + + p0.z*p0.z + p0.z*p1.z + p0.z*p2.z + p0.z*p3.z + + p1.z*p1.z + p1.z*p2.z + p1.z*p3.z + + p2.z*p2.z + p2.z*p3.z + + p3.z*p3.z); + + float inertia_c = (volume * 6.0 / 60.0) * ( + p0.x*p0.x + p0.x*p1.x + p0.x*p2.x + p0.x*p3.x + + p1.x*p1.x + p1.x*p2.x + p1.x*p3.x + + p2.x*p2.x + p2.x*p3.x + + p3.x*p3.x + + p0.y*p0.y + p0.y*p1.y + p0.y*p2.y + p0.y*p3.y + + p1.y*p1.y + p1.y*p2.y + p1.y*p3.y + + p2.y*p2.y + p2.y*p3.y + + p3.y*p3.y); + + float inertia_aa = (volume * 6.0 / 120.0) * (2.0 * (p0.y*p0.z + p1.y*p1.z + p2.y*p2.z + p3.y*p3.z) + + p0.y*p1.z + p0.y*p2.z + p0.y*p3.z + + p1.y*p0.z + p1.y*p2.z + p1.y*p3.z + + p2.y*p0.z + p2.y*p1.z + p2.y*p3.z + + p3.y*p0.z + p3.y*p1.z + p3.y*p2.z); + + float inertia_bb = (volume * 6.0 / 120.0) * (2.0 * (p0.x*p0.z + p1.x*p1.z + p2.x*p2.z + p3.x*p3.z) + + p0.x*p1.z + p0.x*p2.z + p0.x*p3.z + + p1.x*p0.z + p1.x*p2.z + p1.x*p3.z + + p2.x*p0.z + p2.x*p1.z + p2.x*p3.z + + p3.x*p0.z + p3.x*p1.z + p3.x*p2.z); + + float inertia_cc = (volume * 6.0 / 120.0) * (2.0 * (p0.x*p0.y + p1.x*p1.y + p2.x*p2.y + p3.x*p3.y) + + p0.x*p1.y + p0.x*p2.y + p0.x*p3.y + + p1.x*p0.y + p1.x*p2.y + p1.x*p3.y + + p2.x*p0.y + p2.x*p1.y + p2.x*p3.y + + p3.x*p0.y + p3.x*p1.y + p3.x*p2.y); + //3x3 of local inertia tensors of each tetrahedron. Inertia tensors are the diagonal elements + glm::mat3 localMomentInertia = { Vertex(inertia_a, 0.0f, 0.0f), Vertex(0.0f, inertia_b, 0.0f), + Vertex(0.0f, 0.0f, inertia_c) }; + glm::mat3 localProductInertia = { Vertex(inertia_aa, 0.0f, 0.0f), Vertex(0.0f, inertia_bb, 0.0f), + Vertex(0.0f, 0.0f, inertia_cc) }; + + //Parallel axis theorem J = I * m[(R.R)*Identity - RxR] where x is outer cross product + globalMomentOfInertia += localMomentInertia + volume * ((diffDot*identity) - outerDiff); + globalProductOfInertia += localProductInertia + volume * ((diffDot * identity) - outerDiff); + } + volumeAndInertia.push_back(meshVolume); + volumeAndInertia.push_back(globalMomentOfInertia[0][0]); + volumeAndInertia.push_back(globalMomentOfInertia[1][1]); + volumeAndInertia.push_back(globalMomentOfInertia[2][2]); + volumeAndInertia.push_back(globalProductOfInertia[0][0]); + volumeAndInertia.push_back(globalProductOfInertia[1][1]); + volumeAndInertia.push_back(globalProductOfInertia[2][2]); + return volumeAndInertia; +} \ No newline at end of file diff --git a/libraries/physics/src/MeshInfo.h b/libraries/physics/src/MeshInfo.h new file mode 100644 index 0000000000..b400ef9cf0 --- /dev/null +++ b/libraries/physics/src/MeshInfo.h @@ -0,0 +1,34 @@ +// +// MeshInfo.h +// libraries/physics/src +// +// Created by Virendra Singh 2015.02.28 +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#ifndef hifi_MeshInfo_h +#define hifi_MeshInfo_h +#include +#include +#include +using namespace std; +namespace meshinfo{ + typedef glm::vec3 Vertex; + class MeshInfo{ + private: + inline float getVolume(const Vertex p1, const Vertex p2, const Vertex p3, const Vertex p4) const; + vector computeVolumeAndInertia(const Vertex p1, const Vertex p2, const Vertex p3, const Vertex p4) const; + public: + vector *_vertices; + Vertex _centerOfMass; + vector *_triangles; + MeshInfo(vector *vertices, vector *triangles); + ~MeshInfo(); + inline Vertex getCentroid(const Vertex p1, const Vertex p2, const Vertex p3, const Vertex p4) const; + Vertex getMeshCentroid() const; + vector computeMassProperties(); + }; +} +#endif // hifi_MeshInfo_h \ No newline at end of file diff --git a/tests/physics/src/MassPropertiesTests.cpp b/tests/physics/src/MassPropertiesTests.cpp deleted file mode 100644 index f36a494b35..0000000000 --- a/tests/physics/src/MassPropertiesTests.cpp +++ /dev/null @@ -1,232 +0,0 @@ -// -// MassPropertiesTests.cpp -// tests/physics/src -// -// Created by Virendra Singh on 2015.03.02 -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include -#include -#include - -#include "MassPropertiesTests.h" - -void MassPropertiesTests::testWithTetrahedron(){ - glm::vec3 p0(8.33220, -11.86875, 0.93355); - glm::vec3 p1(0.75523, 5.00000, 16.37072); - glm::vec3 p2(52.61236, 5.00000, -5.38580); - glm::vec3 p3(2.00000, 5.00000, 3.00000); - glm::vec3 centroid(15.92492, 0.782813, 3.72962); - double volume = 1873.233236; - double inertia_a = 43520.33257; - double inertia_b = 194711.28938; - double inertia_c = 191168.76173; - double inertia_aa = 4417.66150; - double inertia_bb = -46343.16662; - double inertia_cc = 11996.20119; - massproperties::Tetrahedron tet(p0, p1, p2, p3); - glm::vec3 diff = centroid - tet.getCentroid(); - vector voumeAndInertia = tet.getVolumeAndInertia(); - std::cout << std::setprecision(12); - //test if centroid is correct - if (diff.x > epsilion || diff.y > epsilion || diff.z > epsilion){ - std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Centroid is incorrect : Expected = " << centroid.x << " " << - centroid.y << " " << centroid.z << ", actual = " << tet.getCentroid().x << " " << tet.getCentroid().y << - " " << tet.getCentroid().z << std::endl; - } - - //test if volume is correct - if (abs(volume - voumeAndInertia.at(0)) > epsilion){ - std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Volume is incorrect : Expected = " << volume << " " << - ", actual = " << voumeAndInertia.at(0) << std::endl; - } - - //test if moment of inertia with respect to x axis is correct - if (abs(inertia_a - (voumeAndInertia.at(1))) > epsilion){ - std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Moment of inertia with respect to x axis is incorrect : Expected = " << - inertia_a << " " << ", actual = " << (voumeAndInertia.at(1)) << std::endl; - } - - //test if moment of inertia with respect to y axis is correct - if (abs(inertia_b - (voumeAndInertia.at(2))) > epsilion){ - std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Moment of inertia with respect to y axis is incorrect : Expected = " << - inertia_b << " " << ", actual = " << (voumeAndInertia.at(2)) << std::endl; - } - - //test if moment of inertia with respect to z axis is correct - if (abs(inertia_c - (voumeAndInertia.at(3))) > epsilion){ - std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Moment of inertia with respect to z axis is incorrect : Expected = " << - inertia_c << " " << ", actual = " << (voumeAndInertia.at(3)) << std::endl; - } - - //test if product of inertia with respect to x axis is correct - if (abs(inertia_aa - (voumeAndInertia.at(4))) > epsilion){ - std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Product of inertia with respect to x axis is incorrect : Expected = " << - inertia_aa << " " << ", actual = " << (voumeAndInertia.at(4)) << std::endl; - } - - //test if product of inertia with respect to y axis is correct - if (abs(inertia_bb - (voumeAndInertia.at(5))) > epsilion){ - std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Product of inertia with respect to y axis is incorrect : Expected = " << - inertia_bb << " " << ", actual = " << (voumeAndInertia.at(5)) << std::endl; - } - - //test if product of inertia with respect to z axis is correct - if (abs(inertia_cc - (voumeAndInertia.at(6))) > epsilion){ - std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Product of inertia with respect to z axis is incorrect : Expected = " << - inertia_cc << " " << ", actual = " << (voumeAndInertia.at(6)) << std::endl; - } - -} - -void MassPropertiesTests::testWithCube(){ - massproperties::Vertex p0(1.0, -1.0, -1.0); - massproperties::Vertex p1(1.0, -1.0, 1.0); - massproperties::Vertex p2(-1.0, -1.0, 1.0); - massproperties::Vertex p3(-1.0, -1.0, -1.0); - massproperties::Vertex p4(1.0, 1.0, -1.0); - massproperties::Vertex p5(1.0, 1.0, 1.0); - massproperties::Vertex p6(-1.0, 1.0, 1.0); - massproperties::Vertex p7(-1.0, 1.0, -1.0); - vector vertices; - vertices.push_back(p0); - vertices.push_back(p1); - vertices.push_back(p2); - vertices.push_back(p3); - vertices.push_back(p4); - vertices.push_back(p5); - vertices.push_back(p6); - vertices.push_back(p7); - std::cout << std::setprecision(10); - vector triangles = { 0, 1, 2, 0, 2, 3, 4, 7, 6, 4, 6, 5, 0, 4, 5, 0, 5, 1, 1, 5, 6, 1, 6, 2, 2, 6, - 7, 2, 7, 3, 4, 0, 3, 4, 3, 7 }; - glm::vec3 centerOfMass(0.0, 0.0, 0.0); - double volume = 8.0; - double side = 2.0; - double inertia = (volume * side * side) / 6.0; //inertia of a unit cube is (mass * side * side) /6 - - //test with origin as reference point - massproperties::MassProperties massProp1(&vertices, &triangles, {}); - vector volumeAndInertia1 = massProp1.getMassProperties(); - if (abs(centerOfMass.x - massProp1.getCenterOfMass().x) > epsilion || abs(centerOfMass.y - massProp1.getCenterOfMass().y) > epsilion || - abs(centerOfMass.z - massProp1.getCenterOfMass().z) > epsilion){ - std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Center of mass is incorrect : Expected = " << centerOfMass.x << " " << - centerOfMass.y << " " << centerOfMass.z << ", actual = " << massProp1.getCenterOfMass().x << " " << - massProp1.getCenterOfMass().y << " " << massProp1.getCenterOfMass().z << std::endl; - } - - if (abs(volume - volumeAndInertia1.at(0)) > epsilion){ - std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Volume is incorrect : Expected = " << volume << - ", actual = " << volumeAndInertia1.at(0) << std::endl; - } - - if (abs(inertia - (volumeAndInertia1.at(1))) > epsilion || abs(inertia - (volumeAndInertia1.at(2))) > epsilion || - abs(inertia - (volumeAndInertia1.at(3))) > epsilion){ - std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Moment is incorrect : Expected = " << inertia << " " << - inertia << " " << inertia << ", actual = " << (volumeAndInertia1.at(1)) << " " << (volumeAndInertia1.at(2)) << - " " << (volumeAndInertia1.at(3)) << std::endl; - } - - //test with {2,2,2} as reference point - massproperties::MassProperties massProp2(&vertices, &triangles, { 2, 2, 2 }); - vector volumeAndInertia2 = massProp2.getMassProperties(); - if (abs(centerOfMass.x - massProp2.getCenterOfMass().x) > epsilion || abs(centerOfMass.y - massProp2.getCenterOfMass().y) > epsilion || - abs(centerOfMass.z - massProp2.getCenterOfMass().z) > epsilion){ - std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Center of mass is incorrect : Expected = " << centerOfMass.x << - " " << centerOfMass.y << " " << centerOfMass.z << ", actual = " << massProp2.getCenterOfMass().x << " " << - massProp2.getCenterOfMass().y << " " << massProp2.getCenterOfMass().z << std::endl; - } - - if (abs(volume - volumeAndInertia2.at(0)) > epsilion){ - std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Volume is incorrect : Expected = " << volume << - ", actual = " << volumeAndInertia2.at(0) << std::endl; - } - - if (abs(inertia - (volumeAndInertia2.at(1))) > epsilion || abs(inertia - (volumeAndInertia2.at(2))) > epsilion || - abs(inertia - (volumeAndInertia2.at(3))) > epsilion){ - std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Moment is incorrect : Expected = " << inertia << " " << - inertia << " " << inertia << ", actual = " << (volumeAndInertia2.at(1)) << " " << (volumeAndInertia2.at(2)) << - " " << (volumeAndInertia2.at(3)) << std::endl; - } -} - -void MassPropertiesTests::testWithUnitCube() -{ - massproperties::Vertex p0(0, 0, 1); - massproperties::Vertex p1(1, 0, 1); - massproperties::Vertex p2(0, 1, 1); - massproperties::Vertex p3(1, 1, 1); - massproperties::Vertex p4(0, 0, 0); - massproperties::Vertex p5(1, 0, 0); - massproperties::Vertex p6(0, 1, 0); - massproperties::Vertex p7(1, 1, 0); - vector vertices; - vertices.push_back(p0); - vertices.push_back(p1); - vertices.push_back(p2); - vertices.push_back(p3); - vertices.push_back(p4); - vertices.push_back(p5); - vertices.push_back(p6); - vertices.push_back(p7); - vector triangles = { 0, 1, 2, 1, 3, 2, 2, 3, 7, 2, 7, 6, 1, 7, 3, 1, 5, 7, 6, 7, 4, 7, 5, 4, 0, 4, 1, - 1, 4, 5, 2, 6, 4, 0, 2, 4 }; - glm::vec3 centerOfMass(0.5, 0.5, 0.5); - double volume = 1.0; - double side = 1.0; - double inertia = (volume * side * side) / 6.0; //inertia of a unit cube is (mass * side * side) /6 - std::cout << std::setprecision(10); - - //test with origin as reference point - massproperties::MassProperties massProp1(&vertices, &triangles, {}); - vector volumeAndInertia1 = massProp1.getMassProperties(); - if (abs(centerOfMass.x - massProp1.getCenterOfMass().x) > epsilion || abs(centerOfMass.y - massProp1.getCenterOfMass().y) > epsilion || - abs(centerOfMass.z - massProp1.getCenterOfMass().z) > epsilion){ - std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Center of mass is incorrect : Expected = " << centerOfMass.x << - " " << centerOfMass.y << " " << centerOfMass.z << ", actual = " << massProp1.getCenterOfMass().x << " " << - massProp1.getCenterOfMass().y << " " << massProp1.getCenterOfMass().z << std::endl; - } - - if (abs(volume - volumeAndInertia1.at(0)) > epsilion){ - std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Volume is incorrect : Expected = " << volume << - ", actual = " << volumeAndInertia1.at(0) << std::endl; - } - - if (abs(inertia - (volumeAndInertia1.at(1))) > epsilion || abs(inertia - (volumeAndInertia1.at(2))) > epsilion || - abs(inertia - (volumeAndInertia1.at(3))) > epsilion){ - std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Moment is incorrect : Expected = " << inertia << " " << - inertia << " " << inertia << ", actual = " << (volumeAndInertia1.at(1)) << " " << (volumeAndInertia1.at(2)) << - " " << (volumeAndInertia1.at(3)) << std::endl; - } - - //test with {2,1,2} as reference point - massproperties::MassProperties massProp2(&vertices, &triangles, { 2, 1, 2 }); - vector volumeAndInertia2 = massProp2.getMassProperties(); - if (abs(centerOfMass.x - massProp2.getCenterOfMass().x) > epsilion || abs(centerOfMass.y - massProp2.getCenterOfMass().y) > epsilion || - abs(centerOfMass.z - massProp2.getCenterOfMass().z) > epsilion){ - std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Center of mass is incorrect : Expected = " << centerOfMass.x << " " << - centerOfMass.y << " " << centerOfMass.z << ", actual = " << massProp2.getCenterOfMass().x << " " << - massProp2.getCenterOfMass().y << " " << massProp2.getCenterOfMass().z << std::endl; - } - - if (abs(volume - volumeAndInertia2.at(0)) > epsilion){ - std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Volume is incorrect : Expected = " << volume << - ", actual = " << volumeAndInertia2.at(0) << std::endl; - } - - if (abs(inertia - (volumeAndInertia2.at(1))) > epsilion || abs(inertia - (volumeAndInertia2.at(2))) > epsilion || - abs(inertia - (volumeAndInertia2.at(3))) > epsilion){ - std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Moment is incorrect : Expected = " << inertia << " " << - inertia << " " << inertia << ", actual = " << (volumeAndInertia2.at(1)) << " " << (volumeAndInertia2.at(2)) << - " " << (volumeAndInertia2.at(3)) << std::endl; - } -} -void MassPropertiesTests::runAllTests(){ - testWithTetrahedron(); - testWithUnitCube(); - testWithCube(); -} \ No newline at end of file diff --git a/tests/physics/src/MeshInfoTests.cpp b/tests/physics/src/MeshInfoTests.cpp new file mode 100644 index 0000000000..69a6964128 --- /dev/null +++ b/tests/physics/src/MeshInfoTests.cpp @@ -0,0 +1,198 @@ +// +// MeshInfoTests.cpp +// tests/physics/src +// +// Created by Virendra Singh on 2015.03.02 +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include +#include +#include + +#include "MeshInfoTests.h" +const double epsilon = 0.02; +void MeshInfoTests::testWithTetrahedron(){ + glm::vec3 p0(8.33220, -11.86875, 0.93355); + glm::vec3 p1(0.75523, 5.00000, 16.37072); + glm::vec3 p2(52.61236, 5.00000, -5.38580); + glm::vec3 p3(2.00000, 5.00000, 3.00000); + glm::vec3 centroid(15.92492, 0.782813, 3.72962); + + //translate the tetrahedron so that its apex is on origin + glm::vec3 p11 = p1 - p0; + glm::vec3 p22 = p2 - p0; + glm::vec3 p33 = p3 - p0; + vector vertices = { p11, p22, p33 }; + vector triangles = { 0, 1, 2 }; + + float volume = 1873.233236; + float inertia_a = 43520.33257; + float inertia_b = 194711.28938; + float inertia_c = 191168.76173; + float inertia_aa = 4417.66150; + float inertia_bb = -46343.16662; + float inertia_cc = 11996.20119; + + meshinfo::MeshInfo meshinfo(&vertices,&triangles); + glm::vec3 tetCenterOfMass = meshinfo.getCentroid(p0, p1, p2, p3); + glm::vec3 diff = centroid - tetCenterOfMass; + vector voumeAndInertia = meshinfo.computeMassProperties(); + std::cout << std::setprecision(12); + //test if centroid is correct + if (diff.x > epsilon || diff.y > epsilon || diff.z > epsilon){ + std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Centroid is incorrect : Expected = " << centroid.x << " " << + centroid.y << " " << centroid.z << ", actual = " << tetCenterOfMass.x << " " << tetCenterOfMass.y << + " " << tetCenterOfMass.z << std::endl; + } + + //test if volume is correct + if (abs(volume - voumeAndInertia.at(0)) > epsilon){ + std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Volume is incorrect : Expected = " << volume << " " << + ", actual = " << voumeAndInertia.at(0) << std::endl; + } + + //test if moment of inertia with respect to x axis is correct + if (abs(inertia_a - (voumeAndInertia.at(1))) > epsilon){ + std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Moment of inertia with respect to x axis is incorrect : Expected = " << + inertia_a << " " << ", actual = " << (voumeAndInertia.at(1)) << std::endl; + } + + //test if moment of inertia with respect to y axis is correct + if (abs(inertia_b - (voumeAndInertia.at(2))) > epsilon){ + std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Moment of inertia with respect to y axis is incorrect : Expected = " << + inertia_b << " " << ", actual = " << (voumeAndInertia.at(2)) << std::endl; + } + + //test if moment of inertia with respect to z axis is correct + if (abs(inertia_c - (voumeAndInertia.at(3))) > epsilon){ + std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Moment of inertia with respect to z axis is incorrect : Expected = " << + inertia_c << " " << ", actual = " << (voumeAndInertia.at(3)) << std::endl; + } + + //test if product of inertia with respect to x axis is correct + if (abs(inertia_aa - (voumeAndInertia.at(4))) > epsilon){ + std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Product of inertia with respect to x axis is incorrect : Expected = " << + inertia_aa << " " << ", actual = " << (voumeAndInertia.at(4)) << std::endl; + } + + //test if product of inertia with respect to y axis is correct + if (abs(inertia_bb - (voumeAndInertia.at(5))) > epsilon){ + std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Product of inertia with respect to y axis is incorrect : Expected = " << + inertia_bb << " " << ", actual = " << (voumeAndInertia.at(5)) << std::endl; + } + + //test if product of inertia with respect to z axis is correct + if (abs(inertia_cc - (voumeAndInertia.at(6))) > epsilon){ + std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Product of inertia with respect to z axis is incorrect : Expected = " << + inertia_cc << " " << ", actual = " << (voumeAndInertia.at(6)) << std::endl; + } + +} + +void MeshInfoTests::testWithCube(){ + glm::vec3 p0(1.0, -1.0, -1.0); + glm::vec3 p1(1.0, -1.0, 1.0); + glm::vec3 p2(-1.0, -1.0, 1.0); + glm::vec3 p3(-1.0, -1.0, -1.0); + glm::vec3 p4(1.0, 1.0, -1.0); + glm::vec3 p5(1.0, 1.0, 1.0); + glm::vec3 p6(-1.0, 1.0, 1.0); + glm::vec3 p7(-1.0, 1.0, -1.0); + vector vertices; + vertices.push_back(p0); + vertices.push_back(p1); + vertices.push_back(p2); + vertices.push_back(p3); + vertices.push_back(p4); + vertices.push_back(p5); + vertices.push_back(p6); + vertices.push_back(p7); + std::cout << std::setprecision(10); + vector triangles = { 0, 1, 2, 0, 2, 3, 4, 7, 6, 4, 6, 5, 0, 4, 5, 0, 5, 1, 1, 5, 6, 1, 6, 2, 2, 6, + 7, 2, 7, 3, 4, 0, 3, 4, 3, 7 }; + glm::vec3 centerOfMass(0.0, 0.0, 0.0); + double volume = 8.0; + double side = 2.0; + double inertia = (volume * side * side) / 6.0; //inertia of a unit cube is (mass * side * side) /6 + + //test with origin as reference point + meshinfo::MeshInfo massProp1(&vertices, &triangles); + vector volumeAndInertia1 = massProp1.computeMassProperties(); + if (abs(centerOfMass.x - massProp1.getMeshCentroid().x) > epsilon || abs(centerOfMass.y - massProp1.getMeshCentroid().y) > epsilon || + abs(centerOfMass.z - massProp1.getMeshCentroid().z) > epsilon){ + std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Center of mass is incorrect : Expected = " << centerOfMass.x << " " << + centerOfMass.y << " " << centerOfMass.z << ", actual = " << massProp1.getMeshCentroid().x << " " << + massProp1.getMeshCentroid().y << " " << massProp1.getMeshCentroid().z << std::endl; + } + + if (abs(volume - volumeAndInertia1.at(0)) > epsilon){ + std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Volume is incorrect : Expected = " << volume << + ", actual = " << volumeAndInertia1.at(0) << std::endl; + } + + if (abs(inertia - (volumeAndInertia1.at(1))) > epsilon || abs(inertia - (volumeAndInertia1.at(2))) > epsilon || + abs(inertia - (volumeAndInertia1.at(3))) > epsilon){ + std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Moment of inertia is incorrect : Expected = " << inertia << " " << + inertia << " " << inertia << ", actual = " << (volumeAndInertia1.at(1)) << " " << (volumeAndInertia1.at(2)) << + " " << (volumeAndInertia1.at(3)) << std::endl; + } +} + +void MeshInfoTests::testWithUnitCube() +{ + glm::vec3 p0(0, 0, 1); + glm::vec3 p1(1, 0, 1); + glm::vec3 p2(0, 1, 1); + glm::vec3 p3(1, 1, 1); + glm::vec3 p4(0, 0, 0); + glm::vec3 p5(1, 0, 0); + glm::vec3 p6(0, 1, 0); + glm::vec3 p7(1, 1, 0); + vector vertices; + vertices.push_back(p0); + vertices.push_back(p1); + vertices.push_back(p2); + vertices.push_back(p3); + vertices.push_back(p4); + vertices.push_back(p5); + vertices.push_back(p6); + vertices.push_back(p7); + vector triangles = { 0, 1, 2, 1, 3, 2, 2, 3, 7, 2, 7, 6, 1, 7, 3, 1, 5, 7, 6, 7, 4, 7, 5, 4, 0, 4, 1, + 1, 4, 5, 2, 6, 4, 0, 2, 4 }; + glm::vec3 centerOfMass(0.5, 0.5, 0.5); + double volume = 1.0; + double side = 1.0; + double inertia = (volume * side * side) / 6.0; //inertia of a unit cube is (mass * side * side) /6 + std::cout << std::setprecision(10); + + //test with origin as reference point + meshinfo::MeshInfo massProp1(&vertices, &triangles); + vector volumeAndInertia1 = massProp1.computeMassProperties(); + if (abs(centerOfMass.x - massProp1.getMeshCentroid().x) > epsilon || abs(centerOfMass.y - massProp1.getMeshCentroid().y) > epsilon || + abs(centerOfMass.z - massProp1.getMeshCentroid().z) > epsilon){ + std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Center of mass is incorrect : Expected = " << centerOfMass.x << + " " << centerOfMass.y << " " << centerOfMass.z << ", actual = " << massProp1.getMeshCentroid().x << " " << + massProp1.getMeshCentroid().y << " " << massProp1.getMeshCentroid().z << std::endl; + } + + if (abs(volume - volumeAndInertia1.at(0)) > epsilon){ + std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Volume is incorrect : Expected = " << volume << + ", actual = " << volumeAndInertia1.at(0) << std::endl; + } + + if (abs(inertia - (volumeAndInertia1.at(1))) > epsilon || abs(inertia - (volumeAndInertia1.at(2))) > epsilon || + abs(inertia - (volumeAndInertia1.at(3))) > epsilon){ + std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Moment of inertia is incorrect : Expected = " << inertia << " " << + inertia << " " << inertia << ", actual = " << (volumeAndInertia1.at(1)) << " " << (volumeAndInertia1.at(2)) << + " " << (volumeAndInertia1.at(3)) << std::endl; + } +} +void MeshInfoTests::runAllTests(){ + testWithTetrahedron(); + testWithUnitCube(); + testWithCube(); +} \ No newline at end of file diff --git a/tests/physics/src/MassPropertiesTests.h b/tests/physics/src/MeshInfoTests.h similarity index 66% rename from tests/physics/src/MassPropertiesTests.h rename to tests/physics/src/MeshInfoTests.h index 21ab3ccb0a..d4c954b802 100644 --- a/tests/physics/src/MassPropertiesTests.h +++ b/tests/physics/src/MeshInfoTests.h @@ -1,5 +1,5 @@ // -// MassPropertiesTests.h +// MeshInfoTests.h // tests/physics/src // // Created by Virendra Singh on 2015.03.02 @@ -9,13 +9,12 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#ifndef hifi_MassPropertiesTests_h -#define hifi_MassPropertiesTests_h -#define epsilion 0.02 -namespace MassPropertiesTests{ +#ifndef hifi_MeshInfoTests_h +#define hifi_MeshInfoTests_h +namespace MeshInfoTests{ void testWithTetrahedron(); void testWithUnitCube(); void testWithCube(); void runAllTests(); } -#endif // hifi_MassPropertiesTests_h \ No newline at end of file +#endif // hifi_MeshInfoTests_h \ No newline at end of file diff --git a/tests/physics/src/main.cpp b/tests/physics/src/main.cpp index d42b5d1241..e7dae7aca5 100644 --- a/tests/physics/src/main.cpp +++ b/tests/physics/src/main.cpp @@ -13,7 +13,7 @@ #include "ShapeInfoTests.h" #include "ShapeManagerTests.h" #include "BulletUtilTests.h" -#include "MassPropertiesTests.h" +#include "MeshInfoTests.h" int main(int argc, char** argv) { ShapeColliderTests::runAllTests(); @@ -21,6 +21,6 @@ int main(int argc, char** argv) { ShapeInfoTests::runAllTests(); ShapeManagerTests::runAllTests(); BulletUtilTests::runAllTests(); - MassPropertiesTests::runAllTests(); + MeshInfoTests::runAllTests(); return 0; } From 84e05e361eed107117b6dd0b290282425f85d17b Mon Sep 17 00:00:00 2001 From: Virendra Singh Date: Fri, 6 Mar 2015 22:07:35 +0530 Subject: [PATCH 09/43] Accuracy improved to 0.01 --- libraries/physics/src/MeshInfo.cpp | 221 ++++++++++++++-------------- libraries/physics/src/MeshInfo.h | 18 +-- tests/physics/src/MeshInfoTests.cpp | 126 ++++++++-------- 3 files changed, 184 insertions(+), 181 deletions(-) diff --git a/libraries/physics/src/MeshInfo.cpp b/libraries/physics/src/MeshInfo.cpp index b406cff269..0d9149cb44 100644 --- a/libraries/physics/src/MeshInfo.cpp +++ b/libraries/physics/src/MeshInfo.cpp @@ -24,137 +24,138 @@ _centerOfMass(Vertex(0.0, 0.0, 0.0)){ MeshInfo::~MeshInfo(){ - _vertices = NULL; - _triangles = NULL; + _vertices = NULL; + _triangles = NULL; } inline Vertex MeshInfo::getCentroid(const Vertex p1, const Vertex p2, const Vertex p3, const Vertex p4) const{ - Vertex com; - com.x = (p1.x + p2.x + p3.x + p4.x) / 4.0f; - com.y = (p2.y + p2.y + p3.y + p4.y) / 4.0f; - com.z = (p2.z + p2.z + p3.z + p4.z) / 4.0f; - return com; + Vertex com; + com.x = (p1.x + p2.x + p3.x + p4.x) / 4.0f; + com.y = (p1.y + p2.y + p3.y + p4.y) / 4.0f; + com.z = (p1.z + p2.z + p3.z + p4.z) / 4.0f; + return com; } inline float MeshInfo::getVolume(const Vertex p1, const Vertex p2, const Vertex p3, const Vertex p4) const{ - glm::mat4 tet = { glm::vec4(p1.x, p2.x, p3.x, p4.x), glm::vec4(p1.y, p2.y, p3.y, p4.y), glm::vec4(p1.z, p2.z, p3.z, p4.z), - glm::vec4(1.0f, 1.0f, 1.0f, 1.0f) }; - return glm::determinant(tet) / 6.0f; + glm::mat4 tet = { glm::vec4(p1.x, p2.x, p3.x, p4.x), glm::vec4(p1.y, p2.y, p3.y, p4.y), glm::vec4(p1.z, p2.z, p3.z, p4.z), + glm::vec4(1.0f, 1.0f, 1.0f, 1.0f) }; + return glm::determinant(tet) / 6.0f; } Vertex MeshInfo::getMeshCentroid() const{ - return _centerOfMass; + return _centerOfMass; } vector MeshInfo::computeMassProperties(){ - vector volumeAndInertia = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 }; - Vertex p0(0.0, 0.0, 0.0); - float meshVolume = 0.0f; - glm::mat3 globalMomentOfInertia(0.0); - glm::mat3 globalProductOfInertia(0.0); + vector volumeAndInertia = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 }; + Vertex origin(0.0, 0.0, 0.0); + float meshVolume = 0.0f; + glm::mat3 globalMomentOfInertia(0.0); + glm::mat3 globalProductOfInertia(0.0); - //First we need need the center of mass of the mesh in order to translate the tetrahedron inertia to center of mass of the mesh. - for (int i = 0; i < _triangles->size(); i += 3){ - Vertex p1 = _vertices->at(_triangles->at(i)); - Vertex p2 = _vertices->at(_triangles->at(i + 1)); - Vertex p3 = _vertices->at(_triangles->at(i + 2)); - float volume = getVolume(p1, p2, p3, p0); - Vertex com = getCentroid(p0, p1, p2, p3); - //Translate accumulated center of mass from each tetrahedron to mesh's center of mass using parallel axis theorem - meshVolume += volume; - _centerOfMass += com * volume; - } - if (meshVolume == 0){ - return volumeAndInertia; - } - _centerOfMass = (_centerOfMass / (float)meshVolume); + //First we need need the center of mass of the mesh in order to translate the tetrahedron inertia to center of mass of the mesh. + for (unsigned int i = 0; i < _triangles->size(); i += 3){ + Vertex p1 = _vertices->at(_triangles->at(i)); + Vertex p2 = _vertices->at(_triangles->at(i + 1)); + Vertex p3 = _vertices->at(_triangles->at(i + 2)); + float volume = getVolume(p1, p2, p3, origin); + Vertex com = getCentroid(origin, p1, p2, p3); + //Translate accumulated center of mass from each tetrahedron to mesh's center of mass using parallel axis theorem + meshVolume += volume; + _centerOfMass += com * volume; + } + if (meshVolume == 0){ + return volumeAndInertia; + } + _centerOfMass = (_centerOfMass / (float)meshVolume); - //Translate the moment of inertia from each tetrahedron to mesh's center of mass using parallel axis theorem - for (int i = 0; i < _triangles->size(); i += 3){ - Vertex p1 = _vertices->at(_triangles->at(i)); - Vertex p2 = _vertices->at(_triangles->at(i + 1)); - Vertex p3 = _vertices->at(_triangles->at(i + 2)); - float volume = getVolume(p1, p2, p3, p0); - Vertex com = getCentroid(p0, p1, p2, p3); - glm::mat3 identity; - Vertex diff = _centerOfMass - com; - float diffDot = glm::dot(diff, diff); - glm::mat3 outerDiff = glm::outerProduct(diff, diff); - //centroid is used for calculating inertia tensor relative to center of mass. - // translate the tetrahedron to its center of mass using P = P - centroid - p0 = p0 - com; - p1 = p1 - com; - p2 = p2 - com; - p3 = p3 - com; + //Translate the moment of inertia from each tetrahedron to mesh's center of mass using parallel axis theorem + for (unsigned int i = 0; i < _triangles->size(); i += 3){ + Vertex p1 = _vertices->at(_triangles->at(i)); + Vertex p2 = _vertices->at(_triangles->at(i + 1)); + Vertex p3 = _vertices->at(_triangles->at(i + 2)); + float volume = getVolume(p1, p2, p3, origin); + Vertex com = getCentroid(origin, p1, p2, p3); + glm::mat3 identity; + Vertex diff = _centerOfMass - com; + float diffDot = glm::dot(diff, diff); + glm::mat3 outerDiff = glm::outerProduct(diff, diff); + //centroid is used for calculating inertia tensor relative to center of mass. + // translate the tetrahedron to its center of mass using P = P - centroid + Vertex p0 = origin - com; + p1 = p1 - com; + p2 = p2 - com; + p3 = p3 - com; - //Calculate inertia tensor based on Tonon's Formulae given in the paper mentioned below. - //http://docsdrive.com/pdfs/sciencepublications/jmssp/2005/8-11.pdf - //Explicit exact formulas for the 3-D tetrahedron inertia tensor in terms of its vertex coordinates - F.Tonon + //Calculate inertia tensor based on Tonon's Formulae given in the paper mentioned below. + //http://docsdrive.com/pdfs/sciencepublications/jmssp/2005/8-11.pdf + //Explicit exact formulas for the 3-D tetrahedron inertia tensor in terms of its vertex coordinates - F.Tonon - float inertia_a = (volume * 6.0 / 60.0) * ( - p0.y*p0.y + p0.y*p1.y + p0.y*p2.y + p0.y*p3.y + - p1.y*p1.y + p1.y*p2.y + p1.y*p3.y + - p2.y*p2.y + p2.y*p3.y + - p3.y*p3.y + - p0.z*p0.z + p0.z*p1.z + p0.z*p2.z + p0.z*p3.z + - p1.z*p1.z + p1.z*p2.z + p1.z*p3.z + - p2.z*p2.z + p2.z*p3.z + - p3.z*p3.z); + float inertia_a = (volume * 6.0f / 60.0f) * ( + p0.y*p0.y + p0.y*p1.y + p0.y*p2.y + p0.y*p3.y + + p1.y*p1.y + p1.y*p2.y + p1.y*p3.y + + p2.y*p2.y + p2.y*p3.y + + p3.y*p3.y + + p0.z*p0.z + p0.z*p1.z + p0.z*p2.z + p0.z*p3.z + + p1.z*p1.z + p1.z*p2.z + p1.z*p3.z + + p2.z*p2.z + p2.z*p3.z + + p3.z*p3.z); - float inertia_b = (volume * 6.0 / 60.0) * ( - p0.x*p0.x + p0.x*p1.x + p0.x*p2.x + p0.x*p3.x + - p1.x*p1.x + p1.x*p2.x + p1.x*p3.x + - p2.x*p2.x + p2.x*p3.x + - p3.x*p3.x + - p0.z*p0.z + p0.z*p1.z + p0.z*p2.z + p0.z*p3.z + - p1.z*p1.z + p1.z*p2.z + p1.z*p3.z + - p2.z*p2.z + p2.z*p3.z + - p3.z*p3.z); + float inertia_b = (volume * 6.0f / 60.0f) * ( + p0.x*p0.x + p0.x*p1.x + p0.x*p2.x + p0.x*p3.x + + p1.x*p1.x + p1.x*p2.x + p1.x*p3.x + + p2.x*p2.x + p2.x*p3.x + + p3.x*p3.x + + p0.z*p0.z + p0.z*p1.z + p0.z*p2.z + p0.z*p3.z + + p1.z*p1.z + p1.z*p2.z + p1.z*p3.z + + p2.z*p2.z + p2.z*p3.z + + p3.z*p3.z); - float inertia_c = (volume * 6.0 / 60.0) * ( - p0.x*p0.x + p0.x*p1.x + p0.x*p2.x + p0.x*p3.x + - p1.x*p1.x + p1.x*p2.x + p1.x*p3.x + - p2.x*p2.x + p2.x*p3.x + - p3.x*p3.x + - p0.y*p0.y + p0.y*p1.y + p0.y*p2.y + p0.y*p3.y + - p1.y*p1.y + p1.y*p2.y + p1.y*p3.y + - p2.y*p2.y + p2.y*p3.y + - p3.y*p3.y); + float inertia_c = (volume * 6.0f / 60.0f) * ( + p0.x*p0.x + p0.x*p1.x + p0.x*p2.x + p0.x*p3.x + + p1.x*p1.x + p1.x*p2.x + p1.x*p3.x + + p2.x*p2.x + p2.x*p3.x + + p3.x*p3.x + + p0.y*p0.y + p0.y*p1.y + p0.y*p2.y + p0.y*p3.y + + p1.y*p1.y + p1.y*p2.y + p1.y*p3.y + + p2.y*p2.y + p2.y*p3.y + + p3.y*p3.y); - float inertia_aa = (volume * 6.0 / 120.0) * (2.0 * (p0.y*p0.z + p1.y*p1.z + p2.y*p2.z + p3.y*p3.z) + - p0.y*p1.z + p0.y*p2.z + p0.y*p3.z + - p1.y*p0.z + p1.y*p2.z + p1.y*p3.z + - p2.y*p0.z + p2.y*p1.z + p2.y*p3.z + - p3.y*p0.z + p3.y*p1.z + p3.y*p2.z); + float inertia_aa = (volume * 6.0f / 120.0f) * (2.0f * (p0.y*p0.z + p1.y*p1.z + p2.y*p2.z + p3.y*p3.z) + + p0.y*p1.z + p0.y*p2.z + p0.y*p3.z + + p1.y*p0.z + p1.y*p2.z + p1.y*p3.z + + p2.y*p0.z + p2.y*p1.z + p2.y*p3.z + + p3.y*p0.z + p3.y*p1.z + p3.y*p2.z); - float inertia_bb = (volume * 6.0 / 120.0) * (2.0 * (p0.x*p0.z + p1.x*p1.z + p2.x*p2.z + p3.x*p3.z) + - p0.x*p1.z + p0.x*p2.z + p0.x*p3.z + - p1.x*p0.z + p1.x*p2.z + p1.x*p3.z + - p2.x*p0.z + p2.x*p1.z + p2.x*p3.z + - p3.x*p0.z + p3.x*p1.z + p3.x*p2.z); + float inertia_bb = (volume * 6.0f / 120.0f) * (2.0f * (p0.x*p0.z + p1.x*p1.z + p2.x*p2.z + p3.x*p3.z) + + p0.x*p1.z + p0.x*p2.z + p0.x*p3.z + + p1.x*p0.z + p1.x*p2.z + p1.x*p3.z + + p2.x*p0.z + p2.x*p1.z + p2.x*p3.z + + p3.x*p0.z + p3.x*p1.z + p3.x*p2.z); - float inertia_cc = (volume * 6.0 / 120.0) * (2.0 * (p0.x*p0.y + p1.x*p1.y + p2.x*p2.y + p3.x*p3.y) + - p0.x*p1.y + p0.x*p2.y + p0.x*p3.y + - p1.x*p0.y + p1.x*p2.y + p1.x*p3.y + - p2.x*p0.y + p2.x*p1.y + p2.x*p3.y + - p3.x*p0.y + p3.x*p1.y + p3.x*p2.y); - //3x3 of local inertia tensors of each tetrahedron. Inertia tensors are the diagonal elements - glm::mat3 localMomentInertia = { Vertex(inertia_a, 0.0f, 0.0f), Vertex(0.0f, inertia_b, 0.0f), - Vertex(0.0f, 0.0f, inertia_c) }; - glm::mat3 localProductInertia = { Vertex(inertia_aa, 0.0f, 0.0f), Vertex(0.0f, inertia_bb, 0.0f), - Vertex(0.0f, 0.0f, inertia_cc) }; + float inertia_cc = (volume * 6.0f / 120.0f) * (2.0f * (p0.x*p0.y + p1.x*p1.y + p2.x*p2.y + p3.x*p3.y) + + p0.x*p1.y + p0.x*p2.y + p0.x*p3.y + + p1.x*p0.y + p1.x*p2.y + p1.x*p3.y + + p2.x*p0.y + p2.x*p1.y + p2.x*p3.y + + p3.x*p0.y + p3.x*p1.y + p3.x*p2.y); - //Parallel axis theorem J = I * m[(R.R)*Identity - RxR] where x is outer cross product - globalMomentOfInertia += localMomentInertia + volume * ((diffDot*identity) - outerDiff); - globalProductOfInertia += localProductInertia + volume * ((diffDot * identity) - outerDiff); - } - volumeAndInertia.push_back(meshVolume); - volumeAndInertia.push_back(globalMomentOfInertia[0][0]); - volumeAndInertia.push_back(globalMomentOfInertia[1][1]); - volumeAndInertia.push_back(globalMomentOfInertia[2][2]); - volumeAndInertia.push_back(globalProductOfInertia[0][0]); - volumeAndInertia.push_back(globalProductOfInertia[1][1]); - volumeAndInertia.push_back(globalProductOfInertia[2][2]); - return volumeAndInertia; + //3x3 of local inertia tensors of each tetrahedron. Inertia tensors are the diagonal elements + glm::mat3 localMomentInertia = { Vertex(inertia_a, 0.0f, 0.0f), Vertex(0.0f, inertia_b, 0.0f), + Vertex(0.0f, 0.0f, inertia_c) }; + glm::mat3 localProductInertia = { Vertex(inertia_aa, 0.0f, 0.0f), Vertex(0.0f, inertia_bb, 0.0f), + Vertex(0.0f, 0.0f, inertia_cc) }; + + //Parallel axis theorem J = I * m[(R.R)*Identity - RxR] where x is outer cross product + globalMomentOfInertia += localMomentInertia + volume * ((diffDot*identity) - outerDiff); + globalProductOfInertia += localProductInertia + volume * ((diffDot * identity) - outerDiff); + } + volumeAndInertia[0] = meshVolume; + volumeAndInertia[1] = globalMomentOfInertia[0][0]; + volumeAndInertia[2] = globalMomentOfInertia[1][1]; + volumeAndInertia[3] = globalMomentOfInertia[2][2]; + volumeAndInertia[4] = globalProductOfInertia[0][0]; + volumeAndInertia[5] = globalProductOfInertia[1][1]; + volumeAndInertia[6] = globalProductOfInertia[2][2]; + return volumeAndInertia; } \ No newline at end of file diff --git a/libraries/physics/src/MeshInfo.h b/libraries/physics/src/MeshInfo.h index b400ef9cf0..d267c75ae6 100644 --- a/libraries/physics/src/MeshInfo.h +++ b/libraries/physics/src/MeshInfo.h @@ -16,18 +16,18 @@ using namespace std; namespace meshinfo{ typedef glm::vec3 Vertex; - class MeshInfo{ - private: - inline float getVolume(const Vertex p1, const Vertex p2, const Vertex p3, const Vertex p4) const; - vector computeVolumeAndInertia(const Vertex p1, const Vertex p2, const Vertex p3, const Vertex p4) const; + class MeshInfo{ + private: + inline float getVolume(const Vertex p1, const Vertex p2, const Vertex p3, const Vertex p4) const; + vector computeVolumeAndInertia(const Vertex p1, const Vertex p2, const Vertex p3, const Vertex p4) const; public: - vector *_vertices; - Vertex _centerOfMass; - vector *_triangles; + vector *_vertices; + Vertex _centerOfMass; + vector *_triangles; MeshInfo(vector *vertices, vector *triangles); ~MeshInfo(); - inline Vertex getCentroid(const Vertex p1, const Vertex p2, const Vertex p3, const Vertex p4) const; - Vertex getMeshCentroid() const; + inline Vertex getCentroid(const Vertex p1, const Vertex p2, const Vertex p3, const Vertex p4) const; + Vertex getMeshCentroid() const; vector computeMassProperties(); }; } diff --git a/tests/physics/src/MeshInfoTests.cpp b/tests/physics/src/MeshInfoTests.cpp index 69a6964128..73364b207e 100644 --- a/tests/physics/src/MeshInfoTests.cpp +++ b/tests/physics/src/MeshInfoTests.cpp @@ -14,7 +14,7 @@ #include #include "MeshInfoTests.h" -const double epsilon = 0.02; +const float epsilon = 0.01f; void MeshInfoTests::testWithTetrahedron(){ glm::vec3 p0(8.33220, -11.86875, 0.93355); glm::vec3 p1(0.75523, 5.00000, 16.37072); @@ -22,71 +22,73 @@ void MeshInfoTests::testWithTetrahedron(){ glm::vec3 p3(2.00000, 5.00000, 3.00000); glm::vec3 centroid(15.92492, 0.782813, 3.72962); - //translate the tetrahedron so that its apex is on origin - glm::vec3 p11 = p1 - p0; - glm::vec3 p22 = p2 - p0; - glm::vec3 p33 = p3 - p0; - vector vertices = { p11, p22, p33 }; - vector triangles = { 0, 1, 2 }; + //translate the tetrahedron so that its apex is on origin + glm::vec3 p11 = p1 - p0; + glm::vec3 p22 = p2 - p0; + glm::vec3 p33 = p3 - p0; + vector vertices = { p11, p22, p33 }; + vector triangles = { 0, 1, 2 }; - float volume = 1873.233236; - float inertia_a = 43520.33257; - float inertia_b = 194711.28938; - float inertia_c = 191168.76173; - float inertia_aa = 4417.66150; - float inertia_bb = -46343.16662; - float inertia_cc = 11996.20119; + float volume = 1873.233236f; + float inertia_a = 43520.33257f; + //actual should be 194711.28938f. But for some reason it becomes 194711.296875 during + //runtime due to how floating points are stored. + float inertia_b = 194711.289f; + float inertia_c = 191168.76173f; + float inertia_aa = 4417.66150f; + float inertia_bb = -46343.16662f; + float inertia_cc = 11996.20119f; - meshinfo::MeshInfo meshinfo(&vertices,&triangles); - glm::vec3 tetCenterOfMass = meshinfo.getCentroid(p0, p1, p2, p3); - glm::vec3 diff = centroid - tetCenterOfMass; - vector voumeAndInertia = meshinfo.computeMassProperties(); + meshinfo::MeshInfo meshinfo(&vertices,&triangles); + glm::vec3 tetCenterOfMass = meshinfo.getCentroid(p0, p1, p2, p3); + glm::vec3 diff = centroid - tetCenterOfMass; + vector voumeAndInertia = meshinfo.computeMassProperties(); std::cout << std::setprecision(12); //test if centroid is correct - if (diff.x > epsilon || diff.y > epsilon || diff.z > epsilon){ + if (diff.x > epsilon || diff.y > epsilon || diff.z > epsilon){ std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Centroid is incorrect : Expected = " << centroid.x << " " << - centroid.y << " " << centroid.z << ", actual = " << tetCenterOfMass.x << " " << tetCenterOfMass.y << - " " << tetCenterOfMass.z << std::endl; + centroid.y << " " << centroid.z << ", actual = " << tetCenterOfMass.x << " " << tetCenterOfMass.y << + " " << tetCenterOfMass.z << std::endl; } //test if volume is correct - if (abs(volume - voumeAndInertia.at(0)) > epsilon){ + if (abs(volume - voumeAndInertia.at(0)) > epsilon){ std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Volume is incorrect : Expected = " << volume << " " << ", actual = " << voumeAndInertia.at(0) << std::endl; } //test if moment of inertia with respect to x axis is correct - if (abs(inertia_a - (voumeAndInertia.at(1))) > epsilon){ + if (abs(inertia_a - (voumeAndInertia.at(1))) > epsilon){ std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Moment of inertia with respect to x axis is incorrect : Expected = " << - inertia_a << " " << ", actual = " << (voumeAndInertia.at(1)) << std::endl; + inertia_a << " " << ", actual = " << voumeAndInertia.at(1) << std::endl; } //test if moment of inertia with respect to y axis is correct - if (abs(inertia_b - (voumeAndInertia.at(2))) > epsilon){ + if (abs(inertia_b - voumeAndInertia.at(2)) > epsilon){ std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Moment of inertia with respect to y axis is incorrect : Expected = " << inertia_b << " " << ", actual = " << (voumeAndInertia.at(2)) << std::endl; } //test if moment of inertia with respect to z axis is correct - if (abs(inertia_c - (voumeAndInertia.at(3))) > epsilon){ + if (abs(inertia_c - (voumeAndInertia.at(3))) > epsilon){ std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Moment of inertia with respect to z axis is incorrect : Expected = " << inertia_c << " " << ", actual = " << (voumeAndInertia.at(3)) << std::endl; } //test if product of inertia with respect to x axis is correct - if (abs(inertia_aa - (voumeAndInertia.at(4))) > epsilon){ + if (abs(inertia_aa - (voumeAndInertia.at(4))) > epsilon){ std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Product of inertia with respect to x axis is incorrect : Expected = " << inertia_aa << " " << ", actual = " << (voumeAndInertia.at(4)) << std::endl; } //test if product of inertia with respect to y axis is correct - if (abs(inertia_bb - (voumeAndInertia.at(5))) > epsilon){ + if (abs(inertia_bb - (voumeAndInertia.at(5))) > epsilon){ std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Product of inertia with respect to y axis is incorrect : Expected = " << inertia_bb << " " << ", actual = " << (voumeAndInertia.at(5)) << std::endl; } //test if product of inertia with respect to z axis is correct - if (abs(inertia_cc - (voumeAndInertia.at(6))) > epsilon){ + if (abs(inertia_cc - (voumeAndInertia.at(6))) > epsilon){ std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Product of inertia with respect to z axis is incorrect : Expected = " << inertia_cc << " " << ", actual = " << (voumeAndInertia.at(6)) << std::endl; } @@ -94,15 +96,15 @@ void MeshInfoTests::testWithTetrahedron(){ } void MeshInfoTests::testWithCube(){ - glm::vec3 p0(1.0, -1.0, -1.0); - glm::vec3 p1(1.0, -1.0, 1.0); - glm::vec3 p2(-1.0, -1.0, 1.0); - glm::vec3 p3(-1.0, -1.0, -1.0); - glm::vec3 p4(1.0, 1.0, -1.0); - glm::vec3 p5(1.0, 1.0, 1.0); - glm::vec3 p6(-1.0, 1.0, 1.0); - glm::vec3 p7(-1.0, 1.0, -1.0); - vector vertices; + glm::vec3 p0(1.0, -1.0, -1.0); + glm::vec3 p1(1.0, -1.0, 1.0); + glm::vec3 p2(-1.0, -1.0, 1.0); + glm::vec3 p3(-1.0, -1.0, -1.0); + glm::vec3 p4(1.0, 1.0, -1.0); + glm::vec3 p5(1.0, 1.0, 1.0); + glm::vec3 p6(-1.0, 1.0, 1.0); + glm::vec3 p7(-1.0, 1.0, -1.0); + vector vertices; vertices.push_back(p0); vertices.push_back(p1); vertices.push_back(p2); @@ -120,22 +122,22 @@ void MeshInfoTests::testWithCube(){ double inertia = (volume * side * side) / 6.0; //inertia of a unit cube is (mass * side * side) /6 //test with origin as reference point - meshinfo::MeshInfo massProp1(&vertices, &triangles); + meshinfo::MeshInfo massProp1(&vertices, &triangles); vector volumeAndInertia1 = massProp1.computeMassProperties(); - if (abs(centerOfMass.x - massProp1.getMeshCentroid().x) > epsilon || abs(centerOfMass.y - massProp1.getMeshCentroid().y) > epsilon || - abs(centerOfMass.z - massProp1.getMeshCentroid().z) > epsilon){ + if (abs(centerOfMass.x - massProp1.getMeshCentroid().x) > epsilon || abs(centerOfMass.y - massProp1.getMeshCentroid().y) > epsilon || + abs(centerOfMass.z - massProp1.getMeshCentroid().z) > epsilon){ std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Center of mass is incorrect : Expected = " << centerOfMass.x << " " << - centerOfMass.y << " " << centerOfMass.z << ", actual = " << massProp1.getMeshCentroid().x << " " << - massProp1.getMeshCentroid().y << " " << massProp1.getMeshCentroid().z << std::endl; + centerOfMass.y << " " << centerOfMass.z << ", actual = " << massProp1.getMeshCentroid().x << " " << + massProp1.getMeshCentroid().y << " " << massProp1.getMeshCentroid().z << std::endl; } - if (abs(volume - volumeAndInertia1.at(0)) > epsilon){ + if (abs(volume - volumeAndInertia1.at(0)) > epsilon){ std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Volume is incorrect : Expected = " << volume << ", actual = " << volumeAndInertia1.at(0) << std::endl; } - if (abs(inertia - (volumeAndInertia1.at(1))) > epsilon || abs(inertia - (volumeAndInertia1.at(2))) > epsilon || - abs(inertia - (volumeAndInertia1.at(3))) > epsilon){ + if (abs(inertia - (volumeAndInertia1.at(1))) > epsilon || abs(inertia - (volumeAndInertia1.at(2))) > epsilon || + abs(inertia - (volumeAndInertia1.at(3))) > epsilon){ std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Moment of inertia is incorrect : Expected = " << inertia << " " << inertia << " " << inertia << ", actual = " << (volumeAndInertia1.at(1)) << " " << (volumeAndInertia1.at(2)) << " " << (volumeAndInertia1.at(3)) << std::endl; @@ -144,15 +146,15 @@ void MeshInfoTests::testWithCube(){ void MeshInfoTests::testWithUnitCube() { - glm::vec3 p0(0, 0, 1); - glm::vec3 p1(1, 0, 1); - glm::vec3 p2(0, 1, 1); - glm::vec3 p3(1, 1, 1); - glm::vec3 p4(0, 0, 0); - glm::vec3 p5(1, 0, 0); - glm::vec3 p6(0, 1, 0); - glm::vec3 p7(1, 1, 0); - vector vertices; + glm::vec3 p0(0, 0, 1); + glm::vec3 p1(1, 0, 1); + glm::vec3 p2(0, 1, 1); + glm::vec3 p3(1, 1, 1); + glm::vec3 p4(0, 0, 0); + glm::vec3 p5(1, 0, 0); + glm::vec3 p6(0, 1, 0); + glm::vec3 p7(1, 1, 0); + vector vertices; vertices.push_back(p0); vertices.push_back(p1); vertices.push_back(p2); @@ -172,20 +174,20 @@ void MeshInfoTests::testWithUnitCube() //test with origin as reference point meshinfo::MeshInfo massProp1(&vertices, &triangles); vector volumeAndInertia1 = massProp1.computeMassProperties(); - if (abs(centerOfMass.x - massProp1.getMeshCentroid().x) > epsilon || abs(centerOfMass.y - massProp1.getMeshCentroid().y) > epsilon || - abs(centerOfMass.z - massProp1.getMeshCentroid().z) > epsilon){ + if (abs(centerOfMass.x - massProp1.getMeshCentroid().x) > epsilon || abs(centerOfMass.y - massProp1.getMeshCentroid().y) > + epsilon || abs(centerOfMass.z - massProp1.getMeshCentroid().z) > epsilon){ std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Center of mass is incorrect : Expected = " << centerOfMass.x << - " " << centerOfMass.y << " " << centerOfMass.z << ", actual = " << massProp1.getMeshCentroid().x << " " << - massProp1.getMeshCentroid().y << " " << massProp1.getMeshCentroid().z << std::endl; + " " << centerOfMass.y << " " << centerOfMass.z << ", actual = " << massProp1.getMeshCentroid().x << " " << + massProp1.getMeshCentroid().y << " " << massProp1.getMeshCentroid().z << std::endl; } - if (abs(volume - volumeAndInertia1.at(0)) > epsilon){ + if (abs(volume - volumeAndInertia1.at(0)) > epsilon){ std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Volume is incorrect : Expected = " << volume << ", actual = " << volumeAndInertia1.at(0) << std::endl; } - if (abs(inertia - (volumeAndInertia1.at(1))) > epsilon || abs(inertia - (volumeAndInertia1.at(2))) > epsilon || - abs(inertia - (volumeAndInertia1.at(3))) > epsilon){ + if (abs(inertia - (volumeAndInertia1.at(1))) > epsilon || abs(inertia - (volumeAndInertia1.at(2))) > epsilon || + abs(inertia - (volumeAndInertia1.at(3))) > epsilon){ std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Moment of inertia is incorrect : Expected = " << inertia << " " << inertia << " " << inertia << ", actual = " << (volumeAndInertia1.at(1)) << " " << (volumeAndInertia1.at(2)) << " " << (volumeAndInertia1.at(3)) << std::endl; From c097c6aad2e4c19ef4ec586d5dde7c9fa7077b08 Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Fri, 6 Mar 2015 10:41:53 -0800 Subject: [PATCH 10/43] Adding the self introspection of shaders --- libraries/gpu/src/gpu/Format.h | 28 +- libraries/gpu/src/gpu/GLBackend.h | 4 + libraries/gpu/src/gpu/GLBackendShader.cpp | 331 ++++++++++++++++++++++ libraries/gpu/src/gpu/Shader.cpp | 21 +- libraries/gpu/src/gpu/Shader.h | 72 +++-- libraries/model/src/model/Stage.cpp | 7 +- libraries/render-utils/src/Model.cpp | 9 + 7 files changed, 428 insertions(+), 44 deletions(-) diff --git a/libraries/gpu/src/gpu/Format.h b/libraries/gpu/src/gpu/Format.h index cbf90384ce..73c3c92c0a 100644 --- a/libraries/gpu/src/gpu/Format.h +++ b/libraries/gpu/src/gpu/Format.h @@ -53,6 +53,8 @@ enum Type { NUINT8, NUM_TYPES, + + BOOL = UINT8, }; // Array providing the size in bytes for a given scalar type static const int TYPE_SIZE[NUM_TYPES] = { @@ -81,9 +83,9 @@ enum Dimension { VEC2, VEC3, VEC4, + MAT2, MAT3, MAT4, - NUM_DIMENSIONS, }; // Count (of scalars) in an Element for a given Dimension @@ -92,8 +94,9 @@ static const int DIMENSION_COUNT[NUM_DIMENSIONS] = { 2, 3, 4, + 4, 9, - 16 + 16, }; // Semantic of an Element @@ -118,6 +121,27 @@ enum Semantic { SRGBA, SBGRA, + UNIFORM, + + TEXTURE, + BUFFER, + + SAMPLER_1D, + SAMPLER_2D, + SAMPLER_2D_MS, + SAMPLER_3D, + SAMPLER_CUBE, + SAMPLER_1D_ARRAY, + SAMPLER_2D_ARRAY, + SAMPLER_2D_MS_ARRAY, + SAMPLER_3D_ARRAY, + SAMPLER_CUBE_ARRAY, + SAMPLER_2D_SHADOW, + SAMPLER_2D_SHADOW_ARRAY, + SAMPLER_CUBE_SHADOW, + SAMPLER_CUBE_SHADOW_ARRAY, + + NUM_SEMANTICS, }; diff --git a/libraries/gpu/src/gpu/GLBackend.h b/libraries/gpu/src/gpu/GLBackend.h index 81271851bb..535d7a3e1f 100644 --- a/libraries/gpu/src/gpu/GLBackend.h +++ b/libraries/gpu/src/gpu/GLBackend.h @@ -33,6 +33,7 @@ public: static void checkGLError(); + static bool makeShader(Shader& shader); class GLBuffer : public GPUObject { @@ -65,6 +66,9 @@ public: GLuint _shader; GLuint _program; + GLuint _transformCameraSlot = -1; + GLuint _transformObjectSlot = -1; + GLShader(); ~GLShader(); }; diff --git a/libraries/gpu/src/gpu/GLBackendShader.cpp b/libraries/gpu/src/gpu/GLBackendShader.cpp index 4d84d33a5f..724f1ad019 100755 --- a/libraries/gpu/src/gpu/GLBackendShader.cpp +++ b/libraries/gpu/src/gpu/GLBackendShader.cpp @@ -10,6 +10,9 @@ // #include "GLBackendShared.h" +#include "Format.h" + +using namespace gpu; GLBackend::GLShader::GLShader() : _shader(0), @@ -25,6 +28,78 @@ GLBackend::GLShader::~GLShader() { } } + +void makeBindings(GLBackend::GLShader* shader) { + if(!shader || !shader->_program) { + return; + } + GLuint glprogram = shader->_program; + GLint loc = -1; + + //Check for gpu specific attribute bindings + loc = glGetAttribLocation(glprogram, "position"); + if (loc >= 0) { + glBindAttribLocation(glprogram, gpu::Stream::POSITION, "position"); + } + + loc = glGetAttribLocation(glprogram, "normal"); + if (loc >= 0) { + glBindAttribLocation(glprogram, gpu::Stream::NORMAL, "normal"); + } + + loc = glGetAttribLocation(glprogram, "color"); + if (loc >= 0) { + glBindAttribLocation(glprogram, gpu::Stream::COLOR, "color"); + } + + loc = glGetAttribLocation(glprogram, "texcoord"); + if (loc >= 0) { + glBindAttribLocation(glprogram, gpu::Stream::TEXCOORD, "texcoord"); + } + + loc = glGetAttribLocation(glprogram, "tangent"); + if (loc >= 0) { + glBindAttribLocation(glprogram, gpu::Stream::TANGENT, "tangent"); + } + + loc = glGetAttribLocation(glprogram, "texcoord1"); + if (loc >= 0) { + glBindAttribLocation(glprogram, gpu::Stream::TEXCOORD1, "texcoord1"); + } + + loc = glGetAttribLocation(glprogram, "clusterIndices"); + if (loc >= 0) { + glBindAttribLocation(glprogram, gpu::Stream::SKIN_CLUSTER_INDEX, "clusterIndices"); + } + + loc = glGetAttribLocation(glprogram, "clusterWeights"); + if (loc >= 0) { + glBindAttribLocation(glprogram, gpu::Stream::SKIN_CLUSTER_WEIGHT, "clusterWeights"); + } + + //Check for gpu specific uniform bindings + loc = glGetUniformBlockIndex(glprogram, "transformObjectBuffer"); + if (loc >= 0) { + glUniformBlockBinding(glprogram, loc, gpu::TRANSFORM_OBJECT_SLOT); + shader->_transformObjectSlot = gpu::TRANSFORM_OBJECT_SLOT; + } + + loc = glGetUniformBlockIndex(glprogram, "transformCameraBuffer"); + if (loc >= 0) { + glUniformBlockBinding(glprogram, loc, gpu::TRANSFORM_CAMERA_SLOT); + shader->_transformCameraSlot = gpu::TRANSFORM_OBJECT_SLOT; + } + + // Link again + glLinkProgram(glprogram); + + GLint linked = 0; + glGetProgramiv(glprogram, GL_LINK_STATUS, &linked); + if (!linked) { + qDebug() << "GLShader::makeBindings - failed to link after assigning bindings?"; + } +} + GLBackend::GLShader* compileShader(const Shader& shader) { // Any GLSLprogram ? normally yes... const std::string& shaderSource = shader.getSource().getCode(); @@ -144,6 +219,8 @@ GLBackend::GLShader* compileShader(const Shader& shader) { object->_shader = glshader; object->_program = glprogram; + makeBindings(object); + return object; } @@ -220,6 +297,8 @@ GLBackend::GLShader* compileProgram(const Shader& program) { object->_shader = 0; object->_program = glprogram; + makeBindings(object); + return object; } @@ -265,3 +344,255 @@ GLuint GLBackend::getShaderID(const ShaderPointer& shader) { } } +Element getFormatFromGLUniform(GLenum gltype) { + switch (gltype) { + case GL_FLOAT: return Element(SCALAR, gpu::FLOAT, UNIFORM); + case GL_FLOAT_VEC2: return Element(VEC2, gpu::FLOAT, UNIFORM); + case GL_FLOAT_VEC3: return Element(VEC3, gpu::FLOAT, UNIFORM); + case GL_FLOAT_VEC4: return Element(VEC4, gpu::FLOAT, UNIFORM); + + case GL_DOUBLE: return Element(SCALAR, gpu::FLOAT, UNIFORM); + case GL_DOUBLE_VEC2: return Element(VEC2, gpu::FLOAT, UNIFORM); + case GL_DOUBLE_VEC3: return Element(VEC3, gpu::FLOAT, UNIFORM); + case GL_DOUBLE_VEC4: return Element(VEC4, gpu::FLOAT, UNIFORM); + + case GL_INT: return Element(SCALAR, gpu::INT32, UNIFORM); + case GL_INT_VEC2: return Element(VEC2, gpu::INT32, UNIFORM); + case GL_INT_VEC3: return Element(VEC3, gpu::INT32, UNIFORM); + case GL_INT_VEC4: return Element(VEC4, gpu::INT32, UNIFORM); + + case GL_UNSIGNED_INT: return Element(SCALAR, gpu::UINT32, UNIFORM); + case GL_UNSIGNED_INT_VEC2: return Element(VEC2, gpu::UINT32, UNIFORM); + case GL_UNSIGNED_INT_VEC3: return Element(VEC3, gpu::UINT32, UNIFORM); + case GL_UNSIGNED_INT_VEC4: return Element(VEC4, gpu::UINT32, UNIFORM); + + case GL_BOOL: return Element(SCALAR, gpu::BOOL, UNIFORM); + case GL_BOOL_VEC2: return Element(VEC2, gpu::BOOL, UNIFORM); + case GL_BOOL_VEC3: return Element(VEC3, gpu::BOOL, UNIFORM); + case GL_BOOL_VEC4: return Element(VEC4, gpu::BOOL, UNIFORM); + + + case GL_FLOAT_MAT2: return Element(gpu::MAT2, gpu::FLOAT, UNIFORM); + case GL_FLOAT_MAT3: return Element(MAT3, gpu::FLOAT, UNIFORM); + case GL_FLOAT_MAT4: return Element(MAT4, gpu::FLOAT, UNIFORM); + +/* {GL_FLOAT_MAT2x3 mat2x3}, + {GL_FLOAT_MAT2x4 mat2x4}, + {GL_FLOAT_MAT3x2 mat3x2}, + {GL_FLOAT_MAT3x4 mat3x4}, + {GL_FLOAT_MAT4x2 mat4x2}, + {GL_FLOAT_MAT4x3 mat4x3}, + {GL_DOUBLE_MAT2 dmat2}, + {GL_DOUBLE_MAT3 dmat3}, + {GL_DOUBLE_MAT4 dmat4}, + {GL_DOUBLE_MAT2x3 dmat2x3}, + {GL_DOUBLE_MAT2x4 dmat2x4}, + {GL_DOUBLE_MAT3x2 dmat3x2}, + {GL_DOUBLE_MAT3x4 dmat3x4}, + {GL_DOUBLE_MAT4x2 dmat4x2}, + {GL_DOUBLE_MAT4x3 dmat4x3}, + */ + + case GL_SAMPLER_1D: return Element(SCALAR, gpu::FLOAT, SAMPLER_1D); + case GL_SAMPLER_2D: return Element(SCALAR, gpu::FLOAT, SAMPLER_2D); + case GL_SAMPLER_2D_MULTISAMPLE: return Element(SCALAR, gpu::FLOAT, SAMPLER_2D_MS); + case GL_SAMPLER_3D: return Element(SCALAR, gpu::FLOAT, SAMPLER_3D); + case GL_SAMPLER_CUBE: return Element(SCALAR, gpu::FLOAT, SAMPLER_CUBE); + + case GL_SAMPLER_1D_ARRAY: return Element(SCALAR, gpu::FLOAT, SAMPLER_1D_ARRAY); + case GL_SAMPLER_2D_ARRAY: return Element(SCALAR, gpu::FLOAT, SAMPLER_2D_ARRAY); + case GL_SAMPLER_2D_MULTISAMPLE_ARRAY: return Element(SCALAR, gpu::FLOAT, SAMPLER_2D_MS_ARRAY); + + case GL_SAMPLER_2D_SHADOW: return Element(SCALAR, gpu::FLOAT, SAMPLER_2D_SHADOW); + case GL_SAMPLER_CUBE_SHADOW: return Element(SCALAR, gpu::FLOAT, SAMPLER_CUBE_SHADOW); + + case GL_SAMPLER_2D_ARRAY_SHADOW: return Element(SCALAR, gpu::FLOAT, SAMPLER_2D_SHADOW_ARRAY); + +// {GL_SAMPLER_1D_SHADOW sampler1DShadow}, + // {GL_SAMPLER_1D_ARRAY_SHADOW sampler1DArrayShadow}, + +// {GL_SAMPLER_BUFFER samplerBuffer}, +// {GL_SAMPLER_2D_RECT sampler2DRect}, + // {GL_SAMPLER_2D_RECT_SHADOW sampler2DRectShadow}, + + case GL_INT_SAMPLER_1D: return Element(SCALAR, gpu::INT32, SAMPLER_1D); + case GL_INT_SAMPLER_2D: return Element(SCALAR, gpu::INT32, SAMPLER_2D); + case GL_INT_SAMPLER_2D_MULTISAMPLE: return Element(SCALAR, gpu::INT32, SAMPLER_2D_MS); + case GL_INT_SAMPLER_3D: return Element(SCALAR, gpu::INT32, SAMPLER_3D); + case GL_INT_SAMPLER_CUBE: return Element(SCALAR, gpu::INT32, SAMPLER_CUBE); + + case GL_INT_SAMPLER_1D_ARRAY: return Element(SCALAR, gpu::INT32, SAMPLER_1D_ARRAY); + case GL_INT_SAMPLER_2D_ARRAY: return Element(SCALAR, gpu::INT32, SAMPLER_2D_ARRAY); + case GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: return Element(SCALAR, gpu::INT32, SAMPLER_2D_MS_ARRAY); + + // {GL_INT_SAMPLER_BUFFER isamplerBuffer}, + // {GL_INT_SAMPLER_2D_RECT isampler2DRect}, + + case GL_UNSIGNED_INT_SAMPLER_1D: return Element(SCALAR, gpu::UINT32, SAMPLER_1D); + case GL_UNSIGNED_INT_SAMPLER_2D: return Element(SCALAR, gpu::UINT32, SAMPLER_2D); + case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE: return Element(SCALAR, gpu::UINT32, SAMPLER_2D_MS); + case GL_UNSIGNED_INT_SAMPLER_3D: return Element(SCALAR, gpu::UINT32, SAMPLER_3D); + case GL_UNSIGNED_INT_SAMPLER_CUBE: return Element(SCALAR, gpu::UINT32, SAMPLER_CUBE); + + case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY: return Element(SCALAR, gpu::UINT32, SAMPLER_1D_ARRAY); + case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: return Element(SCALAR, gpu::UINT32, SAMPLER_2D_ARRAY); + case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: return Element(SCALAR, gpu::UINT32, SAMPLER_2D_MS_ARRAY); + +// {GL_UNSIGNED_INT_SAMPLER_BUFFER usamplerBuffer}, +// {GL_UNSIGNED_INT_SAMPLER_2D_RECT usampler2DRect}, +/* + {GL_IMAGE_1D image1D}, + {GL_IMAGE_2D image2D}, + {GL_IMAGE_3D image3D}, + {GL_IMAGE_2D_RECT image2DRect}, + {GL_IMAGE_CUBE imageCube}, + {GL_IMAGE_BUFFER imageBuffer}, + {GL_IMAGE_1D_ARRAY image1DArray}, + {GL_IMAGE_2D_ARRAY image2DArray}, + {GL_IMAGE_2D_MULTISAMPLE image2DMS}, + {GL_IMAGE_2D_MULTISAMPLE_ARRAY image2DMSArray}, + {GL_INT_IMAGE_1D iimage1D}, + {GL_INT_IMAGE_2D iimage2D}, + {GL_INT_IMAGE_3D iimage3D}, + {GL_INT_IMAGE_2D_RECT iimage2DRect}, + {GL_INT_IMAGE_CUBE iimageCube}, + {GL_INT_IMAGE_BUFFER iimageBuffer}, + {GL_INT_IMAGE_1D_ARRAY iimage1DArray}, + {GL_INT_IMAGE_2D_ARRAY iimage2DArray}, + {GL_INT_IMAGE_2D_MULTISAMPLE iimage2DMS}, + {GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY iimage2DMSArray}, + {GL_UNSIGNED_INT_IMAGE_1D uimage1D}, + {GL_UNSIGNED_INT_IMAGE_2D uimage2D}, + {GL_UNSIGNED_INT_IMAGE_3D uimage3D}, + {GL_UNSIGNED_INT_IMAGE_2D_RECT uimage2DRect}, + {GL_UNSIGNED_INT_IMAGE_CUBE uimageCube},+ [0] {_name="fInnerRadius" _location=0 _element={_semantic=15 '\xf' _dimension=0 '\0' _type=0 '\0' } } gpu::Shader::Slot + + {GL_UNSIGNED_INT_IMAGE_BUFFER uimageBuffer}, + {GL_UNSIGNED_INT_IMAGE_1D_ARRAY uimage1DArray}, + {GL_UNSIGNED_INT_IMAGE_2D_ARRAY uimage2DArray}, + {GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE uimage2DMS}, + {GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY uimage2DMSArray}, + {GL_UNSIGNED_INT_ATOMIC_COUNTER atomic_uint} +*/ + default: + return Element(); + } + +}; + + +int makeUniformSlots(GLuint glprogram, Shader::SlotSet& uniforms, Shader::SlotSet& textures, Shader::SlotSet& samplers) { + GLint uniformsCount = 0; + + glGetProgramiv(glprogram, GL_ACTIVE_UNIFORMS, &uniformsCount); + + for (int i = 0; i < uniformsCount; i++) { + const GLint NAME_LENGTH = 256; + GLchar name[NAME_LENGTH]; + GLint length = 0; + GLint size = 0; + GLenum type = 0; + glGetActiveUniform(glprogram, i, NAME_LENGTH, &length, &size, &type, name); + + auto element = getFormatFromGLUniform(type); + uniforms.insert(Shader::Slot(name, i, element)); + } + + return uniformsCount; +} + +int makeUniformBlockSlots(GLuint glprogram, Shader::SlotSet& buffers) { + GLint buffersCount = 0; + + glGetProgramiv(glprogram, GL_ACTIVE_UNIFORM_BLOCKS, &buffersCount); + + for (int i = 0; i < buffersCount; i++) { + const GLint NAME_LENGTH = 256; + GLchar name[NAME_LENGTH]; + GLint length = 0; + GLint size = 0; + GLenum type = 0; + GLint binding = -1; + glGetActiveUniformBlockiv(glprogram, i, GL_UNIFORM_BLOCK_BINDING, &binding); + glGetActiveUniformBlockiv(glprogram, i, GL_UNIFORM_BLOCK_DATA_SIZE, &size); + glGetActiveUniformBlockiv(glprogram, i, GL_UNIFORM_BLOCK_NAME_LENGTH, &length); + glGetActiveUniformBlockName(glprogram, i, NAME_LENGTH, &length, name); + + Element element(SCALAR, gpu::FLOAT, BUFFER); + buffers.insert(Shader::Slot(name, binding, element)); + } + + return buffersCount; +} + +int makeInputSlots(GLuint glprogram, Shader::SlotSet& inputs) { + GLint inputsCount = 0; + + glGetProgramiv(glprogram, GL_ACTIVE_ATTRIBUTES, &inputsCount); + + for (int i = 0; i < inputsCount; i++) { + const GLint NAME_LENGTH = 256; + GLchar name[NAME_LENGTH]; + GLint length = 0; + GLint size = 0; + GLenum type = 0; + glGetActiveAttrib(glprogram, i, NAME_LENGTH, &length, &size, &type, name); + + auto element = getFormatFromGLUniform(type); + inputs.insert(Shader::Slot(name, i, element)); + } + + return inputsCount; +} + +int makeOutputSlots(GLuint glprogram, Shader::SlotSet& outputs) { + /* GLint outputsCount = 0; + + glGetProgramiv(glprogram, GL_ACTIVE_, &outputsCount); + + for (int i = 0; i < inputsCount; i++) { + const GLint NAME_LENGTH = 256; + GLchar name[NAME_LENGTH]; + GLint length = 0; + GLint size = 0; + GLenum type = 0; + glGetActiveAttrib(glprogram, i, NAME_LENGTH, &length, &size, &type, name); + + auto element = getFormatFromGLUniform(type); + outputs.insert(Shader::Slot(name, i, element)); + } + */ + return 0; //inputsCount; +} + +bool GLBackend::makeShader(Shader& shader) { + + // First make sure the SHader has been compiled + GLShader* object = GLBackend::syncGPUObject(shader); + if (!object) { + return false; + } + + if (object->_program) { + Shader::SlotSet uniforms; + Shader::SlotSet textures; + Shader::SlotSet samplers; + makeUniformSlots(object->_program, uniforms, textures, samplers); + + Shader::SlotSet buffers; + makeUniformBlockSlots(object->_program, buffers); + + Shader::SlotSet inputs; + makeInputSlots(object->_program, inputs); + + Shader::SlotSet outputs; + makeOutputSlots(object->_program, outputs); + + shader.defineSlots(uniforms, buffers, textures, samplers, inputs, outputs); + + } else if (object->_shader) { + + } + + return true; +} diff --git a/libraries/gpu/src/gpu/Shader.cpp b/libraries/gpu/src/gpu/Shader.cpp index 87365e245e..164778b0e2 100755 --- a/libraries/gpu/src/gpu/Shader.cpp +++ b/libraries/gpu/src/gpu/Shader.cpp @@ -33,18 +33,6 @@ Shader::Shader(Type type, Pointer& vertex, Pointer& pixel): Shader::~Shader() { } - -/* -Program::Program(): - _storage(), - _type(GRAPHICS) -{ -} - -Program::~Program() -{ -} -*/ Shader* Shader::createVertex(const Source& source) { Shader* shader = new Shader(VERTEX, source); @@ -65,3 +53,12 @@ Shader* Shader::createProgram(Pointer& vertexShader, Pointer& pixelShader) { } return nullptr; } + +void Shader::defineSlots(const SlotSet& uniforms, const SlotSet& buffers, const SlotSet& textures, const SlotSet& samplers, const SlotSet& inputs, const SlotSet& outputs) { + _uniforms = uniforms; + _buffers = buffers; + _textures = textures; + _samplers = samplers; + _inputs = inputs; + _outputs = outputs; +} diff --git a/libraries/gpu/src/gpu/Shader.h b/libraries/gpu/src/gpu/Shader.h index 57177639ae..3a374d91c8 100755 --- a/libraries/gpu/src/gpu/Shader.h +++ b/libraries/gpu/src/gpu/Shader.h @@ -13,6 +13,7 @@ #include "Resource.h" #include +#include namespace gpu { @@ -40,6 +41,24 @@ public: Language _lang = GLSL; }; + class Slot { + public: + + std::string _name; + uint16 _location; + Element _element; + + Slot(const std::string& name, uint16 location, const Element& element) : _name(name), _location(location), _element(element) {} + + + class Less { + public: + bool operator() (const Slot& x, const Slot& y) const { return x._name < y._name; } + }; + }; + typedef std::set SlotSet; + + enum Type { VERTEX = 0, PIXEL, @@ -65,6 +84,22 @@ public: const Shaders& getShaders() const { return _shaders; } + // Access the exposed uniform, input and output slot + const SlotSet& getUniforms() const { return _uniforms; } + const SlotSet& getBuffers() const { return _buffers; } + const SlotSet& getTextures() const { return _textures; } + const SlotSet& getSamplers() const { return _samplers; } + const SlotSet& getInputs() const { return _inputs; } + const SlotSet& getOutputs() const { return _outputs; } + + // Define the list of uniforms, inputs and outputs for the shader + // This call is intendend to build the list of exposed slots in order + // to correctly bind resource to the shader. + // These can be build "manually" from knowledge of the atual shader code + // or automatically by calling "Context::makeShader()", this is the preferred way + void defineSlots(const SlotSet& uniforms, const SlotSet& buffers, const SlotSet& textures, const SlotSet& samplers, const SlotSet& inputs, const SlotSet& outputs); + + protected: Shader(Type type, const Source& source); Shader(Type type, Pointer& vertex, Pointer& pixel); @@ -78,6 +113,14 @@ protected: // if shader is composed of sub shaders, here they are Shaders _shaders; + // List of exposed uniform, input and output slots + SlotSet _uniforms; + SlotSet _buffers; + SlotSet _textures; + SlotSet _samplers; + SlotSet _inputs; + SlotSet _outputs; + // The type of the shader, the master key Type _type; @@ -91,35 +134,6 @@ protected: typedef Shader::Pointer ShaderPointer; typedef std::vector< ShaderPointer > Shaders; -/* -class Program { -public: - - enum Type { - GRAPHICS = 0, - - NUM_TYPES, - }; - - - Program(); - Program(const Program& program); // deep copy of the sysmem shader - Program& operator=(const Program& program); // deep copy of the sysmem texture - ~Program(); - -protected: - Shaders _shaders; - Type _type; - - // This shouldn't be used by anything else than the Backend class with the proper casting. - mutable GPUObject* _gpuObject = NULL; - void setGPUObject(GPUObject* gpuObject) const { _gpuObject = gpuObject; } - GPUObject* getGPUObject() const { return _gpuObject; } - friend class Backend; -}; - -typedef QSharedPointer ShaderPointer; -*/ }; diff --git a/libraries/model/src/model/Stage.cpp b/libraries/model/src/model/Stage.cpp index 9a68779caf..1ebb2e6ece 100644 --- a/libraries/model/src/model/Stage.cpp +++ b/libraries/model/src/model/Stage.cpp @@ -216,8 +216,13 @@ void SunSkyStage::updateGraphicsObject() const { double originAlt = _earthSunModel.getAltitude(); _sunLight->setPosition(Vec3(0.0f, originAlt, 0.0f)); - GLuint program = gpu::GLBackend::getShaderID(_skyShader); + GLuint program = gpu::GLBackend::getShaderID(_skyShader); + static int firstTime = 0; + if (firstTime == 0) { + firstTime++; + gpu::GLBackend::makeShader(*_skyShader); + } } diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index c7e5b71b22..3fcf181812 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -299,6 +299,15 @@ void Model::init() { _program.addShaderFromSourceCode(QGLShader::Fragment, model_frag); initProgram(_program, _locations); + + auto defaultShader = gpu::ShaderPointer( + gpu::Shader::createProgram( + gpu::ShaderPointer(gpu::Shader::createVertex(std::string(model_vert))), + gpu::ShaderPointer(gpu::Shader::createPixel(std::string(model_frag))) + ) + ); + gpu::GLBackend::makeShader(*defaultShader); + _normalMapProgram.addShaderFromSourceCode(QGLShader::Vertex, model_normal_map_vert); _normalMapProgram.addShaderFromSourceCode(QGLShader::Fragment, model_normal_map_frag); initProgram(_normalMapProgram, _normalMapLocations); From 22d9ff278815c88afbbbff56a7b721468ea5501b Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 6 Mar 2015 14:36:08 -0800 Subject: [PATCH 11/43] Show/hide menu bar in non-VR fullscreen --- interface/src/Application.cpp | 53 +++++++++++++++++++++++++++++++++-- interface/src/Application.h | 3 ++ 2 files changed, 53 insertions(+), 3 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 6484e364bc..0e989e8e12 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -459,6 +459,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : // enable mouse tracking; otherwise, we only get drag events _glWidget->setMouseTracking(true); + _fullscreenMenuWidget->setParent(_glWidget); + _toolWindow = new ToolWindow(); _toolWindow->setWindowFlags(_toolWindow->windowFlags() | Qt::WindowStaysOnTopHint); _toolWindow->setWindowTitle("Tools"); @@ -1258,6 +1260,18 @@ void Application::mouseMoveEvent(QMouseEvent* event, unsigned int deviceID) { return; } + if (Menu::getInstance()->isOptionChecked(MenuOption::Fullscreen) + && !Menu::getInstance()->isOptionChecked(MenuOption::EnableVRMode)) { + // Show/hide menu bar in fullscreen + if (event->globalY() > _menuBarHeight) { + _fullscreenMenuWidget->setFixedHeight(0); + Menu::getInstance()->setFixedHeight(0); + } else { + _fullscreenMenuWidget->setFixedHeight(_menuBarHeight); + Menu::getInstance()->setFixedHeight(_menuBarHeight); + } + } + _entities.mouseMoveEvent(event, deviceID); _controllerScriptingInterface.emitMouseMoveEvent(event, deviceID); // send events to any registered scripts @@ -1523,14 +1537,47 @@ void Application::setFullscreen(bool fullscreen) { if (Menu::getInstance()->isOptionChecked(MenuOption::EnableVRMode)) { if (fullscreen) { - // Menu show() after hide() doesn't work with Rift VR display so set height instead. + // Menu hide() disables menu commands, and show() after hide() doesn't work with Rift VR display. + // So set height instead. _window->menuBar()->setMaximumHeight(0); } else { _window->menuBar()->setMaximumHeight(QWIDGETSIZE_MAX); } + } else { + if (fullscreen) { + // Move menu to a QWidget floating above _glWidget so that show/hide doesn't adjust viewport. + _menuBarHeight = Menu::getInstance()->height(); + Menu::getInstance()->setParent(_fullscreenMenuWidget); + Menu::getInstance()->setFixedWidth(_window->windowHandle()->screen()->size().width()); + _fullscreenMenuWidget->show(); + } else { + // Restore menu to being part of MainWindow. + _fullscreenMenuWidget->hide(); + _window->setMenuBar(Menu::getInstance()); + _window->menuBar()->setMaximumHeight(QWIDGETSIZE_MAX); + } } - _window->setWindowState(fullscreen ? (_window->windowState() | Qt::WindowFullScreen) : - (_window->windowState() & ~Qt::WindowFullScreen)); + + // Work around Qt bug that prevents floating menus being shown when in fullscreen mode. + // https://bugreports.qt.io/browse/QTBUG-41883 + // Known issue: Top-level menu items don't highlight when cursor hovers. This is probably a side-effect of the work-around. + // TODO: Remove this work-around once the bug has been fixed and restore the following lines. + //_window->setWindowState(fullscreen ? (_window->windowState() | Qt::WindowFullScreen) : + // (_window->windowState() & ~Qt::WindowFullScreen)); + _window->hide(); + if (fullscreen) { + _window->setWindowState(_window->windowState() | Qt::WindowFullScreen); + // The next line produces the following warning in the log: + // [WARNING][03 / 06 12:17 : 58] QWidget::setMinimumSize: (/ MainWindow) Negative sizes + // (0, -1) are not possible + // This is better than the alternative which is to have the window slightly less than fullscreen with a visible line + // of pixels for the remainder of the screen. + _window->setContentsMargins(0, 0, 0, -1); + } else { + _window->setWindowState(_window->windowState() & ~Qt::WindowFullScreen); + _window->setContentsMargins(0, 0, 0, 0); + } + if (!_aboutToQuit) { _window->show(); } diff --git a/interface/src/Application.h b/interface/src/Application.h index 91a5f7547b..17551ef3db 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -584,6 +584,9 @@ private: QTimer _settingsTimer; GLCanvas* _glWidget = new GLCanvas(); // our GLCanvas has a couple extra features + + QWidget* _fullscreenMenuWidget = new QWidget(); + int _menuBarHeight; }; #endif // hifi_Application_h From 032896f75c69f0bb9b1aa1f4f537034996e94036 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 6 Mar 2015 14:57:27 -0800 Subject: [PATCH 12/43] Fix starting up fullscreen --- interface/src/Application.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 0e989e8e12..410d9c629f 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -460,6 +460,10 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : _glWidget->setMouseTracking(true); _fullscreenMenuWidget->setParent(_glWidget); + _menuBarHeight = Menu::getInstance()->height(); + if (Menu::getInstance()->isOptionChecked(MenuOption::Fullscreen)) { + setFullscreen(true); // Initialize menu bar show/hide + } _toolWindow = new ToolWindow(); _toolWindow->setWindowFlags(_toolWindow->windowFlags() | Qt::WindowStaysOnTopHint); From 23a6326aa175e696e1b8f611dcd99e9e66a11499 Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Sun, 8 Mar 2015 10:53:29 -0700 Subject: [PATCH 13/43] testing the shader features in Model and refining the shader making workflow --- libraries/gpu/src/gpu/Context.cpp | 11 + libraries/gpu/src/gpu/Context.h | 9 + libraries/gpu/src/gpu/Format.h | 22 +- libraries/gpu/src/gpu/GLBackend.h | 2 +- libraries/gpu/src/gpu/GLBackendShader.cpp | 244 ++++++++++++++-------- libraries/gpu/src/gpu/Resource.h | 12 ++ libraries/gpu/src/gpu/Shader.cpp | 9 + libraries/gpu/src/gpu/Shader.h | 43 +++- libraries/model/src/model/Stage.cpp | 3 +- libraries/render-utils/src/Model.cpp | 91 +++++++- libraries/render-utils/src/Model.h | 1 + 11 files changed, 326 insertions(+), 121 deletions(-) diff --git a/libraries/gpu/src/gpu/Context.cpp b/libraries/gpu/src/gpu/Context.cpp index 85227e0557..54387e8f71 100644 --- a/libraries/gpu/src/gpu/Context.cpp +++ b/libraries/gpu/src/gpu/Context.cpp @@ -10,3 +10,14 @@ // #include "Context.h" +// this include should disappear! as soon as the gpu::Context is in place +#include "GLBackend.h" + +using namespace gpu; + +bool Context::makeProgram(Shader& shader, const Shader::BindingSet& bindings) { + if (shader.isProgram()) { + return GLBackend::makeProgram(shader, bindings); + } + return false; +} \ No newline at end of file diff --git a/libraries/gpu/src/gpu/Context.h b/libraries/gpu/src/gpu/Context.h index 632c5f96de..ecff65fd8d 100644 --- a/libraries/gpu/src/gpu/Context.h +++ b/libraries/gpu/src/gpu/Context.h @@ -92,8 +92,17 @@ public: void enqueueBatch(Batch& batch); + + protected: + // This function can only be called by "static Shader::makeProgram()" + // makeProgramShader(...) make a program shader ready to be used in a Batch. + // It compiles the sub shaders, link them and defines the Slots and their bindings. + // If the shader passed is not a program, nothing happens. + static bool makeProgram(Shader& shader, const Shader::BindingSet& bindings = Shader::BindingSet()); + + friend class Shader; }; diff --git a/libraries/gpu/src/gpu/Format.h b/libraries/gpu/src/gpu/Format.h index 73c3c92c0a..8a754bb564 100644 --- a/libraries/gpu/src/gpu/Format.h +++ b/libraries/gpu/src/gpu/Format.h @@ -122,24 +122,10 @@ enum Semantic { SBGRA, UNIFORM, - - TEXTURE, - BUFFER, - - SAMPLER_1D, - SAMPLER_2D, - SAMPLER_2D_MS, - SAMPLER_3D, - SAMPLER_CUBE, - SAMPLER_1D_ARRAY, - SAMPLER_2D_ARRAY, - SAMPLER_2D_MS_ARRAY, - SAMPLER_3D_ARRAY, - SAMPLER_CUBE_ARRAY, - SAMPLER_2D_SHADOW, - SAMPLER_2D_SHADOW_ARRAY, - SAMPLER_CUBE_SHADOW, - SAMPLER_CUBE_SHADOW_ARRAY, + UNIFORM_BUFFER, + SAMPLER, + SAMPLER_MULTISAMPLE, + SAMPLER_SHADOW, NUM_SEMANTICS, diff --git a/libraries/gpu/src/gpu/GLBackend.h b/libraries/gpu/src/gpu/GLBackend.h index 535d7a3e1f..974bb6fb8b 100644 --- a/libraries/gpu/src/gpu/GLBackend.h +++ b/libraries/gpu/src/gpu/GLBackend.h @@ -33,7 +33,7 @@ public: static void checkGLError(); - static bool makeShader(Shader& shader); + static bool makeProgram(Shader& shader, const Shader::BindingSet& bindings = Shader::BindingSet()); class GLBuffer : public GPUObject { diff --git a/libraries/gpu/src/gpu/GLBackendShader.cpp b/libraries/gpu/src/gpu/GLBackendShader.cpp index 724f1ad019..5136461f5c 100755 --- a/libraries/gpu/src/gpu/GLBackendShader.cpp +++ b/libraries/gpu/src/gpu/GLBackendShader.cpp @@ -28,7 +28,6 @@ GLBackend::GLShader::~GLShader() { } } - void makeBindings(GLBackend::GLShader* shader) { if(!shader || !shader->_program) { return; @@ -36,7 +35,7 @@ void makeBindings(GLBackend::GLShader* shader) { GLuint glprogram = shader->_program; GLint loc = -1; - //Check for gpu specific attribute bindings + //Check for gpu specific attribute slotBindings loc = glGetAttribLocation(glprogram, "position"); if (loc >= 0) { glBindAttribLocation(glprogram, gpu::Stream::POSITION, "position"); @@ -77,7 +76,18 @@ void makeBindings(GLBackend::GLShader* shader) { glBindAttribLocation(glprogram, gpu::Stream::SKIN_CLUSTER_WEIGHT, "clusterWeights"); } - //Check for gpu specific uniform bindings + // Link again to take into account the assigned attrib location + glLinkProgram(glprogram); + + GLint linked = 0; + glGetProgramiv(glprogram, GL_LINK_STATUS, &linked); + if (!linked) { + qDebug() << "GLShader::makeBindings - failed to link after assigning slotBindings?"; + } + + // now assign the ubo binding, then DON't relink! + + //Check for gpu specific uniform slotBindings loc = glGetUniformBlockIndex(glprogram, "transformObjectBuffer"); if (loc >= 0) { glUniformBlockBinding(glprogram, loc, gpu::TRANSFORM_OBJECT_SLOT); @@ -90,14 +100,6 @@ void makeBindings(GLBackend::GLShader* shader) { shader->_transformCameraSlot = gpu::TRANSFORM_OBJECT_SLOT; } - // Link again - glLinkProgram(glprogram); - - GLint linked = 0; - glGetProgramiv(glprogram, GL_LINK_STATUS, &linked); - if (!linked) { - qDebug() << "GLShader::makeBindings - failed to link after assigning bindings?"; - } } GLBackend::GLShader* compileShader(const Shader& shader) { @@ -344,37 +346,45 @@ GLuint GLBackend::getShaderID(const ShaderPointer& shader) { } } -Element getFormatFromGLUniform(GLenum gltype) { +class ElementResource { +public: + gpu::Element _element; + uint16 _resource; + + ElementResource(Element&& elem, uint16 resource) : _element(elem), _resource(resource) {} +}; + +ElementResource getFormatFromGLUniform(GLenum gltype) { switch (gltype) { - case GL_FLOAT: return Element(SCALAR, gpu::FLOAT, UNIFORM); - case GL_FLOAT_VEC2: return Element(VEC2, gpu::FLOAT, UNIFORM); - case GL_FLOAT_VEC3: return Element(VEC3, gpu::FLOAT, UNIFORM); - case GL_FLOAT_VEC4: return Element(VEC4, gpu::FLOAT, UNIFORM); + case GL_FLOAT: return ElementResource(Element(SCALAR, gpu::FLOAT, UNIFORM), Resource::BUFFER); + case GL_FLOAT_VEC2: return ElementResource(Element(VEC2, gpu::FLOAT, UNIFORM), Resource::BUFFER); + case GL_FLOAT_VEC3: return ElementResource(Element(VEC3, gpu::FLOAT, UNIFORM), Resource::BUFFER); + case GL_FLOAT_VEC4: return ElementResource(Element(VEC4, gpu::FLOAT, UNIFORM), Resource::BUFFER); - case GL_DOUBLE: return Element(SCALAR, gpu::FLOAT, UNIFORM); - case GL_DOUBLE_VEC2: return Element(VEC2, gpu::FLOAT, UNIFORM); - case GL_DOUBLE_VEC3: return Element(VEC3, gpu::FLOAT, UNIFORM); - case GL_DOUBLE_VEC4: return Element(VEC4, gpu::FLOAT, UNIFORM); + case GL_DOUBLE: return ElementResource(Element(SCALAR, gpu::FLOAT, UNIFORM), Resource::BUFFER); + case GL_DOUBLE_VEC2: return ElementResource(Element(VEC2, gpu::FLOAT, UNIFORM), Resource::BUFFER); + case GL_DOUBLE_VEC3: return ElementResource(Element(VEC3, gpu::FLOAT, UNIFORM), Resource::BUFFER); + case GL_DOUBLE_VEC4: return ElementResource(Element(VEC4, gpu::FLOAT, UNIFORM), Resource::BUFFER); - case GL_INT: return Element(SCALAR, gpu::INT32, UNIFORM); - case GL_INT_VEC2: return Element(VEC2, gpu::INT32, UNIFORM); - case GL_INT_VEC3: return Element(VEC3, gpu::INT32, UNIFORM); - case GL_INT_VEC4: return Element(VEC4, gpu::INT32, UNIFORM); + case GL_INT: return ElementResource(Element(SCALAR, gpu::INT32, UNIFORM), Resource::BUFFER); + case GL_INT_VEC2: return ElementResource(Element(VEC2, gpu::INT32, UNIFORM), Resource::BUFFER); + case GL_INT_VEC3: return ElementResource(Element(VEC3, gpu::INT32, UNIFORM), Resource::BUFFER); + case GL_INT_VEC4: return ElementResource(Element(VEC4, gpu::INT32, UNIFORM), Resource::BUFFER); - case GL_UNSIGNED_INT: return Element(SCALAR, gpu::UINT32, UNIFORM); - case GL_UNSIGNED_INT_VEC2: return Element(VEC2, gpu::UINT32, UNIFORM); - case GL_UNSIGNED_INT_VEC3: return Element(VEC3, gpu::UINT32, UNIFORM); - case GL_UNSIGNED_INT_VEC4: return Element(VEC4, gpu::UINT32, UNIFORM); + case GL_UNSIGNED_INT: return ElementResource(Element(SCALAR, gpu::UINT32, UNIFORM), Resource::BUFFER); + case GL_UNSIGNED_INT_VEC2: return ElementResource(Element(VEC2, gpu::UINT32, UNIFORM), Resource::BUFFER); + case GL_UNSIGNED_INT_VEC3: return ElementResource(Element(VEC3, gpu::UINT32, UNIFORM), Resource::BUFFER); + case GL_UNSIGNED_INT_VEC4: return ElementResource(Element(VEC4, gpu::UINT32, UNIFORM), Resource::BUFFER); - case GL_BOOL: return Element(SCALAR, gpu::BOOL, UNIFORM); - case GL_BOOL_VEC2: return Element(VEC2, gpu::BOOL, UNIFORM); - case GL_BOOL_VEC3: return Element(VEC3, gpu::BOOL, UNIFORM); - case GL_BOOL_VEC4: return Element(VEC4, gpu::BOOL, UNIFORM); + case GL_BOOL: return ElementResource(Element(SCALAR, gpu::BOOL, UNIFORM), Resource::BUFFER); + case GL_BOOL_VEC2: return ElementResource(Element(VEC2, gpu::BOOL, UNIFORM), Resource::BUFFER); + case GL_BOOL_VEC3: return ElementResource(Element(VEC3, gpu::BOOL, UNIFORM), Resource::BUFFER); + case GL_BOOL_VEC4: return ElementResource(Element(VEC4, gpu::BOOL, UNIFORM), Resource::BUFFER); - case GL_FLOAT_MAT2: return Element(gpu::MAT2, gpu::FLOAT, UNIFORM); - case GL_FLOAT_MAT3: return Element(MAT3, gpu::FLOAT, UNIFORM); - case GL_FLOAT_MAT4: return Element(MAT4, gpu::FLOAT, UNIFORM); + case GL_FLOAT_MAT2: return ElementResource(Element(gpu::MAT2, gpu::FLOAT, UNIFORM), Resource::BUFFER); + case GL_FLOAT_MAT3: return ElementResource(Element(MAT3, gpu::FLOAT, UNIFORM), Resource::BUFFER); + case GL_FLOAT_MAT4: return ElementResource(Element(MAT4, gpu::FLOAT, UNIFORM), Resource::BUFFER); /* {GL_FLOAT_MAT2x3 mat2x3}, {GL_FLOAT_MAT2x4 mat2x4}, @@ -393,20 +403,20 @@ Element getFormatFromGLUniform(GLenum gltype) { {GL_DOUBLE_MAT4x3 dmat4x3}, */ - case GL_SAMPLER_1D: return Element(SCALAR, gpu::FLOAT, SAMPLER_1D); - case GL_SAMPLER_2D: return Element(SCALAR, gpu::FLOAT, SAMPLER_2D); - case GL_SAMPLER_2D_MULTISAMPLE: return Element(SCALAR, gpu::FLOAT, SAMPLER_2D_MS); - case GL_SAMPLER_3D: return Element(SCALAR, gpu::FLOAT, SAMPLER_3D); - case GL_SAMPLER_CUBE: return Element(SCALAR, gpu::FLOAT, SAMPLER_CUBE); + case GL_SAMPLER_1D: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_1D); + case GL_SAMPLER_2D: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_2D); + case GL_SAMPLER_2D_MULTISAMPLE: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D); + case GL_SAMPLER_3D: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_3D); + case GL_SAMPLER_CUBE: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_CUBE); - case GL_SAMPLER_1D_ARRAY: return Element(SCALAR, gpu::FLOAT, SAMPLER_1D_ARRAY); - case GL_SAMPLER_2D_ARRAY: return Element(SCALAR, gpu::FLOAT, SAMPLER_2D_ARRAY); - case GL_SAMPLER_2D_MULTISAMPLE_ARRAY: return Element(SCALAR, gpu::FLOAT, SAMPLER_2D_MS_ARRAY); + case GL_SAMPLER_1D_ARRAY: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_1D_ARRAY); + case GL_SAMPLER_2D_ARRAY: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_2D_ARRAY); + case GL_SAMPLER_2D_MULTISAMPLE_ARRAY: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D_ARRAY); - case GL_SAMPLER_2D_SHADOW: return Element(SCALAR, gpu::FLOAT, SAMPLER_2D_SHADOW); - case GL_SAMPLER_CUBE_SHADOW: return Element(SCALAR, gpu::FLOAT, SAMPLER_CUBE_SHADOW); + case GL_SAMPLER_2D_SHADOW: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_SHADOW), Resource::TEXTURE_2D); + case GL_SAMPLER_CUBE_SHADOW: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_SHADOW), Resource::TEXTURE_CUBE); - case GL_SAMPLER_2D_ARRAY_SHADOW: return Element(SCALAR, gpu::FLOAT, SAMPLER_2D_SHADOW_ARRAY); + case GL_SAMPLER_2D_ARRAY_SHADOW: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_SHADOW), Resource::TEXTURE_2D_ARRAY); // {GL_SAMPLER_1D_SHADOW sampler1DShadow}, // {GL_SAMPLER_1D_ARRAY_SHADOW sampler1DArrayShadow}, @@ -415,28 +425,28 @@ Element getFormatFromGLUniform(GLenum gltype) { // {GL_SAMPLER_2D_RECT sampler2DRect}, // {GL_SAMPLER_2D_RECT_SHADOW sampler2DRectShadow}, - case GL_INT_SAMPLER_1D: return Element(SCALAR, gpu::INT32, SAMPLER_1D); - case GL_INT_SAMPLER_2D: return Element(SCALAR, gpu::INT32, SAMPLER_2D); - case GL_INT_SAMPLER_2D_MULTISAMPLE: return Element(SCALAR, gpu::INT32, SAMPLER_2D_MS); - case GL_INT_SAMPLER_3D: return Element(SCALAR, gpu::INT32, SAMPLER_3D); - case GL_INT_SAMPLER_CUBE: return Element(SCALAR, gpu::INT32, SAMPLER_CUBE); + case GL_INT_SAMPLER_1D: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_1D); + case GL_INT_SAMPLER_2D: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_2D); + case GL_INT_SAMPLER_2D_MULTISAMPLE: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D); + case GL_INT_SAMPLER_3D: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_3D); + case GL_INT_SAMPLER_CUBE: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_CUBE); - case GL_INT_SAMPLER_1D_ARRAY: return Element(SCALAR, gpu::INT32, SAMPLER_1D_ARRAY); - case GL_INT_SAMPLER_2D_ARRAY: return Element(SCALAR, gpu::INT32, SAMPLER_2D_ARRAY); - case GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: return Element(SCALAR, gpu::INT32, SAMPLER_2D_MS_ARRAY); + case GL_INT_SAMPLER_1D_ARRAY: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_1D_ARRAY); + case GL_INT_SAMPLER_2D_ARRAY: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_2D_ARRAY); + case GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D_ARRAY); // {GL_INT_SAMPLER_BUFFER isamplerBuffer}, // {GL_INT_SAMPLER_2D_RECT isampler2DRect}, - case GL_UNSIGNED_INT_SAMPLER_1D: return Element(SCALAR, gpu::UINT32, SAMPLER_1D); - case GL_UNSIGNED_INT_SAMPLER_2D: return Element(SCALAR, gpu::UINT32, SAMPLER_2D); - case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE: return Element(SCALAR, gpu::UINT32, SAMPLER_2D_MS); - case GL_UNSIGNED_INT_SAMPLER_3D: return Element(SCALAR, gpu::UINT32, SAMPLER_3D); - case GL_UNSIGNED_INT_SAMPLER_CUBE: return Element(SCALAR, gpu::UINT32, SAMPLER_CUBE); + case GL_UNSIGNED_INT_SAMPLER_1D: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_1D); + case GL_UNSIGNED_INT_SAMPLER_2D: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_2D); + case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D); + case GL_UNSIGNED_INT_SAMPLER_3D: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_3D); + case GL_UNSIGNED_INT_SAMPLER_CUBE: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_CUBE); - case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY: return Element(SCALAR, gpu::UINT32, SAMPLER_1D_ARRAY); - case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: return Element(SCALAR, gpu::UINT32, SAMPLER_2D_ARRAY); - case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: return Element(SCALAR, gpu::UINT32, SAMPLER_2D_MS_ARRAY); + case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_1D_ARRAY); + case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_2D_ARRAY); + case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D_ARRAY); // {GL_UNSIGNED_INT_SAMPLER_BUFFER usamplerBuffer}, // {GL_UNSIGNED_INT_SAMPLER_2D_RECT usampler2DRect}, @@ -475,13 +485,13 @@ Element getFormatFromGLUniform(GLenum gltype) { {GL_UNSIGNED_INT_ATOMIC_COUNTER atomic_uint} */ default: - return Element(); + return ElementResource(Element(), Resource::BUFFER); } }; -int makeUniformSlots(GLuint glprogram, Shader::SlotSet& uniforms, Shader::SlotSet& textures, Shader::SlotSet& samplers) { +int makeUniformSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& uniforms, Shader::SlotSet& textures, Shader::SlotSet& samplers) { GLint uniformsCount = 0; glGetProgramiv(glprogram, GL_ACTIVE_UNIFORMS, &uniformsCount); @@ -493,19 +503,56 @@ int makeUniformSlots(GLuint glprogram, Shader::SlotSet& uniforms, Shader::SlotSe GLint size = 0; GLenum type = 0; glGetActiveUniform(glprogram, i, NAME_LENGTH, &length, &size, &type, name); + GLint location = glGetUniformLocation(glprogram, name); + const GLint INVALID_UNIFORM_LOCATION = -1; - auto element = getFormatFromGLUniform(type); - uniforms.insert(Shader::Slot(name, i, element)); + // Try to make sense of the gltype + auto elementResource = getFormatFromGLUniform(type); + + // The uniform as a standard var type + if (location != INVALID_UNIFORM_LOCATION) { + if (elementResource._resource == Resource::BUFFER) { + uniforms.insert(Shader::Slot(name, location, elementResource._element, elementResource._resource)); + } else { + // For texture/Sampler, the location is the actual binding value + GLint binding = -1; + glGetUniformiv(glprogram, location, &binding); + + auto requestedBinding = slotBindings.find(std::string(name)); + if (requestedBinding != slotBindings.end()) { + if (binding != (*requestedBinding)._location) { + binding = (*requestedBinding)._location; + glUniform1i(location, binding); + } + } + + textures.insert(Shader::Slot(name, binding, elementResource._element, elementResource._resource)); + samplers.insert(Shader::Slot(name, binding, elementResource._element, elementResource._resource)); + } + } } return uniformsCount; } -int makeUniformBlockSlots(GLuint glprogram, Shader::SlotSet& buffers) { - GLint buffersCount = 0; +const GLint UNUSED_SLOT = -1; +bool isUnusedSlot(GLint binding) { + return (binding == UNUSED_SLOT); +} +int makeUniformBlockSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& buffers) { + GLint buffersCount = 0; glGetProgramiv(glprogram, GL_ACTIVE_UNIFORM_BLOCKS, &buffersCount); + // fast exit + if (buffersCount == 0) { + return 0; + } + + GLint maxNumUniformBufferSlots = 0; + glGetIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS, &maxNumUniformBufferSlots); + std::vector uniformBufferSlotMap(maxNumUniformBufferSlots, -1); + for (int i = 0; i < buffersCount; i++) { const GLint NAME_LENGTH = 256; GLchar name[NAME_LENGTH]; @@ -513,19 +560,46 @@ int makeUniformBlockSlots(GLuint glprogram, Shader::SlotSet& buffers) { GLint size = 0; GLenum type = 0; GLint binding = -1; - glGetActiveUniformBlockiv(glprogram, i, GL_UNIFORM_BLOCK_BINDING, &binding); - glGetActiveUniformBlockiv(glprogram, i, GL_UNIFORM_BLOCK_DATA_SIZE, &size); + glGetActiveUniformBlockiv(glprogram, i, GL_UNIFORM_BLOCK_NAME_LENGTH, &length); glGetActiveUniformBlockName(glprogram, i, NAME_LENGTH, &length, name); + glGetActiveUniformBlockiv(glprogram, i, GL_UNIFORM_BLOCK_BINDING, &binding); + glGetActiveUniformBlockiv(glprogram, i, GL_UNIFORM_BLOCK_DATA_SIZE, &size); + + GLuint blockIndex = glGetUniformBlockIndex(glprogram, name); - Element element(SCALAR, gpu::FLOAT, BUFFER); - buffers.insert(Shader::Slot(name, binding, element)); + // CHeck if there is a requested binding for this block + auto requestedBinding = slotBindings.find(std::string(name)); + if (requestedBinding != slotBindings.end()) { + // If yes force it + if (binding != (*requestedBinding)._location) { + binding = (*requestedBinding)._location; + glUniformBlockBinding(glprogram, blockIndex, binding); + } + } else if (binding == 0) { + // If no binding was assigned then just do it finding a free slot + auto slotIt = std::find_if(uniformBufferSlotMap.begin(), uniformBufferSlotMap.end(), isUnusedSlot); + if (slotIt != uniformBufferSlotMap.end()) { + binding = slotIt - uniformBufferSlotMap.begin(); + glUniformBlockBinding(glprogram, blockIndex, binding); + } else { + // This should neve happen, an active ubo cannot find an available slot among the max available?! + binding = -1; + } + } + // If binding is valid record it + if (binding >= 0) { + uniformBufferSlotMap[binding] = blockIndex; + } + + Element element(SCALAR, gpu::UINT32, gpu::UNIFORM_BUFFER); + buffers.insert(Shader::Slot(name, binding, element, Resource::BUFFER)); } return buffersCount; } -int makeInputSlots(GLuint glprogram, Shader::SlotSet& inputs) { +int makeInputSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& inputs) { GLint inputsCount = 0; glGetProgramiv(glprogram, GL_ACTIVE_ATTRIBUTES, &inputsCount); @@ -538,14 +612,16 @@ int makeInputSlots(GLuint glprogram, Shader::SlotSet& inputs) { GLenum type = 0; glGetActiveAttrib(glprogram, i, NAME_LENGTH, &length, &size, &type, name); - auto element = getFormatFromGLUniform(type); - inputs.insert(Shader::Slot(name, i, element)); + GLint binding = glGetAttribLocation(glprogram, name); + + auto elementResource = getFormatFromGLUniform(type); + inputs.insert(Shader::Slot(name, binding, elementResource._element, -1)); } return inputsCount; } -int makeOutputSlots(GLuint glprogram, Shader::SlotSet& outputs) { +int makeOutputSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& outputs) { /* GLint outputsCount = 0; glGetProgramiv(glprogram, GL_ACTIVE_, &outputsCount); @@ -565,28 +641,28 @@ int makeOutputSlots(GLuint glprogram, Shader::SlotSet& outputs) { return 0; //inputsCount; } -bool GLBackend::makeShader(Shader& shader) { +bool GLBackend::makeProgram(Shader& shader, const Shader::BindingSet& slotBindings) { - // First make sure the SHader has been compiled + // First make sure the Shader has been compiled GLShader* object = GLBackend::syncGPUObject(shader); if (!object) { return false; } if (object->_program) { + Shader::SlotSet buffers; + makeUniformBlockSlots(object->_program, slotBindings, buffers); + Shader::SlotSet uniforms; Shader::SlotSet textures; Shader::SlotSet samplers; - makeUniformSlots(object->_program, uniforms, textures, samplers); - - Shader::SlotSet buffers; - makeUniformBlockSlots(object->_program, buffers); + makeUniformSlots(object->_program, slotBindings, uniforms, textures, samplers); Shader::SlotSet inputs; - makeInputSlots(object->_program, inputs); + makeInputSlots(object->_program, slotBindings, inputs); Shader::SlotSet outputs; - makeOutputSlots(object->_program, outputs); + makeOutputSlots(object->_program, slotBindings, outputs); shader.defineSlots(uniforms, buffers, textures, samplers, inputs, outputs); diff --git a/libraries/gpu/src/gpu/Resource.h b/libraries/gpu/src/gpu/Resource.h index 225e3fd927..3da25ae78f 100644 --- a/libraries/gpu/src/gpu/Resource.h +++ b/libraries/gpu/src/gpu/Resource.h @@ -38,6 +38,18 @@ public: // The size in bytes of data stored in the resource virtual Size getSize() const = 0; + enum Type { + BUFFER = 0, + TEXTURE_1D, + TEXTURE_2D, + TEXTURE_3D, + TEXTURE_CUBE, + TEXTURE_1D_ARRAY, + TEXTURE_2D_ARRAY, + TEXTURE_3D_ARRAY, + TEXTURE_CUBE_ARRAY, + }; + protected: Resource() {} diff --git a/libraries/gpu/src/gpu/Shader.cpp b/libraries/gpu/src/gpu/Shader.cpp index 164778b0e2..59838fae9c 100755 --- a/libraries/gpu/src/gpu/Shader.cpp +++ b/libraries/gpu/src/gpu/Shader.cpp @@ -13,6 +13,8 @@ #include #include +#include "Context.h" + using namespace gpu; Shader::Shader(Type type, const Source& source): @@ -62,3 +64,10 @@ void Shader::defineSlots(const SlotSet& uniforms, const SlotSet& buffers, const _inputs = inputs; _outputs = outputs; } + +bool Shader::makeProgram(Shader& shader, const Shader::BindingSet& bindings) { + if (shader.isProgram()) { + return Context::makeProgram(shader, bindings); + } + return false; +} diff --git a/libraries/gpu/src/gpu/Shader.h b/libraries/gpu/src/gpu/Shader.h index 3a374d91c8..9a5bec313b 100755 --- a/libraries/gpu/src/gpu/Shader.h +++ b/libraries/gpu/src/gpu/Shader.h @@ -45,18 +45,28 @@ public: public: std::string _name; - uint16 _location; + uint32 _location; Element _element; + uint16 _resourceType; - Slot(const std::string& name, uint16 location, const Element& element) : _name(name), _location(location), _element(element) {} + Slot(const std::string& name, uint16 location, const Element& element, uint16 resourceType = Resource::BUFFER) : + _name(name), _location(location), _element(element), _resourceType(resourceType) {} - - class Less { - public: - bool operator() (const Slot& x, const Slot& y) const { return x._name < y._name; } - }; }; - typedef std::set SlotSet; + + class Binding { + public: + std::string _name; + uint32 _location; + Binding(const std::string&& name, uint32 loc = 0) : _name(name), _location(loc) {} + }; + + template class Less { + public: + bool operator() (const T& x, const T& y) const { return x._name < y._name; } + }; + typedef std::set> SlotSet; + typedef std::set> BindingSet; enum Type { @@ -89,6 +99,7 @@ public: const SlotSet& getBuffers() const { return _buffers; } const SlotSet& getTextures() const { return _textures; } const SlotSet& getSamplers() const { return _samplers; } + const SlotSet& getInputs() const { return _inputs; } const SlotSet& getOutputs() const { return _outputs; } @@ -96,9 +107,23 @@ public: // This call is intendend to build the list of exposed slots in order // to correctly bind resource to the shader. // These can be build "manually" from knowledge of the atual shader code - // or automatically by calling "Context::makeShader()", this is the preferred way + // or automatically by calling "makeShader()", this is the preferred way void defineSlots(const SlotSet& uniforms, const SlotSet& buffers, const SlotSet& textures, const SlotSet& samplers, const SlotSet& inputs, const SlotSet& outputs); + // makeProgram(...) make a program shader ready to be used in a Batch. + // It compiles the sub shaders, link them and defines the Slots and their bindings. + // If the shader passed is not a program, nothing happens. + // + // It is possible to provide a set of slot bindings (from the name of the slot to a unit number) allowing + // to make sure slots with the same semantics can be always bound on the same location from shader to shader. + // For example, the "diffuseMap" can always be bound to texture unit #1 for different shaders by specifying a Binding("diffuseMap", 1) + // + // As of now (03/2015), the call to makeProgram is in fact calling gpu::Context::makeProgram and does rely + // on the underneath gpu::Context::Backend available. Since we only support glsl, this means that it relies + // on a glContext and the driver to compile the glsl shader. + // Hoppefully in a few years the shader compilation will be completely abstracted in a separate shader compiler library + // independant of the graphics api in use underneath (looking at you opengl & vulkan). + static bool makeProgram(Shader& shader, const Shader::BindingSet& bindings = Shader::BindingSet()); protected: Shader(Type type, const Source& source); diff --git a/libraries/model/src/model/Stage.cpp b/libraries/model/src/model/Stage.cpp index 1ebb2e6ece..d470d841bc 100644 --- a/libraries/model/src/model/Stage.cpp +++ b/libraries/model/src/model/Stage.cpp @@ -15,6 +15,7 @@ #include "SkyFromAtmosphere_vert.h" #include "SkyFromAtmosphere_frag.h" +#include "gpu/Context.h" #include "gpu/GLBackend.h" using namespace model; @@ -221,7 +222,7 @@ void SunSkyStage::updateGraphicsObject() const { static int firstTime = 0; if (firstTime == 0) { firstTime++; - gpu::GLBackend::makeShader(*_skyShader); + gpu::Shader::makeProgram(*_skyShader); } } diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 50ae3fd882..8f8f19c72a 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -299,18 +299,93 @@ void Model::initJointTransforms() { void Model::init() { if (!_program.isLinked()) { +/* + gpu::Shader::BindingSet slotBindings; + slotBindings.insert(gpu::Shader::Binding(std::string("materialBuffer"), 1)); + slotBindings.insert(gpu::Shader::Binding(std::string("diffuseMap"), 0)); + slotBindings.insert(gpu::Shader::Binding(std::string("normalMap"), 1)); + slotBindings.insert(gpu::Shader::Binding(std::string("specularMap"), 2)); + slotBindings.insert(gpu::Shader::Binding(std::string("emissiveMap"), 3)); + + // Vertex shaders + auto modelVertex = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(model_vert))); + auto modelNormalMapVertex = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(model_normal_map_vert))); + auto modelLightmapVertex = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(model_lightmap_vert))); + auto modelLightmapNormalMapVertex = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(model_lightmap_normal_map_vert))); + auto modelShadowVertex = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(model_shadow_vert))); + auto skinModelVertex = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(skin_model_vert))); + auto skinModelNormalMapVertex = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(skin_model_normal_map_vert))); + auto skinModelShadowVertex = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(skin_model_shadow_vert))); + + // Pixel shaders + auto modelPixel = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(model_frag))); + auto modelNormalMapPixel = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(model_normal_map_frag))); + auto modelSpecularMapPixel = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(model_specular_map_frag))); + auto modelNormalSpecularMapPixel = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(model_normal_specular_map_frag))); + auto modelTranslucentPixel = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(model_translucent_frag))); + auto modelShadowPixel = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(model_shadow_frag))); + auto modelLightmapPixel = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(model_lightmap_frag))); + auto modelLightmapNormalMapPixel = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(model_lightmap_normal_map_frag))); + auto modelLightmapSpecularMapPixel = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(model_lightmap_specular_map_frag))); + auto modelLightmapNormalSpecularMapPixel = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(model_lightmap_normal_specular_map_frag))); + + + bool makeResult = false; + + // Programs + auto program = gpu::ShaderPointer(gpu::Shader::createProgram(modelVertex, modelPixel)); + makeResult = gpu::Shader::makeProgram(*program, slotBindings); + + auto normalMapProgram = gpu::ShaderPointer(gpu::Shader::createProgram(modelNormalMapVertex, modelNormalMapPixel)); + makeResult = gpu::Shader::makeProgram(*normalMapProgram, slotBindings); + + auto specularMapProgram = gpu::ShaderPointer(gpu::Shader::createProgram(modelVertex, modelSpecularMapPixel)); + makeResult = gpu::Shader::makeProgram(*specularMapProgram, slotBindings); + + auto normalSpecularMapProgram = gpu::ShaderPointer(gpu::Shader::createProgram(modelNormalMapVertex, modelNormalSpecularMapPixel)); + makeResult = gpu::Shader::makeProgram(*normalSpecularMapProgram, slotBindings); + + auto translucentProgram = gpu::ShaderPointer(gpu::Shader::createProgram(modelVertex, modelTranslucentPixel)); + makeResult = gpu::Shader::makeProgram(*translucentProgram, slotBindings); + + auto shadowProgram = gpu::ShaderPointer(gpu::Shader::createProgram(modelShadowVertex, modelShadowPixel)); + makeResult = gpu::Shader::makeProgram(*shadowProgram, slotBindings); + + auto lightmapProgram = gpu::ShaderPointer(gpu::Shader::createProgram(modelLightmapVertex, modelLightmapPixel)); + makeResult = gpu::Shader::makeProgram(*lightmapProgram, slotBindings); + + auto lightmapNormalMapProgram = gpu::ShaderPointer(gpu::Shader::createProgram(modelLightmapNormalMapVertex, modelLightmapNormalMapPixel)); + makeResult = gpu::Shader::makeProgram(*lightmapNormalMapProgram, slotBindings); + + auto lightmapSpecularMapProgram = gpu::ShaderPointer(gpu::Shader::createProgram(modelLightmapVertex, modelLightmapSpecularMapPixel)); + makeResult = gpu::Shader::makeProgram(*lightmapSpecularMapProgram, slotBindings); + + auto lightmapNormalSpecularMapProgram = gpu::ShaderPointer(gpu::Shader::createProgram(modelLightmapNormalMapVertex, modelLightmapNormalSpecularMapPixel)); + makeResult = gpu::Shader::makeProgram(*lightmapNormalSpecularMapProgram, slotBindings); + + auto skinProgram = gpu::ShaderPointer(gpu::Shader::createProgram(skinModelVertex, modelPixel)); + makeResult = gpu::Shader::makeProgram(*skinProgram, slotBindings); + + auto skinNormalMapProgram = gpu::ShaderPointer(gpu::Shader::createProgram(skinModelNormalMapVertex, modelNormalMapPixel)); + makeResult = gpu::Shader::makeProgram(*skinNormalMapProgram, slotBindings); + + auto skinSpecularMapProgram = gpu::ShaderPointer(gpu::Shader::createProgram(skinModelVertex, modelSpecularMapPixel)); + makeResult = gpu::Shader::makeProgram(*skinSpecularMapProgram, slotBindings); + + auto skinNormalSpecularMapProgram = gpu::ShaderPointer(gpu::Shader::createProgram(skinModelNormalMapVertex, modelNormalSpecularMapPixel)); + makeResult = gpu::Shader::makeProgram(*skinNormalSpecularMapProgram, slotBindings); + + auto skinShadowProgram = gpu::ShaderPointer(gpu::Shader::createProgram(skinModelShadowVertex, modelShadowPixel)); + makeResult = gpu::Shader::makeProgram(*skinShadowProgram, slotBindings); + + auto skinTranslucentProgram = gpu::ShaderPointer(gpu::Shader::createProgram(skinModelVertex, modelTranslucentPixel)); + makeResult = gpu::Shader::makeProgram(*skinTranslucentProgram, slotBindings); +*/ + _program.addShaderFromSourceCode(QGLShader::Vertex, model_vert); _program.addShaderFromSourceCode(QGLShader::Fragment, model_frag); initProgram(_program, _locations); - - auto defaultShader = gpu::ShaderPointer( - gpu::Shader::createProgram( - gpu::ShaderPointer(gpu::Shader::createVertex(std::string(model_vert))), - gpu::ShaderPointer(gpu::Shader::createPixel(std::string(model_frag))) - ) - ); - gpu::GLBackend::makeShader(*defaultShader); _normalMapProgram.addShaderFromSourceCode(QGLShader::Vertex, model_normal_map_vert); _normalMapProgram.addShaderFromSourceCode(QGLShader::Fragment, model_normal_map_frag); diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 303fa770ad..453d721962 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -309,6 +309,7 @@ private: int _blendNumber; int _appliedBlendNumber; + static ProgramObject _program; static ProgramObject _normalMapProgram; static ProgramObject _specularMapProgram; From cc85f468d6268cff98cd2741670bde7696b82f1e Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Mon, 9 Mar 2015 11:14:42 -0700 Subject: [PATCH 14/43] splitting the code of GLBackend into separate .cpp, adding the PIpeline,adn the state to gpu --- libraries/gpu/src/gpu/Batch.cpp | 10 +- libraries/gpu/src/gpu/Batch.h | 9 +- libraries/gpu/src/gpu/Context.h | 20 +- libraries/gpu/src/gpu/GLBackend.cpp | 447 +------------------ libraries/gpu/src/gpu/GLBackend.h | 28 +- libraries/gpu/src/gpu/GLBackendBuffer.cpp | 66 +++ libraries/gpu/src/gpu/GLBackendInput.cpp | 223 +++++++++ libraries/gpu/src/gpu/GLBackendPipeline.cpp | 95 ++++ libraries/gpu/src/gpu/GLBackendShader.cpp | 1 + libraries/gpu/src/gpu/GLBackendTransform.cpp | 156 +++++++ libraries/gpu/src/gpu/Pipeline.cpp | 34 ++ libraries/gpu/src/gpu/Pipeline.h | 53 +++ libraries/gpu/src/gpu/State.cpp | 20 + libraries/gpu/src/gpu/State.h | 88 ++++ libraries/model/src/model/Stage.cpp | 11 +- libraries/model/src/model/Stage.h | 4 +- libraries/render-utils/src/Model.cpp | 2 +- 17 files changed, 803 insertions(+), 464 deletions(-) create mode 100755 libraries/gpu/src/gpu/GLBackendBuffer.cpp create mode 100755 libraries/gpu/src/gpu/GLBackendInput.cpp create mode 100755 libraries/gpu/src/gpu/GLBackendPipeline.cpp create mode 100755 libraries/gpu/src/gpu/GLBackendTransform.cpp create mode 100755 libraries/gpu/src/gpu/Pipeline.cpp create mode 100755 libraries/gpu/src/gpu/Pipeline.h create mode 100755 libraries/gpu/src/gpu/State.cpp create mode 100755 libraries/gpu/src/gpu/State.h diff --git a/libraries/gpu/src/gpu/Batch.cpp b/libraries/gpu/src/gpu/Batch.cpp index ddbc76fce6..9ca0eeaebf 100644 --- a/libraries/gpu/src/gpu/Batch.cpp +++ b/libraries/gpu/src/gpu/Batch.cpp @@ -25,7 +25,8 @@ Batch::Batch() : _buffers(), _textures(), _streamFormats(), - _transforms() + _transforms(), + _pipelines() { } @@ -42,6 +43,7 @@ void Batch::clear() { _textures.clear(); _streamFormats.clear(); _transforms.clear(); + _pipelines.clear(); } uint32 Batch::cacheResource(Resource* res) { @@ -159,6 +161,12 @@ void Batch::setProjectionTransform(const Mat4& proj) { _params.push_back(cacheData(sizeof(Mat4), &proj)); } +void Batch::setPipeline(const PipelinePointer& pipeline) { + ADD_COMMAND(setPipeline); + + _params.push_back(_pipelines.cache(pipeline)); +} + void Batch::setUniformBuffer(uint32 slot, const BufferPointer& buffer, Offset offset, Offset size) { ADD_COMMAND(setUniformBuffer); diff --git a/libraries/gpu/src/gpu/Batch.h b/libraries/gpu/src/gpu/Batch.h index 310cd9fe16..54a6339ae3 100644 --- a/libraries/gpu/src/gpu/Batch.h +++ b/libraries/gpu/src/gpu/Batch.h @@ -21,6 +21,8 @@ #include "Stream.h" #include "Texture.h" +#include "Pipeline.h" + #if defined(NSIGHT_FOUND) #include "nvToolsExt.h" class ProfileRange { @@ -96,7 +98,9 @@ public: void setViewTransform(const Transform& view); void setProjectionTransform(const Mat4& proj); - // Shader Stage + // Pipeline Stage + void setPipeline(const PipelinePointer& pipeline); + void setUniformBuffer(uint32 slot, const BufferPointer& buffer, Offset offset, Offset size); void setUniformBuffer(uint32 slot, const BufferView& view); // not a command, just a shortcut from a BufferView @@ -164,6 +168,7 @@ public: COMMAND_setViewTransform, COMMAND_setProjectionTransform, + COMMAND_setPipeline, COMMAND_setUniformBuffer, COMMAND_setUniformTexture, @@ -281,6 +286,7 @@ public: typedef Cache::Vector TextureCaches; typedef Cache::Vector StreamFormatCaches; typedef Cache::Vector TransformCaches; + typedef Cache::Vector PipelineCaches; typedef unsigned char Byte; typedef std::vector Bytes; @@ -320,6 +326,7 @@ public: TextureCaches _textures; StreamFormatCaches _streamFormats; TransformCaches _transforms; + PipelineCaches _pipelines; protected: }; diff --git a/libraries/gpu/src/gpu/Context.h b/libraries/gpu/src/gpu/Context.h index ecff65fd8d..1e239f0c56 100644 --- a/libraries/gpu/src/gpu/Context.h +++ b/libraries/gpu/src/gpu/Context.h @@ -16,6 +16,7 @@ #include "Resource.h" #include "Texture.h" #include "Shader.h" +#include "Pipeline.h" namespace gpu { @@ -47,7 +48,6 @@ public: template< typename T > static void setGPUObject(const Buffer& buffer, T* bo) { - // buffer.setGPUObject(reinterpret_cast(bo)); buffer.setGPUObject(bo); } template< typename T > @@ -55,30 +55,32 @@ public: return reinterpret_cast(buffer.getGPUObject()); } - //void syncGPUObject(const Buffer& buffer); - template< typename T > static void setGPUObject(const Texture& texture, T* to) { - texture.setGPUObject(reinterpret_cast(to)); + texture.setGPUObject(to); } template< typename T > static T* getGPUObject(const Texture& texture) { return reinterpret_cast(texture.getGPUObject()); } - - //void syncGPUObject(const Texture& texture); - template< typename T > static void setGPUObject(const Shader& shader, T* so) { - shader.setGPUObject(reinterpret_cast(so)); + shader.setGPUObject(so); } template< typename T > static T* getGPUObject(const Shader& shader) { return reinterpret_cast(shader.getGPUObject()); } - // void syncGPUObject(const Shader& shader); + template< typename T > + static void setGPUObject(const Pipeline& pipeline, T* po) { + pipeline.setGPUObject(po); + } + template< typename T > + static T* getGPUObject(const Pipeline& pipeline) { + return reinterpret_cast(pipeline.getGPUObject()); + } protected: diff --git a/libraries/gpu/src/gpu/GLBackend.cpp b/libraries/gpu/src/gpu/GLBackend.cpp index 6314d179ed..4831ecdd4c 100644 --- a/libraries/gpu/src/gpu/GLBackend.cpp +++ b/libraries/gpu/src/gpu/GLBackend.cpp @@ -25,6 +25,7 @@ GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] = (&::gpu::GLBackend::do_setViewTransform), (&::gpu::GLBackend::do_setProjectionTransform), + (&::gpu::GLBackend::do_setPipeline), (&::gpu::GLBackend::do_setUniformBuffer), (&::gpu::GLBackend::do_setUniformTexture), @@ -71,7 +72,8 @@ GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] = GLBackend::GLBackend() : _input(), - _transform() + _transform(), + _pipeline() { initTransform(); } @@ -134,6 +136,7 @@ void GLBackend::checkGLError() { void GLBackend::do_draw(Batch& batch, uint32 paramOffset) { updateInput(); updateTransform(); + updatePipeline(); Primitive primitiveType = (Primitive)batch._params[paramOffset + 2]._uint; GLenum mode = _primitiveToGLmode[primitiveType]; @@ -147,6 +150,7 @@ void GLBackend::do_draw(Batch& batch, uint32 paramOffset) { void GLBackend::do_drawIndexed(Batch& batch, uint32 paramOffset) { updateInput(); updateTransform(); + updatePipeline(); Primitive primitiveType = (Primitive)batch._params[paramOffset + 2]._uint; GLenum mode = _primitiveToGLmode[primitiveType]; @@ -167,389 +171,6 @@ void GLBackend::do_drawIndexedInstanced(Batch& batch, uint32 paramOffset) { CHECK_GL_ERROR(); } -void GLBackend::do_setInputFormat(Batch& batch, uint32 paramOffset) { - Stream::FormatPointer format = batch._streamFormats.get(batch._params[paramOffset]._uint); - - if (format != _input._format) { - _input._format = format; - _input._invalidFormat = true; - } -} - -void GLBackend::do_setInputBuffer(Batch& batch, uint32 paramOffset) { - Offset stride = batch._params[paramOffset + 0]._uint; - Offset offset = batch._params[paramOffset + 1]._uint; - BufferPointer buffer = batch._buffers.get(batch._params[paramOffset + 2]._uint); - uint32 channel = batch._params[paramOffset + 3]._uint; - - if (channel < getNumInputBuffers()) { - _input._buffers[channel] = buffer; - _input._bufferOffsets[channel] = offset; - _input._bufferStrides[channel] = stride; - _input._buffersState.set(channel); - } -} - -#define SUPPORT_LEGACY_OPENGL -#if defined(SUPPORT_LEGACY_OPENGL) -static const int NUM_CLASSIC_ATTRIBS = Stream::TANGENT; -static const GLenum attributeSlotToClassicAttribName[NUM_CLASSIC_ATTRIBS] = { - GL_VERTEX_ARRAY, - GL_NORMAL_ARRAY, - GL_COLOR_ARRAY, - GL_TEXTURE_COORD_ARRAY -}; -#endif - -void GLBackend::updateInput() { - if (_input._invalidFormat || _input._buffersState.any()) { - - if (_input._invalidFormat) { - InputStageState::ActivationCache newActivation; - - // Check expected activation - if (_input._format) { - const Stream::Format::AttributeMap& attributes = _input._format->getAttributes(); - for (Stream::Format::AttributeMap::const_iterator it = attributes.begin(); it != attributes.end(); it++) { - const Stream::Attribute& attrib = (*it).second; - newActivation.set(attrib._slot); - } - } - - // Manage Activation what was and what is expected now - for (unsigned int i = 0; i < newActivation.size(); i++) { - bool newState = newActivation[i]; - if (newState != _input._attributeActivation[i]) { -#if defined(SUPPORT_LEGACY_OPENGL) - if (i < NUM_CLASSIC_ATTRIBS) { - if (newState) { - glEnableClientState(attributeSlotToClassicAttribName[i]); - } - else { - glDisableClientState(attributeSlotToClassicAttribName[i]); - } - } else { -#else - { -#endif - if (newState) { - glEnableVertexAttribArray(i); - } else { - glDisableVertexAttribArray(i); - } - } - CHECK_GL_ERROR(); - - _input._attributeActivation.flip(i); - } - } - } - - // now we need to bind the buffers and assign the attrib pointers - if (_input._format) { - const Buffers& buffers = _input._buffers; - const Offsets& offsets = _input._bufferOffsets; - const Offsets& strides = _input._bufferStrides; - - const Stream::Format::AttributeMap& attributes = _input._format->getAttributes(); - - for (Stream::Format::ChannelMap::const_iterator channelIt = _input._format->getChannels().begin(); - channelIt != _input._format->getChannels().end(); - channelIt++) { - const Stream::Format::ChannelMap::value_type::second_type& channel = (*channelIt).second; - if ((*channelIt).first < buffers.size()) { - int bufferNum = (*channelIt).first; - - if (_input._buffersState.test(bufferNum) || _input._invalidFormat) { - GLuint vbo = gpu::GLBackend::getBufferID((*buffers[bufferNum])); - glBindBuffer(GL_ARRAY_BUFFER, vbo); - CHECK_GL_ERROR(); - _input._buffersState[bufferNum] = false; - - for (unsigned int i = 0; i < channel._slots.size(); i++) { - const Stream::Attribute& attrib = attributes.at(channel._slots[i]); - GLuint slot = attrib._slot; - GLuint count = attrib._element.getDimensionCount(); - GLenum type = _elementTypeToGLType[attrib._element.getType()]; - GLuint stride = strides[bufferNum]; - GLuint pointer = attrib._offset + offsets[bufferNum]; - #if defined(SUPPORT_LEGACY_OPENGL) - if (slot < NUM_CLASSIC_ATTRIBS) { - switch (slot) { - case Stream::POSITION: - glVertexPointer(count, type, stride, reinterpret_cast(pointer)); - break; - case Stream::NORMAL: - glNormalPointer(type, stride, reinterpret_cast(pointer)); - break; - case Stream::COLOR: - glColorPointer(count, type, stride, reinterpret_cast(pointer)); - break; - case Stream::TEXCOORD: - glTexCoordPointer(count, type, stride, reinterpret_cast(pointer)); - break; - }; - } else { - #else - { - #endif - GLboolean isNormalized = attrib._element.isNormalized(); - glVertexAttribPointer(slot, count, type, isNormalized, stride, - reinterpret_cast(pointer)); - } - CHECK_GL_ERROR(); - } - } - } - } - } - // everything format related should be in sync now - _input._invalidFormat = false; - } - -/* TODO: Fancy version GL4.4 - if (_needInputFormatUpdate) { - - InputActivationCache newActivation; - - // Assign the vertex format required - if (_inputFormat) { - const StreamFormat::AttributeMap& attributes = _inputFormat->getAttributes(); - for (StreamFormat::AttributeMap::const_iterator it = attributes.begin(); it != attributes.end(); it++) { - const StreamFormat::Attribute& attrib = (*it).second; - newActivation.set(attrib._slot); - glVertexAttribFormat( - attrib._slot, - attrib._element.getDimensionCount(), - _elementTypeToGLType[attrib._element.getType()], - attrib._element.isNormalized(), - attrib._stride); - } - CHECK_GL_ERROR(); - } - - // Manage Activation what was and what is expected now - for (int i = 0; i < newActivation.size(); i++) { - bool newState = newActivation[i]; - if (newState != _inputAttributeActivation[i]) { - if (newState) { - glEnableVertexAttribArray(i); - } else { - glDisableVertexAttribArray(i); - } - _inputAttributeActivation.flip(i); - } - } - CHECK_GL_ERROR(); - - _needInputFormatUpdate = false; - } - - if (_needInputStreamUpdate) { - if (_inputStream) { - const Stream::Buffers& buffers = _inputStream->getBuffers(); - const Stream::Offsets& offsets = _inputStream->getOffsets(); - const Stream::Strides& strides = _inputStream->getStrides(); - - for (int i = 0; i < buffers.size(); i++) { - GLuint vbo = gpu::GLBackend::getBufferID((*buffers[i])); - glBindVertexBuffer(i, vbo, offsets[i], strides[i]); - } - - CHECK_GL_ERROR(); - } - _needInputStreamUpdate = false; - } -*/ -} - - -void GLBackend::do_setIndexBuffer(Batch& batch, uint32 paramOffset) { - _input._indexBufferType = (Type) batch._params[paramOffset + 2]._uint; - BufferPointer indexBuffer = batch._buffers.get(batch._params[paramOffset + 1]._uint); - _input._indexBufferOffset = batch._params[paramOffset + 0]._uint; - _input._indexBuffer = indexBuffer; - if (indexBuffer) { - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, getBufferID(*indexBuffer)); - } else { - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - } - CHECK_GL_ERROR(); -} - -// Transform Stage - -void GLBackend::do_setModelTransform(Batch& batch, uint32 paramOffset) { - _transform._model = batch._transforms.get(batch._params[paramOffset]._uint); - _transform._invalidModel = true; -} - -void GLBackend::do_setViewTransform(Batch& batch, uint32 paramOffset) { - _transform._view = batch._transforms.get(batch._params[paramOffset]._uint); - _transform._invalidView = true; -} - -void GLBackend::do_setProjectionTransform(Batch& batch, uint32 paramOffset) { - memcpy(&_transform._projection, batch.editData(batch._params[paramOffset]._uint), sizeof(Mat4)); - _transform._invalidProj = true; -} - -void GLBackend::initTransform() { -#if defined(Q_OS_WIN) - glGenBuffers(1, &_transform._transformObjectBuffer); - glGenBuffers(1, &_transform._transformCameraBuffer); - - glBindBuffer(GL_UNIFORM_BUFFER, _transform._transformObjectBuffer); - glBufferData(GL_UNIFORM_BUFFER, sizeof(_transform._transformObject), (const void*) &_transform._transformObject, GL_DYNAMIC_DRAW); - - glBindBuffer(GL_UNIFORM_BUFFER, _transform._transformCameraBuffer); - glBufferData(GL_UNIFORM_BUFFER, sizeof(_transform._transformCamera), (const void*) &_transform._transformCamera, GL_DYNAMIC_DRAW); - - - glBindBuffer(GL_UNIFORM_BUFFER, 0); -#else -#endif -} - -void GLBackend::killTransform() { -#if defined(Q_OS_WIN) - glDeleteBuffers(1, &_transform._transformObjectBuffer); - glDeleteBuffers(1, &_transform._transformCameraBuffer); -#else -#endif -} -void GLBackend::updateTransform() { - // Check all the dirty flags and update the state accordingly - if (_transform._invalidProj) { - _transform._transformCamera._projection = _transform._projection; - } - - if (_transform._invalidView) { - _transform._view.getInverseMatrix(_transform._transformCamera._view); - _transform._view.getMatrix(_transform._transformCamera._viewInverse); - } - - if (_transform._invalidModel) { - _transform._model.getMatrix(_transform._transformObject._model); - _transform._model.getInverseMatrix(_transform._transformObject._modelInverse); - } - - if (_transform._invalidView || _transform._invalidProj) { - Mat4 viewUntranslated = _transform._transformCamera._view; - viewUntranslated[3] = Vec4(0.0f, 0.0f, 0.0f, 1.0f); - _transform._transformCamera._projectionViewUntranslated = _transform._transformCamera._projection * viewUntranslated; - } - - if (_transform._invalidView || _transform._invalidProj) { -#if defined(Q_OS_WIN) - glBindBufferBase(GL_UNIFORM_BUFFER, TRANSFORM_CAMERA_SLOT, 0); - glBindBuffer(GL_ARRAY_BUFFER, _transform._transformCameraBuffer); - glBufferData(GL_ARRAY_BUFFER, sizeof(_transform._transformCamera), (const void*) &_transform._transformCamera, GL_DYNAMIC_DRAW); - glBindBuffer(GL_ARRAY_BUFFER, 0); - CHECK_GL_ERROR(); -#endif - } - - if (_transform._invalidModel) { -#if defined(Q_OS_WIN) - glBindBufferBase(GL_UNIFORM_BUFFER, TRANSFORM_OBJECT_SLOT, 0); - glBindBuffer(GL_ARRAY_BUFFER, _transform._transformObjectBuffer); - glBufferData(GL_ARRAY_BUFFER, sizeof(_transform._transformObject), (const void*) &_transform._transformObject, GL_DYNAMIC_DRAW); - glBindBuffer(GL_ARRAY_BUFFER, 0); - CHECK_GL_ERROR(); -#endif - } - -#if defined(Q_OS_WIN) - glBindBufferBase(GL_UNIFORM_BUFFER, TRANSFORM_OBJECT_SLOT, _transform._transformObjectBuffer); - glBindBufferBase(GL_UNIFORM_BUFFER, TRANSFORM_CAMERA_SLOT, _transform._transformCameraBuffer); - CHECK_GL_ERROR(); -#endif - - -#if defined(Q_OS_MAC) || defined(Q_OS_LINUX) - // Do it again for fixed pipeline until we can get rid of it - if (_transform._invalidProj) { - if (_transform._lastMode != GL_PROJECTION) { - glMatrixMode(GL_PROJECTION); - _transform._lastMode = GL_PROJECTION; - } - glLoadMatrixf(reinterpret_cast< const GLfloat* >(&_transform._projection)); - - CHECK_GL_ERROR(); - } - - if (_transform._invalidModel || _transform._invalidView) { - if (!_transform._model.isIdentity()) { - if (_transform._lastMode != GL_MODELVIEW) { - glMatrixMode(GL_MODELVIEW); - _transform._lastMode = GL_MODELVIEW; - } - Transform::Mat4 modelView; - if (!_transform._view.isIdentity()) { - Transform mvx; - Transform::inverseMult(mvx, _transform._view, _transform._model); - mvx.getMatrix(modelView); - } else { - _transform._model.getMatrix(modelView); - } - glLoadMatrixf(reinterpret_cast< const GLfloat* >(&modelView)); - } else { - if (!_transform._view.isIdentity()) { - if (_transform._lastMode != GL_MODELVIEW) { - glMatrixMode(GL_MODELVIEW); - _transform._lastMode = GL_MODELVIEW; - } - Transform::Mat4 modelView; - _transform._view.getInverseMatrix(modelView); - glLoadMatrixf(reinterpret_cast< const GLfloat* >(&modelView)); - } else { - // TODO: eventually do something about the matrix when neither view nor model is specified? - // glLoadIdentity(); - } - } - CHECK_GL_ERROR(); - } -#endif - - // Flags are clean - _transform._invalidView = _transform._invalidProj = _transform._invalidModel = false; -} - -void GLBackend::do_setUniformBuffer(Batch& batch, uint32 paramOffset) { - GLuint slot = batch._params[paramOffset + 3]._uint; - BufferPointer uniformBuffer = batch._buffers.get(batch._params[paramOffset + 2]._uint); - GLintptr rangeStart = batch._params[paramOffset + 1]._uint; - GLsizeiptr rangeSize = batch._params[paramOffset + 0]._uint; -#if defined(Q_OS_MAC) - GLfloat* data = (GLfloat*) (uniformBuffer->getData() + rangeStart); - glUniform4fv(slot, rangeSize / sizeof(GLfloat[4]), data); - - // NOT working so we ll stick to the uniform float array until we move to core profile - // GLuint bo = getBufferID(*uniformBuffer); - //glUniformBufferEXT(_shader._program, slot, bo); -#elif defined(Q_OS_WIN) - GLuint bo = getBufferID(*uniformBuffer); - glBindBufferRange(GL_UNIFORM_BUFFER, slot, bo, rangeStart, rangeSize); -#else - GLfloat* data = (GLfloat*) (uniformBuffer->getData() + rangeStart); - glUniform4fv(slot, rangeSize / sizeof(GLfloat[4]), data); -#endif - CHECK_GL_ERROR(); -} - -void GLBackend::do_setUniformTexture(Batch& batch, uint32 paramOffset) { - GLuint slot = batch._params[paramOffset + 1]._uint; - TexturePointer uniformTexture = batch._textures.get(batch._params[paramOffset + 0]._uint); - - GLuint to = getTextureID(uniformTexture); - glActiveTexture(GL_TEXTURE0 + slot); - glBindTexture(GL_TEXTURE_2D, to); - - CHECK_GL_ERROR(); -} - - // TODO: As long as we have gl calls explicitely issued from interface // code, we need to be able to record and batch these calls. THe long // term strategy is to get rid of any GL calls in favor of the HIFI GPU API @@ -740,8 +361,10 @@ void Batch::_glUseProgram(GLuint program) { } void GLBackend::do_glUseProgram(Batch& batch, uint32 paramOffset) { - _shader._program = batch._params[paramOffset]._uint; - glUseProgram(_shader._program); + _pipeline._program = batch._params[paramOffset]._uint; + // for this call we still want to execute the glUseProgram in the order of the glCOmmand to avoid any issue + _pipeline._invalidProgram = false; + glUseProgram(_pipeline._program); CHECK_GL_ERROR(); } @@ -998,55 +621,3 @@ void GLBackend::do_glColor4f(Batch& batch, uint32 paramOffset) { CHECK_GL_ERROR(); } -GLBackend::GLBuffer::GLBuffer() : - _stamp(0), - _buffer(0), - _size(0) -{} - -GLBackend::GLBuffer::~GLBuffer() { - if (_buffer != 0) { - glDeleteBuffers(1, &_buffer); - } -} - -GLBackend::GLBuffer* GLBackend::syncGPUObject(const Buffer& buffer) { - GLBuffer* object = Backend::getGPUObject(buffer); - - if (object && (object->_stamp == buffer.getSysmem().getStamp())) { - return object; - } - - // need to have a gpu object? - if (!object) { - object = new GLBuffer(); - glGenBuffers(1, &object->_buffer); - CHECK_GL_ERROR(); - Backend::setGPUObject(buffer, object); - } - - // Now let's update the content of the bo with the sysmem version - // TODO: in the future, be smarter about when to actually upload the glBO version based on the data that did change - //if () { - glBindBuffer(GL_ARRAY_BUFFER, object->_buffer); - glBufferData(GL_ARRAY_BUFFER, buffer.getSysmem().getSize(), buffer.getSysmem().readData(), GL_DYNAMIC_DRAW); - glBindBuffer(GL_ARRAY_BUFFER, 0); - object->_stamp = buffer.getSysmem().getStamp(); - object->_size = buffer.getSysmem().getSize(); - //} - CHECK_GL_ERROR(); - - return object; -} - - - -GLuint GLBackend::getBufferID(const Buffer& buffer) { - GLBuffer* bo = GLBackend::syncGPUObject(buffer); - if (bo) { - return bo->_buffer; - } else { - return 0; - } -} - diff --git a/libraries/gpu/src/gpu/GLBackend.h b/libraries/gpu/src/gpu/GLBackend.h index 974bb6fb8b..2fd27862f9 100644 --- a/libraries/gpu/src/gpu/GLBackend.h +++ b/libraries/gpu/src/gpu/GLBackend.h @@ -75,6 +75,16 @@ public: static GLShader* syncGPUObject(const Shader& shader); static GLuint getShaderID(const ShaderPointer& shader); + + class GLPipeline : public GPUObject { + public: + GLShader* _program; + + GLPipeline(); + ~GLPipeline(); + }; + static GLPipeline* syncGPUObject(const Pipeline& pipeline); + static const int MAX_NUM_ATTRIBUTES = Stream::NUM_INPUT_SLOTS; static const int MAX_NUM_INPUT_BUFFERS = 16; @@ -160,18 +170,24 @@ protected: _lastMode(GL_TEXTURE) {} } _transform; - // Shader Stage + // Pipeline Stage + void do_setPipeline(Batch& batch, uint32 paramOffset); void do_setUniformBuffer(Batch& batch, uint32 paramOffset); void do_setUniformTexture(Batch& batch, uint32 paramOffset); - void updateShader(); - struct ShaderStageState { + void updatePipeline(); + struct PipelineStageState { + PipelinePointer _pipeline; GLuint _program; + bool _invalidProgram; - ShaderStageState() : - _program(0) {} - } _shader; + PipelineStageState() : + _pipeline(), + _program(0), + _invalidProgram(false) + {} + } _pipeline; // TODO: As long as we have gl calls explicitely issued from interface diff --git a/libraries/gpu/src/gpu/GLBackendBuffer.cpp b/libraries/gpu/src/gpu/GLBackendBuffer.cpp new file mode 100755 index 0000000000..12eab21686 --- /dev/null +++ b/libraries/gpu/src/gpu/GLBackendBuffer.cpp @@ -0,0 +1,66 @@ +// +// GLBackendBuffer.cpp +// libraries/gpu/src/gpu +// +// Created by Sam Gateau on 3/8/2015. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#include "GLBackendShared.h" + +using namespace gpu; + +GLBackend::GLBuffer::GLBuffer() : + _stamp(0), + _buffer(0), + _size(0) +{} + +GLBackend::GLBuffer::~GLBuffer() { + if (_buffer != 0) { + glDeleteBuffers(1, &_buffer); + } +} + +GLBackend::GLBuffer* GLBackend::syncGPUObject(const Buffer& buffer) { + GLBuffer* object = Backend::getGPUObject(buffer); + + if (object && (object->_stamp == buffer.getSysmem().getStamp())) { + return object; + } + + // need to have a gpu object? + if (!object) { + object = new GLBuffer(); + glGenBuffers(1, &object->_buffer); + CHECK_GL_ERROR(); + Backend::setGPUObject(buffer, object); + } + + // Now let's update the content of the bo with the sysmem version + // TODO: in the future, be smarter about when to actually upload the glBO version based on the data that did change + //if () { + glBindBuffer(GL_ARRAY_BUFFER, object->_buffer); + glBufferData(GL_ARRAY_BUFFER, buffer.getSysmem().getSize(), buffer.getSysmem().readData(), GL_DYNAMIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); + object->_stamp = buffer.getSysmem().getStamp(); + object->_size = buffer.getSysmem().getSize(); + //} + CHECK_GL_ERROR(); + + return object; +} + + + +GLuint GLBackend::getBufferID(const Buffer& buffer) { + GLBuffer* bo = GLBackend::syncGPUObject(buffer); + if (bo) { + return bo->_buffer; + } else { + return 0; + } +} + diff --git a/libraries/gpu/src/gpu/GLBackendInput.cpp b/libraries/gpu/src/gpu/GLBackendInput.cpp new file mode 100755 index 0000000000..982d2656a0 --- /dev/null +++ b/libraries/gpu/src/gpu/GLBackendInput.cpp @@ -0,0 +1,223 @@ +// +// GLBackendInput.cpp +// libraries/gpu/src/gpu +// +// Created by Sam Gateau on 3/8/2015. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#include "GLBackendShared.h" + +using namespace gpu; + +void GLBackend::do_setInputFormat(Batch& batch, uint32 paramOffset) { + Stream::FormatPointer format = batch._streamFormats.get(batch._params[paramOffset]._uint); + + if (format != _input._format) { + _input._format = format; + _input._invalidFormat = true; + } +} + +void GLBackend::do_setInputBuffer(Batch& batch, uint32 paramOffset) { + Offset stride = batch._params[paramOffset + 0]._uint; + Offset offset = batch._params[paramOffset + 1]._uint; + BufferPointer buffer = batch._buffers.get(batch._params[paramOffset + 2]._uint); + uint32 channel = batch._params[paramOffset + 3]._uint; + + if (channel < getNumInputBuffers()) { + _input._buffers[channel] = buffer; + _input._bufferOffsets[channel] = offset; + _input._bufferStrides[channel] = stride; + _input._buffersState.set(channel); + } +} + +#define SUPPORT_LEGACY_OPENGL +#if defined(SUPPORT_LEGACY_OPENGL) +static const int NUM_CLASSIC_ATTRIBS = Stream::TANGENT; +static const GLenum attributeSlotToClassicAttribName[NUM_CLASSIC_ATTRIBS] = { + GL_VERTEX_ARRAY, + GL_NORMAL_ARRAY, + GL_COLOR_ARRAY, + GL_TEXTURE_COORD_ARRAY +}; +#endif + +void GLBackend::updateInput() { + if (_input._invalidFormat || _input._buffersState.any()) { + + if (_input._invalidFormat) { + InputStageState::ActivationCache newActivation; + + // Check expected activation + if (_input._format) { + const Stream::Format::AttributeMap& attributes = _input._format->getAttributes(); + for (Stream::Format::AttributeMap::const_iterator it = attributes.begin(); it != attributes.end(); it++) { + const Stream::Attribute& attrib = (*it).second; + newActivation.set(attrib._slot); + } + } + + // Manage Activation what was and what is expected now + for (unsigned int i = 0; i < newActivation.size(); i++) { + bool newState = newActivation[i]; + if (newState != _input._attributeActivation[i]) { +#if defined(SUPPORT_LEGACY_OPENGL) + if (i < NUM_CLASSIC_ATTRIBS) { + if (newState) { + glEnableClientState(attributeSlotToClassicAttribName[i]); + } + else { + glDisableClientState(attributeSlotToClassicAttribName[i]); + } + } else { +#else + { +#endif + if (newState) { + glEnableVertexAttribArray(i); + } else { + glDisableVertexAttribArray(i); + } + } + CHECK_GL_ERROR(); + + _input._attributeActivation.flip(i); + } + } + } + + // now we need to bind the buffers and assign the attrib pointers + if (_input._format) { + const Buffers& buffers = _input._buffers; + const Offsets& offsets = _input._bufferOffsets; + const Offsets& strides = _input._bufferStrides; + + const Stream::Format::AttributeMap& attributes = _input._format->getAttributes(); + + for (Stream::Format::ChannelMap::const_iterator channelIt = _input._format->getChannels().begin(); + channelIt != _input._format->getChannels().end(); + channelIt++) { + const Stream::Format::ChannelMap::value_type::second_type& channel = (*channelIt).second; + if ((*channelIt).first < buffers.size()) { + int bufferNum = (*channelIt).first; + + if (_input._buffersState.test(bufferNum) || _input._invalidFormat) { + GLuint vbo = gpu::GLBackend::getBufferID((*buffers[bufferNum])); + glBindBuffer(GL_ARRAY_BUFFER, vbo); + CHECK_GL_ERROR(); + _input._buffersState[bufferNum] = false; + + for (unsigned int i = 0; i < channel._slots.size(); i++) { + const Stream::Attribute& attrib = attributes.at(channel._slots[i]); + GLuint slot = attrib._slot; + GLuint count = attrib._element.getDimensionCount(); + GLenum type = _elementTypeToGLType[attrib._element.getType()]; + GLuint stride = strides[bufferNum]; + GLuint pointer = attrib._offset + offsets[bufferNum]; + #if defined(SUPPORT_LEGACY_OPENGL) + if (slot < NUM_CLASSIC_ATTRIBS) { + switch (slot) { + case Stream::POSITION: + glVertexPointer(count, type, stride, reinterpret_cast(pointer)); + break; + case Stream::NORMAL: + glNormalPointer(type, stride, reinterpret_cast(pointer)); + break; + case Stream::COLOR: + glColorPointer(count, type, stride, reinterpret_cast(pointer)); + break; + case Stream::TEXCOORD: + glTexCoordPointer(count, type, stride, reinterpret_cast(pointer)); + break; + }; + } else { + #else + { + #endif + GLboolean isNormalized = attrib._element.isNormalized(); + glVertexAttribPointer(slot, count, type, isNormalized, stride, + reinterpret_cast(pointer)); + } + CHECK_GL_ERROR(); + } + } + } + } + } + // everything format related should be in sync now + _input._invalidFormat = false; + } + +/* TODO: Fancy version GL4.4 + if (_needInputFormatUpdate) { + + InputActivationCache newActivation; + + // Assign the vertex format required + if (_inputFormat) { + const StreamFormat::AttributeMap& attributes = _inputFormat->getAttributes(); + for (StreamFormat::AttributeMap::const_iterator it = attributes.begin(); it != attributes.end(); it++) { + const StreamFormat::Attribute& attrib = (*it).second; + newActivation.set(attrib._slot); + glVertexAttribFormat( + attrib._slot, + attrib._element.getDimensionCount(), + _elementTypeToGLType[attrib._element.getType()], + attrib._element.isNormalized(), + attrib._stride); + } + CHECK_GL_ERROR(); + } + + // Manage Activation what was and what is expected now + for (int i = 0; i < newActivation.size(); i++) { + bool newState = newActivation[i]; + if (newState != _inputAttributeActivation[i]) { + if (newState) { + glEnableVertexAttribArray(i); + } else { + glDisableVertexAttribArray(i); + } + _inputAttributeActivation.flip(i); + } + } + CHECK_GL_ERROR(); + + _needInputFormatUpdate = false; + } + + if (_needInputStreamUpdate) { + if (_inputStream) { + const Stream::Buffers& buffers = _inputStream->getBuffers(); + const Stream::Offsets& offsets = _inputStream->getOffsets(); + const Stream::Strides& strides = _inputStream->getStrides(); + + for (int i = 0; i < buffers.size(); i++) { + GLuint vbo = gpu::GLBackend::getBufferID((*buffers[i])); + glBindVertexBuffer(i, vbo, offsets[i], strides[i]); + } + + CHECK_GL_ERROR(); + } + _needInputStreamUpdate = false; + } +*/ +} + + +void GLBackend::do_setIndexBuffer(Batch& batch, uint32 paramOffset) { + _input._indexBufferType = (Type) batch._params[paramOffset + 2]._uint; + BufferPointer indexBuffer = batch._buffers.get(batch._params[paramOffset + 1]._uint); + _input._indexBufferOffset = batch._params[paramOffset + 0]._uint; + _input._indexBuffer = indexBuffer; + if (indexBuffer) { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, getBufferID(*indexBuffer)); + } else { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + } + CHECK_GL_ERROR(); +} diff --git a/libraries/gpu/src/gpu/GLBackendPipeline.cpp b/libraries/gpu/src/gpu/GLBackendPipeline.cpp new file mode 100755 index 0000000000..eba904ae4f --- /dev/null +++ b/libraries/gpu/src/gpu/GLBackendPipeline.cpp @@ -0,0 +1,95 @@ +// +// GLBackendPipeline.cpp +// libraries/gpu/src/gpu +// +// Created by Sam Gateau on 3/8/2015. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#include "GLBackendShared.h" + +#include "Format.h" + +using namespace gpu; + +GLBackend::GLPipeline::GLPipeline() : + _program(nullptr) +{} + +GLBackend::GLPipeline::~GLPipeline() { + _program = nullptr; +} + +GLBackend::GLPipeline* GLBackend::syncGPUObject(const Pipeline& pipeline) { + GLPipeline* object = Backend::getGPUObject(pipeline); + + // If GPU object already created then good + if (object) { + return object; + } + + return nullptr; +} + +void GLBackend::do_setPipeline(Batch& batch, uint32 paramOffset) { + PipelinePointer pipeline = batch._pipelines.get(batch._params[paramOffset + 0]._uint); + + if (pipeline == _pipeline._pipeline) { + return; + } + + auto pipelineObject = syncGPUObject((*pipeline)); + if (!pipelineObject) { + return; + } + + _pipeline._pipeline = pipeline; + _pipeline._program = pipelineObject->_program->_program; + _pipeline._invalidProgram = true; +} + +void GLBackend::do_setUniformBuffer(Batch& batch, uint32 paramOffset) { + GLuint slot = batch._params[paramOffset + 3]._uint; + BufferPointer uniformBuffer = batch._buffers.get(batch._params[paramOffset + 2]._uint); + GLintptr rangeStart = batch._params[paramOffset + 1]._uint; + GLsizeiptr rangeSize = batch._params[paramOffset + 0]._uint; +#if defined(Q_OS_MAC) + GLfloat* data = (GLfloat*) (uniformBuffer->getData() + rangeStart); + glUniform4fv(slot, rangeSize / sizeof(GLfloat[4]), data); + + // NOT working so we ll stick to the uniform float array until we move to core profile + // GLuint bo = getBufferID(*uniformBuffer); + //glUniformBufferEXT(_shader._program, slot, bo); +#elif defined(Q_OS_WIN) + GLuint bo = getBufferID(*uniformBuffer); + glBindBufferRange(GL_UNIFORM_BUFFER, slot, bo, rangeStart, rangeSize); +#else + GLfloat* data = (GLfloat*) (uniformBuffer->getData() + rangeStart); + glUniform4fv(slot, rangeSize / sizeof(GLfloat[4]), data); +#endif + CHECK_GL_ERROR(); +} + +void GLBackend::do_setUniformTexture(Batch& batch, uint32 paramOffset) { + GLuint slot = batch._params[paramOffset + 1]._uint; + TexturePointer uniformTexture = batch._textures.get(batch._params[paramOffset + 0]._uint); + + GLuint to = getTextureID(uniformTexture); + glActiveTexture(GL_TEXTURE0 + slot); + glBindTexture(GL_TEXTURE_2D, to); + + CHECK_GL_ERROR(); +} + + +void GLBackend::updatePipeline() { + if (_pipeline._invalidProgram) { + glUseProgram(_pipeline._program); + CHECK_GL_ERROR(); + + _pipeline._invalidProgram = true; + } +} + diff --git a/libraries/gpu/src/gpu/GLBackendShader.cpp b/libraries/gpu/src/gpu/GLBackendShader.cpp index 5136461f5c..8adce580a5 100755 --- a/libraries/gpu/src/gpu/GLBackendShader.cpp +++ b/libraries/gpu/src/gpu/GLBackendShader.cpp @@ -672,3 +672,4 @@ bool GLBackend::makeProgram(Shader& shader, const Shader::BindingSet& slotBindin return true; } + diff --git a/libraries/gpu/src/gpu/GLBackendTransform.cpp b/libraries/gpu/src/gpu/GLBackendTransform.cpp new file mode 100755 index 0000000000..6e928bcf09 --- /dev/null +++ b/libraries/gpu/src/gpu/GLBackendTransform.cpp @@ -0,0 +1,156 @@ +// +// GLBackendTransform.cpp +// libraries/gpu/src/gpu +// +// Created by Sam Gateau on 3/8/2015. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#include "GLBackendShared.h" + +#include "Format.h" + +using namespace gpu; + +// Transform Stage + +void GLBackend::do_setModelTransform(Batch& batch, uint32 paramOffset) { + _transform._model = batch._transforms.get(batch._params[paramOffset]._uint); + _transform._invalidModel = true; +} + +void GLBackend::do_setViewTransform(Batch& batch, uint32 paramOffset) { + _transform._view = batch._transforms.get(batch._params[paramOffset]._uint); + _transform._invalidView = true; +} + +void GLBackend::do_setProjectionTransform(Batch& batch, uint32 paramOffset) { + memcpy(&_transform._projection, batch.editData(batch._params[paramOffset]._uint), sizeof(Mat4)); + _transform._invalidProj = true; +} + +void GLBackend::initTransform() { +#if defined(Q_OS_WIN) + glGenBuffers(1, &_transform._transformObjectBuffer); + glGenBuffers(1, &_transform._transformCameraBuffer); + + glBindBuffer(GL_UNIFORM_BUFFER, _transform._transformObjectBuffer); + glBufferData(GL_UNIFORM_BUFFER, sizeof(_transform._transformObject), (const void*) &_transform._transformObject, GL_DYNAMIC_DRAW); + + glBindBuffer(GL_UNIFORM_BUFFER, _transform._transformCameraBuffer); + glBufferData(GL_UNIFORM_BUFFER, sizeof(_transform._transformCamera), (const void*) &_transform._transformCamera, GL_DYNAMIC_DRAW); + + + glBindBuffer(GL_UNIFORM_BUFFER, 0); +#else +#endif +} + +void GLBackend::killTransform() { +#if defined(Q_OS_WIN) + glDeleteBuffers(1, &_transform._transformObjectBuffer); + glDeleteBuffers(1, &_transform._transformCameraBuffer); +#else +#endif +} +void GLBackend::updateTransform() { + // Check all the dirty flags and update the state accordingly + if (_transform._invalidProj) { + _transform._transformCamera._projection = _transform._projection; + } + + if (_transform._invalidView) { + _transform._view.getInverseMatrix(_transform._transformCamera._view); + _transform._view.getMatrix(_transform._transformCamera._viewInverse); + } + + if (_transform._invalidModel) { + _transform._model.getMatrix(_transform._transformObject._model); + _transform._model.getInverseMatrix(_transform._transformObject._modelInverse); + } + + if (_transform._invalidView || _transform._invalidProj) { + Mat4 viewUntranslated = _transform._transformCamera._view; + viewUntranslated[3] = Vec4(0.0f, 0.0f, 0.0f, 1.0f); + _transform._transformCamera._projectionViewUntranslated = _transform._transformCamera._projection * viewUntranslated; + } + + if (_transform._invalidView || _transform._invalidProj) { +#if defined(Q_OS_WIN) + glBindBufferBase(GL_UNIFORM_BUFFER, TRANSFORM_CAMERA_SLOT, 0); + glBindBuffer(GL_ARRAY_BUFFER, _transform._transformCameraBuffer); + glBufferData(GL_ARRAY_BUFFER, sizeof(_transform._transformCamera), (const void*) &_transform._transformCamera, GL_DYNAMIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); + CHECK_GL_ERROR(); +#endif + } + + if (_transform._invalidModel) { +#if defined(Q_OS_WIN) + glBindBufferBase(GL_UNIFORM_BUFFER, TRANSFORM_OBJECT_SLOT, 0); + glBindBuffer(GL_ARRAY_BUFFER, _transform._transformObjectBuffer); + glBufferData(GL_ARRAY_BUFFER, sizeof(_transform._transformObject), (const void*) &_transform._transformObject, GL_DYNAMIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); + CHECK_GL_ERROR(); +#endif + } + +#if defined(Q_OS_WIN) + glBindBufferBase(GL_UNIFORM_BUFFER, TRANSFORM_OBJECT_SLOT, _transform._transformObjectBuffer); + glBindBufferBase(GL_UNIFORM_BUFFER, TRANSFORM_CAMERA_SLOT, _transform._transformCameraBuffer); + CHECK_GL_ERROR(); +#endif + + +#if defined(Q_OS_MAC) || defined(Q_OS_LINUX) + // Do it again for fixed pipeline until we can get rid of it + if (_transform._invalidProj) { + if (_transform._lastMode != GL_PROJECTION) { + glMatrixMode(GL_PROJECTION); + _transform._lastMode = GL_PROJECTION; + } + glLoadMatrixf(reinterpret_cast< const GLfloat* >(&_transform._projection)); + + CHECK_GL_ERROR(); + } + + if (_transform._invalidModel || _transform._invalidView) { + if (!_transform._model.isIdentity()) { + if (_transform._lastMode != GL_MODELVIEW) { + glMatrixMode(GL_MODELVIEW); + _transform._lastMode = GL_MODELVIEW; + } + Transform::Mat4 modelView; + if (!_transform._view.isIdentity()) { + Transform mvx; + Transform::inverseMult(mvx, _transform._view, _transform._model); + mvx.getMatrix(modelView); + } else { + _transform._model.getMatrix(modelView); + } + glLoadMatrixf(reinterpret_cast< const GLfloat* >(&modelView)); + } else { + if (!_transform._view.isIdentity()) { + if (_transform._lastMode != GL_MODELVIEW) { + glMatrixMode(GL_MODELVIEW); + _transform._lastMode = GL_MODELVIEW; + } + Transform::Mat4 modelView; + _transform._view.getInverseMatrix(modelView); + glLoadMatrixf(reinterpret_cast< const GLfloat* >(&modelView)); + } else { + // TODO: eventually do something about the matrix when neither view nor model is specified? + // glLoadIdentity(); + } + } + CHECK_GL_ERROR(); + } +#endif + + // Flags are clean + _transform._invalidView = _transform._invalidProj = _transform._invalidModel = false; +} + + diff --git a/libraries/gpu/src/gpu/Pipeline.cpp b/libraries/gpu/src/gpu/Pipeline.cpp new file mode 100755 index 0000000000..931d330c2b --- /dev/null +++ b/libraries/gpu/src/gpu/Pipeline.cpp @@ -0,0 +1,34 @@ +// +// Pipeline.cpp +// libraries/gpu/src/gpu +// +// Created by Sam Gateau on 3/8/2015. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "Pipeline.h" +#include +#include + +using namespace gpu; + +Pipeline::Pipeline(): + _program(), + _states() +{ +} + +Pipeline::~Pipeline() +{ +} + +Pipeline* Pipeline::create(const ShaderPointer& program, const States& states) { + Pipeline* pipeline = new Pipeline(); + pipeline->_program = program; + pipeline->_states = states; + + return pipeline; +} diff --git a/libraries/gpu/src/gpu/Pipeline.h b/libraries/gpu/src/gpu/Pipeline.h new file mode 100755 index 0000000000..c2fc944bb7 --- /dev/null +++ b/libraries/gpu/src/gpu/Pipeline.h @@ -0,0 +1,53 @@ +// +// Pipeline.h +// libraries/gpu/src/gpu +// +// Created by Sam Gateau on 3/8/2015. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#ifndef hifi_gpu_Pipeline_h +#define hifi_gpu_Pipeline_h + +#include "Resource.h" +#include +#include + +#include "Shader.h" +#include "State.h" + +namespace gpu { + +class Pipeline { +public: + static Pipeline* create(const ShaderPointer& program, const States& states); + ~Pipeline(); + + const ShaderPointer& getProgram() const { return _program; } + + const States& getStates() const { return _states; } + +protected: + ShaderPointer _program; + States _states; + + Pipeline(); + Pipeline(const Pipeline& pipeline); // deep copy of the sysmem shader + Pipeline& operator=(const Pipeline& pipeline); // deep copy of the sysmem texture + + // This shouldn't be used by anything else than the Backend class with the proper casting. + mutable GPUObject* _gpuObject = NULL; + void setGPUObject(GPUObject* gpuObject) const { _gpuObject = gpuObject; } + GPUObject* getGPUObject() const { return _gpuObject; } + friend class Backend; +}; + +typedef QSharedPointer< Pipeline > PipelinePointer; +typedef std::vector< PipelinePointer > Pipelines; + +}; + + +#endif diff --git a/libraries/gpu/src/gpu/State.cpp b/libraries/gpu/src/gpu/State.cpp new file mode 100755 index 0000000000..0b8edb7cd2 --- /dev/null +++ b/libraries/gpu/src/gpu/State.cpp @@ -0,0 +1,20 @@ +// +// State.cpp +// libraries/gpu/src/gpu +// +// Created by Sam Gateau on 3/8/2015. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "State.h" +#include + +using namespace gpu; + + +State::~State() +{ +} diff --git a/libraries/gpu/src/gpu/State.h b/libraries/gpu/src/gpu/State.h new file mode 100755 index 0000000000..92ef4c1c8d --- /dev/null +++ b/libraries/gpu/src/gpu/State.h @@ -0,0 +1,88 @@ +// +// Pipeline.h +// libraries/gpu/src/gpu +// +// Created by Sam Gateau on 3/8/2015. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#ifndef hifi_gpu_State_h +#define hifi_gpu_State_h + +#include "Format.h" +#include +#include + + +namespace gpu { + +class GPUObject; + +class State { +public: + State() {} + virtual ~State(); + + // Work in progress, not used + /* + enum Field { + FILL_MODE, + CULL_MODE, + DEPTH_BIAS, + DEPTH_BIAS_CLAMP, + DEPTH_BIASSLOPE_SCALE, + + FRONT_CLOCKWISE, + DEPTH_CLIP_ENABLE, + SCISSR_ENABLE, + MULTISAMPLE_ENABLE, + ANTIALISED_LINE_ENABLE, + + DEPTH_ENABLE, + DEPTH_WRITE_MASK, + DEPTH_FUNCTION, + + STENCIL_ENABLE, + STENCIL_READ_MASK, + STENCIL_WRITE_MASK, + STENCIL_FUNCTION_FRONT, + STENCIL_FUNCTION_BACK, + STENCIL_REFERENCE, + + BLEND_INDEPENDANT_ENABLE, + BLEND_ENABLE, + BLEND_SOURCE, + BLEND_DESTINATION, + BLEND_OPERATION, + BLEND_SOURCE_ALPHA, + BLEND_DESTINATION_ALPHA, + BLEND_OPERATION_ALPHA, + BLEND_WRITE_MASK, + BLEND_FACTOR, + + SAMPLE_MASK, + + ALPHA_TO_COVERAGE_ENABLE, + }; + */ + +protected: + State(const State& state); + State& operator=(const State& state); + + // This shouldn't be used by anything else than the Backend class with the proper casting. + mutable GPUObject* _gpuObject = NULL; + void setGPUObject(GPUObject* gpuObject) const { _gpuObject = gpuObject; } + GPUObject* getGPUObject() const { return _gpuObject; } + friend class Backend; +}; + +typedef QSharedPointer< State > StatePointer; +typedef std::vector< StatePointer > States; + +}; + + +#endif diff --git a/libraries/model/src/model/Stage.cpp b/libraries/model/src/model/Stage.cpp index d470d841bc..e4786df792 100644 --- a/libraries/model/src/model/Stage.cpp +++ b/libraries/model/src/model/Stage.cpp @@ -15,8 +15,6 @@ #include "SkyFromAtmosphere_vert.h" #include "SkyFromAtmosphere_frag.h" -#include "gpu/Context.h" -#include "gpu/GLBackend.h" using namespace model; @@ -156,12 +154,14 @@ SunSkyStage::SunSkyStage() : // Begining of march setYearTime(60.0f); - _skyShader = gpu::ShaderPointer( + auto skyShader = gpu::ShaderPointer( gpu::Shader::createProgram( gpu::ShaderPointer(gpu::Shader::createVertex(std::string(SkyFromAtmosphere_vert))), gpu::ShaderPointer(gpu::Shader::createPixel(std::string(SkyFromAtmosphere_frag))) ) ); + _skyPipeline = gpu::PipelinePointer(gpu::Pipeline::create(skyShader, gpu::States())); + } SunSkyStage::~SunSkyStage() { @@ -217,12 +217,11 @@ void SunSkyStage::updateGraphicsObject() const { double originAlt = _earthSunModel.getAltitude(); _sunLight->setPosition(Vec3(0.0f, originAlt, 0.0f)); - - GLuint program = gpu::GLBackend::getShaderID(_skyShader); static int firstTime = 0; if (firstTime == 0) { firstTime++; - gpu::Shader::makeProgram(*_skyShader); + bool result = gpu::Shader::makeProgram(*(_skyPipeline->getProgram())); + } } diff --git a/libraries/model/src/model/Stage.h b/libraries/model/src/model/Stage.h index 4de0edb96b..61914b5a6b 100644 --- a/libraries/model/src/model/Stage.h +++ b/libraries/model/src/model/Stage.h @@ -11,7 +11,7 @@ #ifndef hifi_model_Stage_h #define hifi_model_Stage_h -#include "gpu/Shader.h" +#include "gpu/Pipeline.h" #include "Light.h" @@ -145,7 +145,7 @@ public: protected: LightPointer _sunLight; - gpu::ShaderPointer _skyShader; + gpu::PipelinePointer _skyPipeline; float _dayTime; int _yearTime; diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 8f8f19c72a..80781c3522 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -299,7 +299,7 @@ void Model::initJointTransforms() { void Model::init() { if (!_program.isLinked()) { -/* +/* //Work in progress not used yet gpu::Shader::BindingSet slotBindings; slotBindings.insert(gpu::Shader::Binding(std::string("materialBuffer"), 1)); slotBindings.insert(gpu::Shader::Binding(std::string("diffuseMap"), 0)); From ee5409b0a8c4a9e8e6d4d7b89872ed6a90ddec6a Mon Sep 17 00:00:00 2001 From: samcake Date: Mon, 9 Mar 2015 14:25:59 -0700 Subject: [PATCH 15/43] fixing compilation on macosx --- libraries/gpu/src/gpu/GLBackendShader.cpp | 616 +++++++++++----------- libraries/model/src/model/Stage.cpp | 9 +- 2 files changed, 316 insertions(+), 309 deletions(-) diff --git a/libraries/gpu/src/gpu/GLBackendShader.cpp b/libraries/gpu/src/gpu/GLBackendShader.cpp index 8adce580a5..9bcc278d8e 100755 --- a/libraries/gpu/src/gpu/GLBackendShader.cpp +++ b/libraries/gpu/src/gpu/GLBackendShader.cpp @@ -29,13 +29,13 @@ GLBackend::GLShader::~GLShader() { } void makeBindings(GLBackend::GLShader* shader) { - if(!shader || !shader->_program) { - return; - } - GLuint glprogram = shader->_program; - GLint loc = -1; - - //Check for gpu specific attribute slotBindings + if(!shader || !shader->_program) { + return; + } + GLuint glprogram = shader->_program; + GLint loc = -1; + + //Check for gpu specific attribute slotBindings loc = glGetAttribLocation(glprogram, "position"); if (loc >= 0) { glBindAttribLocation(glprogram, gpu::Stream::POSITION, "position"); @@ -74,234 +74,235 @@ void makeBindings(GLBackend::GLShader* shader) { loc = glGetAttribLocation(glprogram, "clusterWeights"); if (loc >= 0) { glBindAttribLocation(glprogram, gpu::Stream::SKIN_CLUSTER_WEIGHT, "clusterWeights"); - } - - // Link again to take into account the assigned attrib location - glLinkProgram(glprogram); - - GLint linked = 0; - glGetProgramiv(glprogram, GL_LINK_STATUS, &linked); - if (!linked) { - qDebug() << "GLShader::makeBindings - failed to link after assigning slotBindings?"; - } - - // now assign the ubo binding, then DON't relink! - - //Check for gpu specific uniform slotBindings - loc = glGetUniformBlockIndex(glprogram, "transformObjectBuffer"); - if (loc >= 0) { - glUniformBlockBinding(glprogram, loc, gpu::TRANSFORM_OBJECT_SLOT); - shader->_transformObjectSlot = gpu::TRANSFORM_OBJECT_SLOT; - } - - loc = glGetUniformBlockIndex(glprogram, "transformCameraBuffer"); - if (loc >= 0) { - glUniformBlockBinding(glprogram, loc, gpu::TRANSFORM_CAMERA_SLOT); - shader->_transformCameraSlot = gpu::TRANSFORM_OBJECT_SLOT; } + // Link again to take into account the assigned attrib location + glLinkProgram(glprogram); + + GLint linked = 0; + glGetProgramiv(glprogram, GL_LINK_STATUS, &linked); + if (!linked) { + qDebug() << "GLShader::makeBindings - failed to link after assigning slotBindings?"; + } + + // now assign the ubo binding, then DON't relink! + + //Check for gpu specific uniform slotBindings +#if defined(Q_OS_WIN) + loc = glGetUniformBlockIndex(glprogram, "transformObjectBuffer"); + if (loc >= 0) { + glUniformBlockBinding(glprogram, loc, gpu::TRANSFORM_OBJECT_SLOT); + shader->_transformObjectSlot = gpu::TRANSFORM_OBJECT_SLOT; + } + + loc = glGetUniformBlockIndex(glprogram, "transformCameraBuffer"); + if (loc >= 0) { + glUniformBlockBinding(glprogram, loc, gpu::TRANSFORM_CAMERA_SLOT); + shader->_transformCameraSlot = gpu::TRANSFORM_OBJECT_SLOT; + } +#endif } GLBackend::GLShader* compileShader(const Shader& shader) { - // Any GLSLprogram ? normally yes... - const std::string& shaderSource = shader.getSource().getCode(); - if (shaderSource.empty()) { - qDebug() << "GLShader::compileShader - no GLSL shader source code ? so failed to create"; - return nullptr; - } - - // Shader domain - const GLenum SHADER_DOMAINS[2] = { GL_VERTEX_SHADER, GL_FRAGMENT_SHADER }; - GLenum shaderDomain = SHADER_DOMAINS[shader.getType()]; - - // Create the shader object - GLuint glshader = glCreateShader(shaderDomain); - if (!glshader) { - qDebug() << "GLShader::compileShader - failed to create the gl shader object"; - return nullptr; - } - - // Assign the source - const GLchar* srcstr = shaderSource.c_str(); - glShaderSource(glshader, 1, &srcstr, NULL); - - // Compile ! - glCompileShader(glshader); - - // check if shader compiled - GLint compiled = 0; - glGetShaderiv(glshader, GL_COMPILE_STATUS, &compiled); - - // if compilation fails - if (!compiled) { - // save the source code to a temp file so we can debug easily - /* std::ofstream filestream; - filestream.open("debugshader.glsl"); - if (filestream.is_open()) { - filestream << shaderSource->source; - filestream.close(); - } - */ - - GLint infoLength = 0; - glGetShaderiv(glshader, GL_INFO_LOG_LENGTH, &infoLength); - - char* temp = new char[infoLength] ; - glGetShaderInfoLog(glshader, infoLength, NULL, temp); - - qDebug() << "GLShader::compileShader - failed to compile the gl shader object:"; - qDebug() << temp; - - /* - filestream.open("debugshader.glsl.info.txt"); - if (filestream.is_open()) { - filestream << std::string(temp); - filestream.close(); - } - */ - delete[] temp; - - glDeleteShader(glshader); - return nullptr; - } - - GLuint glprogram = 0; -#ifdef SEPARATE_PROGRAM - // so far so good, program is almost done, need to link: - GLuint glprogram = glCreateProgram(); - if (!glprogram) { - qDebug() << "GLShader::compileShader - failed to create the gl shader & gl program object"; - return nullptr; - } - - glProgramParameteri(glprogram, GL_PROGRAM_SEPARABLE, GL_TRUE); - glAttachShader(glprogram, glshader); - glLinkProgram(glprogram); - - GLint linked = 0; - glGetProgramiv(glprogram, GL_LINK_STATUS, &linked); - - if (!linked) { - /* - // save the source code to a temp file so we can debug easily - std::ofstream filestream; - filestream.open("debugshader.glsl"); - if (filestream.is_open()) { - filestream << shaderSource->source; - filestream.close(); - } - */ - - GLint infoLength = 0; - glGetProgramiv(glprogram, GL_INFO_LOG_LENGTH, &infoLength); - - char* temp = new char[infoLength] ; - glGetProgramInfoLog(glprogram, infoLength, NULL, temp); - - qDebug() << "GLShader::compileShader - failed to LINK the gl program object :"; - qDebug() << temp; - - /* - filestream.open("debugshader.glsl.info.txt"); - if (filestream.is_open()) { - filestream << String(temp); - filestream.close(); - } - */ - delete[] temp; - - glDeleteShader(glshader); - glDeleteProgram(glprogram); - return nullptr; - } -#endif - - // So far so good, the shader is created successfully - GLBackend::GLShader* object = new GLBackend::GLShader(); - object->_shader = glshader; - object->_program = glprogram; - - makeBindings(object); - - return object; + // Any GLSLprogram ? normally yes... + const std::string& shaderSource = shader.getSource().getCode(); + if (shaderSource.empty()) { + qDebug() << "GLShader::compileShader - no GLSL shader source code ? so failed to create"; + return nullptr; + } + + // Shader domain + const GLenum SHADER_DOMAINS[2] = { GL_VERTEX_SHADER, GL_FRAGMENT_SHADER }; + GLenum shaderDomain = SHADER_DOMAINS[shader.getType()]; + + // Create the shader object + GLuint glshader = glCreateShader(shaderDomain); + if (!glshader) { + qDebug() << "GLShader::compileShader - failed to create the gl shader object"; + return nullptr; + } + + // Assign the source + const GLchar* srcstr = shaderSource.c_str(); + glShaderSource(glshader, 1, &srcstr, NULL); + + // Compile ! + glCompileShader(glshader); + + // check if shader compiled + GLint compiled = 0; + glGetShaderiv(glshader, GL_COMPILE_STATUS, &compiled); + + // if compilation fails + if (!compiled) { + // save the source code to a temp file so we can debug easily + /* std::ofstream filestream; + filestream.open("debugshader.glsl"); + if (filestream.is_open()) { + filestream << shaderSource->source; + filestream.close(); + } + */ + + GLint infoLength = 0; + glGetShaderiv(glshader, GL_INFO_LOG_LENGTH, &infoLength); + + char* temp = new char[infoLength] ; + glGetShaderInfoLog(glshader, infoLength, NULL, temp); + + qDebug() << "GLShader::compileShader - failed to compile the gl shader object:"; + qDebug() << temp; + + /* + filestream.open("debugshader.glsl.info.txt"); + if (filestream.is_open()) { + filestream << std::string(temp); + filestream.close(); + } + */ + delete[] temp; + + glDeleteShader(glshader); + return nullptr; + } + + GLuint glprogram = 0; +#ifdef SEPARATE_PROGRAM + // so far so good, program is almost done, need to link: + GLuint glprogram = glCreateProgram(); + if (!glprogram) { + qDebug() << "GLShader::compileShader - failed to create the gl shader & gl program object"; + return nullptr; + } + + glProgramParameteri(glprogram, GL_PROGRAM_SEPARABLE, GL_TRUE); + glAttachShader(glprogram, glshader); + glLinkProgram(glprogram); + + GLint linked = 0; + glGetProgramiv(glprogram, GL_LINK_STATUS, &linked); + + if (!linked) { + /* + // save the source code to a temp file so we can debug easily + std::ofstream filestream; + filestream.open("debugshader.glsl"); + if (filestream.is_open()) { + filestream << shaderSource->source; + filestream.close(); + } + */ + + GLint infoLength = 0; + glGetProgramiv(glprogram, GL_INFO_LOG_LENGTH, &infoLength); + + char* temp = new char[infoLength] ; + glGetProgramInfoLog(glprogram, infoLength, NULL, temp); + + qDebug() << "GLShader::compileShader - failed to LINK the gl program object :"; + qDebug() << temp; + + /* + filestream.open("debugshader.glsl.info.txt"); + if (filestream.is_open()) { + filestream << String(temp); + filestream.close(); + } + */ + delete[] temp; + + glDeleteShader(glshader); + glDeleteProgram(glprogram); + return nullptr; + } +#endif + + // So far so good, the shader is created successfully + GLBackend::GLShader* object = new GLBackend::GLShader(); + object->_shader = glshader; + object->_program = glprogram; + + makeBindings(object); + + return object; } GLBackend::GLShader* compileProgram(const Shader& program) { - if(!program.isProgram()) { - return nullptr; - } - - // Let's go through every shaders and make sure they are ready to go - std::vector< GLuint > shaderObjects; - for (auto subShader : program.getShaders()) { - GLuint so = GLBackend::getShaderID(subShader); - if (!so) { - qDebug() << "GLShader::compileProgram - One of the shaders of the program is not compiled?"; - return nullptr; - } - shaderObjects.push_back(so); - } - - // so far so good, program is almost done, need to link: - GLuint glprogram = glCreateProgram(); - if (!glprogram) { - qDebug() << "GLShader::compileProgram - failed to create the gl program object"; - return nullptr; - } - - // glProgramParameteri(glprogram, GL_PROGRAM_, GL_TRUE); - // Create the program from the sub shaders - for (auto so : shaderObjects) { - glAttachShader(glprogram, so); - } - - // Link! - glLinkProgram(glprogram); - - GLint linked = 0; - glGetProgramiv(glprogram, GL_LINK_STATUS, &linked); - - if (!linked) { - /* - // save the source code to a temp file so we can debug easily - std::ofstream filestream; - filestream.open("debugshader.glsl"); - if (filestream.is_open()) { - filestream << shaderSource->source; - filestream.close(); - } - */ - - GLint infoLength = 0; - glGetProgramiv(glprogram, GL_INFO_LOG_LENGTH, &infoLength); - - char* temp = new char[infoLength] ; - glGetProgramInfoLog(glprogram, infoLength, NULL, temp); - - qDebug() << "GLShader::compileProgram - failed to LINK the gl program object :"; - qDebug() << temp; - - /* - filestream.open("debugshader.glsl.info.txt"); - if (filestream.is_open()) { - filestream << std::string(temp); - filestream.close(); - } - */ - delete[] temp; - - glDeleteProgram(glprogram); - return nullptr; - } - - // So far so good, the program is created successfully - GLBackend::GLShader* object = new GLBackend::GLShader(); - object->_shader = 0; - object->_program = glprogram; - - makeBindings(object); - - return object; + if(!program.isProgram()) { + return nullptr; + } + + // Let's go through every shaders and make sure they are ready to go + std::vector< GLuint > shaderObjects; + for (auto subShader : program.getShaders()) { + GLuint so = GLBackend::getShaderID(subShader); + if (!so) { + qDebug() << "GLShader::compileProgram - One of the shaders of the program is not compiled?"; + return nullptr; + } + shaderObjects.push_back(so); + } + + // so far so good, program is almost done, need to link: + GLuint glprogram = glCreateProgram(); + if (!glprogram) { + qDebug() << "GLShader::compileProgram - failed to create the gl program object"; + return nullptr; + } + + // glProgramParameteri(glprogram, GL_PROGRAM_, GL_TRUE); + // Create the program from the sub shaders + for (auto so : shaderObjects) { + glAttachShader(glprogram, so); + } + + // Link! + glLinkProgram(glprogram); + + GLint linked = 0; + glGetProgramiv(glprogram, GL_LINK_STATUS, &linked); + + if (!linked) { + /* + // save the source code to a temp file so we can debug easily + std::ofstream filestream; + filestream.open("debugshader.glsl"); + if (filestream.is_open()) { + filestream << shaderSource->source; + filestream.close(); + } + */ + + GLint infoLength = 0; + glGetProgramiv(glprogram, GL_INFO_LOG_LENGTH, &infoLength); + + char* temp = new char[infoLength] ; + glGetProgramInfoLog(glprogram, infoLength, NULL, temp); + + qDebug() << "GLShader::compileProgram - failed to LINK the gl program object :"; + qDebug() << temp; + + /* + filestream.open("debugshader.glsl.info.txt"); + if (filestream.is_open()) { + filestream << std::string(temp); + filestream.close(); + } + */ + delete[] temp; + + glDeleteProgram(glprogram); + return nullptr; + } + + // So far so good, the program is created successfully + GLBackend::GLShader* object = new GLBackend::GLShader(); + object->_shader = 0; + object->_program = glprogram; + + makeBindings(object); + + return object; } GLBackend::GLShader* GLBackend::syncGPUObject(const Shader& shader) { @@ -360,127 +361,135 @@ ElementResource getFormatFromGLUniform(GLenum gltype) { case GL_FLOAT_VEC2: return ElementResource(Element(VEC2, gpu::FLOAT, UNIFORM), Resource::BUFFER); case GL_FLOAT_VEC3: return ElementResource(Element(VEC3, gpu::FLOAT, UNIFORM), Resource::BUFFER); case GL_FLOAT_VEC4: return ElementResource(Element(VEC4, gpu::FLOAT, UNIFORM), Resource::BUFFER); - +/* case GL_DOUBLE: return ElementResource(Element(SCALAR, gpu::FLOAT, UNIFORM), Resource::BUFFER); case GL_DOUBLE_VEC2: return ElementResource(Element(VEC2, gpu::FLOAT, UNIFORM), Resource::BUFFER); case GL_DOUBLE_VEC3: return ElementResource(Element(VEC3, gpu::FLOAT, UNIFORM), Resource::BUFFER); case GL_DOUBLE_VEC4: return ElementResource(Element(VEC4, gpu::FLOAT, UNIFORM), Resource::BUFFER); - +*/ case GL_INT: return ElementResource(Element(SCALAR, gpu::INT32, UNIFORM), Resource::BUFFER); case GL_INT_VEC2: return ElementResource(Element(VEC2, gpu::INT32, UNIFORM), Resource::BUFFER); case GL_INT_VEC3: return ElementResource(Element(VEC3, gpu::INT32, UNIFORM), Resource::BUFFER); case GL_INT_VEC4: return ElementResource(Element(VEC4, gpu::INT32, UNIFORM), Resource::BUFFER); - + case GL_UNSIGNED_INT: return ElementResource(Element(SCALAR, gpu::UINT32, UNIFORM), Resource::BUFFER); +#if defined(Q_OS_WIN) case GL_UNSIGNED_INT_VEC2: return ElementResource(Element(VEC2, gpu::UINT32, UNIFORM), Resource::BUFFER); case GL_UNSIGNED_INT_VEC3: return ElementResource(Element(VEC3, gpu::UINT32, UNIFORM), Resource::BUFFER); case GL_UNSIGNED_INT_VEC4: return ElementResource(Element(VEC4, gpu::UINT32, UNIFORM), Resource::BUFFER); - +#endif + case GL_BOOL: return ElementResource(Element(SCALAR, gpu::BOOL, UNIFORM), Resource::BUFFER); case GL_BOOL_VEC2: return ElementResource(Element(VEC2, gpu::BOOL, UNIFORM), Resource::BUFFER); case GL_BOOL_VEC3: return ElementResource(Element(VEC3, gpu::BOOL, UNIFORM), Resource::BUFFER); case GL_BOOL_VEC4: return ElementResource(Element(VEC4, gpu::BOOL, UNIFORM), Resource::BUFFER); - - + + case GL_FLOAT_MAT2: return ElementResource(Element(gpu::MAT2, gpu::FLOAT, UNIFORM), Resource::BUFFER); case GL_FLOAT_MAT3: return ElementResource(Element(MAT3, gpu::FLOAT, UNIFORM), Resource::BUFFER); case GL_FLOAT_MAT4: return ElementResource(Element(MAT4, gpu::FLOAT, UNIFORM), Resource::BUFFER); - -/* {GL_FLOAT_MAT2x3 mat2x3}, - {GL_FLOAT_MAT2x4 mat2x4}, - {GL_FLOAT_MAT3x2 mat3x2}, - {GL_FLOAT_MAT3x4 mat3x4}, - {GL_FLOAT_MAT4x2 mat4x2}, - {GL_FLOAT_MAT4x3 mat4x3}, - {GL_DOUBLE_MAT2 dmat2}, - {GL_DOUBLE_MAT3 dmat3}, - {GL_DOUBLE_MAT4 dmat4}, - {GL_DOUBLE_MAT2x3 dmat2x3}, - {GL_DOUBLE_MAT2x4 dmat2x4}, - {GL_DOUBLE_MAT3x2 dmat3x2}, - {GL_DOUBLE_MAT3x4 dmat3x4}, - {GL_DOUBLE_MAT4x2 dmat4x2}, - {GL_DOUBLE_MAT4x3 dmat4x3}, - */ - + +/* {GL_FLOAT_MAT2x3 mat2x3}, + {GL_FLOAT_MAT2x4 mat2x4}, + {GL_FLOAT_MAT3x2 mat3x2}, + {GL_FLOAT_MAT3x4 mat3x4}, + {GL_FLOAT_MAT4x2 mat4x2}, + {GL_FLOAT_MAT4x3 mat4x3}, + {GL_DOUBLE_MAT2 dmat2}, + {GL_DOUBLE_MAT3 dmat3}, + {GL_DOUBLE_MAT4 dmat4}, + {GL_DOUBLE_MAT2x3 dmat2x3}, + {GL_DOUBLE_MAT2x4 dmat2x4}, + {GL_DOUBLE_MAT3x2 dmat3x2}, + {GL_DOUBLE_MAT3x4 dmat3x4}, + {GL_DOUBLE_MAT4x2 dmat4x2}, + {GL_DOUBLE_MAT4x3 dmat4x3}, + */ + case GL_SAMPLER_1D: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_1D); case GL_SAMPLER_2D: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_2D); - case GL_SAMPLER_2D_MULTISAMPLE: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D); + case GL_SAMPLER_3D: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_3D); case GL_SAMPLER_CUBE: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_CUBE); - + +#if defined(Q_OS_WIN) + case GL_SAMPLER_2D_MULTISAMPLE: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D); case GL_SAMPLER_1D_ARRAY: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_1D_ARRAY); case GL_SAMPLER_2D_ARRAY: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_2D_ARRAY); case GL_SAMPLER_2D_MULTISAMPLE_ARRAY: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D_ARRAY); - +#endif + case GL_SAMPLER_2D_SHADOW: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_SHADOW), Resource::TEXTURE_2D); +#if defined(Q_OS_WIN) case GL_SAMPLER_CUBE_SHADOW: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_SHADOW), Resource::TEXTURE_CUBE); case GL_SAMPLER_2D_ARRAY_SHADOW: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_SHADOW), Resource::TEXTURE_2D_ARRAY); - -// {GL_SAMPLER_1D_SHADOW sampler1DShadow}, - // {GL_SAMPLER_1D_ARRAY_SHADOW sampler1DArrayShadow}, - -// {GL_SAMPLER_BUFFER samplerBuffer}, -// {GL_SAMPLER_2D_RECT sampler2DRect}, - // {GL_SAMPLER_2D_RECT_SHADOW sampler2DRectShadow}, - +#endif + +// {GL_SAMPLER_1D_SHADOW sampler1DShadow}, + // {GL_SAMPLER_1D_ARRAY_SHADOW sampler1DArrayShadow}, + +// {GL_SAMPLER_BUFFER samplerBuffer}, +// {GL_SAMPLER_2D_RECT sampler2DRect}, + // {GL_SAMPLER_2D_RECT_SHADOW sampler2DRectShadow}, + +#if defined(Q_OS_WIN) case GL_INT_SAMPLER_1D: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_1D); case GL_INT_SAMPLER_2D: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_2D); case GL_INT_SAMPLER_2D_MULTISAMPLE: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D); case GL_INT_SAMPLER_3D: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_3D); case GL_INT_SAMPLER_CUBE: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_CUBE); - + case GL_INT_SAMPLER_1D_ARRAY: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_1D_ARRAY); case GL_INT_SAMPLER_2D_ARRAY: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_2D_ARRAY); case GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D_ARRAY); - - // {GL_INT_SAMPLER_BUFFER isamplerBuffer}, - // {GL_INT_SAMPLER_2D_RECT isampler2DRect}, - + + // {GL_INT_SAMPLER_BUFFER isamplerBuffer}, + // {GL_INT_SAMPLER_2D_RECT isampler2DRect}, + case GL_UNSIGNED_INT_SAMPLER_1D: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_1D); case GL_UNSIGNED_INT_SAMPLER_2D: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_2D); case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D); case GL_UNSIGNED_INT_SAMPLER_3D: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_3D); case GL_UNSIGNED_INT_SAMPLER_CUBE: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_CUBE); - + case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_1D_ARRAY); case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_2D_ARRAY); case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D_ARRAY); - -// {GL_UNSIGNED_INT_SAMPLER_BUFFER usamplerBuffer}, -// {GL_UNSIGNED_INT_SAMPLER_2D_RECT usampler2DRect}, -/* - {GL_IMAGE_1D image1D}, - {GL_IMAGE_2D image2D}, - {GL_IMAGE_3D image3D}, - {GL_IMAGE_2D_RECT image2DRect}, - {GL_IMAGE_CUBE imageCube}, - {GL_IMAGE_BUFFER imageBuffer}, - {GL_IMAGE_1D_ARRAY image1DArray}, - {GL_IMAGE_2D_ARRAY image2DArray}, - {GL_IMAGE_2D_MULTISAMPLE image2DMS}, - {GL_IMAGE_2D_MULTISAMPLE_ARRAY image2DMSArray}, - {GL_INT_IMAGE_1D iimage1D}, - {GL_INT_IMAGE_2D iimage2D}, - {GL_INT_IMAGE_3D iimage3D}, - {GL_INT_IMAGE_2D_RECT iimage2DRect}, - {GL_INT_IMAGE_CUBE iimageCube}, - {GL_INT_IMAGE_BUFFER iimageBuffer}, - {GL_INT_IMAGE_1D_ARRAY iimage1DArray}, - {GL_INT_IMAGE_2D_ARRAY iimage2DArray}, - {GL_INT_IMAGE_2D_MULTISAMPLE iimage2DMS}, - {GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY iimage2DMSArray}, - {GL_UNSIGNED_INT_IMAGE_1D uimage1D}, - {GL_UNSIGNED_INT_IMAGE_2D uimage2D}, - {GL_UNSIGNED_INT_IMAGE_3D uimage3D}, - {GL_UNSIGNED_INT_IMAGE_2D_RECT uimage2DRect}, - {GL_UNSIGNED_INT_IMAGE_CUBE uimageCube},+ [0] {_name="fInnerRadius" _location=0 _element={_semantic=15 '\xf' _dimension=0 '\0' _type=0 '\0' } } gpu::Shader::Slot - - {GL_UNSIGNED_INT_IMAGE_BUFFER uimageBuffer}, - {GL_UNSIGNED_INT_IMAGE_1D_ARRAY uimage1DArray}, - {GL_UNSIGNED_INT_IMAGE_2D_ARRAY uimage2DArray}, - {GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE uimage2DMS}, +#endif +// {GL_UNSIGNED_INT_SAMPLER_BUFFER usamplerBuffer}, +// {GL_UNSIGNED_INT_SAMPLER_2D_RECT usampler2DRect}, +/* + {GL_IMAGE_1D image1D}, + {GL_IMAGE_2D image2D}, + {GL_IMAGE_3D image3D}, + {GL_IMAGE_2D_RECT image2DRect}, + {GL_IMAGE_CUBE imageCube}, + {GL_IMAGE_BUFFER imageBuffer}, + {GL_IMAGE_1D_ARRAY image1DArray}, + {GL_IMAGE_2D_ARRAY image2DArray}, + {GL_IMAGE_2D_MULTISAMPLE image2DMS}, + {GL_IMAGE_2D_MULTISAMPLE_ARRAY image2DMSArray}, + {GL_INT_IMAGE_1D iimage1D}, + {GL_INT_IMAGE_2D iimage2D}, + {GL_INT_IMAGE_3D iimage3D}, + {GL_INT_IMAGE_2D_RECT iimage2DRect}, + {GL_INT_IMAGE_CUBE iimageCube}, + {GL_INT_IMAGE_BUFFER iimageBuffer}, + {GL_INT_IMAGE_1D_ARRAY iimage1DArray}, + {GL_INT_IMAGE_2D_ARRAY iimage2DArray}, + {GL_INT_IMAGE_2D_MULTISAMPLE iimage2DMS}, + {GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY iimage2DMSArray}, + {GL_UNSIGNED_INT_IMAGE_1D uimage1D}, + {GL_UNSIGNED_INT_IMAGE_2D uimage2D}, + {GL_UNSIGNED_INT_IMAGE_3D uimage3D}, + {GL_UNSIGNED_INT_IMAGE_2D_RECT uimage2DRect}, + {GL_UNSIGNED_INT_IMAGE_CUBE uimageCube},+ [0] {_name="fInnerRadius" _location=0 _element={_semantic=15 '\xf' _dimension=0 '\0' _type=0 '\0' } } gpu::Shader::Slot + + {GL_UNSIGNED_INT_IMAGE_BUFFER uimageBuffer}, + {GL_UNSIGNED_INT_IMAGE_1D_ARRAY uimage1DArray}, + {GL_UNSIGNED_INT_IMAGE_2D_ARRAY uimage2DArray}, + {GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE uimage2DMS}, {GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY uimage2DMSArray}, {GL_UNSIGNED_INT_ATOMIC_COUNTER atomic_uint} */ @@ -542,6 +551,7 @@ bool isUnusedSlot(GLint binding) { int makeUniformBlockSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& buffers) { GLint buffersCount = 0; +#if defined(Q_OS_WIN) glGetProgramiv(glprogram, GL_ACTIVE_UNIFORM_BLOCKS, &buffersCount); // fast exit @@ -595,7 +605,7 @@ int makeUniformBlockSlots(GLuint glprogram, const Shader::BindingSet& slotBindin Element element(SCALAR, gpu::UINT32, gpu::UNIFORM_BUFFER); buffers.insert(Shader::Slot(name, binding, element, Resource::BUFFER)); } - +#endif return buffersCount; } diff --git a/libraries/model/src/model/Stage.cpp b/libraries/model/src/model/Stage.cpp index e4786df792..473d3c7613 100644 --- a/libraries/model/src/model/Stage.cpp +++ b/libraries/model/src/model/Stage.cpp @@ -154,12 +154,9 @@ SunSkyStage::SunSkyStage() : // Begining of march setYearTime(60.0f); - auto skyShader = gpu::ShaderPointer( - gpu::Shader::createProgram( - gpu::ShaderPointer(gpu::Shader::createVertex(std::string(SkyFromAtmosphere_vert))), - gpu::ShaderPointer(gpu::Shader::createPixel(std::string(SkyFromAtmosphere_frag))) - ) - ); + auto skyFromAtmosphereVertex = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(SkyFromAtmosphere_vert))); + auto skyFromAtmosphereFragment = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(SkyFromAtmosphere_frag))); + auto skyShader = gpu::ShaderPointer(gpu::Shader::createProgram(skyFromAtmosphereVertex, skyFromAtmosphereFragment)); _skyPipeline = gpu::PipelinePointer(gpu::Pipeline::create(skyShader, gpu::States())); } From d9c071a2a9a6ed4c471a0efa498330b942724baf Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Tue, 10 Mar 2015 00:47:27 -0700 Subject: [PATCH 16/43] trying to put the atmospheric effect in a shader header and use it on the directional lighting pass --- libraries/gpu/src/gpu/Pipeline.h | 2 +- libraries/model/src/model/Atmosphere.slh | 244 ++++++++++++++++++ libraries/model/src/model/Stage.cpp | 50 ++++ libraries/model/src/model/Stage.h | 56 ++++ .../render-utils/src/DeferredGlobalLight.slh | 10 + .../src/DeferredLightingEffect.cpp | 31 +++ .../render-utils/src/DeferredLightingEffect.h | 4 + tools/scribe/src/main.cpp | 12 +- 8 files changed, 406 insertions(+), 3 deletions(-) create mode 100755 libraries/model/src/model/Atmosphere.slh diff --git a/libraries/gpu/src/gpu/Pipeline.h b/libraries/gpu/src/gpu/Pipeline.h index c2fc944bb7..faa671c30f 100755 --- a/libraries/gpu/src/gpu/Pipeline.h +++ b/libraries/gpu/src/gpu/Pipeline.h @@ -44,7 +44,7 @@ protected: friend class Backend; }; -typedef QSharedPointer< Pipeline > PipelinePointer; +typedef std::shared_ptr< Pipeline > PipelinePointer; typedef std::vector< PipelinePointer > Pipelines; }; diff --git a/libraries/model/src/model/Atmosphere.slh b/libraries/model/src/model/Atmosphere.slh new file mode 100755 index 0000000000..11b64dde70 --- /dev/null +++ b/libraries/model/src/model/Atmosphere.slh @@ -0,0 +1,244 @@ + +<@if not MODEL_ATMOSPHERE_SLH@> +<@def MODEL_ATMOSPHERE_SLH@> + + + +struct Atmosphere { + vec4 _invWaveLength; + vec4 _radiuses; + vec4 _scales; + vec4 _scatterings; + vec4 _control; +}; + +const int numSamples = 2; + +vec3 getAtmosphereInvWaveLength(Atmosphere a) { return a._invWaveLength.xyz; } // 1 / pow(wavelength, 4) for the red, green, and blue channels + +float getAtmosphereInnerRadius(Atmosphere a) { return a._radiuses.x; } // The inner (planetary) radius +float getAtmosphereOuterRadius(Atmosphere a) { return a._radiuses.y; } // The outer (atmosphere) radius + +float getAtmosphereScale(Atmosphere a) { return a._scales.x; } // 1 / (outerRadius - innerRadius) +float getAtmosphereScaleDepth(Atmosphere a) { return a._scales.y; } // The scale depth (i.e. the altitude at which the atmosphere's average density is found) +float getAtmosphereScaleOverScaleDepth(Atmosphere a) { return a._scales.z; } // scale / scaleDepth + +vec4 getAtmosphereScattering(Atmosphere a) { return a._scatterings; } // The full Mie and Rayleigh scattering coefficients +float getAtmosphereKrESun(Atmosphere a) { return a._scatterings.x; } // Kr * ESun +float getAtmosphereKmESun(Atmosphere a) { return a._scatterings.y; } // Km * ESun +float getAtmosphereKr4PI(Atmosphere a) { return a._scatterings.z; } // Kr * 4 * PI +float getAtmosphereKm4PI(Atmosphere a) { return a._scatterings.w; } // Km * 4 * PI + +float getAtmosphereNumSamples(Atmosphere a) { return a._control.x; } // numSamples +vec2 getAtmosphereGAndG2(Atmosphere a) { return a._control.yz; } // g and g2 + +float atmosphereScale(float scaleDepth, float fCos) +{ + float x = 1.0 - fCos; + return scaleDepth * exp(-0.00287 + x*(0.459 + x*(3.83 + x*(-6.80 + x*5.25)))); +} + +vec4 evalAtmosphereContribution(Atmosphere atmospheric, vec3 position, vec3 cameraPos, vec3 lightPos) { + float fInnerRadius = getAtmosphereInnerRadius(atmospheric); + float fSamples = getAtmosphereNumSamples(atmospheric); + + vec3 v3InvWavelength = getAtmosphereInvWaveLength(atmospheric); + vec4 scatteringCoefs = getAtmosphereScattering(atmospheric); + float fKrESun = scatteringCoefs.x; + float fKmESun = scatteringCoefs.y; + float fKr4PI = scatteringCoefs.z; + float fKm4PI = scatteringCoefs.w; + + vec2 gAndg2 = getAtmosphereGAndG2(atmospheric); + float g = gAndg2.x; + float g2 = gAndg2.y; + + float fScale = getAtmosphereScale(atmospheric); + float fScaleDepth = getAtmosphereScaleDepth(atmospheric); + float fScaleOverScaleDepth = getAtmosphereScaleOverScaleDepth(atmospheric); + + // Get the ray from the camera to the vertex, and its length (which is the far point of the ray passing through the atmosphere) + vec3 v3Pos = position; + vec3 v3Ray = v3Pos - cameraPos; + float fFar = length(v3Ray); + v3Ray /= fFar; + + // Calculate the ray's starting position, then calculate its scattering offset + vec3 v3Start = cameraPos; + float fHeight = length(v3Start); + float fDepthStart = exp(fScaleOverScaleDepth * (fInnerRadius - fHeight)); + float fStartAngle = dot(v3Ray, v3Start) / fHeight; + float fStartOffset = fDepthStart * atmosphereScale(fScaleDepth, fStartAngle); + + // Initialize the scattering loop variables + //gl_FrontColor = vec4(0.0, 0.0, 0.0, 0.0); + float fSampleLength = fFar / fSamples; + float fScaledLength = fSampleLength * fScale; + vec3 v3SampleRay = v3Ray * fSampleLength; + vec3 v3SamplePoint = v3Start + v3SampleRay * 0.5; + + // Now loop through the sample rays + vec3 v3FrontColor = vec3(0.0, 0.0, 0.0); + int nSamples = numSamples; + // int nSamples = int(fSamples); + for(int i=0; i +uniform atmosphereBuffer { + Atmosphere _atmosphere; +}; +Atmosphere getAtmosphere() { + return _atmosphere; +} +<@else@> +uniform vec4 atmosphereBuffer[9]; +Atmosphere getAtmosphere() { + Atmosphere atmosphere; + atmosphere._invWaveLength = atmosphereBuffer[0]; + atmosphere._radiuses = atmosphereBuffer[1]; + atmosphere._scales = atmosphereBuffer[2]; + atmosphere._scatterings = atmosphereBuffer[3]; + atmosphere._control = atmosphereBuffer[4]; + + return atmosphere; +} +<@endif@> + + + +<@endif@> diff --git a/libraries/model/src/model/Stage.cpp b/libraries/model/src/model/Stage.cpp index 473d3c7613..970539a908 100644 --- a/libraries/model/src/model/Stage.cpp +++ b/libraries/model/src/model/Stage.cpp @@ -134,6 +134,56 @@ void EarthSunModel::setSunLongitude(float lon) { _sunLongitude = validateLongitude(lon); invalidate(); } + +Atmosphere::Atmosphere() { + // only if created from nothing shall we create the Buffer to store the properties + Data data; + _dataBuffer = gpu::BufferView(new gpu::Buffer(sizeof(Data), (const gpu::Buffer::Byte*) &data)); + + setScatteringWavelength(_scatteringWavelength); + setRayleighScattering(_rayleighScattering); + setInnerOuterRadiuses(getInnerRadius(), getOuterRadius()); +} + +void Atmosphere::setScatteringWavelength(Vec3 wavelength) { + _scatteringWavelength = wavelength; + Data& data = editData(); + data._invWaveLength = Vec4(1.0f / powf(wavelength.x, 4.0f), 1.0f / powf(wavelength.y, 4.0f), 1.0f / powf(wavelength.z, 4.0f), 0.0f); +} + +void Atmosphere::setRayleighScattering(float scattering) { + _rayleighScattering = scattering; + updateScattering(); +} + +void Atmosphere::setMieScattering(float scattering) { + _mieScattering = scattering; + updateScattering(); +} + +void Atmosphere::setSunBrightness(float brightness) { + _sunBrightness = brightness; + updateScattering(); +} + +void Atmosphere::updateScattering() { + Data& data = editData(); + + data._scatterings.x = getRayleighScattering() * getSunBrightness(); + data._scatterings.y = getMieScattering() * getSunBrightness(); + + data._scatterings.z = getRayleighScattering() * 4.0f * glm::pi(); + data._scatterings.w = getMieScattering() * 4.0f * glm::pi(); +} + +void Atmosphere::setInnerOuterRadiuses(float inner, float outer) { + Data& data = editData(); + data._radiuses.x = inner; + data._radiuses.y = outer; + data._scales.x = 1.0f / (outer - inner); + data._scales.z = data._scales.x / data._scales.y; +} + const int NUM_DAYS_PER_YEAR = 365; const float NUM_HOURS_PER_DAY = 24.0f; diff --git a/libraries/model/src/model/Stage.h b/libraries/model/src/model/Stage.h index 61914b5a6b..3cc8b4a6e9 100644 --- a/libraries/model/src/model/Stage.h +++ b/libraries/model/src/model/Stage.h @@ -106,6 +106,60 @@ protected: static Mat4d evalWorldToGeoLocationMat(double longitude, double latitude, double altitude, double scale); }; + +class Atmosphere { +public: + + Atmosphere(); + Atmosphere(const Atmosphere& atmosphere); + Atmosphere& operator= (const Atmosphere& atmosphere); + virtual ~Atmosphere() {}; + + + void setScatteringWavelength(Vec3 wavelength); + const Vec3& getScatteringWavelength() const { return _scatteringWavelength; } + + void setRayleighScattering(float scattering); + float getRayleighScattering() const { return _rayleighScattering; } + + void setMieScattering(float scattering); + float getMieScattering() const { return _mieScattering; } + + void setSunBrightness(float brightness); + float getSunBrightness() const { return _sunBrightness; } + + void setInnerOuterRadiuses(float inner, float outer); + float getInnerRadius() const { return getData()._radiuses.x; } + float getOuterRadius() const { return getData()._radiuses.y; } + + // Data to access the attribute values of the atmosphere + class Data { + public: + Vec4 _invWaveLength = Vec4(0.0f); + Vec4 _radiuses = Vec4(6000.0f, 6025.0f, 0.0f, 0.0f); + Vec4 _scales = Vec4(0.0f, 0.25f, 0.0f, 0.0f); + Vec4 _scatterings = Vec4(0.0f); + Vec4 _control = Vec4(2.0f, -0.990f, -0.990f*-0.990f, 0.f); + + Data() {} + }; + + const UniformBufferView& getDataBuffer() const { return _dataBuffer; } + +protected: + UniformBufferView _dataBuffer; + Vec3 _scatteringWavelength = Vec3(0.650f, 0.570f, 0.475f); + float _rayleighScattering = 0.0025f; + float _mieScattering = 0.0010f; + float _sunBrightness = 20.0f; + + const Data& getData() const { return _dataBuffer.get(); } + Data& editData() { return _dataBuffer.edit(); } + + void updateScattering(); +}; +typedef QSharedPointer< Atmosphere > AtmospherePointer; + // Sun sky stage generates the rendering primitives to display a scene realistically // at the specified location and time around earth class SunSkyStage { @@ -141,9 +195,11 @@ public: float getSunIntensity() const { return getSunLight()->getIntensity(); } LightPointer getSunLight() const { valid(); return _sunLight; } + AtmospherePointer getAtmosphere() const { valid(); return _atmosphere; } protected: LightPointer _sunLight; + AtmospherePointer _atmosphere; gpu::PipelinePointer _skyPipeline; diff --git a/libraries/render-utils/src/DeferredGlobalLight.slh b/libraries/render-utils/src/DeferredGlobalLight.slh index 3039c92d67..b48b61fd9f 100755 --- a/libraries/render-utils/src/DeferredGlobalLight.slh +++ b/libraries/render-utils/src/DeferredGlobalLight.slh @@ -13,6 +13,7 @@ <@include DeferredLighting.slh@> + struct SphericalHarmonics { vec4 L00; vec4 L1m1; @@ -50,6 +51,7 @@ uniform SphericalHarmonics ambientSphere; // Everything about light <@include model/Light.slh@> +<@include model/Atmosphere.slh@> // The view Matrix uniform mat4 invViewMat; @@ -89,6 +91,14 @@ vec3 evalAmbienSphereGlobalColor(float shadowAttenuation, vec3 position, vec3 no color += vec3(diffuse + shading.rgb) * shading.w * shadowAttenuation * getLightColor(light) * getLightIntensity(light); + + vec3 fragPos = vec3(invViewMat * vec4(position, 1.0)); + vec3 cameraPos = invViewMat[3].xyz; + vec3 lightPos = 10000.0 * getLightDirection(light); + + Atmosphere atmo = getAtmosphere(); + vec4 atmoColor = evalAtmosphereContribution(atmo, fragPos, cameraPos, lightPos); + return color; } diff --git a/libraries/render-utils/src/DeferredLightingEffect.cpp b/libraries/render-utils/src/DeferredLightingEffect.cpp index c8ad3711d3..96ab790cd6 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.cpp +++ b/libraries/render-utils/src/DeferredLightingEffect.cpp @@ -270,6 +270,12 @@ void DeferredLightingEffect::render() { batch.setUniformBuffer(locations->lightBufferUnit, globalLight->getSchemaBuffer()); gpu::GLBackend::renderBatch(batch); } + + if (_atmosphere && (locations->atmosphereBufferUnit >= 0)) { + gpu::Batch batch; + batch.setUniformBuffer(locations->atmosphereBufferUnit, _atmosphere->getDataBuffer()); + gpu::GLBackend::renderBatch(batch); + } glUniformMatrix4fv(locations->invViewMat, 1, false, reinterpret_cast< const GLfloat* >(&invViewMat)); } @@ -511,6 +517,31 @@ void DeferredLightingEffect::loadLightProgram(const char* fragSource, bool limit locations.lightBufferUnit = -1; } #endif + +#if defined(Q_OS_MAC) + loc = program.uniformLocation("atmosphereBufferUnit"); + if (loc >= 0) { + locations.atmosphereBufferUnit = loc; + } else { + locations.atmosphereBufferUnit = -1; + } +#elif defined(Q_OS_WIN) + loc = glGetUniformBlockIndex(program.programId(), "atmosphereBufferUnit"); + if (loc >= 0) { + glUniformBlockBinding(program.programId(), loc, 1); + locations.atmosphereBufferUnit = 1; + } else { + locations.atmosphereBufferUnit = -1; + } +#else + loc = program.uniformLocation("atmosphereBufferUnit"); + if (loc >= 0) { + locations.atmosphereBufferUnit = loc; + } else { + locations.atmosphereBufferUnit = -1; + } +#endif + program.release(); } diff --git a/libraries/render-utils/src/DeferredLightingEffect.h b/libraries/render-utils/src/DeferredLightingEffect.h index 951c36f038..900c5243cb 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.h +++ b/libraries/render-utils/src/DeferredLightingEffect.h @@ -20,6 +20,7 @@ #include "ProgramObject.h" #include "model/Light.h" +#include "model/Stage.h" class AbstractViewStateInterface; class PostLightingRenderable; @@ -73,6 +74,7 @@ public: // update global lighting void setAmbientLightMode(int preset); void setGlobalLight(const glm::vec3& direction, const glm::vec3& diffuse, float intensity); + void setGlobalAtmosphere(const model::AtmospherePointer& atmosphere) { _atmosphere = atmosphere; } private: DeferredLightingEffect() {} @@ -89,6 +91,7 @@ private: int radius; int ambientSphere; int lightBufferUnit; + int atmosphereBufferUnit; int invViewMat; }; @@ -146,6 +149,7 @@ private: AbstractViewStateInterface* _viewState; int _ambientLightMode = 0; + model::AtmospherePointer _atmosphere; }; /// Simple interface for objects that require something to be rendered after deferred lighting. diff --git a/tools/scribe/src/main.cpp b/tools/scribe/src/main.cpp index f68272c0ab..10f0a026ca 100755 --- a/tools/scribe/src/main.cpp +++ b/tools/scribe/src/main.cpp @@ -192,9 +192,17 @@ int main (int argc, char** argv) { targetStringStream << "#ifndef scribe_" << targetName << "_h" << std::endl; targetStringStream << "#define scribe_" << targetName << "_h" << std::endl << std::endl; - targetStringStream << "const char " << targetName << "[] = R\"XXXX(" << destStringStream.str() << ")XXXX\";"; + // targetStringStream << "const char " << targetName << "[] = R\"XXXX(" << destStringStream.str() << ")XXXX\";"; + std::istringstream destStringStreamAgain(destStringStream.str()); + targetStringStream << "const char " << targetName << "[] = \n"; + while (!destStringStreamAgain.eof()) { + std::string lineToken; + std::getline(destStringStreamAgain, lineToken); + // targetStringStream << "\"" << lineToken << "\"\n"; + targetStringStream << "R\"X(" << lineToken << ")X\"\"\\n\"\n"; + } - targetStringStream << std::endl << std::endl; + targetStringStream << ";\n" << std::endl << std::endl; targetStringStream << "#endif" << std::endl; } else { targetStringStream << destStringStream.str(); From da16baf0ca2d9e409600d481f8f15351c1dd7c2f Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Tue, 10 Mar 2015 10:24:16 -0700 Subject: [PATCH 17/43] removing the atmosphere effect from the directional lighting --- libraries/render-utils/src/DeferredGlobalLight.slh | 9 --------- 1 file changed, 9 deletions(-) diff --git a/libraries/render-utils/src/DeferredGlobalLight.slh b/libraries/render-utils/src/DeferredGlobalLight.slh index b48b61fd9f..556e7421af 100755 --- a/libraries/render-utils/src/DeferredGlobalLight.slh +++ b/libraries/render-utils/src/DeferredGlobalLight.slh @@ -51,7 +51,6 @@ uniform SphericalHarmonics ambientSphere; // Everything about light <@include model/Light.slh@> -<@include model/Atmosphere.slh@> // The view Matrix uniform mat4 invViewMat; @@ -91,14 +90,6 @@ vec3 evalAmbienSphereGlobalColor(float shadowAttenuation, vec3 position, vec3 no color += vec3(diffuse + shading.rgb) * shading.w * shadowAttenuation * getLightColor(light) * getLightIntensity(light); - - vec3 fragPos = vec3(invViewMat * vec4(position, 1.0)); - vec3 cameraPos = invViewMat[3].xyz; - vec3 lightPos = 10000.0 * getLightDirection(light); - - Atmosphere atmo = getAtmosphere(); - vec4 atmoColor = evalAtmosphereContribution(atmo, fragPos, cameraPos, lightPos); - return color; } From d25d3bc6abe25c9db6a9d90ec6dc0b99716e4bf8 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Mon, 9 Mar 2015 19:34:46 -0700 Subject: [PATCH 18/43] Get users online from Web API --- examples/users.js | 60 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 examples/users.js diff --git a/examples/users.js b/examples/users.js new file mode 100644 index 0000000000..4b56fce505 --- /dev/null +++ b/examples/users.js @@ -0,0 +1,60 @@ +// +// users.js +// examples +// +// Created by David Rowe on 9 Mar 2015. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +var usersWindow = (function () { + + var API_URL = "https://metaverse.highfidelity.io/api/v1/users?status=online", + HTTP_GET_TIMEOUT = 60000, // ms = 1 minute + usersRequest, + processUsers, + usersTimedOut, + usersTimer, + UPDATE_TIMEOUT = 1000; // ms = 1s + + function requestUsers() { + usersRequest = new XMLHttpRequest(); + usersRequest.open("GET", API_URL, true); + usersRequest.timeout = HTTP_GET_TIMEOUT * 1000; + usersRequest.ontimeout = usersTimedOut; + usersRequest.onreadystatechange = processUsers; + usersRequest.send(); + } + + processUsers = function () { + if (usersRequest.readyState === usersRequest.DONE) { + if (usersRequest.status === 200) { + // TODO: Process users data + } else { + print("Error: Request for users status returned " + usersRequest.status + " " + usersRequest.statusText); + } + + usersTimer = Script.setTimeout(requestUsers, UPDATE_TIMEOUT); // Update while after finished processing. + } + }; + + usersTimedOut = function () { + print("Error: Request for users status timed out"); + usersTimer = Script.setTimeout(requestUsers, HTTP_GET_TIMEOUT); // Try again after a longer delay. + }; + + function setUp() { + + requestUsers(); + } + + function tearDown() { + Script.clearTimeout(usersTimer); + + } + + setUp(); + Script.scriptEnding.connect(tearDown); +}()); From 63d8da0233ad634fc908723601cfd692a988b8e0 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Mon, 9 Mar 2015 22:29:24 -0700 Subject: [PATCH 19/43] Display user names in 2D window --- examples/users.js | 103 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 98 insertions(+), 5 deletions(-) diff --git a/examples/users.js b/examples/users.js index 4b56fce505..1ee5a59115 100644 --- a/examples/users.js +++ b/examples/users.js @@ -11,13 +11,77 @@ var usersWindow = (function () { - var API_URL = "https://metaverse.highfidelity.io/api/v1/users?status=online", + var WINDOW_BOUNDS_2D = { x: 100, y: 100, width: 300, height: 200 }, + WINDOW_MARGIN_2D = 12, + WINDOW_FOREGROUND_COLOR_2D = { red: 240, green: 240, blue: 240 }, + WINDOW_FOREGROUND_ALPHA_2D = 0.9, + WINDOW_BACKGROUND_COLOR_2D = { red: 120, green: 120, blue: 120 }, + WINDOW_BACKGROUND_ALPHA_2D = 0.7, + usersPane2D, + USERS_PANE_TEXT_WIDTH_2D = WINDOW_BOUNDS_2D.width - 2 * WINDOW_MARGIN_2D, + USERS_FONT_2D = { size: 14 }, + USERNAME_SPACER = "\u00a0\u00a0\u00a0\u00a0", // Nonbreaking spaces + usernameSpacerWidth2D, + + usersOnline, + usersInLines = [], // 2D array of lines of users + + API_URL = "https://metaverse.highfidelity.io/api/v1/users?status=online", HTTP_GET_TIMEOUT = 60000, // ms = 1 minute usersRequest, processUsers, usersTimedOut, usersTimer, - UPDATE_TIMEOUT = 1000; // ms = 1s + UPDATE_TIMEOUT = 5000; // ms = 5s + + function updateWindow() { + var displayText = "", + numUsersDisplayed = 0, + lineText = "", + lineWidth = 0, + myUsername = GlobalServices.myUsername, + usernameWidth, + user, + i; + + usersInLines = []; + + for (i = 0; i < usersOnline.length; i += 1) { + user = usersOnline[i]; + if (user.username !== myUsername && user.online) { + usernameWidth = Overlays.textSize(usersPane2D, user.username).width; + + if (usersInLines.length === 0 || lineWidth + usernameSpacerWidth2D + usernameWidth > USERS_PANE_TEXT_WIDTH_2D) { + displayText += "\n" + lineText; + + // New line + usersInLines.push([i]); // New array in new line or userLines array + usersOnline[i].leftX = 0; // Augment usersOnline data + usersOnline[i].rightX = usernameWidth; + + lineText = user.username; + lineWidth = usernameWidth; + + } else { + // Append + usersInLines[usersInLines.length - 1].push(i); + + usersOnline[i].leftX = lineWidth + usernameSpacerWidth2D; + usersOnline[i].rightX = lineWidth + usernameSpacerWidth2D + usernameWidth; + + lineText += USERNAME_SPACER + user.username; + lineWidth = usersOnline[i].rightX; + } + + numUsersDisplayed += 1; + } + } + + displayText += "\n" + lineText; + displayText = displayText.slice(2); // Remove leading "\n"s. + + Overlays.editOverlay(usersPane2D, { text: numUsersDisplayed > 0 ? displayText : "No users online" }); + } function requestUsers() { usersRequest = new XMLHttpRequest(); @@ -29,14 +93,29 @@ var usersWindow = (function () { } processUsers = function () { + var response; + if (usersRequest.readyState === usersRequest.DONE) { if (usersRequest.status === 200) { - // TODO: Process users data + response = JSON.parse(usersRequest.responseText); + if (response.status !== "success") { + print("Error: Request for users status returned status = " + response.status); + usersTimer = Script.setTimeout(requestUsers, HTTP_GET_TIMEOUT); // Try again after a longer delay. + return; + } + if (!response.hasOwnProperty("data") || !response.data.hasOwnProperty("users")) { + print("Error: Request for users status returned invalid data"); + usersTimer = Script.setTimeout(requestUsers, HTTP_GET_TIMEOUT); // Try again after a longer delay. + return; + } + + usersOnline = response.data.users; + updateWindow(); } else { print("Error: Request for users status returned " + usersRequest.status + " " + usersRequest.statusText); } - usersTimer = Script.setTimeout(requestUsers, UPDATE_TIMEOUT); // Update while after finished processing. + usersTimer = Script.setTimeout(requestUsers, UPDATE_TIMEOUT); // Update after finished processing. } }; @@ -46,13 +125,27 @@ var usersWindow = (function () { }; function setUp() { + usersPane2D = Overlays.addOverlay("text", { + bounds: WINDOW_BOUNDS_2D, + topMargin: WINDOW_MARGIN_2D, + leftMargin: WINDOW_MARGIN_2D, + color: WINDOW_FOREGROUND_COLOR_2D, + alpha: WINDOW_FOREGROUND_ALPHA_2D, + backgroundColor: WINDOW_BACKGROUND_COLOR_2D, + backgroundAlpha: WINDOW_BACKGROUND_ALPHA_2D, + text: "", + font: USERS_FONT_2D, + visible: true + }); + + usernameSpacerWidth2D = Overlays.textSize(usersPane2D, USERNAME_SPACER).width; requestUsers(); } function tearDown() { Script.clearTimeout(usersTimer); - + Overlays.deleteOverlay(usersPane2D); } setUp(); From abb62fd8f974537dbd0e195d1852797ae749cb4d Mon Sep 17 00:00:00 2001 From: David Rowe Date: Mon, 9 Mar 2015 22:29:54 -0700 Subject: [PATCH 20/43] Fix typo found in passing --- interface/src/ui/overlays/TextOverlay.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/ui/overlays/TextOverlay.h b/interface/src/ui/overlays/TextOverlay.h index 6387b42bc3..aea8487fd3 100644 --- a/interface/src/ui/overlays/TextOverlay.h +++ b/interface/src/ui/overlays/TextOverlay.h @@ -55,7 +55,7 @@ public: virtual TextOverlay* createClone() const; virtual QScriptValue getProperty(const QString& property); - QSizeF textSize(const QString& test) const; // Pixels + QSizeF textSize(const QString& text) const; // Pixels private: QString _text; From a4fe9c1a12e9a7d3983e129210d836d134fc47ed Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 10 Mar 2015 12:32:48 -0700 Subject: [PATCH 21/43] Click on username to TP to them --- examples/users.js | 70 +++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 61 insertions(+), 9 deletions(-) diff --git a/examples/users.js b/examples/users.js index 1ee5a59115..edc9b0f28e 100644 --- a/examples/users.js +++ b/examples/users.js @@ -20,11 +20,13 @@ var usersWindow = (function () { usersPane2D, USERS_PANE_TEXT_WIDTH_2D = WINDOW_BOUNDS_2D.width - 2 * WINDOW_MARGIN_2D, USERS_FONT_2D = { size: 14 }, + usersLineHeight, + usersLineSpacing, USERNAME_SPACER = "\u00a0\u00a0\u00a0\u00a0", // Nonbreaking spaces usernameSpacerWidth2D, usersOnline, - usersInLines = [], // 2D array of lines of users + linesOfUserIndexes = [], // 2D array of lines of indexes into usersOnline API_URL = "https://metaverse.highfidelity.io/api/v1/users?status=online", HTTP_GET_TIMEOUT = 60000, // ms = 1 minute @@ -34,29 +36,75 @@ var usersWindow = (function () { usersTimer, UPDATE_TIMEOUT = 5000; // ms = 5s + function onMousePressEvent(event) { + var clickedOverlay, + numLinesBefore, + overlayX, + overlayY, + minY, + maxY, + lineClicked, + i, + userIndex, + foundUser; + + clickedOverlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y }); + if (clickedOverlay === usersPane2D) { + overlayX = event.x - WINDOW_BOUNDS_2D.x - WINDOW_MARGIN_2D; + overlayY = event.y - WINDOW_BOUNDS_2D.y - WINDOW_MARGIN_2D; + + numLinesBefore = Math.floor(overlayY / (usersLineHeight + usersLineSpacing)); + minY = numLinesBefore * (usersLineHeight + usersLineSpacing); + maxY = minY + usersLineHeight; + + lineClicked = -1; + if (minY <= overlayY && overlayY <= maxY) { + lineClicked = numLinesBefore; + } + + if (0 <= lineClicked && lineClicked < linesOfUserIndexes.length) { + foundUser = false; + i = 0; + while (!foundUser && i < linesOfUserIndexes[lineClicked].length) { + userIndex = linesOfUserIndexes[lineClicked][i]; + foundUser = usersOnline[userIndex].leftX <= overlayX && overlayX <= usersOnline[userIndex].rightX; + i += 1; + } + + if (foundUser) { + location.goToUser(usersOnline[userIndex].username); + } + } + } + } + function updateWindow() { var displayText = "", numUsersDisplayed = 0, lineText = "", lineWidth = 0, - myUsername = GlobalServices.myUsername, + myUsername = GlobalServices.username, usernameWidth, user, i; - usersInLines = []; - + // Create 2D array of lines x IDs into usersOnline. + // Augment usersOnline entries with x-coordinates of left and right of displayed username. + linesOfUserIndexes = []; for (i = 0; i < usersOnline.length; i += 1) { user = usersOnline[i]; if (user.username !== myUsername && user.online) { + usernameWidth = Overlays.textSize(usersPane2D, user.username).width; - if (usersInLines.length === 0 || lineWidth + usernameSpacerWidth2D + usernameWidth > USERS_PANE_TEXT_WIDTH_2D) { + if (linesOfUserIndexes.length === 0 + || lineWidth + usernameSpacerWidth2D + usernameWidth > USERS_PANE_TEXT_WIDTH_2D) { + displayText += "\n" + lineText; // New line - usersInLines.push([i]); // New array in new line or userLines array - usersOnline[i].leftX = 0; // Augment usersOnline data + linesOfUserIndexes.push([i]); + usersOnline[i].leftX = 0; usersOnline[i].rightX = usernameWidth; lineText = user.username; @@ -64,8 +112,7 @@ var usersWindow = (function () { } else { // Append - usersInLines[usersInLines.length - 1].push(i); - + linesOfUserIndexes[linesOfUserIndexes.length - 1].push(i); usersOnline[i].leftX = lineWidth + usernameSpacerWidth2D; usersOnline[i].rightX = lineWidth + usernameSpacerWidth2D + usernameWidth; @@ -138,8 +185,13 @@ var usersWindow = (function () { visible: true }); + usersLineHeight = Math.floor(Overlays.textSize(usersPane2D, "1").height); + usersLineSpacing = Math.floor(Overlays.textSize(usersPane2D, "1\n2").height - 2 * usersLineHeight); + usernameSpacerWidth2D = Overlays.textSize(usersPane2D, USERNAME_SPACER).width; + Controller.mousePressEvent.connect(onMousePressEvent); + requestUsers(); } From 59762175e4f0e8b53ae8e28506b05d3305aa3a6f Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 10 Mar 2015 12:44:34 -0700 Subject: [PATCH 22/43] Grow/shrink window as size of users list changes --- examples/users.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/examples/users.js b/examples/users.js index edc9b0f28e..03ded27c6a 100644 --- a/examples/users.js +++ b/examples/users.js @@ -11,7 +11,7 @@ var usersWindow = (function () { - var WINDOW_BOUNDS_2D = { x: 100, y: 100, width: 300, height: 200 }, + var WINDOW_BOUNDS_2D = { x: 100, y: 100, width: 300, height: 0 }, WINDOW_MARGIN_2D = 12, WINDOW_FOREGROUND_COLOR_2D = { red: 240, green: 240, blue: 240 }, WINDOW_FOREGROUND_ALPHA_2D = 0.9, @@ -127,7 +127,11 @@ var usersWindow = (function () { displayText += "\n" + lineText; displayText = displayText.slice(2); // Remove leading "\n"s. - Overlays.editOverlay(usersPane2D, { text: numUsersDisplayed > 0 ? displayText : "No users online" }); + Overlays.editOverlay(usersPane2D, { + text: numUsersDisplayed > 0 ? displayText : "No users online", + height: (numUsersDisplayed > 0 ? linesOfUserIndexes.length : 1) * (usersLineHeight + usersLineSpacing) + - usersLineSpacing + 2 * WINDOW_MARGIN_2D + }); } function requestUsers() { From 4e8fcd47c0857aa2c59552c3d7e2527260d3edc0 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 10 Mar 2015 13:51:25 -0700 Subject: [PATCH 23/43] Show/hide users window with menu item --- examples/users.js | 46 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 44 insertions(+), 2 deletions(-) diff --git a/examples/users.js b/examples/users.js index 03ded27c6a..7004805996 100644 --- a/examples/users.js +++ b/examples/users.js @@ -33,9 +33,15 @@ var usersWindow = (function () { usersRequest, processUsers, usersTimedOut, - usersTimer, + usersTimer = null, UPDATE_TIMEOUT = 5000; // ms = 5s + MENU_NAME = "Tools", + MENU_ITEM = "Users Online", + MENI_ITEM_AFTER = "Chat...", + + isVisible = false; + function onMousePressEvent(event) { var clickedOverlay, numLinesBefore, @@ -48,6 +54,10 @@ var usersWindow = (function () { userIndex, foundUser; + if (!isVisible) { + return; + } + clickedOverlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y }); if (clickedOverlay === usersPane2D) { overlayX = event.x - WINDOW_BOUNDS_2D.x - WINDOW_MARGIN_2D; @@ -175,6 +185,27 @@ var usersWindow = (function () { usersTimer = Script.setTimeout(requestUsers, HTTP_GET_TIMEOUT); // Try again after a longer delay. }; + function setVisible(visible) { + isVisible = visible; + + if (isVisible) { + if (usersTimer === null) { + requestUsers(); + } + } else { + Script.clearTimeout(usersTimer); + usersTimer = null; + } + + Overlays.editOverlay(usersPane2D, { visible: isVisible }); + } + + function onMenuItemEvent(event) { + if (event === MENU_ITEM) { + setVisible(Menu.isOptionChecked(MENU_ITEM)); + } + } + function setUp() { usersPane2D = Overlays.addOverlay("text", { bounds: WINDOW_BOUNDS_2D, @@ -186,7 +217,7 @@ var usersWindow = (function () { backgroundAlpha: WINDOW_BACKGROUND_ALPHA_2D, text: "", font: USERS_FONT_2D, - visible: true + visible: isVisible }); usersLineHeight = Math.floor(Overlays.textSize(usersPane2D, "1").height); @@ -196,10 +227,21 @@ var usersWindow = (function () { Controller.mousePressEvent.connect(onMousePressEvent); + Menu.addMenuItem({ + menuName: MENU_NAME, + menuItemName: MENU_ITEM, + afterItem: MENI_ITEM_AFTER, + isCheckable: true, + isChecked: isVisible + }); + Menu.menuItemEvent.connect(onMenuItemEvent); + requestUsers(); } function tearDown() { + Menu.removeMenuItem(MENU_NAME, MENU_ITEM); + Script.clearTimeout(usersTimer); Overlays.deleteOverlay(usersPane2D); } From 3bd3f5a6d22ca68d508c90a541015ab6c122c581 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 10 Mar 2015 14:23:23 -0700 Subject: [PATCH 24/43] make sure UserActivityLogger::close() waits for the pending log message to complete --- libraries/networking/src/AccountManager.cpp | 12 ++++++++++++ libraries/networking/src/AccountManager.h | 4 ++++ libraries/networking/src/UserActivityLogger.cpp | 4 +++- 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/libraries/networking/src/AccountManager.cpp b/libraries/networking/src/AccountManager.cpp index 2a809f2a7c..e71c80efc8 100644 --- a/libraries/networking/src/AccountManager.cpp +++ b/libraries/networking/src/AccountManager.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -299,6 +300,8 @@ void AccountManager::processReply() { passErrorToCallback(requestReply); } delete requestReply; + + emit replyFinished(); } void AccountManager::passSuccessToCallback(QNetworkReply* requestReply) { @@ -339,6 +342,15 @@ void AccountManager::passErrorToCallback(QNetworkReply* requestReply) { } } +void AccountManager::waitForAllPendingReplies() { + while (_pendingCallbackMap.size() > 0) { + QEventLoop loop; + QObject::connect(this, &AccountManager::replyFinished, &loop, &QEventLoop::quit); + loop.exec(); + } +} + + void AccountManager::persistAccountToSettings() { if (_shouldPersistToSettingsFile) { // store this access token into the local settings diff --git a/libraries/networking/src/AccountManager.h b/libraries/networking/src/AccountManager.h index 2c9a441db1..22d070fbe6 100644 --- a/libraries/networking/src/AccountManager.h +++ b/libraries/networking/src/AccountManager.h @@ -72,6 +72,8 @@ public: void requestProfile(); DataServerAccountInfo& getAccountInfo() { return _accountInfo; } + + void waitForAllPendingReplies(); public slots: void requestAccessToken(const QString& login, const QString& password); @@ -93,6 +95,8 @@ signals: void loginFailed(); void logoutComplete(); void balanceChanged(qint64 newBalance); + void replyFinished(); + private slots: void processReply(); void handleKeypairGenerationError(); diff --git a/libraries/networking/src/UserActivityLogger.cpp b/libraries/networking/src/UserActivityLogger.cpp index f2646369c1..f74ea99c1e 100644 --- a/libraries/networking/src/UserActivityLogger.cpp +++ b/libraries/networking/src/UserActivityLogger.cpp @@ -61,7 +61,7 @@ void UserActivityLogger::logAction(QString action, QJsonObject details, JSONCall params.errorCallbackReceiver = this; params.errorCallbackMethod = "requestError"; } - + accountManager.authenticatedRequest(USER_ACTIVITY_URL, QNetworkAccessManager::PostOperation, params, @@ -89,6 +89,8 @@ void UserActivityLogger::launch(QString applicationVersion) { void UserActivityLogger::close() { const QString ACTION_NAME = "close"; logAction(ACTION_NAME, QJsonObject()); + + AccountManager::getInstance().waitForAllPendingReplies(); } void UserActivityLogger::changedDisplayName(QString displayName) { From 3d531e8304b99aa9923088268b8e67e10b427b1e Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 10 Mar 2015 14:24:49 -0700 Subject: [PATCH 25/43] add f at end of float literal --- libraries/physics/src/PhysicsEngine.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index 66f8afb226..634c3707a8 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -616,7 +616,7 @@ void PhysicsEngine::setAvatarData(AvatarData *avatarData) { glmToBullet(_avatarData->getPosition()))); // XXX these values should be computed from the character model. - btScalar characterRadius = 0.3; + btScalar characterRadius = 0.3f; btScalar characterHeight = 1.75 - 2.0f * characterRadius; btScalar stepHeight = btScalar(0.35); From 503afc8c7324c971e45db608fa1ef3f5b1e25be0 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 10 Mar 2015 14:26:19 -0700 Subject: [PATCH 26/43] erase deleted entities from pending sort list --- libraries/entities/src/EntitySimulation.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/entities/src/EntitySimulation.cpp b/libraries/entities/src/EntitySimulation.cpp index 758cd6771a..b093dbe4f4 100644 --- a/libraries/entities/src/EntitySimulation.cpp +++ b/libraries/entities/src/EntitySimulation.cpp @@ -99,10 +99,11 @@ void EntitySimulation::sortEntitiesThatMoved() { _mortalEntities.remove(entity); _updateableEntities.remove(entity); removeEntityInternal(entity); + itemItr = _entitiesToBeSorted.erase(itemItr); } else { moveOperator.addEntityToMoveList(entity, newCube); + ++itemItr; } - ++itemItr; } if (moveOperator.hasMovingEntities()) { PerformanceTimer perfTimer("recurseTreeWithOperator"); From a0e7125e119ce7afa6b01d6ef808bf063fe54494 Mon Sep 17 00:00:00 2001 From: Virendra Singh Date: Wed, 11 Mar 2015 03:11:17 +0530 Subject: [PATCH 27/43] code revamp and correction in parallel axis theorem --- libraries/physics/src/MeshInfo.cpp | 85 ++++++++++++--------------- libraries/physics/src/MeshInfo.h | 1 - tests/physics/src/MeshInfoTests.cpp | 89 ++++++++++++++++++++--------- tests/physics/src/MeshInfoTests.h | 1 + 4 files changed, 99 insertions(+), 77 deletions(-) diff --git a/libraries/physics/src/MeshInfo.cpp b/libraries/physics/src/MeshInfo.cpp index 0d9149cb44..8df5ff914d 100644 --- a/libraries/physics/src/MeshInfo.cpp +++ b/libraries/physics/src/MeshInfo.cpp @@ -10,7 +10,7 @@ // #include "MeshInfo.h" -#include df +#include using namespace meshinfo; //class to compute volume, mass, center of mass, and inertia tensor of a mesh. @@ -29,14 +29,6 @@ MeshInfo::~MeshInfo(){ } -inline Vertex MeshInfo::getCentroid(const Vertex p1, const Vertex p2, const Vertex p3, const Vertex p4) const{ - Vertex com; - com.x = (p1.x + p2.x + p3.x + p4.x) / 4.0f; - com.y = (p1.y + p2.y + p3.y + p4.y) / 4.0f; - com.z = (p1.z + p2.z + p3.z + p4.z) / 4.0f; - return com; -} - inline float MeshInfo::getVolume(const Vertex p1, const Vertex p2, const Vertex p3, const Vertex p4) const{ glm::mat4 tet = { glm::vec4(p1.x, p2.x, p3.x, p4.x), glm::vec4(p1.y, p2.y, p3.y, p4.y), glm::vec4(p1.z, p2.z, p3.z, p4.z), glm::vec4(1.0f, 1.0f, 1.0f, 1.0f) }; @@ -50,37 +42,19 @@ Vertex MeshInfo::getMeshCentroid() const{ vector MeshInfo::computeMassProperties(){ vector volumeAndInertia = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 }; Vertex origin(0.0, 0.0, 0.0); + glm::mat3 identity; float meshVolume = 0.0f; - glm::mat3 globalMomentOfInertia(0.0); - glm::mat3 globalProductOfInertia(0.0); + glm::mat3 globalInertiaTensors(0.0); - //First we need need the center of mass of the mesh in order to translate the tetrahedron inertia to center of mass of the mesh. for (unsigned int i = 0; i < _triangles->size(); i += 3){ Vertex p1 = _vertices->at(_triangles->at(i)); Vertex p2 = _vertices->at(_triangles->at(i + 1)); Vertex p3 = _vertices->at(_triangles->at(i + 2)); - float volume = getVolume(p1, p2, p3, origin); - Vertex com = getCentroid(origin, p1, p2, p3); - //Translate accumulated center of mass from each tetrahedron to mesh's center of mass using parallel axis theorem + float volume = getVolume(p1, p2, p3, origin); + Vertex com = 0.25f * (p1 + p2 + p3); meshVolume += volume; _centerOfMass += com * volume; - } - if (meshVolume == 0){ - return volumeAndInertia; - } - _centerOfMass = (_centerOfMass / (float)meshVolume); - //Translate the moment of inertia from each tetrahedron to mesh's center of mass using parallel axis theorem - for (unsigned int i = 0; i < _triangles->size(); i += 3){ - Vertex p1 = _vertices->at(_triangles->at(i)); - Vertex p2 = _vertices->at(_triangles->at(i + 1)); - Vertex p3 = _vertices->at(_triangles->at(i + 2)); - float volume = getVolume(p1, p2, p3, origin); - Vertex com = getCentroid(origin, p1, p2, p3); - glm::mat3 identity; - Vertex diff = _centerOfMass - com; - float diffDot = glm::dot(diff, diff); - glm::mat3 outerDiff = glm::outerProduct(diff, diff); //centroid is used for calculating inertia tensor relative to center of mass. // translate the tetrahedron to its center of mass using P = P - centroid Vertex p0 = origin - com; @@ -92,7 +66,7 @@ vector MeshInfo::computeMassProperties(){ //http://docsdrive.com/pdfs/sciencepublications/jmssp/2005/8-11.pdf //Explicit exact formulas for the 3-D tetrahedron inertia tensor in terms of its vertex coordinates - F.Tonon - float inertia_a = (volume * 6.0f / 60.0f) * ( + float i11 = (volume * 0.1f) * ( p0.y*p0.y + p0.y*p1.y + p0.y*p2.y + p0.y*p3.y + p1.y*p1.y + p1.y*p2.y + p1.y*p3.y + p2.y*p2.y + p2.y*p3.y + @@ -102,7 +76,7 @@ vector MeshInfo::computeMassProperties(){ p2.z*p2.z + p2.z*p3.z + p3.z*p3.z); - float inertia_b = (volume * 6.0f / 60.0f) * ( + float i22 = (volume * 0.1f) * ( p0.x*p0.x + p0.x*p1.x + p0.x*p2.x + p0.x*p3.x + p1.x*p1.x + p1.x*p2.x + p1.x*p3.x + p2.x*p2.x + p2.x*p3.x + @@ -112,7 +86,7 @@ vector MeshInfo::computeMassProperties(){ p2.z*p2.z + p2.z*p3.z + p3.z*p3.z); - float inertia_c = (volume * 6.0f / 60.0f) * ( + float i33 = (volume * 0.1f) * ( p0.x*p0.x + p0.x*p1.x + p0.x*p2.x + p0.x*p3.x + p1.x*p1.x + p1.x*p2.x + p1.x*p3.x + p2.x*p2.x + p2.x*p3.x + @@ -122,40 +96,53 @@ vector MeshInfo::computeMassProperties(){ p2.y*p2.y + p2.y*p3.y + p3.y*p3.y); - float inertia_aa = (volume * 6.0f / 120.0f) * (2.0f * (p0.y*p0.z + p1.y*p1.z + p2.y*p2.z + p3.y*p3.z) + + float i23 = -(volume * 0.05f) * (2.0f * (p0.y*p0.z + p1.y*p1.z + p2.y*p2.z + p3.y*p3.z) + p0.y*p1.z + p0.y*p2.z + p0.y*p3.z + p1.y*p0.z + p1.y*p2.z + p1.y*p3.z + p2.y*p0.z + p2.y*p1.z + p2.y*p3.z + p3.y*p0.z + p3.y*p1.z + p3.y*p2.z); - float inertia_bb = (volume * 6.0f / 120.0f) * (2.0f * (p0.x*p0.z + p1.x*p1.z + p2.x*p2.z + p3.x*p3.z) + + float i21 = -(volume * 0.05f) * (2.0f * (p0.x*p0.z + p1.x*p1.z + p2.x*p2.z + p3.x*p3.z) + p0.x*p1.z + p0.x*p2.z + p0.x*p3.z + p1.x*p0.z + p1.x*p2.z + p1.x*p3.z + p2.x*p0.z + p2.x*p1.z + p2.x*p3.z + p3.x*p0.z + p3.x*p1.z + p3.x*p2.z); - float inertia_cc = (volume * 6.0f / 120.0f) * (2.0f * (p0.x*p0.y + p1.x*p1.y + p2.x*p2.y + p3.x*p3.y) + + float i31 = -(volume * 0.05f) * (2.0f * (p0.x*p0.y + p1.x*p1.y + p2.x*p2.y + p3.x*p3.y) + p0.x*p1.y + p0.x*p2.y + p0.x*p3.y + p1.x*p0.y + p1.x*p2.y + p1.x*p3.y + p2.x*p0.y + p2.x*p1.y + p2.x*p3.y + p3.x*p0.y + p3.x*p1.y + p3.x*p2.y); //3x3 of local inertia tensors of each tetrahedron. Inertia tensors are the diagonal elements - glm::mat3 localMomentInertia = { Vertex(inertia_a, 0.0f, 0.0f), Vertex(0.0f, inertia_b, 0.0f), - Vertex(0.0f, 0.0f, inertia_c) }; - glm::mat3 localProductInertia = { Vertex(inertia_aa, 0.0f, 0.0f), Vertex(0.0f, inertia_bb, 0.0f), - Vertex(0.0f, 0.0f, inertia_cc) }; + // | I11 -I12 -I13 | + // I = | -I21 I22 -I23 | + // | -I31 -I32 I33 | + glm::mat3 localInertiaTensors = { Vertex(i11, i21, i31), Vertex(i21, i22, i23), + Vertex(i31, i23, i33) }; + //Translate the inertia tensors from center of mass to origin //Parallel axis theorem J = I * m[(R.R)*Identity - RxR] where x is outer cross product - globalMomentOfInertia += localMomentInertia + volume * ((diffDot*identity) - outerDiff); - globalProductOfInertia += localProductInertia + volume * ((diffDot * identity) - outerDiff); + globalInertiaTensors += localInertiaTensors + volume * ((glm::dot(com, com) * identity) - + glm::outerProduct(com, com)); } + + //Translate accumulated center of mass from each tetrahedron to mesh's center of mass using parallel axis theorem + if (meshVolume == 0){ + return volumeAndInertia; + } + _centerOfMass = (_centerOfMass / meshVolume); + + //Translate the inertia tensors from origin to mesh's center of mass. + globalInertiaTensors = globalInertiaTensors - meshVolume * ((glm::dot(_centerOfMass, _centerOfMass) * + identity) - glm::outerProduct(_centerOfMass, _centerOfMass)); + volumeAndInertia[0] = meshVolume; - volumeAndInertia[1] = globalMomentOfInertia[0][0]; - volumeAndInertia[2] = globalMomentOfInertia[1][1]; - volumeAndInertia[3] = globalMomentOfInertia[2][2]; - volumeAndInertia[4] = globalProductOfInertia[0][0]; - volumeAndInertia[5] = globalProductOfInertia[1][1]; - volumeAndInertia[6] = globalProductOfInertia[2][2]; + volumeAndInertia[1] = globalInertiaTensors[0][0]; //i11 + volumeAndInertia[2] = globalInertiaTensors[1][1]; //i22 + volumeAndInertia[3] = globalInertiaTensors[2][2]; //i33 + volumeAndInertia[4] = -globalInertiaTensors[2][1]; //i23 or i32 + volumeAndInertia[5] = -globalInertiaTensors[1][0]; //i21 or i12 + volumeAndInertia[6] = -globalInertiaTensors[2][0]; //i13 or i31 return volumeAndInertia; } \ No newline at end of file diff --git a/libraries/physics/src/MeshInfo.h b/libraries/physics/src/MeshInfo.h index d267c75ae6..67dbc8b0d6 100644 --- a/libraries/physics/src/MeshInfo.h +++ b/libraries/physics/src/MeshInfo.h @@ -26,7 +26,6 @@ namespace meshinfo{ vector *_triangles; MeshInfo(vector *vertices, vector *triangles); ~MeshInfo(); - inline Vertex getCentroid(const Vertex p1, const Vertex p2, const Vertex p3, const Vertex p4) const; Vertex getMeshCentroid() const; vector computeMassProperties(); }; diff --git a/tests/physics/src/MeshInfoTests.cpp b/tests/physics/src/MeshInfoTests.cpp index 73364b207e..58c38017a6 100644 --- a/tests/physics/src/MeshInfoTests.cpp +++ b/tests/physics/src/MeshInfoTests.cpp @@ -14,7 +14,7 @@ #include #include "MeshInfoTests.h" -const float epsilon = 0.01f; +const float epsilon = 0.015f; void MeshInfoTests::testWithTetrahedron(){ glm::vec3 p0(8.33220, -11.86875, 0.93355); glm::vec3 p1(0.75523, 5.00000, 16.37072); @@ -40,9 +40,12 @@ void MeshInfoTests::testWithTetrahedron(){ float inertia_cc = 11996.20119f; meshinfo::MeshInfo meshinfo(&vertices,&triangles); - glm::vec3 tetCenterOfMass = meshinfo.getCentroid(p0, p1, p2, p3); - glm::vec3 diff = centroid - tetCenterOfMass; vector voumeAndInertia = meshinfo.computeMassProperties(); + glm::vec3 tetCenterOfMass = meshinfo.getMeshCentroid(); + + //get original center of mass + tetCenterOfMass = tetCenterOfMass + p0; + glm::vec3 diff = centroid - tetCenterOfMass; std::cout << std::setprecision(12); //test if centroid is correct if (diff.x > epsilon || diff.y > epsilon || diff.z > epsilon){ @@ -95,6 +98,37 @@ void MeshInfoTests::testWithTetrahedron(){ } +void MeshInfoTests::testWithTetrahedronAsMesh(){ + glm::vec3 p0(8.33220, -11.86875, 0.93355); + glm::vec3 p1(0.75523, 5.00000, 16.37072); + glm::vec3 p2(52.61236, 5.00000, -5.38580); + glm::vec3 p3(2.00000, 5.00000, 3.00000); + glm::vec3 centroid(15.92492, 0.782813, 3.72962); + float volume = 1873.233236f; + float inertia_a = 43520.33257f; + //actual should be 194711.28938f. But for some reason it becomes 194711.296875 during + //runtime due to how floating points are stored. + float inertia_b = 194711.289f; + float inertia_c = 191168.76173f; + float inertia_aa = 4417.66150f; + float inertia_bb = -46343.16662f; + float inertia_cc = 11996.20119f; + std::cout << std::setprecision(12); + vector vertices = { p0, p1, p2, p3 }; + vector triangles = { 0, 1, 2, 0, 3, 1, 1, 3, 2, 0, 2, 3 }; //clockwise direction + meshinfo::MeshInfo massProp(&vertices, &triangles); + vector volumeAndInertia = massProp.computeMassProperties(); + std::cout << volumeAndInertia[0] << " " << volumeAndInertia[1] << " " << volumeAndInertia[2] + << " " << volumeAndInertia[3] + << " " << volumeAndInertia[4] + << " " << volumeAndInertia[5] << " " << volumeAndInertia[6] << std::endl; + + //translate the tetrahedron so that the model is placed at origin i.e. com is at origin + p0 -= centroid; + p1 -= centroid; + p2 -= centroid; + p3 -= centroid; +} void MeshInfoTests::testWithCube(){ glm::vec3 p0(1.0, -1.0, -1.0); glm::vec3 p1(1.0, -1.0, 1.0); @@ -122,25 +156,25 @@ void MeshInfoTests::testWithCube(){ double inertia = (volume * side * side) / 6.0; //inertia of a unit cube is (mass * side * side) /6 //test with origin as reference point - meshinfo::MeshInfo massProp1(&vertices, &triangles); - vector volumeAndInertia1 = massProp1.computeMassProperties(); - if (abs(centerOfMass.x - massProp1.getMeshCentroid().x) > epsilon || abs(centerOfMass.y - massProp1.getMeshCentroid().y) > epsilon || - abs(centerOfMass.z - massProp1.getMeshCentroid().z) > epsilon){ + meshinfo::MeshInfo massProp(&vertices, &triangles); + vector volumeAndInertia = massProp.computeMassProperties(); + if (abs(centerOfMass.x - massProp.getMeshCentroid().x) > epsilon || abs(centerOfMass.y - massProp.getMeshCentroid().y) > epsilon || + abs(centerOfMass.z - massProp.getMeshCentroid().z) > epsilon){ std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Center of mass is incorrect : Expected = " << centerOfMass.x << " " << - centerOfMass.y << " " << centerOfMass.z << ", actual = " << massProp1.getMeshCentroid().x << " " << - massProp1.getMeshCentroid().y << " " << massProp1.getMeshCentroid().z << std::endl; + centerOfMass.y << " " << centerOfMass.z << ", actual = " << massProp.getMeshCentroid().x << " " << + massProp.getMeshCentroid().y << " " << massProp.getMeshCentroid().z << std::endl; } - if (abs(volume - volumeAndInertia1.at(0)) > epsilon){ + if (abs(volume - volumeAndInertia.at(0)) > epsilon){ std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Volume is incorrect : Expected = " << volume << - ", actual = " << volumeAndInertia1.at(0) << std::endl; + ", actual = " << volumeAndInertia.at(0) << std::endl; } - if (abs(inertia - (volumeAndInertia1.at(1))) > epsilon || abs(inertia - (volumeAndInertia1.at(2))) > epsilon || - abs(inertia - (volumeAndInertia1.at(3))) > epsilon){ + if (abs(inertia - (volumeAndInertia.at(1))) > epsilon || abs(inertia - (volumeAndInertia.at(2))) > epsilon || + abs(inertia - (volumeAndInertia.at(3))) > epsilon){ std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Moment of inertia is incorrect : Expected = " << inertia << " " << - inertia << " " << inertia << ", actual = " << (volumeAndInertia1.at(1)) << " " << (volumeAndInertia1.at(2)) << - " " << (volumeAndInertia1.at(3)) << std::endl; + inertia << " " << inertia << ", actual = " << (volumeAndInertia.at(1)) << " " << (volumeAndInertia.at(2)) << + " " << (volumeAndInertia.at(3)) << std::endl; } } @@ -172,29 +206,30 @@ void MeshInfoTests::testWithUnitCube() std::cout << std::setprecision(10); //test with origin as reference point - meshinfo::MeshInfo massProp1(&vertices, &triangles); - vector volumeAndInertia1 = massProp1.computeMassProperties(); - if (abs(centerOfMass.x - massProp1.getMeshCentroid().x) > epsilon || abs(centerOfMass.y - massProp1.getMeshCentroid().y) > - epsilon || abs(centerOfMass.z - massProp1.getMeshCentroid().z) > epsilon){ + meshinfo::MeshInfo massProp(&vertices, &triangles); + vector volumeAndInertia = massProp.computeMassProperties(); + if (abs(centerOfMass.x - massProp.getMeshCentroid().x) > epsilon || abs(centerOfMass.y - massProp.getMeshCentroid().y) > + epsilon || abs(centerOfMass.z - massProp.getMeshCentroid().z) > epsilon){ std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Center of mass is incorrect : Expected = " << centerOfMass.x << - " " << centerOfMass.y << " " << centerOfMass.z << ", actual = " << massProp1.getMeshCentroid().x << " " << - massProp1.getMeshCentroid().y << " " << massProp1.getMeshCentroid().z << std::endl; + " " << centerOfMass.y << " " << centerOfMass.z << ", actual = " << massProp.getMeshCentroid().x << " " << + massProp.getMeshCentroid().y << " " << massProp.getMeshCentroid().z << std::endl; } - if (abs(volume - volumeAndInertia1.at(0)) > epsilon){ + if (abs(volume - volumeAndInertia.at(0)) > epsilon){ std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Volume is incorrect : Expected = " << volume << - ", actual = " << volumeAndInertia1.at(0) << std::endl; + ", actual = " << volumeAndInertia.at(0) << std::endl; } - if (abs(inertia - (volumeAndInertia1.at(1))) > epsilon || abs(inertia - (volumeAndInertia1.at(2))) > epsilon || - abs(inertia - (volumeAndInertia1.at(3))) > epsilon){ + if (abs(inertia - (volumeAndInertia.at(1))) > epsilon || abs(inertia - (volumeAndInertia.at(2))) > epsilon || + abs(inertia - (volumeAndInertia.at(3))) > epsilon){ std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Moment of inertia is incorrect : Expected = " << inertia << " " << - inertia << " " << inertia << ", actual = " << (volumeAndInertia1.at(1)) << " " << (volumeAndInertia1.at(2)) << - " " << (volumeAndInertia1.at(3)) << std::endl; + inertia << " " << inertia << ", actual = " << (volumeAndInertia.at(1)) << " " << (volumeAndInertia.at(2)) << + " " << (volumeAndInertia.at(3)) << std::endl; } } void MeshInfoTests::runAllTests(){ testWithTetrahedron(); + testWithTetrahedronAsMesh(); testWithUnitCube(); testWithCube(); } \ No newline at end of file diff --git a/tests/physics/src/MeshInfoTests.h b/tests/physics/src/MeshInfoTests.h index d4c954b802..0ddd8d0944 100644 --- a/tests/physics/src/MeshInfoTests.h +++ b/tests/physics/src/MeshInfoTests.h @@ -16,5 +16,6 @@ namespace MeshInfoTests{ void testWithUnitCube(); void testWithCube(); void runAllTests(); + void testWithTetrahedronAsMesh(); } #endif // hifi_MeshInfoTests_h \ No newline at end of file From 7954dd4d67b4a73825bbd61ffacc0c32cccc0c5d Mon Sep 17 00:00:00 2001 From: Virendra Singh Date: Wed, 11 Mar 2015 03:27:26 +0530 Subject: [PATCH 28/43] anticlockwise triangle order --- tests/physics/src/MeshInfoTests.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/physics/src/MeshInfoTests.cpp b/tests/physics/src/MeshInfoTests.cpp index 58c38017a6..fa841f3930 100644 --- a/tests/physics/src/MeshInfoTests.cpp +++ b/tests/physics/src/MeshInfoTests.cpp @@ -115,7 +115,7 @@ void MeshInfoTests::testWithTetrahedronAsMesh(){ float inertia_cc = 11996.20119f; std::cout << std::setprecision(12); vector vertices = { p0, p1, p2, p3 }; - vector triangles = { 0, 1, 2, 0, 3, 1, 1, 3, 2, 0, 2, 3 }; //clockwise direction + vector triangles = { 0, 2, 1, 0, 3, 2, 0, 1, 3, 1, 2, 3 }; meshinfo::MeshInfo massProp(&vertices, &triangles); vector volumeAndInertia = massProp.computeMassProperties(); std::cout << volumeAndInertia[0] << " " << volumeAndInertia[1] << " " << volumeAndInertia[2] From 8ede9fb39be383fc1e0ad7f129f0f3c9feabcad4 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 10 Mar 2015 15:50:30 -0700 Subject: [PATCH 29/43] fix stereo to stereo resampling --- .../utilities/diagnostics/orbitingSound.js | 12 +++++----- libraries/audio-client/src/AudioClient.cpp | 24 +++++++++++++------ libraries/audio/src/AudioInjector.h | 6 ++++- 3 files changed, 28 insertions(+), 14 deletions(-) diff --git a/examples/utilities/diagnostics/orbitingSound.js b/examples/utilities/diagnostics/orbitingSound.js index 1af6fab827..370ed38595 100644 --- a/examples/utilities/diagnostics/orbitingSound.js +++ b/examples/utilities/diagnostics/orbitingSound.js @@ -20,11 +20,11 @@ var trailingLoudness = 0.0; var soundClip = SoundCache.getSound("https://s3.amazonaws.com/hifi-public/sounds/Tabla+Loops/Tabla1.wav"); var properties = { - type: "Box", - position: orbitCenter, - dimensions: { x: 0.25, y: 0.25, z: 0.25 }, - color: { red: 100, green: 0, blue : 0 } - }; + type: "Box", + position: orbitCenter, + dimensions: { x: 0.25, y: 0.25, z: 0.25 }, + color: { red: 100, green: 0, blue : 0 } +}; var objectId = Entities.addEntity(properties); var sound = Audio.playSound(soundClip, { position: orbitCenter, loop: true, volume: 0.5 }); @@ -34,7 +34,7 @@ function update(deltaTime) { currentPosition = { x: orbitCenter.x + Math.cos(time * SPEED) * RADIUS, y: orbitCenter.y, z: orbitCenter.z + Math.sin(time * SPEED) * RADIUS }; trailingLoudness = 0.9 * trailingLoudness + 0.1 * sound.loudness; Entities.editEntity( objectId, { position: currentPosition, color: { red: Math.min(trailingLoudness * 2000, 255), green: 0, blue: 0 } } ); - sound.setOptions({ position: currentPosition }); + sound.options = { position: currentPosition }; } Script.scriptEnding.connect(function() { diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 6e068e0635..c083fbc085 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -320,6 +320,7 @@ bool adjustedFormatForAudioDevice(const QAudioDeviceInfo& audioDevice, #ifdef Q_OS_ANDROID adjustedAudioFormat.setSampleRate(FORTY_FOUR); #else + const int HALF_FORTY_FOUR = FORTY_FOUR / 2; if (audioDevice.supportedSampleRates().contains(AudioConstants::SAMPLE_RATE * 2)) { @@ -397,18 +398,24 @@ soxr_error_t possibleResampling(soxr_t resampler, numSourceSamples, sourceAudioFormat, destinationAudioFormat); - qDebug() << "resample from" << sourceAudioFormat << "to" << destinationAudioFormat - << "from" << numChannelCoversionSamples << "to" << numDestinationSamples; - resampleError = soxr_process(resampler, channelConversionSamples, numChannelCoversionSamples, NULL, destinationSamples, numDestinationSamples, NULL); delete[] channelConversionSamples; } else { + + unsigned int numAdjustedSourceSamples = numSourceSamples; + unsigned int numAdjustedDestinationSamples = numDestinationSamples; + + if (sourceAudioFormat.channelCount() == 2 && destinationAudioFormat.channelCount() == 2) { + numAdjustedSourceSamples /= 2; + numAdjustedDestinationSamples /= 2; + } + resampleError = soxr_process(resampler, - sourceSamples, numSourceSamples, NULL, - destinationSamples, numDestinationSamples, NULL); + sourceSamples, numAdjustedSourceSamples, NULL, + destinationSamples, numAdjustedDestinationSamples, NULL); } return resampleError; @@ -429,9 +436,12 @@ soxr_t soxrResamplerFromInputFormatToOutputFormat(const QAudioFormat& sourceAudi // setup soxr_quality_spec_t for quality options soxr_quality_spec_t qualitySpec = soxr_quality_spec(SOXR_MQ, 0); + int channelCount = (sourceAudioFormat.channelCount() == 2 && destinationAudioFormat.channelCount() == 2) + ? 2 : 1; + soxr_t newResampler = soxr_create(sourceAudioFormat.sampleRate(), destinationAudioFormat.sampleRate(), - 1, + channelCount, &soxrError, &inputToNetworkSpec, &qualitySpec, 0); if (soxrError) { @@ -1136,7 +1146,7 @@ bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDevice if (!outputDeviceInfo.isNull()) { qDebug() << "The audio output device " << outputDeviceInfo.deviceName() << "is available."; _outputAudioDeviceName = outputDeviceInfo.deviceName().trimmed(); - + if (adjustedFormatForAudioDevice(outputDeviceInfo, _desiredOutputFormat, _outputFormat)) { qDebug() << "The format to be used for audio output is" << _outputFormat; diff --git a/libraries/audio/src/AudioInjector.h b/libraries/audio/src/AudioInjector.h index 25d0c1699d..ca39dcbdc4 100644 --- a/libraries/audio/src/AudioInjector.h +++ b/libraries/audio/src/AudioInjector.h @@ -30,6 +30,8 @@ class AbstractAudioInterface; class AudioInjector : public QObject { Q_OBJECT + + Q_PROPERTY(AudioInjectorOptions options WRITE setOptions READ getOptions) public: AudioInjector(QObject* parent); AudioInjector(Sound* sound, const AudioInjectorOptions& injectorOptions); @@ -51,7 +53,9 @@ public slots: void triggerDeleteAfterFinish() { _shouldDeleteAfterFinish = true; } void stopAndDeleteLater(); - void setOptions(AudioInjectorOptions& options) { _options = options; } + const AudioInjectorOptions& getOptions() const { return _options; } + void setOptions(const AudioInjectorOptions& options) { _options = options; } + void setCurrentSendPosition(int currentSendPosition) { _currentSendPosition = currentSendPosition; } float getLoudness() const { return _loudness; } bool isPlaying() const { return !_isFinished; } From 3579bb46ab5a91e52bcc0eae87f9adda5fd98c4d Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 10 Mar 2015 17:04:30 -0700 Subject: [PATCH 30/43] handle touch update without a touch begin event --- examples/lookWithTouch.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/examples/lookWithTouch.js b/examples/lookWithTouch.js index 68b29c305b..e9e7b0735a 100644 --- a/examples/lookWithTouch.js +++ b/examples/lookWithTouch.js @@ -38,6 +38,13 @@ function touchUpdateEvent(event) { if (wantDebugging) { print("touchUpdateEvent event.x,y=" + event.x + ", " + event.y); } + + if (!startedTouching) { + // handle Qt 5.4.x bug where we get touch update without a touch begin event + startedTouching = true; + lastX = event.x; + lastY = event.y; + } var MOUSE_YAW_SCALE = -0.25; var MOUSE_PITCH_SCALE = -12.5; From d071bc50a48437bfa6431d6567dc2e63c3e40878 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 10 Mar 2015 17:20:08 -0700 Subject: [PATCH 31/43] Change from paragraph of usernames to single column of usernames --- examples/users.js | 78 ++++++++++++----------------------------------- 1 file changed, 19 insertions(+), 59 deletions(-) diff --git a/examples/users.js b/examples/users.js index 7004805996..20693c220e 100644 --- a/examples/users.js +++ b/examples/users.js @@ -11,22 +11,21 @@ var usersWindow = (function () { - var WINDOW_BOUNDS_2D = { x: 100, y: 100, width: 300, height: 0 }, + var WINDOW_BOUNDS_2D = { x: 100, y: 100, width: 150, height: 0 }, WINDOW_MARGIN_2D = 12, WINDOW_FOREGROUND_COLOR_2D = { red: 240, green: 240, blue: 240 }, WINDOW_FOREGROUND_ALPHA_2D = 0.9, WINDOW_BACKGROUND_COLOR_2D = { red: 120, green: 120, blue: 120 }, WINDOW_BACKGROUND_ALPHA_2D = 0.7, usersPane2D, - USERS_PANE_TEXT_WIDTH_2D = WINDOW_BOUNDS_2D.width - 2 * WINDOW_MARGIN_2D, USERS_FONT_2D = { size: 14 }, usersLineHeight, usersLineSpacing, USERNAME_SPACER = "\u00a0\u00a0\u00a0\u00a0", // Nonbreaking spaces usernameSpacerWidth2D, - usersOnline, - linesOfUserIndexes = [], // 2D array of lines of indexes into usersOnline + usersOnline, // Raw data + linesOfUsers, // Array of indexes pointing into usersOnline API_URL = "https://metaverse.highfidelity.io/api/v1/users?status=online", HTTP_GET_TIMEOUT = 60000, // ms = 1 minute @@ -34,7 +33,7 @@ var usersWindow = (function () { processUsers, usersTimedOut, usersTimer = null, - UPDATE_TIMEOUT = 5000; // ms = 5s + UPDATE_TIMEOUT = 5000, // ms = 5s MENU_NAME = "Tools", MENU_ITEM = "Users Online", @@ -49,10 +48,7 @@ var usersWindow = (function () { overlayY, minY, maxY, - lineClicked, - i, - userIndex, - foundUser; + lineClicked; if (!isVisible) { return; @@ -72,74 +68,38 @@ var usersWindow = (function () { lineClicked = numLinesBefore; } - if (0 <= lineClicked && lineClicked < linesOfUserIndexes.length) { - foundUser = false; - i = 0; - while (!foundUser && i < linesOfUserIndexes[lineClicked].length) { - userIndex = linesOfUserIndexes[lineClicked][i]; - foundUser = usersOnline[userIndex].leftX <= overlayX && overlayX <= usersOnline[userIndex].rightX; - i += 1; - } + if (0 <= lineClicked && lineClicked < linesOfUsers.length + && overlayX <= usersOnline[linesOfUsers[lineClicked]].usernameWidth) { - if (foundUser) { - location.goToUser(usersOnline[userIndex].username); - } + print("Go to " + usersOnline[linesOfUsers[lineClicked]].username); + // DJRTODO + //location.goToUser(usersOnline[userIndex].username); } } } function updateWindow() { var displayText = "", - numUsersDisplayed = 0, - lineText = "", - lineWidth = 0, - myUsername = GlobalServices.username, - usernameWidth, + myUsername, user, i; - // Create 2D array of lines x IDs into usersOnline. - // Augment usersOnline entries with x-coordinates of left and right of displayed username. - linesOfUserIndexes = []; + myUsername = GlobalServices.username; + linesOfUsers = []; for (i = 0; i < usersOnline.length; i += 1) { user = usersOnline[i]; if (user.username !== myUsername && user.online) { - - usernameWidth = Overlays.textSize(usersPane2D, user.username).width; - - if (linesOfUserIndexes.length === 0 - || lineWidth + usernameSpacerWidth2D + usernameWidth > USERS_PANE_TEXT_WIDTH_2D) { - - displayText += "\n" + lineText; - - // New line - linesOfUserIndexes.push([i]); - usersOnline[i].leftX = 0; - usersOnline[i].rightX = usernameWidth; - - lineText = user.username; - lineWidth = usernameWidth; - - } else { - // Append - linesOfUserIndexes[linesOfUserIndexes.length - 1].push(i); - usersOnline[i].leftX = lineWidth + usernameSpacerWidth2D; - usersOnline[i].rightX = lineWidth + usernameSpacerWidth2D + usernameWidth; - - lineText += USERNAME_SPACER + user.username; - lineWidth = usersOnline[i].rightX; - } - - numUsersDisplayed += 1; + usersOnline[i].usernameWidth = Overlays.textSize(usersPane2D, user.username).width; + linesOfUsers.push(i); + displayText += "\n" + user.username; } } - displayText += "\n" + lineText; - displayText = displayText.slice(2); // Remove leading "\n"s. + displayText = displayText.slice(1); // Remove leading "\n". Overlays.editOverlay(usersPane2D, { - text: numUsersDisplayed > 0 ? displayText : "No users online", - height: (numUsersDisplayed > 0 ? linesOfUserIndexes.length : 1) * (usersLineHeight + usersLineSpacing) + text: linesOfUsers.length > 0 ? displayText : "No users online", + height: (linesOfUsers.length > 0 ? linesOfUsers.length : 1) * (usersLineHeight + usersLineSpacing) - usersLineSpacing + 2 * WINDOW_MARGIN_2D }); } From 76f3378223772e10c95927ac60caed77784b4cbd Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 10 Mar 2015 18:05:10 -0700 Subject: [PATCH 32/43] don't pass no-libraries option to windeployqt --- cmake/macros/CopyDllsBesideWindowsExecutable.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/macros/CopyDllsBesideWindowsExecutable.cmake b/cmake/macros/CopyDllsBesideWindowsExecutable.cmake index 24996fde03..e8d499bab8 100644 --- a/cmake/macros/CopyDllsBesideWindowsExecutable.cmake +++ b/cmake/macros/CopyDllsBesideWindowsExecutable.cmake @@ -37,7 +37,7 @@ macro(COPY_DLLS_BESIDE_WINDOWS_EXECUTABLE) add_custom_command( TARGET ${TARGET_NAME} POST_BUILD - COMMAND CMD /C "SET PATH=%PATH%;${QT_DIR}/bin && ${WINDEPLOYQT_COMMAND} --no-libraries $" + COMMAND CMD /C "SET PATH=%PATH%;${QT_DIR}/bin && ${WINDEPLOYQT_COMMAND} $" ) endif () endmacro() \ No newline at end of file From 931a234ba74e0fed0d14d721acb18e3b824216f9 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 10 Mar 2015 18:08:19 -0700 Subject: [PATCH 33/43] fix reset for close button SVG in LoginDialog --- interface/src/ui/LoginDialog.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/interface/src/ui/LoginDialog.cpp b/interface/src/ui/LoginDialog.cpp index 4817727909..004f863901 100644 --- a/interface/src/ui/LoginDialog.cpp +++ b/interface/src/ui/LoginDialog.cpp @@ -58,6 +58,7 @@ void LoginDialog::reset() { _ui->emailLineEdit->setFocus(); _ui->logoLabel->setPixmap(QPixmap(PathUtils::resourcesPath() + "images/hifi-logo.svg")); _ui->loginButton->setIcon(QIcon(PathUtils::resourcesPath() + "images/login.svg")); + _ui->closeButton->setIcon(QIcon(PathUtils::resourcesPath() + "images/close.svg")); _ui->infoLabel->setVisible(false); _ui->errorLabel->setVisible(false); From a3c4429e0beb6a2e8d4fe8ea04ac507cee24a1ce Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 11 Mar 2015 09:36:26 -0700 Subject: [PATCH 34/43] Longer wait until requesting status again after request error --- examples/users.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/users.js b/examples/users.js index 20693c220e..c78f48df02 100644 --- a/examples/users.js +++ b/examples/users.js @@ -134,6 +134,8 @@ var usersWindow = (function () { updateWindow(); } else { print("Error: Request for users status returned " + usersRequest.status + " " + usersRequest.statusText); + usersTimer = Script.setTimeout(requestUsers, HTTP_GET_TIMEOUT); // Try again after a longer delay. + return; } usersTimer = Script.setTimeout(requestUsers, UPDATE_TIMEOUT); // Update after finished processing. From ad08a7dde2734654d8fc706b37caf731ca4fc09d Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 11 Mar 2015 09:37:18 -0700 Subject: [PATCH 35/43] Position window at bottom left of screen --- examples/users.js | 38 ++++++++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/examples/users.js b/examples/users.js index c78f48df02..ee64514562 100644 --- a/examples/users.js +++ b/examples/users.js @@ -11,12 +11,13 @@ var usersWindow = (function () { - var WINDOW_BOUNDS_2D = { x: 100, y: 100, width: 150, height: 0 }, + var WINDOW_WIDTH_2D = 150, WINDOW_MARGIN_2D = 12, WINDOW_FOREGROUND_COLOR_2D = { red: 240, green: 240, blue: 240 }, WINDOW_FOREGROUND_ALPHA_2D = 0.9, WINDOW_BACKGROUND_COLOR_2D = { red: 120, green: 120, blue: 120 }, WINDOW_BACKGROUND_ALPHA_2D = 0.7, + windowHeight = 0, usersPane2D, USERS_FONT_2D = { size: 14 }, usersLineHeight, @@ -39,7 +40,9 @@ var usersWindow = (function () { MENU_ITEM = "Users Online", MENI_ITEM_AFTER = "Chat...", - isVisible = false; + isVisible = false, + + viewportHeight; function onMousePressEvent(event) { var clickedOverlay, @@ -56,8 +59,9 @@ var usersWindow = (function () { clickedOverlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y }); if (clickedOverlay === usersPane2D) { - overlayX = event.x - WINDOW_BOUNDS_2D.x - WINDOW_MARGIN_2D; - overlayY = event.y - WINDOW_BOUNDS_2D.y - WINDOW_MARGIN_2D; + + overlayX = event.x - WINDOW_MARGIN_2D; + overlayY = event.y - viewportHeight + windowHeight - WINDOW_MARGIN_2D; numLinesBefore = Math.floor(overlayY / (usersLineHeight + usersLineSpacing)); minY = numLinesBefore * (usersLineHeight + usersLineSpacing); @@ -70,10 +74,8 @@ var usersWindow = (function () { if (0 <= lineClicked && lineClicked < linesOfUsers.length && overlayX <= usersOnline[linesOfUsers[lineClicked]].usernameWidth) { - - print("Go to " + usersOnline[linesOfUsers[lineClicked]].username); - // DJRTODO - //location.goToUser(usersOnline[userIndex].username); + //print("Go to " + usersOnline[linesOfUsers[lineClicked]].username); + location.goToUser(usersOnline[userIndex].username); } } } @@ -96,11 +98,13 @@ var usersWindow = (function () { } displayText = displayText.slice(1); // Remove leading "\n". + windowHeight = (linesOfUsers.length > 0 ? linesOfUsers.length : 1) * (usersLineHeight + usersLineSpacing) + - usersLineSpacing + 2 * WINDOW_MARGIN_2D; Overlays.editOverlay(usersPane2D, { - text: linesOfUsers.length > 0 ? displayText : "No users online", - height: (linesOfUsers.length > 0 ? linesOfUsers.length : 1) * (usersLineHeight + usersLineSpacing) - - usersLineSpacing + 2 * WINDOW_MARGIN_2D + y: viewportHeight - windowHeight, + height: windowHeight, + text: linesOfUsers.length > 0 ? displayText : "No users online" }); } @@ -168,9 +172,16 @@ var usersWindow = (function () { } } + function update() { + viewportHeight = Controller.getViewportDimensions().y; + Overlays.editOverlay(usersPane2D, { + y: viewportHeight - windowHeight + }); + } + function setUp() { usersPane2D = Overlays.addOverlay("text", { - bounds: WINDOW_BOUNDS_2D, + bounds: { x: 0, y: 0, width: WINDOW_WIDTH_2D, height: 0 }, topMargin: WINDOW_MARGIN_2D, leftMargin: WINDOW_MARGIN_2D, color: WINDOW_FOREGROUND_COLOR_2D, @@ -182,6 +193,7 @@ var usersWindow = (function () { visible: isVisible }); + viewportHeight = Controller.getViewportDimensions().y; usersLineHeight = Math.floor(Overlays.textSize(usersPane2D, "1").height); usersLineSpacing = Math.floor(Overlays.textSize(usersPane2D, "1\n2").height - 2 * usersLineHeight); @@ -198,6 +210,8 @@ var usersWindow = (function () { }); Menu.menuItemEvent.connect(onMenuItemEvent); + Script.update.connect(update); + requestUsers(); } From e4f7e35587c5f1a0aca057e802be8535790dfc9b Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 11 Mar 2015 10:10:11 -0700 Subject: [PATCH 36/43] Hide edit tools when inactive --- examples/editEntities.js | 24 +++++++++++++++++------- examples/libraries/toolBars.js | 4 ++++ 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/examples/editEntities.js b/examples/editEntities.js index 9568f4207f..faef875d9b 100644 --- a/examples/editEntities.js +++ b/examples/editEntities.js @@ -125,7 +125,7 @@ var toolBar = (function () { width: toolWidth, height: toolHeight, alpha: 0.9, - visible: true + visible: false }); browseModelsButton = toolBar.addTool({ @@ -133,7 +133,7 @@ var toolBar = (function () { width: toolWidth, height: toolHeight, alpha: 0.9, - visible: true + visible: false }); newCubeButton = toolBar.addTool({ @@ -142,7 +142,7 @@ var toolBar = (function () { width: toolWidth, height: toolHeight, alpha: 0.9, - visible: true + visible: false }); newSphereButton = toolBar.addTool({ @@ -151,7 +151,7 @@ var toolBar = (function () { width: toolWidth, height: toolHeight, alpha: 0.9, - visible: true + visible: false }); newLightButton = toolBar.addTool({ @@ -160,7 +160,7 @@ var toolBar = (function () { width: toolWidth, height: toolHeight, alpha: 0.9, - visible: true + visible: false }); newTextButton = toolBar.addTool({ @@ -169,9 +169,8 @@ var toolBar = (function () { width: toolWidth, height: toolHeight, alpha: 0.9, - visible: true + visible: false }); - } that.setActive = function(active) { @@ -196,11 +195,22 @@ var toolBar = (function () { propertiesTool.setVisible(true); Window.setFocus(); } + that.showTools(isActive); } } toolBar.selectTool(activeButton, isActive); }; + // Sets visibility of tool buttons, excluding the power button + that.showTools = function(doShow) { + toolBar.showTool(newModelButton, doShow); + toolBar.showTool(browseModelsButton, doShow); + toolBar.showTool(newCubeButton, doShow); + toolBar.showTool(newSphereButton, doShow); + toolBar.showTool(newLightButton, doShow); + toolBar.showTool(newTextButton, doShow); + }; + var RESIZE_INTERVAL = 50; var RESIZE_TIMEOUT = 120000; // 2 minutes var RESIZE_MAX_CHECKS = RESIZE_TIMEOUT / RESIZE_INTERVAL; diff --git a/examples/libraries/toolBars.js b/examples/libraries/toolBars.js index 5802625d7b..951b6704ec 100644 --- a/examples/libraries/toolBars.js +++ b/examples/libraries/toolBars.js @@ -271,6 +271,10 @@ ToolBar = function(x, y, direction) { }); } } + + this.showTool = function(tool, doShow) { + this.tools[tool].show(doShow); + } this.show = function(doShow) { for(var tool in this.tools) { From c30bf5667ff03f8b1833a2d9eb1834977f969fa4 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 11 Mar 2015 10:15:34 -0700 Subject: [PATCH 37/43] Fix teleporting to user --- examples/users.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/users.js b/examples/users.js index ee64514562..5f6d9e0854 100644 --- a/examples/users.js +++ b/examples/users.js @@ -75,7 +75,7 @@ var usersWindow = (function () { if (0 <= lineClicked && lineClicked < linesOfUsers.length && overlayX <= usersOnline[linesOfUsers[lineClicked]].usernameWidth) { //print("Go to " + usersOnline[linesOfUsers[lineClicked]].username); - location.goToUser(usersOnline[userIndex].username); + location.goToUser(usersOnline[linesOfUsers[lineClicked]].username); } } } From d36399c65d845c8355e811b85a47859e2df0531d Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 11 Mar 2015 10:21:49 -0700 Subject: [PATCH 38/43] Add heading --- examples/users.js | 38 ++++++++++++++++++++++++++++++++------ 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/examples/users.js b/examples/users.js index 5f6d9e0854..2340039618 100644 --- a/examples/users.js +++ b/examples/users.js @@ -15,10 +15,13 @@ var usersWindow = (function () { WINDOW_MARGIN_2D = 12, WINDOW_FOREGROUND_COLOR_2D = { red: 240, green: 240, blue: 240 }, WINDOW_FOREGROUND_ALPHA_2D = 0.9, - WINDOW_BACKGROUND_COLOR_2D = { red: 120, green: 120, blue: 120 }, + WINDOW_HEADING_COLOR_2D = { red: 180, green: 180, blue: 180 }, + WINDOW_HEADING_ALPHA_2D = 0.9, + WINDOW_BACKGROUND_COLOR_2D = { red: 80, green: 80, blue: 80 }, WINDOW_BACKGROUND_ALPHA_2D = 0.7, windowHeight = 0, usersPane2D, + usersHeading2D, USERS_FONT_2D = { size: 14 }, usersLineHeight, usersLineSpacing, @@ -61,7 +64,7 @@ var usersWindow = (function () { if (clickedOverlay === usersPane2D) { overlayX = event.x - WINDOW_MARGIN_2D; - overlayY = event.y - viewportHeight + windowHeight - WINDOW_MARGIN_2D; + overlayY = event.y - viewportHeight + windowHeight - WINDOW_MARGIN_2D - (usersLineHeight + usersLineSpacing); numLinesBefore = Math.floor(overlayY / (usersLineHeight + usersLineSpacing)); minY = numLinesBefore * (usersLineHeight + usersLineSpacing); @@ -97,14 +100,18 @@ var usersWindow = (function () { } } - displayText = displayText.slice(1); // Remove leading "\n". - windowHeight = (linesOfUsers.length > 0 ? linesOfUsers.length : 1) * (usersLineHeight + usersLineSpacing) - - usersLineSpacing + 2 * WINDOW_MARGIN_2D; + windowHeight = (linesOfUsers.length > 0 ? linesOfUsers.length + 1 : 1) * (usersLineHeight + usersLineSpacing) + - usersLineSpacing + 2 * WINDOW_MARGIN_2D; // First or only line is for heading Overlays.editOverlay(usersPane2D, { y: viewportHeight - windowHeight, height: windowHeight, - text: linesOfUsers.length > 0 ? displayText : "No users online" + text: displayText + }); + + Overlays.editOverlay(usersHeading2D, { + y: viewportHeight - windowHeight + WINDOW_MARGIN_2D, + text: linesOfUsers.length > 0 ? "Online" : "No users online" }); } @@ -164,6 +171,7 @@ var usersWindow = (function () { } Overlays.editOverlay(usersPane2D, { visible: isVisible }); + Overlays.editOverlay(usersHeading2D, { visible: isVisible }); } function onMenuItemEvent(event) { @@ -177,6 +185,9 @@ var usersWindow = (function () { Overlays.editOverlay(usersPane2D, { y: viewportHeight - windowHeight }); + Overlays.editOverlay(usersHeading2D, { + y: viewportHeight - windowHeight + WINDOW_MARGIN_2D + }) } function setUp() { @@ -193,6 +204,20 @@ var usersWindow = (function () { visible: isVisible }); + usersHeading2D = Overlays.addOverlay("text", { + x: WINDOW_MARGIN_2D, + width: WINDOW_WIDTH_2D - 2 * WINDOW_MARGIN_2D, + height: USERS_FONT_2D.size, + topMargin: 0, + leftMargin: 0, + color: WINDOW_HEADING_COLOR_2D, + alpha: WINDOW_HEADING_ALPHA_2D, + backgroundAlpha: 0.0, + text: "No users online", + font: USERS_FONT_2D, + visible: isVisible + }); + viewportHeight = Controller.getViewportDimensions().y; usersLineHeight = Math.floor(Overlays.textSize(usersPane2D, "1").height); usersLineSpacing = Math.floor(Overlays.textSize(usersPane2D, "1\n2").height - 2 * usersLineHeight); @@ -219,6 +244,7 @@ var usersWindow = (function () { Menu.removeMenuItem(MENU_NAME, MENU_ITEM); Script.clearTimeout(usersTimer); + Overlays.deleteOverlay(usersHeading2D); Overlays.deleteOverlay(usersPane2D); } From 22ff97fd276d8c9c41bf96606af605627e5aebb0 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 11 Mar 2015 10:25:24 -0700 Subject: [PATCH 39/43] Make visible by default --- examples/users.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/users.js b/examples/users.js index 2340039618..8919ab08b4 100644 --- a/examples/users.js +++ b/examples/users.js @@ -43,7 +43,7 @@ var usersWindow = (function () { MENU_ITEM = "Users Online", MENI_ITEM_AFTER = "Chat...", - isVisible = false, + isVisible = true, viewportHeight; From 532e4feebb627fa7a8646e26604c8bd6ad21d84d Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 11 Mar 2015 10:30:09 -0700 Subject: [PATCH 40/43] Remove dead code and fix typos --- examples/users.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/examples/users.js b/examples/users.js index 8919ab08b4..e40c3878ce 100644 --- a/examples/users.js +++ b/examples/users.js @@ -25,8 +25,6 @@ var usersWindow = (function () { USERS_FONT_2D = { size: 14 }, usersLineHeight, usersLineSpacing, - USERNAME_SPACER = "\u00a0\u00a0\u00a0\u00a0", // Nonbreaking spaces - usernameSpacerWidth2D, usersOnline, // Raw data linesOfUsers, // Array of indexes pointing into usersOnline @@ -187,7 +185,7 @@ var usersWindow = (function () { }); Overlays.editOverlay(usersHeading2D, { y: viewportHeight - windowHeight + WINDOW_MARGIN_2D - }) + }); } function setUp() { @@ -219,11 +217,10 @@ var usersWindow = (function () { }); viewportHeight = Controller.getViewportDimensions().y; + usersLineHeight = Math.floor(Overlays.textSize(usersPane2D, "1").height); usersLineSpacing = Math.floor(Overlays.textSize(usersPane2D, "1\n2").height - 2 * usersLineHeight); - usernameSpacerWidth2D = Overlays.textSize(usersPane2D, USERNAME_SPACER).width; - Controller.mousePressEvent.connect(onMousePressEvent); Menu.addMenuItem({ From bbffa6474bc5a777eaf3e132c04912023a37404b Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 11 Mar 2015 10:32:14 -0700 Subject: [PATCH 41/43] Add users.js to default scripts --- examples/defaultScripts.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/defaultScripts.js b/examples/defaultScripts.js index 65682deb74..d4efad9ee5 100644 --- a/examples/defaultScripts.js +++ b/examples/defaultScripts.js @@ -17,4 +17,5 @@ Script.load("headMove.js"); Script.load("inspect.js"); Script.load("lobby.js"); Script.load("notifications.js"); -Script.load("lookWithMouse.js") +Script.load("lookWithMouse.js"); +Script.load("users.js"); From 1a18decc6d003f4e224897afe17511eabf83bb6b Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 11 Mar 2015 10:49:36 -0700 Subject: [PATCH 42/43] Fix typo --- examples/users.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/users.js b/examples/users.js index e40c3878ce..e2b0e7f053 100644 --- a/examples/users.js +++ b/examples/users.js @@ -39,7 +39,7 @@ var usersWindow = (function () { MENU_NAME = "Tools", MENU_ITEM = "Users Online", - MENI_ITEM_AFTER = "Chat...", + MENU_ITEM_AFTER = "Chat...", isVisible = true, @@ -226,7 +226,7 @@ var usersWindow = (function () { Menu.addMenuItem({ menuName: MENU_NAME, menuItemName: MENU_ITEM, - afterItem: MENI_ITEM_AFTER, + afterItem: MENU_ITEM_AFTER, isCheckable: true, isChecked: isVisible }); From fd423e5019c72745ec756da0a0d9975875133744 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 11 Mar 2015 11:07:02 -0700 Subject: [PATCH 43/43] Fix units --- examples/users.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/users.js b/examples/users.js index e2b0e7f053..0274cd7321 100644 --- a/examples/users.js +++ b/examples/users.js @@ -116,7 +116,7 @@ var usersWindow = (function () { function requestUsers() { usersRequest = new XMLHttpRequest(); usersRequest.open("GET", API_URL, true); - usersRequest.timeout = HTTP_GET_TIMEOUT * 1000; + usersRequest.timeout = HTTP_GET_TIMEOUT; usersRequest.ontimeout = usersTimedOut; usersRequest.onreadystatechange = processUsers; usersRequest.send();