diff --git a/CMakeLists.txt b/CMakeLists.txt index 94b05b93..6d052d6f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,7 +18,7 @@ SET(OUR_EXECUTABLE_OUTPUT_PATH "${PROJECT_BINARY_DIR}/bin") ENABLE_TESTING() # Options -OPTION(COMPILE_EXAMPLES "Select this if you want to build the examples" OFF) +OPTION(COMPILE_TESTBED "Select this if you want to build the testbed application" OFF) OPTION(COMPILE_TESTS "Select this if you want to build the tests" OFF) OPTION(PROFILING_ENABLED "Select this if you want to compile with enabled profiling" OFF) OPTION(DOUBLE_PRECISION_ENABLED "Select this if you want to compile using double precision floating @@ -142,10 +142,10 @@ SET (REACTPHYSICS3D_SOURCES # Create the library ADD_LIBRARY (reactphysics3d STATIC ${REACTPHYSICS3D_SOURCES}) -# If we need to compile the examples -IF(COMPILE_EXAMPLES) - add_subdirectory(examples/) -ENDIF(COMPILE_EXAMPLES) +# If we need to compile the testbed application +IF(COMPILE_TESTBED) + add_subdirectory(testbed/) +ENDIF(COMPILE_TESTBED) # If we need to compile the tests IF(COMPILE_TESTS) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt deleted file mode 100644 index 3743dc56..00000000 --- a/examples/CMakeLists.txt +++ /dev/null @@ -1,11 +0,0 @@ -# Minimum cmake version required -CMAKE_MINIMUM_REQUIRED(VERSION 2.6) - -# Set a variable for the directory of the opengl-framework -SET(OPENGLFRAMEWORK_DIR "${CMAKE_CURRENT_SOURCE_DIR}/common/opengl-framework") - -ADD_SUBDIRECTORY(common/) -ADD_SUBDIRECTORY(cubes/) -ADD_SUBDIRECTORY(joints/) -ADD_SUBDIRECTORY(collisionshapes/) -ADD_SUBDIRECTORY(raycast/) diff --git a/examples/collisionshapes/CMakeLists.txt b/examples/collisionshapes/CMakeLists.txt deleted file mode 100644 index 6bdd5d3e..00000000 --- a/examples/collisionshapes/CMakeLists.txt +++ /dev/null @@ -1,42 +0,0 @@ -# Minimum cmake version required -cmake_minimum_required(VERSION 2.6) - -# Project configuration -PROJECT(CollisionShapes) - -# Where to build the executables -SET(EXECUTABLE_OUTPUT_PATH "${OUR_EXECUTABLE_OUTPUT_PATH}/collisionshapes") -SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}) -SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${EXECUTABLE_OUTPUT_PATH}) -SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${EXECUTABLE_OUTPUT_PATH}) - -# Copy the shaders used for the demo into the build directory -FILE(COPY "${OPENGLFRAMEWORK_DIR}/src/shaders/" DESTINATION "${EXECUTABLE_OUTPUT_PATH}/shaders/") - -# Copy the meshes used for the demo into the build directory -FILE(COPY "../common/meshes/" DESTINATION "${EXECUTABLE_OUTPUT_PATH}/meshes/") - -# Headers -INCLUDE_DIRECTORIES("${OPENGLFRAMEWORK_DIR}/src/" "../common/glfw/include/" "../common/") - -# Source files -SET(COLLISION_SHAPES_SOURCES - CollisionShapes.cpp - Scene.cpp - Scene.h - "../common/VisualContactPoint.cpp" - "../common/ConvexMesh.cpp" - "../common/Capsule.cpp" - "../common/Sphere.cpp" - "../common/Cylinder.cpp" - "../common/Cone.cpp" - "../common/Dumbbell.cpp" - "../common/Box.cpp" - "../common/Viewer.cpp" -) - -# Create the executable -ADD_EXECUTABLE(collisionshapes ${COLLISION_SHAPES_SOURCES}) - -# Link with libraries -TARGET_LINK_LIBRARIES(collisionshapes reactphysics3d openglframework glfw ${GLFW_LIBRARIES}) diff --git a/examples/collisionshapes/CollisionShapes.cpp b/examples/collisionshapes/CollisionShapes.cpp deleted file mode 100644 index f8affc1d..00000000 --- a/examples/collisionshapes/CollisionShapes.cpp +++ /dev/null @@ -1,152 +0,0 @@ -/******************************************************************************** -* ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2015 Daniel Chappuis * -********************************************************************************* -* * -* This software is provided 'as-is', without any express or implied warranty. * -* In no event will the authors be held liable for any damages arising from the * -* use of this software. * -* * -* Permission is granted to anyone to use this software for any purpose, * -* including commercial applications, and to alter it and redistribute it * -* freely, subject to the following restrictions: * -* * -* 1. The origin of this software must not be misrepresented; you must not claim * -* that you wrote the original software. If you use this software in a * -* product, an acknowledgment in the product documentation would be * -* appreciated but is not required. * -* * -* 2. Altered source versions must be plainly marked as such, and must not be * -* misrepresented as being the original software. * -* * -* 3. This notice may not be removed or altered from any source distribution. * -* * -********************************************************************************/ - -// Libraries -#include "Scene.h" -#include "../common/Viewer.h" - -// Declarations -void simulate(); -void render(); -void update(); -void mouseButton(GLFWwindow* window, int button, int action, int mods); -void mouseMotion(GLFWwindow* window, double x, double y); -void keyboard(GLFWwindow* window, int key, int scancode, int action, int mods); -void scroll(GLFWwindow* window, double xAxis, double yAxis); -void init(); - -// Namespaces -using namespace openglframework; - -// Global variables -Viewer* viewer; -Scene* scene; - -// Main function -int main(int argc, char** argv) { - - // Create and initialize the Viewer - viewer = new Viewer(); - Vector2 windowsSize = Vector2(800, 600); - Vector2 windowsPosition = Vector2(100, 100); - viewer->init(argc, argv, "ReactPhysics3D Examples - Collision Shapes", - windowsSize, windowsPosition, true); - - // If the shaders and meshes folders are not specified as an argument - if (argc < 3) { - std::cerr << "Error : You need to specify the shaders folder as the first argument" - << " and the meshes folder as the second argument" << std::endl; - return 1; - } - - // Get the path of the shaders folder - std::string shaderFolderPath(argv[1]); - std::string meshFolderPath(argv[2]); - - // Register callback methods - viewer->registerUpdateFunction(update); - viewer->registerKeyboardCallback(keyboard); - viewer->registerMouseButtonCallback(mouseButton); - viewer->registerMouseCursorCallback(mouseMotion); - viewer->registerScrollingCallback(scroll); - - // Create the scene - scene = new Scene(viewer, shaderFolderPath, meshFolderPath); - - init(); - - viewer->startMainLoop(); - - delete viewer; - delete scene; - - return 0; -} - -// Update function that is called each frame -void update() { - - // Take a simulation step - simulate(); - - // Render - render(); -} - -// Simulate function -void simulate() { - - // Physics simulation - scene->simulate(); - - viewer->computeFPS(); -} - -// Initialization -void init() { - - // Define the background color (black) - glClearColor(0.0, 0.0, 0.0, 1.0); -} - -// Callback method to receive keyboard events -void keyboard(GLFWwindow* window, int key, int scancode, int action, int mods) { - if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) { - glfwSetWindowShouldClose(window, GL_TRUE); - } - else if (key == GLFW_KEY_SPACE && action == GLFW_PRESS) { - scene->pauseContinueSimulation(); - } -} - -// Callback method to receive scrolling events -void scroll(GLFWwindow* window, double xAxis, double yAxis) { - viewer->scrollingEvent(static_cast(yAxis)); -} - -// Called when a mouse button event occurs -void mouseButton(GLFWwindow* window, int button, int action, int mods) { - viewer->mouseButtonEvent(button, action); -} - -// Called when a mouse motion event occurs -void mouseMotion(GLFWwindow* window, double x, double y) { - viewer->mouseMotionEvent(x, y); -} - -// Display the scene -void render() { - - // Render the scene - scene->render(); - - // Display the FPS - viewer->displayGUI(); - - // Check the OpenGL errors - Viewer::checkOpenGLErrors(); -} - - diff --git a/examples/common/CMakeLists.txt b/examples/common/CMakeLists.txt deleted file mode 100644 index 0c4f652b..00000000 --- a/examples/common/CMakeLists.txt +++ /dev/null @@ -1,5 +0,0 @@ -# Minimum cmake version required -CMAKE_MINIMUM_REQUIRED(VERSION 2.6) - -ADD_SUBDIRECTORY(opengl-framework/) -ADD_SUBDIRECTORY(glfw/) diff --git a/examples/common/VisualContactPoint.cpp b/examples/common/VisualContactPoint.cpp deleted file mode 100644 index 77337192..00000000 --- a/examples/common/VisualContactPoint.cpp +++ /dev/null @@ -1,115 +0,0 @@ -/******************************************************************************** -* ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2015 Daniel Chappuis * -********************************************************************************* -* * -* This software is provided 'as-is', without any express or implied warranty. * -* In no event will the authors be held liable for any damages arising from the * -* use of this software. * -* * -* Permission is granted to anyone to use this software for any purpose, * -* including commercial applications, and to alter it and redistribute it * -* freely, subject to the following restrictions: * -* * -* 1. The origin of this software must not be misrepresented; you must not claim * -* that you wrote the original software. If you use this software in a * -* product, an acknowledgment in the product documentation would be * -* appreciated but is not required. * -* * -* 2. Altered source versions must be plainly marked as such, and must not be * -* misrepresented as being the original software. * -* * -* 3. This notice may not be removed or altered from any source distribution. * -* * -********************************************************************************/ - -// Libraries -#include "VisualContactPoint.h" - -// Initialization of static variables -int VisualContactPoint::mNbTotalPoints = 0; -bool VisualContactPoint::mIsMeshInitialized = false; -openglframework::Mesh VisualContactPoint::mMesh; - -// Constructor -VisualContactPoint::VisualContactPoint(const openglframework::Vector3& position) { - - assert(mIsMeshInitialized); - - // Initialize the position where the sphere will be rendered - translateWorld(position); -} - -// Destructor -VisualContactPoint::~VisualContactPoint() { - -} - -// Load and initialize the mesh for all the contact points -void VisualContactPoint::createStaticData(const std::string& meshFolderPath) { - - if (!mIsMeshInitialized) { - - // Load the mesh from a file - openglframework::MeshReaderWriter::loadMeshFromFile(meshFolderPath + "sphere.obj", mMesh); - - // Calculate the normals of the mesh - mMesh.calculateNormals(); - - mMesh.scaleVertices(VISUAL_CONTACT_POINT_RADIUS); - - mIsMeshInitialized = true; - } -} - -// Destroy the mesh for the contact points -void VisualContactPoint::destroyStaticData() { - - mMesh.destroy(); - mIsMeshInitialized = false; -} - -// Render the sphere at the correct position and with the correct orientation -void VisualContactPoint::render(openglframework::Shader& shader, - const openglframework::Matrix4& worldToCameraMatrix) { - - // Bind the shader - shader.bind(); - - // Set the model to camera matrix - const openglframework::Matrix4 localToCameraMatrix = worldToCameraMatrix * mTransformMatrix; - shader.setMatrix4x4Uniform("localToCameraMatrix", localToCameraMatrix); - - // Set the normal matrix (inverse transpose of the 3x3 upper-left sub matrix of the - // model-view matrix) - const openglframework::Matrix3 normalMatrix = - localToCameraMatrix.getUpperLeft3x3Matrix().getInverse().getTranspose(); - shader.setMatrix3x3Uniform("normalMatrix", normalMatrix); - - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_NORMAL_ARRAY); - if (mMesh.hasTexture()) { - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - } - - glVertexPointer(3, GL_FLOAT, 0, mMesh.getVerticesPointer()); - glNormalPointer(GL_FLOAT, 0, mMesh.getNormalsPointer()); - if(mMesh.hasTexture()) { - glTexCoordPointer(2, GL_FLOAT, 0, mMesh.getUVTextureCoordinatesPointer()); - } - - // For each part of the mesh - for (unsigned int i=0; iinit(argc, argv, "ReactPhysics3D Examples - Cubes", windowsSize, windowsPosition, true); - - // If the shaders folder is not specified as an argument - if (argc < 2) { - std::cerr << "Error : You need to specify the shaders folder as argument !" << std::endl; - return 1; - } - - // Get the path of the shaders folder - std::string shaderFolderPath(argv[1]); - - // Register callback methods - viewer->registerUpdateFunction(update); - viewer->registerKeyboardCallback(keyboard); - viewer->registerMouseButtonCallback(mouseButton); - viewer->registerMouseCursorCallback(mouseMotion); - viewer->registerScrollingCallback(scroll); - - // Create the scene - scene = new Scene(viewer, shaderFolderPath); - - init(); - - viewer->startMainLoop(); - - delete viewer; - delete scene; - - return 0; -} - -// Update function that is called each frame -void update() { - - // Take a simulation step - simulate(); - - // Render - render(); -} - -// Simulate function -void simulate() { - - // Physics simulation - scene->simulate(); - - // Compute the current framerate - viewer->computeFPS(); -} - -// Initialization -void init() { - - // Define the background color (black) - glClearColor(0.0, 0.0, 0.0, 1.0); -} - -// Callback method to receive keyboard events -void keyboard(GLFWwindow* window, int key, int scancode, int action, int mods) { - if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) { - glfwSetWindowShouldClose(window, GL_TRUE); - } - else if (key == GLFW_KEY_SPACE && action == GLFW_PRESS) { - scene->pauseContinueSimulation(); - } -} - -// Callback method to receive scrolling events -void scroll(GLFWwindow* window, double xAxis, double yAxis) { - viewer->scrollingEvent(static_cast(yAxis)); -} - -// Called when a mouse button event occurs -void mouseButton(GLFWwindow* window, int button, int action, int mods) { - viewer->mouseButtonEvent(button, action); -} - -// Called when a mouse motion event occurs -void mouseMotion(GLFWwindow* window, double x, double y) { - viewer->mouseMotionEvent(x, y); -} - -// Display the scene -void render() { - - // Render the scene - scene->render(); - - // Display the FPS - viewer->displayGUI(); - - // Check the OpenGL errors - Viewer::checkOpenGLErrors(); -} diff --git a/examples/joints/CMakeLists.txt b/examples/joints/CMakeLists.txt deleted file mode 100644 index ba304b9d..00000000 --- a/examples/joints/CMakeLists.txt +++ /dev/null @@ -1,34 +0,0 @@ -# Minimum cmake version required -cmake_minimum_required(VERSION 2.6) - -# Project configuration -PROJECT(Joints) - -# Where to build the executables -SET(EXECUTABLE_OUTPUT_PATH "${OUR_EXECUTABLE_OUTPUT_PATH}/joints") -SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}) -SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${EXECUTABLE_OUTPUT_PATH}) -SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${EXECUTABLE_OUTPUT_PATH}) - -# Copy the shaders used for the demo into the build directory -FILE(COPY "${OPENGLFRAMEWORK_DIR}/src/shaders/" DESTINATION "${EXECUTABLE_OUTPUT_PATH}/shaders/") - -# Headers -INCLUDE_DIRECTORIES("${OPENGLFRAMEWORK_DIR}/src/" "../common/glfw/include/" "../common/") - -# Source files -SET(JOINTS_SOURCES - Joints.cpp - Scene.cpp - Scene.h - "../common/Box.cpp" - "../common/Box.h" - "../common/Viewer.cpp" - "../common/Viewer.h" -) - -# Create the executable -ADD_EXECUTABLE(joints ${JOINTS_SOURCES}) - -# Link with libraries -TARGET_LINK_LIBRARIES(joints reactphysics3d openglframework glfw ${GLFW_LIBRARIES}) diff --git a/examples/joints/Joints.cpp b/examples/joints/Joints.cpp deleted file mode 100644 index 1b1b7e00..00000000 --- a/examples/joints/Joints.cpp +++ /dev/null @@ -1,149 +0,0 @@ -/******************************************************************************** -* ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2015 Daniel Chappuis * -********************************************************************************* -* * -* This software is provided 'as-is', without any express or implied warranty. * -* In no event will the authors be held liable for any damages arising from the * -* use of this software. * -* * -* Permission is granted to anyone to use this software for any purpose, * -* including commercial applications, and to alter it and redistribute it * -* freely, subject to the following restrictions: * -* * -* 1. The origin of this software must not be misrepresented; you must not claim * -* that you wrote the original software. If you use this software in a * -* product, an acknowledgment in the product documentation would be * -* appreciated but is not required. * -* * -* 2. Altered source versions must be plainly marked as such, and must not be * -* misrepresented as being the original software. * -* * -* 3. This notice may not be removed or altered from any source distribution. * -* * -********************************************************************************/ - -// Libraries -#include "Scene.h" -#include "../common/Viewer.h" - -// Declarations -void simulate(); -void update(); -void render(); -void mouseButton(GLFWwindow* window, int button, int action, int mods); -void mouseMotion(GLFWwindow* window, double x, double y); -void keyboard(GLFWwindow* window, int key, int scancode, int action, int mods); -void scroll(GLFWwindow* window, double xAxis, double yAxis); -void init(); - -// Namespaces -using namespace openglframework; - -// Global variables -Viewer* viewer; -Scene* scene; - -// Main function -int main(int argc, char** argv) { - - // Create and initialize the Viewer - viewer = new Viewer(); - Vector2 windowsSize = Vector2(800, 600); - Vector2 windowsPosition = Vector2(100, 100); - viewer->init(argc, argv, "ReactPhysics3D Examples - Joints", windowsSize, windowsPosition, true); - - // If the shaders folder is not specified as an argument - if (argc < 2) { - std::cerr << "Error : You need to specify the shaders folder as argument !" << std::endl; - return 1; - } - - // Get the path of the shaders folder - std::string shaderFolderPath(argv[1]); - - // Register callback methods - viewer->registerUpdateFunction(update); - viewer->registerKeyboardCallback(keyboard); - viewer->registerMouseButtonCallback(mouseButton); - viewer->registerMouseCursorCallback(mouseMotion); - viewer->registerScrollingCallback(scroll); - - // Create the scene - scene = new Scene(viewer, shaderFolderPath); - - init(); - - viewer->startMainLoop(); - - delete viewer; - delete scene; - - return 0; -} - -// Update function that is called each frame -void update() { - - // Take a simulation step - simulate(); - - // Render - render(); -} - -// Simulate function -void simulate() { - - // Physics simulation - scene->simulate(); - - viewer->computeFPS(); -} - -// Initialization -void init() { - - // Define the background color (black) - glClearColor(0.0, 0.0, 0.0, 1.0); -} - -// Callback method to receive keyboard events -void keyboard(GLFWwindow* window, int key, int scancode, int action, int mods) { - if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) { - glfwSetWindowShouldClose(window, GL_TRUE); - } - else if (key == GLFW_KEY_SPACE && action == GLFW_PRESS) { - scene->pauseContinueSimulation(); - } -} - -// Callback method to receive scrolling events -void scroll(GLFWwindow* window, double xAxis, double yAxis) { - viewer->scrollingEvent(static_cast(yAxis)); -} - -// Called when a mouse button event occurs -void mouseButton(GLFWwindow* window, int button, int action, int mods) { - viewer->mouseButtonEvent(button, action); -} - -// Called when a mouse motion event occurs -void mouseMotion(GLFWwindow* window, double x, double y) { - viewer->mouseMotionEvent(x, y); -} - -// Display the scene -void render() { - - // Render the scene - scene->render(); - - // Display the FPS - viewer->displayGUI(); - - // Check the OpenGL errors - Viewer::checkOpenGLErrors(); -} - - diff --git a/examples/raycast/CMakeLists.txt b/examples/raycast/CMakeLists.txt deleted file mode 100644 index 01440b00..00000000 --- a/examples/raycast/CMakeLists.txt +++ /dev/null @@ -1,43 +0,0 @@ -# Minimum cmake version required -cmake_minimum_required(VERSION 2.6) - -# Project configuration -PROJECT(Raycast) - -# Where to build the executables -SET(EXECUTABLE_OUTPUT_PATH "${OUR_EXECUTABLE_OUTPUT_PATH}/raycast") -SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}) -SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${EXECUTABLE_OUTPUT_PATH}) -SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${EXECUTABLE_OUTPUT_PATH}) - -# Copy the shaders used for the demo into the build directory -FILE(COPY "${OPENGLFRAMEWORK_DIR}/src/shaders/" DESTINATION "${EXECUTABLE_OUTPUT_PATH}/shaders/") - -# Copy the meshes used for the demo into the build directory -FILE(COPY "../common/meshes/" DESTINATION "${EXECUTABLE_OUTPUT_PATH}/meshes/") - -# Headers -INCLUDE_DIRECTORIES("${OPENGLFRAMEWORK_DIR}/src/" "../common/glfw/include/" "../common/") - -# Source files -SET(RAYCAST_SOURCES - Raycast.cpp - Scene.cpp - Scene.h - "../common/VisualContactPoint.cpp" - "../common/ConvexMesh.cpp" - "../common/Capsule.cpp" - "../common/Sphere.cpp" - "../common/Line.cpp" - "../common/Cylinder.cpp" - "../common/Cone.cpp" - "../common/Dumbbell.cpp" - "../common/Box.cpp" - "../common/Viewer.cpp" -) - -# Create the executable -ADD_EXECUTABLE(raycast ${RAYCAST_SOURCES}) - -# Link with libraries -TARGET_LINK_LIBRARIES(raycast reactphysics3d openglframework glfw ${GLFW_LIBRARIES}) diff --git a/examples/raycast/Raycast.cpp b/examples/raycast/Raycast.cpp deleted file mode 100644 index e971069a..00000000 --- a/examples/raycast/Raycast.cpp +++ /dev/null @@ -1,155 +0,0 @@ -/******************************************************************************** -* ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2015 Daniel Chappuis * -********************************************************************************* -* * -* This software is provided 'as-is', without any express or implied warranty. * -* In no event will the authors be held liable for any damages arising from the * -* use of this software. * -* * -* Permission is granted to anyone to use this software for any purpose, * -* including commercial applications, and to alter it and redistribute it * -* freely, subject to the following restrictions: * -* * -* 1. The origin of this software must not be misrepresented; you must not claim * -* that you wrote the original software. If you use this software in a * -* product, an acknowledgment in the product documentation would be * -* appreciated but is not required. * -* * -* 2. Altered source versions must be plainly marked as such, and must not be * -* misrepresented as being the original software. * -* * -* 3. This notice may not be removed or altered from any source distribution. * -* * -********************************************************************************/ - -// Libraries -#include "Scene.h" -#include "../common/Viewer.h" - -// Declarations -void simulate(); -void render(); -void update(); -void mouseButton(GLFWwindow* window, int button, int action, int mods); -void mouseMotion(GLFWwindow* window, double x, double y); -void keyboard(GLFWwindow* window, int key, int scancode, int action, int mods); -void scroll(GLFWwindow* window, double xAxis, double yAxis); -void init(); - -// Namespaces -using namespace openglframework; - -// Global variables -Viewer* viewer; -Scene* scene; - -// Main function -int main(int argc, char** argv) { - - // Create and initialize the Viewer - viewer = new Viewer(); - Vector2 windowsSize = Vector2(800, 600); - Vector2 windowsPosition = Vector2(100, 100); - viewer->init(argc, argv, "ReactPhysics3D Examples - Raycast", - windowsSize, windowsPosition, true); - - // If the shaders and meshes folders are not specified as an argument - if (argc < 3) { - std::cerr << "Error : You need to specify the shaders folder as the first argument" - << " and the meshes folder as the second argument" << std::endl; - return 1; - } - - // Get the path of the shaders folder - std::string shaderFolderPath(argv[1]); - std::string meshFolderPath(argv[2]); - - // Register callback methods - viewer->registerUpdateFunction(update); - viewer->registerKeyboardCallback(keyboard); - viewer->registerMouseButtonCallback(mouseButton); - viewer->registerMouseCursorCallback(mouseMotion); - viewer->registerScrollingCallback(scroll); - - // Create the scene - scene = new Scene(viewer, shaderFolderPath, meshFolderPath); - - init(); - - viewer->startMainLoop(); - - delete viewer; - delete scene; - - return 0; -} - -// Update function that is called each frame -void update() { - - // Take a simulation step - simulate(); - - // Render - render(); -} - -// Simulate function -void simulate() { - - // Physics simulation - scene->simulate(); - - viewer->computeFPS(); -} - -// Initialization -void init() { - - // Define the background color (black) - glClearColor(0.0, 0.0, 0.0, 1.0); -} - -// Callback method to receive keyboard events -void keyboard(GLFWwindow* window, int key, int scancode, int action, int mods) { - if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) { - glfwSetWindowShouldClose(window, GL_TRUE); - } - if (key == GLFW_KEY_SPACE && action == GLFW_PRESS) { - scene->changeBody(); - } - if (key == GLFW_KEY_N && action == GLFW_PRESS) { - scene->showHideNormals(); - } -} - -// Callback method to receive scrolling events -void scroll(GLFWwindow* window, double xAxis, double yAxis) { - viewer->scrollingEvent(static_cast(yAxis)); -} - -// Called when a mouse button event occurs -void mouseButton(GLFWwindow* window, int button, int action, int mods) { - viewer->mouseButtonEvent(button, action); -} - -// Called when a mouse motion event occurs -void mouseMotion(GLFWwindow* window, double x, double y) { - viewer->mouseMotionEvent(x, y); -} - -// Display the scene -void render() { - - // Render the scene - scene->render(); - - // Display the FPS - viewer->displayGUI(); - - // Check the OpenGL errors - Viewer::checkOpenGLErrors(); -} - - diff --git a/src/body/Body.h b/src/body/Body.h index 946aa61f..e16a15c4 100644 --- a/src/body/Body.h +++ b/src/body/Body.h @@ -83,9 +83,6 @@ class Body { /// Private assignment operator Body& operator=(const Body& body); - /// Set the variable to know whether or not the body is sleeping - virtual void setIsSleeping(bool isSleeping); - public : // -------------------- Methods -------------------- // @@ -105,6 +102,9 @@ class Body { /// Set whether or not the body is allowed to go to sleep void setIsAllowedToSleep(bool isAllowedToSleep); + /// Set the variable to know whether or not the body is sleeping + virtual void setIsSleeping(bool isSleeping); + /// Return whether or not the body is sleeping bool isSleeping() const; diff --git a/src/body/CollisionBody.cpp b/src/body/CollisionBody.cpp index f9a4702d..779cc747 100644 --- a/src/body/CollisionBody.cpp +++ b/src/body/CollisionBody.cpp @@ -41,8 +41,6 @@ CollisionBody::CollisionBody(const Transform& transform, CollisionWorld& world, : Body(id), mType(DYNAMIC), mTransform(transform), mProxyCollisionShapes(NULL), mNbCollisionShapes(0), mContactManifoldsList(NULL), mWorld(world) { - // Initialize the old transform - mOldTransform = transform; } // Destructor diff --git a/src/body/CollisionBody.h b/src/body/CollisionBody.h index c75631a5..955a2130 100644 --- a/src/body/CollisionBody.h +++ b/src/body/CollisionBody.h @@ -73,9 +73,6 @@ class CollisionBody : public Body { /// Position and orientation of the body Transform mTransform; - /// Last position and orientation of the body - Transform mOldTransform; - /// First element of the linked list of proxy collision shapes of this body ProxyShape* mProxyCollisionShapes; @@ -102,9 +99,6 @@ class CollisionBody : public Body { /// Remove all the collision shapes void removeAllCollisionShapes(); - /// Update the old transform with the current one. - void updateOldTransform(); - /// Update the broad-phase state for this body (because it has moved for instance) virtual void updateBroadPhaseState() const; @@ -138,7 +132,7 @@ class CollisionBody : public Body { const Transform& getTransform() const; /// Set the current position and orientation - void setTransform(const Transform& transform); + virtual void setTransform(const Transform& transform); /// Add a collision shape to the body. virtual ProxyShape* addCollisionShape(const CollisionShape& collisionShape, @@ -241,12 +235,6 @@ inline void CollisionBody::setTransform(const Transform& transform) { updateBroadPhaseState(); } -// Update the old transform with the current one. -/// This is used to compute the interpolated position and orientation of the body -inline void CollisionBody::updateOldTransform() { - mOldTransform = mTransform; -} - // Return the first element of the linked list of contact manifolds involving this body /** * @return A pointer to the first element of the linked-list with the contact diff --git a/src/body/RigidBody.cpp b/src/body/RigidBody.cpp index ddf1ea83..32f540f8 100644 --- a/src/body/RigidBody.cpp +++ b/src/body/RigidBody.cpp @@ -265,6 +265,64 @@ void RigidBody::removeCollisionShape(const ProxyShape* proxyShape) { recomputeMassInformation(); } +// Set the linear velocity of the rigid body. +/** + * @param linearVelocity Linear velocity vector of the body + */ +void RigidBody::setLinearVelocity(const Vector3& linearVelocity) { + + // If it is a static body, we do nothing + if (mType == STATIC) return; + + // Update the linear velocity of the current body state + mLinearVelocity = linearVelocity; + + // If the linear velocity is not zero, awake the body + if (mLinearVelocity.lengthSquare() > decimal(0.0)) { + setIsSleeping(false); + } +} + +// Set the angular velocity. +/** +* @param angularVelocity The angular velocity vector of the body +*/ +void RigidBody::setAngularVelocity(const Vector3& angularVelocity) { + + // If it is a static body, we do nothing + if (mType == STATIC) return; + + // Set the angular velocity + mAngularVelocity = angularVelocity; + + // If the velocity is not zero, awake the body + if (mAngularVelocity.lengthSquare() > decimal(0.0)) { + setIsSleeping(false); + } +} + +// Set the current position and orientation +/** + * @param transform The transformation of the body that transforms the local-space + * of the body into world-space + */ +void RigidBody::setTransform(const Transform& transform) { + + // Update the transform of the body + mTransform = transform; + + const Vector3 oldCenterOfMass = mCenterOfMassWorld; + + // Compute the new center of mass in world-space coordinates + mCenterOfMassWorld = mTransform * mCenterOfMassLocal; + + // Update the linear velocity of the center of mass + mLinearVelocity += mAngularVelocity.cross(mCenterOfMassWorld - oldCenterOfMass); + + // Update the broad-phase state of the body + updateBroadPhaseState(); +} + // Recompute the center of mass, total mass and inertia tensor of the body using all // the collision shapes attached to the body. void RigidBody::recomputeMassInformation() { diff --git a/src/body/RigidBody.h b/src/body/RigidBody.h index 964fa286..e8cc573b 100644 --- a/src/body/RigidBody.h +++ b/src/body/RigidBody.h @@ -118,9 +118,6 @@ class RigidBody : public CollisionBody { /// Update the broad-phase state for this body (because it has moved for instance) virtual void updateBroadPhaseState() const; - /// Set the variable to know whether or not the body is sleeping - virtual void setIsSleeping(bool isSleeping); - public : // -------------------- Methods -------------------- // @@ -134,6 +131,9 @@ class RigidBody : public CollisionBody { /// Set the type of the body (static, kinematic or dynamic) void setType(BodyType type); + /// Set the current position and orientation + virtual void setTransform(const Transform& transform); + /// Return the mass of the body decimal getMass() const; @@ -149,6 +149,9 @@ class RigidBody : public CollisionBody { /// Set the angular velocity. void setAngularVelocity(const Vector3& angularVelocity); + /// Set the variable to know whether or not the body is sleeping + virtual void setIsSleeping(bool isSleeping); + /// Return the local inertia tensor of the body (in body coordinates) const Matrix3x3& getInertiaTensorLocal() const; @@ -252,20 +255,6 @@ inline Vector3 RigidBody::getAngularVelocity() const { return mAngularVelocity; } -// Set the angular velocity. -/// You should only call this method for a kinematic body. Otherwise, it -/// will do nothing. -/** -* @param angularVelocity The angular velocity vector of the body -*/ -inline void RigidBody::setAngularVelocity(const Vector3& angularVelocity) { - - // If it is a kinematic body - if (mType == KINEMATIC) { - mAngularVelocity = angularVelocity; - } -} - // Return the local inertia tensor of the body (in local-space coordinates) /** * @return The 3x3 inertia tensor matrix of the body (in local-space coordinates) @@ -310,22 +299,6 @@ inline Matrix3x3 RigidBody::getInertiaTensorInverseWorld() const { mTransform.getOrientation().getMatrix().getTranspose(); } -// Set the linear velocity of the rigid body. -/// You should only call this method for a kinematic body. Otherwise, it -/// will do nothing. -/** - * @param linearVelocity Linear velocity vector of the body - */ -inline void RigidBody::setLinearVelocity(const Vector3& linearVelocity) { - - // If it is a kinematic body - if (mType == KINEMATIC) { - - // Update the linear velocity of the current body state - mLinearVelocity = linearVelocity; - } -} - // Return true if the gravity needs to be applied to this rigid body /** * @return True if the gravity is applied to the body diff --git a/src/engine/DynamicsWorld.cpp b/src/engine/DynamicsWorld.cpp index 927b28ee..7fe62461 100644 --- a/src/engine/DynamicsWorld.cpp +++ b/src/engine/DynamicsWorld.cpp @@ -142,9 +142,6 @@ void DynamicsWorld::update(decimal timeStep) { // Reset the external force and torque applied to the bodies resetBodiesForceAndTorque(); - - // Compute and set the interpolation factor to all the bodies - setInterpolationFactorToAllBodies(); } // Integrate position and orientation of the rigid bodies. @@ -331,9 +328,6 @@ void DynamicsWorld::integrateRigidBodiesVelocities() { mConstrainedAngularVelocities[indexBody] *= clamp(angularDamping, decimal(0.0), decimal(1.0)); - // Update the old Transform of the body - bodies[b]->updateOldTransform(); - indexBody++; } } @@ -958,3 +952,23 @@ void DynamicsWorld::testCollision(CollisionCallback* callback) { // Perform the collision detection and report contacts mCollisionDetection.reportCollisionBetweenShapes(callback, emptySet, emptySet); } + +/// Return the list of all contacts of the world +std::vector DynamicsWorld::getContactsList() const { + + std::vector contactManifolds; + + // For each currently overlapping pair of bodies + std::map::const_iterator it; + for (it = mCollisionDetection.mOverlappingPairs.begin(); + it != mCollisionDetection.mOverlappingPairs.end(); ++it) { + + OverlappingPair* pair = it->second; + + // Get the contact manifold + contactManifolds.push_back(pair->getContactManifold()); + } + + // Return all the contact manifold + return contactManifolds; +} diff --git a/src/engine/DynamicsWorld.h b/src/engine/DynamicsWorld.h index bb0e9acb..519ff6d9 100644 --- a/src/engine/DynamicsWorld.h +++ b/src/engine/DynamicsWorld.h @@ -186,18 +186,18 @@ class DynamicsWorld : public CollisionWorld { /// Destructor virtual ~DynamicsWorld(); - /// Start the physics simulation - void start(); - - /// Stop the physics simulation - void stop(); - /// Update the physics simulation void update(decimal timeStep); + /// Get the number of iterations for the velocity constraint solver + uint getNbIterationsVelocitySolver() const; + /// Set the number of iterations for the velocity constraint solver void setNbIterationsVelocitySolver(uint nbIterations); + /// Get the number of iterations for the position constraint solver + uint getNbIterationsPositionSolver() const; + /// Set the number of iterations for the position constraint solver void setNbIterationsPositionSolver(uint nbIterations); @@ -226,6 +226,9 @@ class DynamicsWorld : public CollisionWorld { /// Return the gravity vector of the world Vector3 getGravity() const; + /// Set the gravity vector of the world + void setGravity(Vector3& gravity); + /// Return if the gravity is on bool isGravityEnabled() const; @@ -238,9 +241,6 @@ class DynamicsWorld : public CollisionWorld { /// Return the number of joints in the world uint getNbJoints() const; - /// Return the current physics time (in seconds) - long double getPhysicsTime() const; - /// Return an iterator to the beginning of the rigid bodies of the physics world std::set::iterator getRigidBodiesBeginIterator(); @@ -297,6 +297,9 @@ class DynamicsWorld : public CollisionWorld { /// Test and report collisions between all shapes of the world virtual void testCollision(CollisionCallback* callback); + /// Return the list of all contacts of the world + std::vector getContactsList() const; + // -------------------- Friendship -------------------- // friend class RigidBody; @@ -311,7 +314,12 @@ inline void DynamicsWorld::resetBodiesForceAndTorque() { (*it)->mExternalForce.setToZero(); (*it)->mExternalTorque.setToZero(); } -} +} + +// Get the number of iterations for the velocity constraint solver +inline uint DynamicsWorld::getNbIterationsVelocitySolver() const { + return mNbVelocitySolverIterations; +} // Set the number of iterations for the velocity constraint solver /** @@ -321,6 +329,11 @@ inline void DynamicsWorld::setNbIterationsVelocitySolver(uint nbIterations) { mNbVelocitySolverIterations = nbIterations; } +// Get the number of iterations for the position constraint solver +inline uint DynamicsWorld::getNbIterationsPositionSolver() const { + return mNbPositionSolverIterations; +} + // Set the number of iterations for the position constraint solver /** * @param nbIterations Number of iterations for the position solver @@ -375,6 +388,14 @@ inline Vector3 DynamicsWorld::getGravity() const { return mGravity; } +// Set the gravity vector of the world +/** + * @param gravity The gravity vector (in meter per seconds squared) + */ +inline void DynamicsWorld::setGravity(Vector3& gravity) { + mGravity = gravity; +} + // Return if the gravity is enaled /** * @return True if the gravity is enabled in the world diff --git a/src/mathematics/Vector2.cpp b/src/mathematics/Vector2.cpp index ccad70dc..80c0ca34 100644 --- a/src/mathematics/Vector2.cpp +++ b/src/mathematics/Vector2.cpp @@ -54,7 +54,9 @@ Vector2::~Vector2() { Vector2 Vector2::getUnit() const { decimal lengthVector = length(); - assert(lengthVector > MACHINE_EPSILON); + if (lengthVector < MACHINE_EPSILON) { + return *this; + } // Compute and return the unit vector decimal lengthInv = decimal(1.0) / lengthVector; diff --git a/src/mathematics/Vector2.h b/src/mathematics/Vector2.h index 666d60c7..1083d26a 100644 --- a/src/mathematics/Vector2.h +++ b/src/mathematics/Vector2.h @@ -178,7 +178,9 @@ inline decimal Vector2::dot(const Vector2& vector) const { // Normalize the vector inline void Vector2::normalize() { decimal l = length(); - assert(l > std::numeric_limits::epsilon()); + if (l < MACHINE_EPSILON) { + return; + } x /= l; y /= l; } diff --git a/src/mathematics/Vector3.cpp b/src/mathematics/Vector3.cpp index 1e33136a..48bb714f 100644 --- a/src/mathematics/Vector3.cpp +++ b/src/mathematics/Vector3.cpp @@ -55,7 +55,9 @@ Vector3::~Vector3() { Vector3 Vector3::getUnit() const { decimal lengthVector = length(); - assert(lengthVector > MACHINE_EPSILON); + if (lengthVector < MACHINE_EPSILON) { + return *this; + } // Compute and return the unit vector decimal lengthInv = decimal(1.0) / lengthVector; diff --git a/src/mathematics/Vector3.h b/src/mathematics/Vector3.h index f8710fe3..f8ffb77f 100644 --- a/src/mathematics/Vector3.h +++ b/src/mathematics/Vector3.h @@ -193,7 +193,9 @@ inline Vector3 Vector3::cross(const Vector3& vector) const { // Normalize the vector inline void Vector3::normalize() { decimal l = length(); - assert(l > std::numeric_limits::epsilon()); + if (l < MACHINE_EPSILON) { + return; + } x /= l; y /= l; z /= l; diff --git a/testbed/CMakeLists.txt b/testbed/CMakeLists.txt new file mode 100644 index 00000000..4d3ecdac --- /dev/null +++ b/testbed/CMakeLists.txt @@ -0,0 +1,97 @@ +# Minimum cmake version required +cmake_minimum_required(VERSION 2.6) + +# Project configuration +PROJECT(Testbed) + +# Where to build the executables +SET(EXECUTABLE_OUTPUT_PATH "${OUR_EXECUTABLE_OUTPUT_PATH}/testbed") +SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}) +SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${EXECUTABLE_OUTPUT_PATH}) +SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${EXECUTABLE_OUTPUT_PATH}) + +ADD_SUBDIRECTORY(opengl-framework/) +ADD_SUBDIRECTORY(glfw/) + +# Copy the shaders used for the demo into the build directory +FILE(COPY "shaders/" DESTINATION "${EXECUTABLE_OUTPUT_PATH}/shaders/") + +# Copy the meshes used for the demo into the build directory +FILE(COPY "meshes/" DESTINATION "${EXECUTABLE_OUTPUT_PATH}/meshes/") + +# Copy the fonts used for the GUI into the build directory +FILE(COPY "imgui/DroidSans.ttf" DESTINATION "${EXECUTABLE_OUTPUT_PATH}") + +# Enable C++11 features +SET(CMAKE_CXX_FLAGS "-std=c++0x") + +#ADD_DEFINITIONS(-DGL3) + +# Headers +INCLUDE_DIRECTORIES("src/" "opengl-framework/src/" "glfw/include/" "common/" "scenes/" "imgui/") + +# Testbed source files +SET(TESTBED_SOURCES + src/Main.cpp + src/TestbedApplication.h + src/TestbedApplication.cpp + src/Gui.h + src/Gui.cpp + src/Scene.h + src/Scene.cpp + src/SceneDemo.h + src/SceneDemo.cpp + src/Timer.h + src/Timer.cpp +) + +# IMGUI source files +SET(IMGUI_SOURCES + imgui/imgui.cpp + imgui/imgui.h + imgui/imguiRenderGL3.h + imgui/imguiRenderGL3.cpp + imgui/stb_truetype.h +) + +# Common source files +SET(COMMON_SOURCES + common/Box.h + common/Box.cpp + common/Cone.h + common/Cone.cpp + common/Sphere.h + common/Sphere.cpp + common/Line.h + common/Line.cpp + common/Capsule.h + common/Capsule.cpp + common/ConvexMesh.h + common/ConvexMesh.cpp + common/Cylinder.h + common/Cylinder.cpp + common/Dumbbell.h + common/Dumbbell.cpp + common/PhysicsObject.h + common/PhysicsObject.cpp + common/VisualContactPoint.h + common/VisualContactPoint.cpp +) + +# Examples scenes source files +SET(SCENES_SOURCES + scenes/cubes/CubesScene.h + scenes/cubes/CubesScene.cpp + scenes/joints/JointsScene.h + scenes/joints/JointsScene.cpp + scenes/raycast/RaycastScene.h + scenes/raycast/RaycastScene.cpp + scenes/collisionshapes/CollisionShapesScene.h + scenes/collisionshapes/CollisionShapesScene.cpp +) + +# Create the executable +ADD_EXECUTABLE(testbed ${TESTBED_SOURCES} ${SCENES_SOURCES} ${COMMON_SOURCES} ${IMGUI_SOURCES}) + +# Link with libraries +TARGET_LINK_LIBRARIES(testbed reactphysics3d openglframework glfw ${GLFW_LIBRARIES}) diff --git a/examples/common/Box.cpp b/testbed/common/Box.cpp similarity index 56% rename from examples/common/Box.cpp rename to testbed/common/Box.cpp index d0c7a1df..dc5e9d94 100644 --- a/examples/common/Box.cpp +++ b/testbed/common/Box.cpp @@ -31,35 +31,89 @@ // Initialize static variables openglframework::VertexBufferObject Box::mVBOVertices(GL_ARRAY_BUFFER); -openglframework::VertexBufferObject Box::mVBOIndices(GL_ELEMENT_ARRAY_BUFFER); -bool Box::areVBOsCreated = false; -VertexData Box::mCubeVertices[8] = { - {openglframework::Vector3(1,1,1),openglframework::Vector3(1,1,1),openglframework::Color(1,0,0,1)}, - {openglframework::Vector3(-1,1,1),openglframework::Vector3(-1,1,1),openglframework::Color(1,0,0,1)}, - {openglframework::Vector3(-1,-1,1),openglframework::Vector3(-1,-1,1),openglframework::Color(1,0,0,1)}, - {openglframework::Vector3(1,-1,1),openglframework::Vector3(1,-1,1),openglframework::Color(1,0,0,1)}, - {openglframework::Vector3(1,-1,-1),openglframework::Vector3(1,-1,-1),openglframework::Color(1,0,0,1)}, - {openglframework::Vector3(-1,-1,-1),openglframework::Vector3(-1,-1,-1),openglframework::Color(1,0,0,1)}, - {openglframework::Vector3(-1,1,-1),openglframework::Vector3(-1,1,-1),openglframework::Color(1,0,0,1)}, - {openglframework::Vector3(1,1,-1),openglframework::Vector3(1,1,-1),openglframework::Color(1,0,0,1)} +openglframework::VertexBufferObject Box::mVBONormals(GL_ARRAY_BUFFER); +openglframework::VertexArrayObject Box::mVAO; +int Box::totalNbBoxes = 0; +GLfloat Box::mCubeVertices[108] = { + -1.0f,-1.0f,-1.0f, // triangle 1 : begin + -1.0f,-1.0f, 1.0f, + -1.0f, 1.0f, 1.0f, // triangle 1 : end + 1.0f, 1.0f,-1.0f, // triangle 2 : begin + -1.0f,-1.0f,-1.0f, + -1.0f, 1.0f,-1.0f, // triangle 2 : end + 1.0f,-1.0f, 1.0f, + -1.0f,-1.0f,-1.0f, + 1.0f,-1.0f,-1.0f, + 1.0f, 1.0f,-1.0f, + 1.0f,-1.0f,-1.0f, + -1.0f,-1.0f,-1.0f, + -1.0f,-1.0f,-1.0f, + -1.0f, 1.0f, 1.0f, + -1.0f, 1.0f,-1.0f, + 1.0f,-1.0f, 1.0f, + -1.0f,-1.0f, 1.0f, + -1.0f,-1.0f,-1.0f, + -1.0f, 1.0f, 1.0f, + -1.0f,-1.0f, 1.0f, + 1.0f,-1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, + 1.0f,-1.0f,-1.0f, + 1.0f, 1.0f,-1.0f, + 1.0f,-1.0f,-1.0f, + 1.0f, 1.0f, 1.0f, + 1.0f,-1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, + 1.0f, 1.0f,-1.0f, + -1.0f, 1.0f,-1.0f, + 1.0f, 1.0f, 1.0f, + -1.0f, 1.0f,-1.0f, + -1.0f, 1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, + -1.0f, 1.0f, 1.0f, + 1.0f,-1.0f, 1.0f +}; +GLfloat Box::mCubeNormals[108] = { + -1.0f, 0.0f, 0.0f, // triangle 1 : begin + -1.0f, 0.0f, 0.0f, + -1.0f, 0.0f, 0.0f, // triangle 1 : end + 0.0f, 0.0f,-1.0f, // triangle 2 : begin + 0.0f, 0.0f,-1.0f, + 0.0f, 0.0f,-1.0f, // triangle 2 : end + 0.0f,-1.0f, 0.0f, + 0.0f,-1.0f, 0.0f, + 0.0f,-1.0f, 0.0f,// + 0.0f, 0.0f,-1.0f, + 0.0f, 0.0f,-1.0f, + 0.0f, 0.0f,-1.0f,// + -1.0f, 0.0f, 0.0f, + -1.0f, 0.0f, 0.0f, + -1.0f, 0.0f,0.0f,// + 0.0f,-1.0f, 0.0f, + 0.0f,-1.0f, 0.0f, + 0.0f,-1.0f, 0.0f,// + 0.0f, 0.0f, 1.0f, + 0.0f, 0.0f, 1.0f, + 0.0f, 0.0f, 1.0f,// + 1.0f, 0.0f, 0.0f, + 1.0f, 0.0f, 0.0f, + 1.0f, 0.0f, 0.0f,// + 1.0f, 0.0f, 0.0f, + 1.0f, 0.0f, 0.0f, + 1.0f, 0.0f, 0.0f,// + 0.0f, 1.0f, 0.0f, + 0.0f, 1.0f, 0.0f, + 0.0f, 1.0f, 0.0f,// + 0.0f, 1.0f, 0.0f, + 0.0f, 1.0f, 0.0f, + 0.0f, 1.0f, 0.0f,// + 0.0f, 0.0f, 1.0f, + 0.0f, 0.0f, 1.0f, + 0.0f, 0.0f, 1.0f// }; -GLuint Box::mCubeIndices[36] = { 0, 1, 2, - 2, 3, 0, - 7, 4, 5, - 5, 6, 7, - 6, 5, 2, - 2, 1, 6, - 7, 0, 3, - 3, 4, 7, - 7, 6, 1, - 1, 0, 7, - 3, 2, 5, - 5, 4, 3}; - // Constructor Box::Box(const openglframework::Vector3& size, const openglframework::Vector3 &position, reactphysics3d::CollisionWorld* world) - : openglframework::Object3D(), mColor(0.5f, 0.5f, 0.5f, 1.0f) { + : openglframework::Object3D() { // Initialize the size of the box mSize[0] = size.x * 0.5f; @@ -85,25 +139,30 @@ Box::Box(const openglframework::Vector3& size, const openglframework::Vector3 &p rp3d::Quaternion initOrientation = rp3d::Quaternion::identity(); rp3d::Transform transform(initPosition, initOrientation); + mPreviousTransform = transform; + // Create a rigid body in the dynamics world - mRigidBody = world->createCollisionBody(transform); + mBody = world->createCollisionBody(transform); // Add the collision shape to the body - mRigidBody->addCollisionShape(collisionShape, rp3d::Transform::identity()); + mBody->addCollisionShape(collisionShape, rp3d::Transform::identity()); // If the Vertex Buffer object has not been created yet - if (!areVBOsCreated) { + if (totalNbBoxes == 0) { + // Create the Vertex Buffer - createVBO(); + createVBOAndVAO(); } + totalNbBoxes++; + mTransformMatrix = mTransformMatrix * mScalingMatrix; } // Constructor -Box::Box(const openglframework::Vector3& size, const openglframework::Vector3 &position, +Box::Box(const openglframework::Vector3& size, const openglframework::Vector3& position, float mass, reactphysics3d::DynamicsWorld* world) - : openglframework::Object3D(), mColor(0.5f, 0.5f, 0.5f, 1.0f) { + : openglframework::Object3D() { // Initialize the size of the box mSize[0] = size.x * 0.5f; @@ -129,111 +188,145 @@ Box::Box(const openglframework::Vector3& size, const openglframework::Vector3 &p rp3d::Quaternion initOrientation = rp3d::Quaternion::identity(); rp3d::Transform transform(initPosition, initOrientation); + mPreviousTransform = transform; + // Create a rigid body in the dynamics world rp3d::RigidBody* body = world->createRigidBody(transform); // Add the collision shape to the body body->addCollisionShape(collisionShape, rp3d::Transform::identity(), mass); - mRigidBody = body; + mBody = body; // If the Vertex Buffer object has not been created yet - if (!areVBOsCreated) { + if (totalNbBoxes == 0) { + // Create the Vertex Buffer - createVBO(); + createVBOAndVAO(); } + totalNbBoxes++; + mTransformMatrix = mTransformMatrix * mScalingMatrix; } // Destructor Box::~Box() { + if (totalNbBoxes == 1) { + + // Destroy the VBOs and VAO + mVBOVertices.destroy(); + mVBONormals.destroy(); + mVAO.destroy(); + } + + totalNbBoxes--; } // Render the cube at the correct position and with the correct orientation void Box::render(openglframework::Shader& shader, const openglframework::Matrix4& worldToCameraMatrix) { + // Bind the VAO + mVAO.bind(); + // Bind the shader shader.bind(); + mVBOVertices.bind(); + // Set the model to camera matrix - const openglframework::Matrix4 localToCameraMatrix = worldToCameraMatrix * mTransformMatrix; - shader.setMatrix4x4Uniform("localToCameraMatrix", localToCameraMatrix); + shader.setMatrix4x4Uniform("localToWorldMatrix", mTransformMatrix); + shader.setMatrix4x4Uniform("worldToCameraMatrix", worldToCameraMatrix); // Set the normal matrix (inverse transpose of the 3x3 upper-left sub matrix of the // model-view matrix) + const openglframework::Matrix4 localToCameraMatrix = worldToCameraMatrix * mTransformMatrix; const openglframework::Matrix3 normalMatrix = localToCameraMatrix.getUpperLeft3x3Matrix().getInverse().getTranspose(); - shader.setMatrix3x3Uniform("normalMatrix", normalMatrix); + shader.setMatrix3x3Uniform("normalMatrix", normalMatrix, false); // Set the vertex color - openglframework::Vector4 color(mColor.r, mColor.g, mColor.b, mColor.a); - shader.setVector4Uniform("vertexColor", color); + openglframework::Color currentColor = mBody->isSleeping() ? mSleepingColor : mColor; + openglframework::Vector4 color(currentColor.r, currentColor.g, currentColor.b, currentColor.a); + shader.setVector4Uniform("vertexColor", color, false); - // Bind the vertices VBO - mVBOVertices.bind(); + // Get the location of shader attribute variables + GLint vertexPositionLoc = shader.getAttribLocation("vertexPosition"); + GLint vertexNormalLoc = shader.getAttribLocation("vertexNormal", false); - // Enable the vertex, normal and color arrays - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_COLOR_ARRAY); - glEnableClientState(GL_NORMAL_ARRAY); + glEnableVertexAttribArray(vertexPositionLoc); + glVertexAttribPointer(vertexPositionLoc, 3, GL_FLOAT, GL_FALSE, 0, NULL); - // Set the arrays pointers - glVertexPointer(3, GL_FLOAT, sizeof(VertexData), MEMBER_OFFSET(VertexData, position)); - glNormalPointer(GL_FLOAT, sizeof(VertexData), MEMBER_OFFSET(VertexData, normal)); - glColorPointer(3, GL_FLOAT, sizeof(VertexData), MEMBER_OFFSET(VertexData, color)); + mVBONormals.bind(); - // Bind the indices VBO - mVBOIndices.bind(); + if (vertexNormalLoc != -1) glEnableVertexAttribArray(vertexNormalLoc); + if (vertexNormalLoc != -1) glVertexAttribPointer(vertexNormalLoc, 3, GL_FLOAT, GL_FALSE, 0, NULL); // Draw the geometry of the box - glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, (char*)NULL); + glDrawArrays(GL_TRIANGLES, 0, 36); - // Unbind the VBOs + glDisableVertexAttribArray(vertexPositionLoc); + if (vertexNormalLoc != -1) glDisableVertexAttribArray(vertexNormalLoc); + + mVBONormals.unbind(); mVBOVertices.unbind(); - mVBOIndices.unbind(); - // Disable the arrays - glDisableClientState(GL_VERTEX_ARRAY); - glDisableClientState(GL_COLOR_ARRAY); - glDisableClientState(GL_NORMAL_ARRAY); + // Unbind the VAO + mVAO.unbind(); // Unbind the shader shader.unbind(); } -// Update the transform matrix of the box -void Box::updateTransform() { - // Get the interpolated transform of the rigid body - rp3d::Transform transform = mRigidBody->getInterpolatedTransform(); - - // Compute the transform used for rendering the box - rp3d::decimal matrix[16]; - transform.getOpenGLMatrix(matrix); - openglframework::Matrix4 newMatrix(matrix[0], matrix[4], matrix[8], matrix[12], - matrix[1], matrix[5], matrix[9], matrix[13], - matrix[2], matrix[6], matrix[10], matrix[14], - matrix[3], matrix[7], matrix[11], matrix[15]); - - // Apply the scaling matrix to have the correct box dimensions - mTransformMatrix = newMatrix * mScalingMatrix; -} // Create the Vertex Buffer Objects used to render to box with OpenGL. /// We create two VBOs (one for vertices and one for indices) to render all the boxes /// in the simulation. -void Box::createVBO() { +void Box::createVBOAndVAO() { - // Create the VBOs + // Create the VBO for the vertices data mVBOVertices.create(); - mVBOIndices.create(); - - // Copy the data into the VBOs + mVBOVertices.bind(); mVBOVertices.copyDataIntoVBO(sizeof(mCubeVertices), mCubeVertices, GL_STATIC_DRAW); - mVBOIndices.copyDataIntoVBO(sizeof(mCubeIndices), mCubeIndices, GL_STATIC_DRAW); + mVBOVertices.unbind(); - areVBOsCreated = true; + // Create th VBO for the normals data + mVBONormals.create(); + mVBONormals.bind(); + mVBONormals.copyDataIntoVBO(sizeof(mCubeNormals), mCubeNormals, GL_STATIC_DRAW); + mVBONormals.unbind(); + + // Create the VAO for both VBOs + mVAO.create(); + mVAO.bind(); + + // Bind the VBO of vertices + mVBOVertices.bind(); + + // Bind the VBO of indices + mVBONormals.bind(); + + // Unbind the VAO + mVAO.unbind(); +} + +// Reset the transform +void Box::resetTransform(const rp3d::Transform& transform) { + + // Reset the transform + mBody->setTransform(transform); + + mBody->setIsSleeping(false); + + // Reset the velocity of the rigid body + rp3d::RigidBody* rigidBody = dynamic_cast(mBody); + if (rigidBody != NULL) { + rigidBody->setLinearVelocity(rp3d::Vector3(0, 0, 0)); + rigidBody->setAngularVelocity(rp3d::Vector3(0, 0, 0)); + } + + updateTransform(1.0f); } diff --git a/examples/common/Box.h b/testbed/common/Box.h similarity index 64% rename from examples/common/Box.h rename to testbed/common/Box.h index 2cc333d8..a879c92a 100644 --- a/examples/common/Box.h +++ b/testbed/common/Box.h @@ -29,22 +29,10 @@ // Libraries #include "openglframework.h" #include "reactphysics3d.h" - -// Structure VertexData -struct VertexData { - - /// Vertex position - openglframework::Vector3 position; - - /// Vertex normal - openglframework::Vector3 normal; - - // Vertex color - openglframework::Color color; -}; +#include "PhysicsObject.h" // Class Box -class Box : public openglframework::Object3D { +class Box : public openglframework::Object3D, public PhysicsObject { private : @@ -53,34 +41,31 @@ class Box : public openglframework::Object3D { /// Size of each side of the box float mSize[3]; - /// Rigid body used to simulate the dynamics of the box - rp3d::CollisionBody* mRigidBody; - /// Scaling matrix (applied to a cube to obtain the correct box dimensions) openglframework::Matrix4 mScalingMatrix; /// Vertex Buffer Object for the vertices data used to render the box with OpenGL static openglframework::VertexBufferObject mVBOVertices; - /// Vertex Buffer Object for the indices used to render the box with OpenGL - static openglframework::VertexBufferObject mVBOIndices; + /// Vertex Buffer Object for the normales used to render the box with OpenGL + static openglframework::VertexBufferObject mVBONormals; - /// Vertex data for each vertex of the cube (used to render the box) - static VertexData mCubeVertices[8]; + /// Vertex Array Object for the vertex data + static openglframework::VertexArrayObject mVAO; - /// Indices of the cube (used to render the box) - static GLuint mCubeIndices[36]; + /// Vertices coordinates of the triangles of the box + static GLfloat mCubeVertices[108]; - /// True if the VBOs have already been created - static bool areVBOsCreated; + /// Vertices normals of the triangles of the box + static GLfloat mCubeNormals[108]; - /// Main color of the box - openglframework::Color mColor; + /// Total number of boxes created + static int totalNbBoxes; // -------------------- Methods -------------------- // - /// Create a Vertex Buffer Object to render to box with OpenGL - static void createVBO(); + /// Create a the VAO and VBOs to render to box with OpenGL + static void createVBOAndVAO(); public : @@ -97,35 +82,20 @@ class Box : public openglframework::Object3D { /// Destructor ~Box(); - /// Return a pointer to the collision body of the box - reactphysics3d::CollisionBody* getCollisionBody(); - - /// Return a pointer to the rigid body of the box - reactphysics3d::RigidBody* getRigidBody(); - - /// Update the transform matrix of the box - void updateTransform(); - /// Render the cube at the correct position and with the correct orientation void render(openglframework::Shader& shader, const openglframework::Matrix4& worldToCameraMatrix); - /// Set the color of the box - void setColor(const openglframework::Color& color); + /// Set the position of the box + void resetTransform(const rp3d::Transform& transform); + + /// Update the transform matrix of the object + virtual void updateTransform(float interpolationFactor); }; -// Return a pointer to the collision body of the box -inline rp3d::CollisionBody* Box::getCollisionBody() { - return mRigidBody; +// Update the transform matrix of the object +inline void Box::updateTransform(float interpolationFactor) { + mTransformMatrix = computeTransform(interpolationFactor, mScalingMatrix); } -// Return a pointer to the rigid body of the box -inline rp3d::RigidBody* Box::getRigidBody() { - return dynamic_cast(mRigidBody); -} - -// Set the color of the box -inline void Box::setColor(const openglframework::Color& color) { - mColor = color; -} #endif diff --git a/examples/common/Capsule.cpp b/testbed/common/Capsule.cpp similarity index 57% rename from examples/common/Capsule.cpp rename to testbed/common/Capsule.cpp index faf8484e..b65146d4 100644 --- a/examples/common/Capsule.cpp +++ b/testbed/common/Capsule.cpp @@ -26,6 +26,13 @@ // Libraries #include "Capsule.h" +openglframework::VertexBufferObject Capsule::mVBOVertices(GL_ARRAY_BUFFER); +openglframework::VertexBufferObject Capsule::mVBONormals(GL_ARRAY_BUFFER); +openglframework::VertexBufferObject Capsule::mVBOTextureCoords(GL_ARRAY_BUFFER); +openglframework::VertexBufferObject Capsule::mVBOIndices(GL_ELEMENT_ARRAY_BUFFER); +openglframework::VertexArrayObject Capsule::mVAO; +int Capsule::totalNbCapsules = 0; + // Constructor Capsule::Capsule(float radius, float height, const openglframework::Vector3& position, reactphysics3d::CollisionWorld* world, @@ -57,13 +64,22 @@ Capsule::Capsule(float radius, float height, const openglframework::Vector3& pos rp3d::Quaternion initOrientation = rp3d::Quaternion::identity(); rp3d::Transform transform(initPosition, initOrientation); + mPreviousTransform = transform; + // Create a rigid body corresponding in the dynamics world - mRigidBody = world->createCollisionBody(transform); + mBody = world->createCollisionBody(transform); // Add a collision shape to the body and specify the mass of the shape - mRigidBody->addCollisionShape(collisionShape, rp3d::Transform::identity()); + mBody->addCollisionShape(collisionShape, rp3d::Transform::identity()); mTransformMatrix = mTransformMatrix * mScalingMatrix; + + // Create the VBOs and VAO + if (totalNbCapsules == 0) { + createVBOAndVAO(); + } + + totalNbCapsules++; } // Constructor @@ -103,16 +119,35 @@ Capsule::Capsule(float radius, float height, const openglframework::Vector3& pos // Add a collision shape to the body and specify the mass of the shape body->addCollisionShape(collisionShape, rp3d::Transform::identity(), mass); - mRigidBody = body; + mBody = body; mTransformMatrix = mTransformMatrix * mScalingMatrix; + + // Create the VBOs and VAO + if (totalNbCapsules == 0) { + createVBOAndVAO(); + } + + totalNbCapsules++; } // Destructor Capsule::~Capsule() { - // Destroy the mesh - destroy(); + if (totalNbCapsules == 1) { + + // Destroy the mesh + destroy(); + + // Destroy the VBOs and VAO + mVBOIndices.destroy(); + mVBOVertices.destroy(); + mVBONormals.destroy(); + mVBOTextureCoords.destroy(); + mVAO.destroy(); + } + + totalNbCapsules--; } // Render the sphere at the correct position and with the correct orientation @@ -123,57 +158,126 @@ void Capsule::render(openglframework::Shader& shader, shader.bind(); // Set the model to camera matrix - const openglframework::Matrix4 localToCameraMatrix = worldToCameraMatrix * mTransformMatrix; - shader.setMatrix4x4Uniform("localToCameraMatrix", localToCameraMatrix); + shader.setMatrix4x4Uniform("localToWorldMatrix", mTransformMatrix); + shader.setMatrix4x4Uniform("worldToCameraMatrix", worldToCameraMatrix); // Set the normal matrix (inverse transpose of the 3x3 upper-left sub matrix of the // model-view matrix) + const openglframework::Matrix4 localToCameraMatrix = worldToCameraMatrix * mTransformMatrix; const openglframework::Matrix3 normalMatrix = localToCameraMatrix.getUpperLeft3x3Matrix().getInverse().getTranspose(); - shader.setMatrix3x3Uniform("normalMatrix", normalMatrix); + shader.setMatrix3x3Uniform("normalMatrix", normalMatrix, false); - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_NORMAL_ARRAY); - if (hasTexture()) { - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - } + // Set the vertex color + openglframework::Color currentColor = mBody->isSleeping() ? mSleepingColor : mColor; + openglframework::Vector4 color(currentColor.r, currentColor.g, currentColor.b, currentColor.a); + shader.setVector4Uniform("vertexColor", color, false); - glVertexPointer(3, GL_FLOAT, 0, getVerticesPointer()); - glNormalPointer(GL_FLOAT, 0, getNormalsPointer()); - if(hasTexture()) { - glTexCoordPointer(2, GL_FLOAT, 0, getUVTextureCoordinatesPointer()); - } + // Bind the VAO + mVAO.bind(); + + mVBOVertices.bind(); + + // Get the location of shader attribute variables + GLint vertexPositionLoc = shader.getAttribLocation("vertexPosition"); + GLint vertexNormalLoc = shader.getAttribLocation("vertexNormal", false); + + glEnableVertexAttribArray(vertexPositionLoc); + glVertexAttribPointer(vertexPositionLoc, 3, GL_FLOAT, GL_FALSE, 0, (char*)NULL); + + mVBONormals.bind(); + + if (vertexNormalLoc != -1) glVertexAttribPointer(vertexNormalLoc, 3, GL_FLOAT, GL_FALSE, 0, (char*)NULL); + if (vertexNormalLoc != -1) glEnableVertexAttribArray(vertexNormalLoc); // For each part of the mesh for (unsigned int i=0; igetInterpolatedTransform(); + // Create the VBO for the vertices data + mVBOVertices.create(); + mVBOVertices.bind(); + size_t sizeVertices = mVertices.size() * sizeof(openglframework::Vector3); + mVBOVertices.copyDataIntoVBO(sizeVertices, getVerticesPointer(), GL_STATIC_DRAW); + mVBOVertices.unbind(); - // Compute the transform used for rendering the sphere - rp3d::decimal matrix[16]; - transform.getOpenGLMatrix(matrix); - openglframework::Matrix4 newMatrix(matrix[0], matrix[4], matrix[8], matrix[12], - matrix[1], matrix[5], matrix[9], matrix[13], - matrix[2], matrix[6], matrix[10], matrix[14], - matrix[3], matrix[7], matrix[11], matrix[15]); + // Create the VBO for the normals data + mVBONormals.create(); + mVBONormals.bind(); + size_t sizeNormals = mNormals.size() * sizeof(openglframework::Vector3); + mVBONormals.copyDataIntoVBO(sizeNormals, getNormalsPointer(), GL_STATIC_DRAW); + mVBONormals.unbind(); - // Apply the scaling matrix to have the correct sphere dimensions - mTransformMatrix = newMatrix * mScalingMatrix; + if (hasTexture()) { + // Create the VBO for the texture co data + mVBOTextureCoords.create(); + mVBOTextureCoords.bind(); + size_t sizeTextureCoords = mUVs.size() * sizeof(openglframework::Vector2); + mVBOTextureCoords.copyDataIntoVBO(sizeTextureCoords, getUVTextureCoordinatesPointer(), GL_STATIC_DRAW); + mVBOTextureCoords.unbind(); + } + + // Create th VBO for the indices data + mVBOIndices.create(); + mVBOIndices.bind(); + size_t sizeIndices = mIndices[0].size() * sizeof(uint); + mVBOIndices.copyDataIntoVBO(sizeIndices, getIndicesPointer(), GL_STATIC_DRAW); + mVBOIndices.unbind(); + + // Create the VAO for both VBOs + mVAO.create(); + mVAO.bind(); + + // Bind the VBO of vertices + mVBOVertices.bind(); + + // Bind the VBO of normals + mVBONormals.bind(); + + if (hasTexture()) { + // Bind the VBO of texture coords + mVBOTextureCoords.bind(); + } + + // Bind the VBO of indices + mVBOIndices.bind(); + + // Unbind the VAO + mVAO.unbind(); +} + +// Reset the transform +void Capsule::resetTransform(const rp3d::Transform& transform) { + + // Reset the transform + mBody->setTransform(transform); + + mBody->setIsSleeping(false); + + // Reset the velocity of the rigid body + rp3d::RigidBody* rigidBody = dynamic_cast(mBody); + if (rigidBody != NULL) { + rigidBody->setLinearVelocity(rp3d::Vector3(0, 0, 0)); + rigidBody->setAngularVelocity(rp3d::Vector3(0, 0, 0)); + } + + updateTransform(1.0f); } diff --git a/examples/common/Capsule.h b/testbed/common/Capsule.h similarity index 70% rename from examples/common/Capsule.h rename to testbed/common/Capsule.h index 2ce438d1..c1a37779 100644 --- a/examples/common/Capsule.h +++ b/testbed/common/Capsule.h @@ -29,9 +29,10 @@ // Libraries #include "openglframework.h" #include "reactphysics3d.h" +#include "PhysicsObject.h" // Class Sphere -class Capsule : public openglframework::Mesh { +class Capsule : public openglframework::Mesh, public PhysicsObject { private : @@ -43,14 +44,35 @@ class Capsule : public openglframework::Mesh { /// Height of the capsule float mHeight; - /// Rigid body used to simulate the dynamics of the sphere - rp3d::CollisionBody* mRigidBody; - /// Scaling matrix (applied to a sphere to obtain the correct sphere dimensions) openglframework::Matrix4 mScalingMatrix; + /// Previous transform (for interpolation) + rp3d::Transform mPreviousTransform; + + /// Vertex Buffer Object for the vertices data + static openglframework::VertexBufferObject mVBOVertices; + + /// Vertex Buffer Object for the normals data + static openglframework::VertexBufferObject mVBONormals; + + /// Vertex Buffer Object for the texture coords + static openglframework::VertexBufferObject mVBOTextureCoords; + + /// Vertex Buffer Object for the indices + static openglframework::VertexBufferObject mVBOIndices; + + /// Vertex Array Object for the vertex data + static openglframework::VertexArrayObject mVAO; + + // Total number of capsules created + static int totalNbCapsules; + // -------------------- Methods -------------------- // + // Create the Vertex Buffer Objects used to render with OpenGL. + void createVBOAndVAO(); + public : // -------------------- Methods -------------------- // @@ -67,28 +89,20 @@ class Capsule : public openglframework::Mesh { /// Destructor ~Capsule(); - /// Return a pointer to the collision body of the box - reactphysics3d::CollisionBody* getCollisionBody(); - - /// Return a pointer to the rigid body of the box - reactphysics3d::RigidBody* getRigidBody(); - - /// Update the transform matrix of the sphere - void updateTransform(); - /// Render the sphere at the correct position and with the correct orientation void render(openglframework::Shader& shader, const openglframework::Matrix4& worldToCameraMatrix); + + /// Set the position of the box + void resetTransform(const rp3d::Transform& transform); + + /// Update the transform matrix of the object + virtual void updateTransform(float interpolationFactor); }; -// Return a pointer to the collision body of the box -inline rp3d::CollisionBody* Capsule::getCollisionBody() { - return mRigidBody; -} - -// Return a pointer to the rigid body of the box -inline rp3d::RigidBody* Capsule::getRigidBody() { - return dynamic_cast(mRigidBody); +// Update the transform matrix of the object +inline void Capsule::updateTransform(float interpolationFactor) { + mTransformMatrix = computeTransform(interpolationFactor, mScalingMatrix); } #endif diff --git a/examples/common/Cone.cpp b/testbed/common/Cone.cpp similarity index 58% rename from examples/common/Cone.cpp rename to testbed/common/Cone.cpp index 9e3f24b6..6657ed9d 100644 --- a/examples/common/Cone.cpp +++ b/testbed/common/Cone.cpp @@ -26,6 +26,13 @@ // Libraries #include "Cone.h" +openglframework::VertexBufferObject Cone::mVBOVertices(GL_ARRAY_BUFFER); +openglframework::VertexBufferObject Cone::mVBONormals(GL_ARRAY_BUFFER); +openglframework::VertexBufferObject Cone::mVBOTextureCoords(GL_ARRAY_BUFFER); +openglframework::VertexBufferObject Cone::mVBOIndices(GL_ELEMENT_ARRAY_BUFFER); +openglframework::VertexArrayObject Cone::mVAO; +int Cone::totalNbCones = 0; + // Constructor Cone::Cone(float radius, float height, const openglframework::Vector3 &position, reactphysics3d::CollisionWorld* world, @@ -57,13 +64,22 @@ Cone::Cone(float radius, float height, const openglframework::Vector3 &position, rp3d::Quaternion initOrientation = rp3d::Quaternion::identity(); rp3d::Transform transform(initPosition, initOrientation); + mPreviousTransform = transform; + // Create a rigid body corresponding to the cone in the dynamics world - mRigidBody = world->createCollisionBody(transform); + mBody = world->createCollisionBody(transform); // Add a collision shape to the body and specify the mass of the shape - mRigidBody->addCollisionShape(collisionShape, rp3d::Transform::identity()); + mBody->addCollisionShape(collisionShape, rp3d::Transform::identity()); mTransformMatrix = mTransformMatrix * mScalingMatrix; + + // Create the VBOs and VAO + if (totalNbCones == 0) { + createVBOAndVAO(); + } + + totalNbCones++; } // Constructor @@ -103,16 +119,34 @@ Cone::Cone(float radius, float height, const openglframework::Vector3 &position, // Add a collision shape to the body and specify the mass of the shape body->addCollisionShape(collisionShape, rp3d::Transform::identity(), mass); - mRigidBody = body; + mBody = body; mTransformMatrix = mTransformMatrix * mScalingMatrix; + + // Create the VBOs and VAO + if (totalNbCones == 0) { + createVBOAndVAO(); + } + + totalNbCones++; } // Destructor Cone::~Cone() { - // Destroy the mesh - destroy(); + if (totalNbCones == 1) { + // Destroy the mesh + destroy(); + + // Destroy the VBOs and VAO + mVBOIndices.destroy(); + mVBOVertices.destroy(); + mVBONormals.destroy(); + mVBOTextureCoords.destroy(); + mVAO.destroy(); + } + + totalNbCones--; } // Render the cone at the correct position and with the correct orientation @@ -123,57 +157,126 @@ void Cone::render(openglframework::Shader& shader, shader.bind(); // Set the model to camera matrix - const openglframework::Matrix4 localToCameraMatrix = worldToCameraMatrix * mTransformMatrix; - shader.setMatrix4x4Uniform("localToCameraMatrix", localToCameraMatrix); + shader.setMatrix4x4Uniform("localToWorldMatrix", mTransformMatrix); + shader.setMatrix4x4Uniform("worldToCameraMatrix", worldToCameraMatrix); // Set the normal matrix (inverse transpose of the 3x3 upper-left sub matrix of the // model-view matrix) + const openglframework::Matrix4 localToCameraMatrix = worldToCameraMatrix * mTransformMatrix; const openglframework::Matrix3 normalMatrix = localToCameraMatrix.getUpperLeft3x3Matrix().getInverse().getTranspose(); - shader.setMatrix3x3Uniform("normalMatrix", normalMatrix); + shader.setMatrix3x3Uniform("normalMatrix", normalMatrix, false); - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_NORMAL_ARRAY); - if (hasTexture()) { - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - } + // Set the vertex color + openglframework::Vector4 color(mColor.r, mColor.g, mColor.b, mColor.a); + shader.setVector4Uniform("vertexColor", color, false); - glVertexPointer(3, GL_FLOAT, 0, getVerticesPointer()); - glNormalPointer(GL_FLOAT, 0, getNormalsPointer()); - if(hasTexture()) { - glTexCoordPointer(2, GL_FLOAT, 0, getUVTextureCoordinatesPointer()); - } + // Bind the VAO + mVAO.bind(); + + mVBOVertices.bind(); + + // Get the location of shader attribute variables + GLint vertexPositionLoc = shader.getAttribLocation("vertexPosition"); + GLint vertexNormalLoc = shader.getAttribLocation("vertexNormal", false); + + glEnableVertexAttribArray(vertexPositionLoc); + glVertexAttribPointer(vertexPositionLoc, 3, GL_FLOAT, GL_FALSE, 0, (char*)NULL); + + mVBONormals.bind(); + + if (vertexNormalLoc != -1) glVertexAttribPointer(vertexNormalLoc, 3, GL_FLOAT, GL_FALSE, 0, (char*)NULL); + if (vertexNormalLoc != -1) glEnableVertexAttribArray(vertexNormalLoc); // For each part of the mesh for (unsigned int i=0; igetInterpolatedTransform(); + // Create the VBO for the vertices data + mVBOVertices.create(); + mVBOVertices.bind(); + size_t sizeVertices = mVertices.size() * sizeof(openglframework::Vector3); + mVBOVertices.copyDataIntoVBO(sizeVertices, getVerticesPointer(), GL_STATIC_DRAW); + mVBOVertices.unbind(); - // Compute the transform used for rendering the cone - rp3d::decimal matrix[16]; - transform.getOpenGLMatrix(matrix); - openglframework::Matrix4 newMatrix(matrix[0], matrix[4], matrix[8], matrix[12], - matrix[1], matrix[5], matrix[9], matrix[13], - matrix[2], matrix[6], matrix[10], matrix[14], - matrix[3], matrix[7], matrix[11], matrix[15]); + // Create the VBO for the normals data + mVBONormals.create(); + mVBONormals.bind(); + size_t sizeNormals = mNormals.size() * sizeof(openglframework::Vector3); + mVBONormals.copyDataIntoVBO(sizeNormals, getNormalsPointer(), GL_STATIC_DRAW); + mVBONormals.unbind(); - // Apply the scaling matrix to have the correct cone dimensions - mTransformMatrix = newMatrix * mScalingMatrix; + if (hasTexture()) { + // Create the VBO for the texture co data + mVBOTextureCoords.create(); + mVBOTextureCoords.bind(); + size_t sizeTextureCoords = mUVs.size() * sizeof(openglframework::Vector2); + mVBOTextureCoords.copyDataIntoVBO(sizeTextureCoords, getUVTextureCoordinatesPointer(), GL_STATIC_DRAW); + mVBOTextureCoords.unbind(); + } + + // Create th VBO for the indices data + mVBOIndices.create(); + mVBOIndices.bind(); + size_t sizeIndices = mIndices[0].size() * sizeof(uint); + mVBOIndices.copyDataIntoVBO(sizeIndices, getIndicesPointer(), GL_STATIC_DRAW); + mVBOIndices.unbind(); + + // Create the VAO for both VBOs + mVAO.create(); + mVAO.bind(); + + // Bind the VBO of vertices + mVBOVertices.bind(); + + // Bind the VBO of normals + mVBONormals.bind(); + + if (hasTexture()) { + // Bind the VBO of texture coords + mVBOTextureCoords.bind(); + } + + // Bind the VBO of indices + mVBOIndices.bind(); + + // Unbind the VAO + mVAO.unbind(); } + +// Reset the transform +void Cone::resetTransform(const rp3d::Transform& transform) { + + // Reset the transform + mBody->setTransform(transform); + + mBody->setIsSleeping(false); + + // Reset the velocity of the rigid body + rp3d::RigidBody* rigidBody = dynamic_cast(mBody); + if (rigidBody != NULL) { + rigidBody->setLinearVelocity(rp3d::Vector3(0, 0, 0)); + rigidBody->setAngularVelocity(rp3d::Vector3(0, 0, 0)); + } + + updateTransform(1.0f); +} + diff --git a/examples/common/Cone.h b/testbed/common/Cone.h similarity index 70% rename from examples/common/Cone.h rename to testbed/common/Cone.h index d3002eab..1d256752 100644 --- a/examples/common/Cone.h +++ b/testbed/common/Cone.h @@ -29,9 +29,10 @@ // Libraries #include "openglframework.h" #include "reactphysics3d.h" +#include "PhysicsObject.h" // Class Cone -class Cone : public openglframework::Mesh { +class Cone : public openglframework::Mesh, public PhysicsObject { private : @@ -43,14 +44,35 @@ class Cone : public openglframework::Mesh { /// Height of the cone float mHeight; - /// Rigid body used to simulate the dynamics of the cone - rp3d::CollisionBody* mRigidBody; - /// Scaling matrix (applied to a sphere to obtain the correct cone dimensions) openglframework::Matrix4 mScalingMatrix; + /// Previous transform (for interpolation) + rp3d::Transform mPreviousTransform; + + /// Vertex Buffer Object for the vertices data + static openglframework::VertexBufferObject mVBOVertices; + + /// Vertex Buffer Object for the normals data + static openglframework::VertexBufferObject mVBONormals; + + /// Vertex Buffer Object for the texture coords + static openglframework::VertexBufferObject mVBOTextureCoords; + + /// Vertex Buffer Object for the indices + static openglframework::VertexBufferObject mVBOIndices; + + /// Vertex Array Object for the vertex data + static openglframework::VertexArrayObject mVAO; + + // Total number of cones created + static int totalNbCones; + // -------------------- Methods -------------------- // + // Create the Vertex Buffer Objects used to render with OpenGL. + void createVBOAndVAO(); + public : // -------------------- Methods -------------------- // @@ -66,28 +88,20 @@ class Cone : public openglframework::Mesh { /// Destructor ~Cone(); - /// Return a pointer to the collision body of the box - reactphysics3d::CollisionBody* getCollisionBody(); - - /// Return a pointer to the rigid body of the box - reactphysics3d::RigidBody* getRigidBody(); - - /// Update the transform matrix of the cone - void updateTransform(); - /// Render the cone at the correct position and with the correct orientation void render(openglframework::Shader& shader, const openglframework::Matrix4& worldToCameraMatrix); + + /// Set the position of the box + void resetTransform(const rp3d::Transform& transform); + + /// Update the transform matrix of the object + virtual void updateTransform(float interpolationFactor); }; -// Return a pointer to the collision body of the box -inline rp3d::CollisionBody* Cone::getCollisionBody() { - return mRigidBody; -} - -// Return a pointer to the rigid body of the box -inline rp3d::RigidBody* Cone::getRigidBody() { - return dynamic_cast(mRigidBody); +// Update the transform matrix of the object +inline void Cone::updateTransform(float interpolationFactor) { + mTransformMatrix = computeTransform(interpolationFactor, mScalingMatrix); } #endif diff --git a/examples/common/ConvexMesh.cpp b/testbed/common/ConvexMesh.cpp similarity index 65% rename from examples/common/ConvexMesh.cpp rename to testbed/common/ConvexMesh.cpp index dbb2c28a..cd766e5a 100644 --- a/examples/common/ConvexMesh.cpp +++ b/testbed/common/ConvexMesh.cpp @@ -30,7 +30,9 @@ ConvexMesh::ConvexMesh(const openglframework::Vector3 &position, reactphysics3d::CollisionWorld* world, const std::string& meshFolderPath) - : openglframework::Mesh() { + : openglframework::Mesh(), mVBOVertices(GL_ARRAY_BUFFER), + mVBONormals(GL_ARRAY_BUFFER), mVBOTextureCoords(GL_ARRAY_BUFFER), + mVBOIndices(GL_ELEMENT_ARRAY_BUFFER) { // Load the mesh from a file openglframework::MeshReaderWriter::loadMeshFromFile(meshFolderPath + "convexmesh.obj", *this); @@ -79,18 +81,25 @@ ConvexMesh::ConvexMesh(const openglframework::Vector3 &position, rp3d::Quaternion initOrientation = rp3d::Quaternion::identity(); rp3d::Transform transform(initPosition, initOrientation); + mPreviousTransform = transform; + // Create a rigid body corresponding to the sphere in the dynamics world - mRigidBody = world->createCollisionBody(transform); + mBody = world->createCollisionBody(transform); // Add a collision shape to the body and specify the mass of the collision shape - mRigidBody->addCollisionShape(collisionShape, rp3d::Transform::identity()); + mBody->addCollisionShape(collisionShape, rp3d::Transform::identity()); + + // Create the VBOs and VAO + createVBOAndVAO(); } // Constructor ConvexMesh::ConvexMesh(const openglframework::Vector3 &position, float mass, reactphysics3d::DynamicsWorld* dynamicsWorld, const std::string& meshFolderPath) - : openglframework::Mesh() { + : openglframework::Mesh(), mVBOVertices(GL_ARRAY_BUFFER), + mVBONormals(GL_ARRAY_BUFFER), mVBOTextureCoords(GL_ARRAY_BUFFER), + mVBOIndices(GL_ELEMENT_ARRAY_BUFFER) { // Load the mesh from a file openglframework::MeshReaderWriter::loadMeshFromFile(meshFolderPath + "convexmesh.obj", *this); @@ -144,7 +153,10 @@ ConvexMesh::ConvexMesh(const openglframework::Vector3 &position, float mass, // Add a collision shape to the body and specify the mass of the collision shape body->addCollisionShape(collisionShape, rp3d::Transform::identity(), mass); - mRigidBody = body; + mBody = body; + + // Create the VBOs and VAO + createVBOAndVAO(); } // Destructor @@ -152,6 +164,13 @@ ConvexMesh::~ConvexMesh() { // Destroy the mesh destroy(); + + // Destroy the VBOs and VAO + mVBOIndices.destroy(); + mVBOVertices.destroy(); + mVBONormals.destroy(); + mVBOTextureCoords.destroy(); + mVAO.destroy(); } // Render the sphere at the correct position and with the correct orientation @@ -162,57 +181,125 @@ void ConvexMesh::render(openglframework::Shader& shader, shader.bind(); // Set the model to camera matrix - const openglframework::Matrix4 localToCameraMatrix = worldToCameraMatrix * mTransformMatrix; - shader.setMatrix4x4Uniform("localToCameraMatrix", localToCameraMatrix); + shader.setMatrix4x4Uniform("localToWorldMatrix", mTransformMatrix); + shader.setMatrix4x4Uniform("worldToCameraMatrix", worldToCameraMatrix); // Set the normal matrix (inverse transpose of the 3x3 upper-left sub matrix of the // model-view matrix) + const openglframework::Matrix4 localToCameraMatrix = worldToCameraMatrix * mTransformMatrix; const openglframework::Matrix3 normalMatrix = localToCameraMatrix.getUpperLeft3x3Matrix().getInverse().getTranspose(); - shader.setMatrix3x3Uniform("normalMatrix", normalMatrix); + shader.setMatrix3x3Uniform("normalMatrix", normalMatrix, false); - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_NORMAL_ARRAY); - if (hasTexture()) { - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - } + // Set the vertex color + openglframework::Vector4 color(mColor.r, mColor.g, mColor.b, mColor.a); + shader.setVector4Uniform("vertexColor", color, false); - glVertexPointer(3, GL_FLOAT, 0, getVerticesPointer()); - glNormalPointer(GL_FLOAT, 0, getNormalsPointer()); - if(hasTexture()) { - glTexCoordPointer(2, GL_FLOAT, 0, getUVTextureCoordinatesPointer()); - } + // Bind the VAO + mVAO.bind(); + + mVBOVertices.bind(); + + // Get the location of shader attribute variables + GLint vertexPositionLoc = shader.getAttribLocation("vertexPosition"); + GLint vertexNormalLoc = shader.getAttribLocation("vertexNormal", false); + + glEnableVertexAttribArray(vertexPositionLoc); + glVertexAttribPointer(vertexPositionLoc, 3, GL_FLOAT, GL_FALSE, 0, (char*)NULL); + + mVBONormals.bind(); + + if (vertexNormalLoc != -1) glVertexAttribPointer(vertexNormalLoc, 3, GL_FLOAT, GL_FALSE, 0, (char*)NULL); + if (vertexNormalLoc != -1) glEnableVertexAttribArray(vertexNormalLoc); // For each part of the mesh for (unsigned int i=0; igetInterpolatedTransform(); + // Create the VBO for the vertices data + mVBOVertices.create(); + mVBOVertices.bind(); + size_t sizeVertices = mVertices.size() * sizeof(openglframework::Vector3); + mVBOVertices.copyDataIntoVBO(sizeVertices, getVerticesPointer(), GL_STATIC_DRAW); + mVBOVertices.unbind(); - // Compute the transform used for rendering the sphere - rp3d::decimal matrix[16]; - transform.getOpenGLMatrix(matrix); - openglframework::Matrix4 newMatrix(matrix[0], matrix[4], matrix[8], matrix[12], - matrix[1], matrix[5], matrix[9], matrix[13], - matrix[2], matrix[6], matrix[10], matrix[14], - matrix[3], matrix[7], matrix[11], matrix[15]); + // Create the VBO for the normals data + mVBONormals.create(); + mVBONormals.bind(); + size_t sizeNormals = mNormals.size() * sizeof(openglframework::Vector3); + mVBONormals.copyDataIntoVBO(sizeNormals, getNormalsPointer(), GL_STATIC_DRAW); + mVBONormals.unbind(); - // Apply the scaling matrix to have the correct sphere dimensions - mTransformMatrix = newMatrix; + if (hasTexture()) { + // Create the VBO for the texture co data + mVBOTextureCoords.create(); + mVBOTextureCoords.bind(); + size_t sizeTextureCoords = mUVs.size() * sizeof(openglframework::Vector2); + mVBOTextureCoords.copyDataIntoVBO(sizeTextureCoords, getUVTextureCoordinatesPointer(), GL_STATIC_DRAW); + mVBOTextureCoords.unbind(); + } + + // Create th VBO for the indices data + mVBOIndices.create(); + mVBOIndices.bind(); + size_t sizeIndices = mIndices[0].size() * sizeof(uint); + mVBOIndices.copyDataIntoVBO(sizeIndices, getIndicesPointer(), GL_STATIC_DRAW); + mVBOIndices.unbind(); + + // Create the VAO for both VBOs + mVAO.create(); + mVAO.bind(); + + // Bind the VBO of vertices + mVBOVertices.bind(); + + // Bind the VBO of normals + mVBONormals.bind(); + + if (hasTexture()) { + // Bind the VBO of texture coords + mVBOTextureCoords.bind(); + } + + // Bind the VBO of indices + mVBOIndices.bind(); + + // Unbind the VAO + mVAO.unbind(); +} + +// Reset the transform +void ConvexMesh::resetTransform(const rp3d::Transform& transform) { + + // Reset the transform + mBody->setTransform(transform); + + mBody->setIsSleeping(false); + + // Reset the velocity of the rigid body + rp3d::RigidBody* rigidBody = dynamic_cast(mBody); + if (rigidBody != NULL) { + rigidBody->setLinearVelocity(rp3d::Vector3(0, 0, 0)); + rigidBody->setAngularVelocity(rp3d::Vector3(0, 0, 0)); + } + + updateTransform(1.0f); } diff --git a/examples/common/ConvexMesh.h b/testbed/common/ConvexMesh.h similarity index 69% rename from examples/common/ConvexMesh.h rename to testbed/common/ConvexMesh.h index f97ec5ec..4a4cdcdb 100644 --- a/examples/common/ConvexMesh.h +++ b/testbed/common/ConvexMesh.h @@ -29,19 +29,38 @@ // Libraries #include "openglframework.h" #include "reactphysics3d.h" +#include "PhysicsObject.h" // Class ConvexMesh -class ConvexMesh : public openglframework::Mesh { +class ConvexMesh : public openglframework::Mesh, public PhysicsObject { private : // -------------------- Attributes -------------------- // - /// Rigid body used to simulate the dynamics of the mesh - rp3d::CollisionBody* mRigidBody; + /// Previous transform (for interpolation) + rp3d::Transform mPreviousTransform; + + /// Vertex Buffer Object for the vertices data + openglframework::VertexBufferObject mVBOVertices; + + /// Vertex Buffer Object for the normals data + openglframework::VertexBufferObject mVBONormals; + + /// Vertex Buffer Object for the texture coords + openglframework::VertexBufferObject mVBOTextureCoords; + + /// Vertex Buffer Object for the indices + openglframework::VertexBufferObject mVBOIndices; + + /// Vertex Array Object for the vertex data + openglframework::VertexArrayObject mVAO; // -------------------- Methods -------------------- // + // Create the Vertex Buffer Objects used to render with OpenGL. + void createVBOAndVAO(); + public : // -------------------- Methods -------------------- // @@ -57,28 +76,20 @@ class ConvexMesh : public openglframework::Mesh { /// Destructor ~ConvexMesh(); - /// Return a pointer to the collision body of the box - reactphysics3d::CollisionBody* getCollisionBody(); - - /// Return a pointer to the rigid body of the box - reactphysics3d::RigidBody* getRigidBody(); - - /// Update the transform matrix of the mesh - void updateTransform(); - /// Render the mesh at the correct position and with the correct orientation void render(openglframework::Shader& shader, const openglframework::Matrix4& worldToCameraMatrix); + + /// Set the position of the box + void resetTransform(const rp3d::Transform& transform); + + /// Update the transform matrix of the object + virtual void updateTransform(float interpolationFactor); }; -// Return a pointer to the collision body of the box -inline rp3d::CollisionBody* ConvexMesh::getCollisionBody() { - return mRigidBody; -} - -// Return a pointer to the rigid body of the box -inline rp3d::RigidBody* ConvexMesh::getRigidBody() { - return dynamic_cast(mRigidBody); +// Update the transform matrix of the object +inline void ConvexMesh::updateTransform(float interpolationFactor) { + mTransformMatrix = computeTransform(interpolationFactor, openglframework::Matrix4::identity()); } #endif diff --git a/examples/common/Cylinder.cpp b/testbed/common/Cylinder.cpp similarity index 58% rename from examples/common/Cylinder.cpp rename to testbed/common/Cylinder.cpp index 7c2b7690..2aa45923 100644 --- a/examples/common/Cylinder.cpp +++ b/testbed/common/Cylinder.cpp @@ -26,6 +26,13 @@ // Libraries #include "Cylinder.h" +openglframework::VertexBufferObject Cylinder::mVBOVertices(GL_ARRAY_BUFFER); +openglframework::VertexBufferObject Cylinder::mVBONormals(GL_ARRAY_BUFFER); +openglframework::VertexBufferObject Cylinder::mVBOTextureCoords(GL_ARRAY_BUFFER); +openglframework::VertexBufferObject Cylinder::mVBOIndices(GL_ELEMENT_ARRAY_BUFFER); +openglframework::VertexArrayObject Cylinder::mVAO; +int Cylinder::totalNbCylinders = 0; + // Constructor Cylinder::Cylinder(float radius, float height, const openglframework::Vector3& position, reactphysics3d::CollisionWorld* world, @@ -57,13 +64,22 @@ Cylinder::Cylinder(float radius, float height, const openglframework::Vector3& p rp3d::Quaternion initOrientation = rp3d::Quaternion::identity(); rp3d::Transform transform(initPosition, initOrientation); + mPreviousTransform = transform; + // Create a rigid body corresponding to the cylinder in the dynamics world - mRigidBody = world->createCollisionBody(transform); + mBody = world->createCollisionBody(transform); // Add a collision shape to the body and specify the mass of the shape - mRigidBody->addCollisionShape(collisionShape, rp3d::Transform::identity()); + mBody->addCollisionShape(collisionShape, rp3d::Transform::identity()); mTransformMatrix = mTransformMatrix * mScalingMatrix; + + // Create the VBOs and VAO + if (totalNbCylinders == 0) { + createVBOAndVAO(); + } + + totalNbCylinders++; } // Constructor @@ -105,14 +121,33 @@ Cylinder::Cylinder(float radius, float height, const openglframework::Vector3& p mTransformMatrix = mTransformMatrix * mScalingMatrix; - mRigidBody = body; + mBody = body; + + // Create the VBOs and VAO + if (totalNbCylinders == 0) { + createVBOAndVAO(); + } + + totalNbCylinders++; } // Destructor Cylinder::~Cylinder() { - // Destroy the mesh - destroy(); + if (totalNbCylinders == 1) { + + // Destroy the mesh + destroy(); + + // Destroy the VBOs and VAO + mVBOIndices.destroy(); + mVBOVertices.destroy(); + mVBONormals.destroy(); + mVBOTextureCoords.destroy(); + mVAO.destroy(); + } + + totalNbCylinders--; } // Render the cylinder at the correct position and with the correct orientation @@ -123,57 +158,125 @@ void Cylinder::render(openglframework::Shader& shader, shader.bind(); // Set the model to camera matrix - const openglframework::Matrix4 localToCameraMatrix = worldToCameraMatrix * mTransformMatrix; - shader.setMatrix4x4Uniform("localToCameraMatrix", localToCameraMatrix); + shader.setMatrix4x4Uniform("localToWorldMatrix", mTransformMatrix); + shader.setMatrix4x4Uniform("worldToCameraMatrix", worldToCameraMatrix); // Set the normal matrix (inverse transpose of the 3x3 upper-left sub matrix of the // model-view matrix) + const openglframework::Matrix4 localToCameraMatrix = worldToCameraMatrix * mTransformMatrix; const openglframework::Matrix3 normalMatrix = localToCameraMatrix.getUpperLeft3x3Matrix().getInverse().getTranspose(); - shader.setMatrix3x3Uniform("normalMatrix", normalMatrix); + shader.setMatrix3x3Uniform("normalMatrix", normalMatrix, false); - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_NORMAL_ARRAY); - if (hasTexture()) { - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - } + // Set the vertex color + openglframework::Vector4 color(mColor.r, mColor.g, mColor.b, mColor.a); + shader.setVector4Uniform("vertexColor", color, false); - glVertexPointer(3, GL_FLOAT, 0, getVerticesPointer()); - glNormalPointer(GL_FLOAT, 0, getNormalsPointer()); - if(hasTexture()) { - glTexCoordPointer(2, GL_FLOAT, 0, getUVTextureCoordinatesPointer()); - } + // Bind the VAO + mVAO.bind(); + + mVBOVertices.bind(); + + // Get the location of shader attribute variables + GLint vertexPositionLoc = shader.getAttribLocation("vertexPosition"); + GLint vertexNormalLoc = shader.getAttribLocation("vertexNormal", false); + + glEnableVertexAttribArray(vertexPositionLoc); + glVertexAttribPointer(vertexPositionLoc, 3, GL_FLOAT, GL_FALSE, 0, (char*)NULL); + + mVBONormals.bind(); + + if (vertexNormalLoc != -1) glVertexAttribPointer(vertexNormalLoc, 3, GL_FLOAT, GL_FALSE, 0, (char*)NULL); + if (vertexNormalLoc != -1) glEnableVertexAttribArray(vertexNormalLoc); // For each part of the mesh for (unsigned int i=0; igetInterpolatedTransform(); + // Create the VBO for the vertices data + mVBOVertices.create(); + mVBOVertices.bind(); + size_t sizeVertices = mVertices.size() * sizeof(openglframework::Vector3); + mVBOVertices.copyDataIntoVBO(sizeVertices, getVerticesPointer(), GL_STATIC_DRAW); + mVBOVertices.unbind(); - // Compute the transform used for rendering the cylinder - rp3d::decimal matrix[16]; - transform.getOpenGLMatrix(matrix); - openglframework::Matrix4 newMatrix(matrix[0], matrix[4], matrix[8], matrix[12], - matrix[1], matrix[5], matrix[9], matrix[13], - matrix[2], matrix[6], matrix[10], matrix[14], - matrix[3], matrix[7], matrix[11], matrix[15]); + // Create the VBO for the normals data + mVBONormals.create(); + mVBONormals.bind(); + size_t sizeNormals = mNormals.size() * sizeof(openglframework::Vector3); + mVBONormals.copyDataIntoVBO(sizeNormals, getNormalsPointer(), GL_STATIC_DRAW); + mVBONormals.unbind(); - // Apply the scaling matrix to have the correct cylinder dimensions - mTransformMatrix = newMatrix * mScalingMatrix; + if (hasTexture()) { + // Create the VBO for the texture co data + mVBOTextureCoords.create(); + mVBOTextureCoords.bind(); + size_t sizeTextureCoords = mUVs.size() * sizeof(openglframework::Vector2); + mVBOTextureCoords.copyDataIntoVBO(sizeTextureCoords, getUVTextureCoordinatesPointer(), GL_STATIC_DRAW); + mVBOTextureCoords.unbind(); + } + + // Create th VBO for the indices data + mVBOIndices.create(); + mVBOIndices.bind(); + size_t sizeIndices = mIndices[0].size() * sizeof(uint); + mVBOIndices.copyDataIntoVBO(sizeIndices, getIndicesPointer(), GL_STATIC_DRAW); + mVBOIndices.unbind(); + + // Create the VAO for both VBOs + mVAO.create(); + mVAO.bind(); + + // Bind the VBO of vertices + mVBOVertices.bind(); + + // Bind the VBO of normals + mVBONormals.bind(); + + if (hasTexture()) { + // Bind the VBO of texture coords + mVBOTextureCoords.bind(); + } + + // Bind the VBO of indices + mVBOIndices.bind(); + + // Unbind the VAO + mVAO.unbind(); +} + +// Reset the transform +void Cylinder::resetTransform(const rp3d::Transform& transform) { + + // Reset the transform + mBody->setTransform(transform); + + mBody->setIsSleeping(false); + + // Reset the velocity of the rigid body + rp3d::RigidBody* rigidBody = dynamic_cast(mBody); + if (rigidBody != NULL) { + rigidBody->setLinearVelocity(rp3d::Vector3(0, 0, 0)); + rigidBody->setAngularVelocity(rp3d::Vector3(0, 0, 0)); + } + + updateTransform(1.0f); } diff --git a/examples/common/Cylinder.h b/testbed/common/Cylinder.h similarity index 68% rename from examples/common/Cylinder.h rename to testbed/common/Cylinder.h index 9236fe20..17379e7c 100644 --- a/examples/common/Cylinder.h +++ b/testbed/common/Cylinder.h @@ -29,9 +29,10 @@ // Libraries #include "openglframework.h" #include "reactphysics3d.h" +#include "PhysicsObject.h" // Class Cylinder -class Cylinder : public openglframework::Mesh { +class Cylinder : public openglframework::Mesh, public PhysicsObject { private : @@ -43,14 +44,35 @@ class Cylinder : public openglframework::Mesh { /// Height of the cylinder float mHeight; - /// Rigid body used to simulate the dynamics of the cylinder - rp3d::CollisionBody* mRigidBody; - /// Scaling matrix (applied to a sphere to obtain the correct cylinder dimensions) openglframework::Matrix4 mScalingMatrix; + /// Previous transform (for interpolation) + rp3d::Transform mPreviousTransform; + + /// Vertex Buffer Object for the vertices data + static openglframework::VertexBufferObject mVBOVertices; + + /// Vertex Buffer Object for the normals data + static openglframework::VertexBufferObject mVBONormals; + + /// Vertex Buffer Object for the texture coords + static openglframework::VertexBufferObject mVBOTextureCoords; + + /// Vertex Buffer Object for the indices + static openglframework::VertexBufferObject mVBOIndices; + + /// Vertex Array Object for the vertex data + static openglframework::VertexArrayObject mVAO; + + // Total number of capsules created + static int totalNbCylinders; + // -------------------- Methods -------------------- // + // Create the Vertex Buffer Objects used to render with OpenGL. + void createVBOAndVAO(); + public : // -------------------- Methods -------------------- // @@ -61,33 +83,25 @@ class Cylinder : public openglframework::Mesh { /// Constructor Cylinder(float radius, float height, const openglframework::Vector3& position, - float mass, rp3d::DynamicsWorld* dynamicsWorld, const std::string &meshFolderPath); + float mass, rp3d::DynamicsWorld* dynamicsWorld, const std::string &meshFolderPath); /// Destructor ~Cylinder(); - /// Return a pointer to the collision body of the box - reactphysics3d::CollisionBody* getCollisionBody(); - - /// Return a pointer to the rigid body of the box - reactphysics3d::RigidBody* getRigidBody(); - - /// Update the transform matrix of the cylinder - void updateTransform(); - /// Render the cylinder at the correct position and with the correct orientation void render(openglframework::Shader& shader, const openglframework::Matrix4& worldToCameraMatrix); + + /// Set the position of the box + void resetTransform(const rp3d::Transform& transform); + + /// Update the transform matrix of the object + virtual void updateTransform(float interpolationFactor); }; -// Return a pointer to the collision body of the box -inline rp3d::CollisionBody* Cylinder::getCollisionBody() { - return mRigidBody; -} - -// Return a pointer to the rigid body of the box -inline rp3d::RigidBody* Cylinder::getRigidBody() { - return dynamic_cast(mRigidBody); +// Update the transform matrix of the object +inline void Cylinder::updateTransform(float interpolationFactor) { + mTransformMatrix = computeTransform(interpolationFactor, mScalingMatrix); } #endif diff --git a/examples/common/Dumbbell.cpp b/testbed/common/Dumbbell.cpp similarity index 66% rename from examples/common/Dumbbell.cpp rename to testbed/common/Dumbbell.cpp index a358db61..630630ee 100644 --- a/examples/common/Dumbbell.cpp +++ b/testbed/common/Dumbbell.cpp @@ -26,6 +26,13 @@ // Libraries #include "Dumbbell.h" +openglframework::VertexBufferObject Dumbbell::mVBOVertices(GL_ARRAY_BUFFER); +openglframework::VertexBufferObject Dumbbell::mVBONormals(GL_ARRAY_BUFFER); +openglframework::VertexBufferObject Dumbbell::mVBOTextureCoords(GL_ARRAY_BUFFER); +openglframework::VertexBufferObject Dumbbell::mVBOIndices(GL_ELEMENT_ARRAY_BUFFER); +openglframework::VertexArrayObject Dumbbell::mVAO; +int Dumbbell::totalNbDumbbells = 0; + // Constructor Dumbbell::Dumbbell(const openglframework::Vector3 &position, reactphysics3d::DynamicsWorld* dynamicsWorld, const std::string& meshFolderPath) @@ -64,6 +71,8 @@ Dumbbell::Dumbbell(const openglframework::Vector3 &position, rp3d::Quaternion initOrientation(angleAroundX, 0, 0); rp3d::Transform transformBody(initPosition, initOrientation); + mPreviousTransform = transformBody; + // Initial transform of the first sphere collision shape of the dumbbell (in local-space) rp3d::Transform transformSphereShape1(rp3d::Vector3(0, 4.0, 0), rp3d::Quaternion::identity()); @@ -84,6 +93,13 @@ Dumbbell::Dumbbell(const openglframework::Vector3 &position, mBody = body; mTransformMatrix = mTransformMatrix * mScalingMatrix; + + // Create the VBOs and VAO + if (totalNbDumbbells == 0) { + createVBOAndVAO(); + } + + totalNbDumbbells++; } // Constructor @@ -142,13 +158,32 @@ Dumbbell::Dumbbell(const openglframework::Vector3 &position, mBody->addCollisionShape(cylinderCollisionShape, transformCylinderShape); mTransformMatrix = mTransformMatrix * mScalingMatrix; + + // Create the VBOs and VAO + if (totalNbDumbbells == 0) { + createVBOAndVAO(); + } + + totalNbDumbbells++; } // Destructor Dumbbell::~Dumbbell() { - // Destroy the mesh - destroy(); + if (totalNbDumbbells == 1) { + + // Destroy the mesh + destroy(); + + // Destroy the VBOs and VAO + mVBOIndices.destroy(); + mVBOVertices.destroy(); + mVBONormals.destroy(); + mVBOTextureCoords.destroy(); + mVAO.destroy(); + } + + totalNbDumbbells--; } // Render the sphere at the correct position and with the correct orientation @@ -159,58 +194,125 @@ void Dumbbell::render(openglframework::Shader& shader, shader.bind(); // Set the model to camera matrix - const openglframework::Matrix4 localToCameraMatrix = worldToCameraMatrix * mTransformMatrix; - shader.setMatrix4x4Uniform("localToCameraMatrix", localToCameraMatrix); + shader.setMatrix4x4Uniform("localToWorldMatrix", mTransformMatrix); + shader.setMatrix4x4Uniform("worldToCameraMatrix", worldToCameraMatrix); // Set the normal matrix (inverse transpose of the 3x3 upper-left sub matrix of the // model-view matrix) + const openglframework::Matrix4 localToCameraMatrix = worldToCameraMatrix * mTransformMatrix; const openglframework::Matrix3 normalMatrix = localToCameraMatrix.getUpperLeft3x3Matrix().getInverse().getTranspose(); - shader.setMatrix3x3Uniform("normalMatrix", normalMatrix); + shader.setMatrix3x3Uniform("normalMatrix", normalMatrix, false); - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_NORMAL_ARRAY); - if (hasTexture()) { - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - } + // Set the vertex color + openglframework::Vector4 color(mColor.r, mColor.g, mColor.b, mColor.a); + shader.setVector4Uniform("vertexColor", color, false); - glVertexPointer(3, GL_FLOAT, 0, getVerticesPointer()); - glNormalPointer(GL_FLOAT, 0, getNormalsPointer()); - if(hasTexture()) { - glTexCoordPointer(2, GL_FLOAT, 0, getUVTextureCoordinatesPointer()); - } + // Bind the VAO + mVAO.bind(); + + mVBOVertices.bind(); + + // Get the location of shader attribute variables + GLint vertexPositionLoc = shader.getAttribLocation("vertexPosition"); + GLint vertexNormalLoc = shader.getAttribLocation("vertexNormal", false); + + glEnableVertexAttribArray(vertexPositionLoc); + glVertexAttribPointer(vertexPositionLoc, 3, GL_FLOAT, GL_FALSE, 0, (char*)NULL); + + mVBONormals.bind(); + + if (vertexNormalLoc != -1) glEnableVertexAttribArray(vertexNormalLoc); + if (vertexNormalLoc != -1) glVertexAttribPointer(vertexNormalLoc, 3, GL_FLOAT, GL_FALSE, 0, (char*)NULL); // For each part of the mesh for (unsigned int i=0; igetInterpolatedTransform(); + // Create the VBO for the vertices data + mVBOVertices.create(); + mVBOVertices.bind(); + size_t sizeVertices = mVertices.size() * sizeof(openglframework::Vector3); + mVBOVertices.copyDataIntoVBO(sizeVertices, getVerticesPointer(), GL_STATIC_DRAW); + mVBOVertices.unbind(); - // Compute the transform used for rendering the sphere - rp3d::decimal matrix[16]; - transform.getOpenGLMatrix(matrix); - openglframework::Matrix4 newMatrix(matrix[0], matrix[4], matrix[8], matrix[12], - matrix[1], matrix[5], matrix[9], matrix[13], - matrix[2], matrix[6], matrix[10], matrix[14], - matrix[3], matrix[7], matrix[11], matrix[15]); + // Create the VBO for the normals data + mVBONormals.create(); + mVBONormals.bind(); + size_t sizeNormals = mNormals.size() * sizeof(openglframework::Vector3); + mVBONormals.copyDataIntoVBO(sizeNormals, getNormalsPointer(), GL_STATIC_DRAW); + mVBONormals.unbind(); - // Apply the scaling matrix to have the correct sphere dimensions - mTransformMatrix = newMatrix * mScalingMatrix; + if (hasTexture()) { + // Create the VBO for the texture co data + mVBOTextureCoords.create(); + mVBOTextureCoords.bind(); + size_t sizeTextureCoords = mUVs.size() * sizeof(openglframework::Vector2); + mVBOTextureCoords.copyDataIntoVBO(sizeTextureCoords, getUVTextureCoordinatesPointer(), GL_STATIC_DRAW); + mVBOTextureCoords.unbind(); + } + + // Create th VBO for the indices data + mVBOIndices.create(); + mVBOIndices.bind(); + size_t sizeIndices = mIndices[0].size() * sizeof(uint); + mVBOIndices.copyDataIntoVBO(sizeIndices, getIndicesPointer(), GL_STATIC_DRAW); + mVBOIndices.unbind(); + + // Create the VAO for both VBOs + mVAO.create(); + mVAO.bind(); + + // Bind the VBO of vertices + mVBOVertices.bind(); + + // Bind the VBO of normals + mVBONormals.bind(); + + if (hasTexture()) { + // Bind the VBO of texture coords + mVBOTextureCoords.bind(); + } + + // Bind the VBO of indices + mVBOIndices.bind(); + + // Unbind the VAO + mVAO.unbind(); } +// Reset the transform +void Dumbbell::resetTransform(const rp3d::Transform& transform) { + + // Reset the transform + mBody->setTransform(transform); + + mBody->setIsSleeping(false); + + // Reset the velocity of the rigid body + rp3d::RigidBody* rigidBody = dynamic_cast(mBody); + if (rigidBody != NULL) { + rigidBody->setLinearVelocity(rp3d::Vector3(0, 0, 0)); + rigidBody->setAngularVelocity(rp3d::Vector3(0, 0, 0)); + } + + updateTransform(1.0f); +} diff --git a/examples/common/Dumbbell.h b/testbed/common/Dumbbell.h similarity index 69% rename from examples/common/Dumbbell.h rename to testbed/common/Dumbbell.h index ff673951..cde7cea9 100644 --- a/examples/common/Dumbbell.h +++ b/testbed/common/Dumbbell.h @@ -29,9 +29,10 @@ // Libraries #include "openglframework.h" #include "reactphysics3d.h" +#include "PhysicsObject.h" // Class Sphere -class Dumbbell : public openglframework::Mesh { +class Dumbbell : public openglframework::Mesh, public PhysicsObject { private : @@ -40,14 +41,35 @@ class Dumbbell : public openglframework::Mesh { /// Radius of the spheres float mRadius; - /// Rigid body used to simulate the dynamics of the sphere - rp3d::CollisionBody* mBody; - /// Scaling matrix (applied to a sphere to obtain the correct sphere dimensions) openglframework::Matrix4 mScalingMatrix; + /// Previous transform (for interpolation) + rp3d::Transform mPreviousTransform; + + /// Vertex Buffer Object for the vertices data + static openglframework::VertexBufferObject mVBOVertices; + + /// Vertex Buffer Object for the normals data + static openglframework::VertexBufferObject mVBONormals; + + /// Vertex Buffer Object for the texture coords + static openglframework::VertexBufferObject mVBOTextureCoords; + + /// Vertex Buffer Object for the indices + static openglframework::VertexBufferObject mVBOIndices; + + /// Vertex Array Object for the vertex data + static openglframework::VertexArrayObject mVAO; + + // Total number of capsules created + static int totalNbDumbbells; + // -------------------- Methods -------------------- // + // Create the Vertex Buffer Objects used to render with OpenGL. + void createVBOAndVAO(); + public : // -------------------- Methods -------------------- // @@ -64,28 +86,20 @@ class Dumbbell : public openglframework::Mesh { /// Destructor ~Dumbbell(); - /// Return a pointer to the rigid body - rp3d::RigidBody* getRigidBody(); - - /// Return a pointer to the body - rp3d::CollisionBody* getCollisionBody(); - - /// Update the transform matrix of the sphere - void updateTransform(); - /// Render the sphere at the correct position and with the correct orientation void render(openglframework::Shader& shader, const openglframework::Matrix4& worldToCameraMatrix); + + /// Set the position of the box + void resetTransform(const rp3d::Transform& transform); + + /// Update the transform matrix of the object + virtual void updateTransform(float interpolationFactor); }; -// Return a pointer to the rigid body of the sphere -inline rp3d::RigidBody* Dumbbell::getRigidBody() { - return dynamic_cast(mBody); -} - -// Return a pointer to the body -inline rp3d::CollisionBody* Dumbbell::getCollisionBody() { - return mBody; +// Update the transform matrix of the object +inline void Dumbbell::updateTransform(float interpolationFactor) { + mTransformMatrix = computeTransform(interpolationFactor, mScalingMatrix); } #endif diff --git a/examples/common/Line.cpp b/testbed/common/Line.cpp similarity index 90% rename from examples/common/Line.cpp rename to testbed/common/Line.cpp index 9263c1d6..f95aedaa 100644 --- a/examples/common/Line.cpp +++ b/testbed/common/Line.cpp @@ -47,7 +47,12 @@ void Line::render(openglframework::Shader& shader, shader.bind(); // Set the model to camera matrix - shader.setMatrix4x4Uniform("localToCameraMatrix", worldToCameraMatrix); + shader.setMatrix4x4Uniform("localToWorldMatrix", openglframework::Matrix4::identity()); + shader.setMatrix4x4Uniform("worldToCameraMatrix", worldToCameraMatrix); + + // Set the vertex color + openglframework::Vector4 color(1, 0, 0, 1); + shader.setVector4Uniform("vertexColor", color, false); glBegin(GL_LINES); glVertex3f(mWorldPoint1.x, mWorldPoint1.y, mWorldPoint1.z); diff --git a/examples/common/Line.h b/testbed/common/Line.h similarity index 97% rename from examples/common/Line.h rename to testbed/common/Line.h index 41de3266..5f6b0d1f 100644 --- a/examples/common/Line.h +++ b/testbed/common/Line.h @@ -58,9 +58,6 @@ class Line : public openglframework::Object3D { /// Return the second point of the line openglframework::Vector3 getPoint2() const; - /// Update the transform matrix of the sphere - void updateTransform(); - /// Render the line at the correct position and with the correct orientation void render(openglframework::Shader& shader, const openglframework::Matrix4& worldToCameraMatrix); diff --git a/testbed/common/PhysicsObject.cpp b/testbed/common/PhysicsObject.cpp new file mode 100644 index 00000000..3bc7be77 --- /dev/null +++ b/testbed/common/PhysicsObject.cpp @@ -0,0 +1,59 @@ +/******************************************************************************** +* ReactPhysics3D physics library, http://www.reactphysics3d.com * +* Copyright (c) 2010-2015 Daniel Chappuis * +********************************************************************************* +* * +* This software is provided 'as-is', without any express or implied warranty. * +* In no event will the authors be held liable for any damages arising from the * +* use of this software. * +* * +* Permission is granted to anyone to use this software for any purpose, * +* including commercial applications, and to alter it and redistribute it * +* freely, subject to the following restrictions: * +* * +* 1. The origin of this software must not be misrepresented; you must not claim * +* that you wrote the original software. If you use this software in a * +* product, an acknowledgment in the product documentation would be * +* appreciated but is not required. * +* * +* 2. Altered source versions must be plainly marked as such, and must not be * +* misrepresented as being the original software. * +* * +* 3. This notice may not be removed or altered from any source distribution. * +* * +********************************************************************************/ + +// Libraries +#include "PhysicsObject.h" + +/// Constructor +PhysicsObject::PhysicsObject() { + + mColor = openglframework::Color(1, 1, 1, 1); + mSleepingColor = openglframework::Color(1, 0, 0, 1); +} + +// Compute the new transform matrix +openglframework::Matrix4 PhysicsObject::computeTransform(float interpolationFactor, + const openglframework::Matrix4& scalingMatrix) { + + // Get the transform of the rigid body + rp3d::Transform transform = mBody->getTransform(); + + // Interpolate the transform between the previous one and the new one + rp3d::Transform interpolatedTransform = rp3d::Transform::interpolateTransforms(mPreviousTransform, + transform, + interpolationFactor); + mPreviousTransform = transform; + + // Compute the transform used for rendering the box + rp3d::decimal matrix[16]; + interpolatedTransform.getOpenGLMatrix(matrix); + openglframework::Matrix4 newMatrix(matrix[0], matrix[4], matrix[8], matrix[12], + matrix[1], matrix[5], matrix[9], matrix[13], + matrix[2], matrix[6], matrix[10], matrix[14], + matrix[3], matrix[7], matrix[11], matrix[15]); + + // Apply the scaling matrix to have the correct box dimensions + return newMatrix * scalingMatrix; +} diff --git a/testbed/common/PhysicsObject.h b/testbed/common/PhysicsObject.h new file mode 100644 index 00000000..1e1ef69d --- /dev/null +++ b/testbed/common/PhysicsObject.h @@ -0,0 +1,96 @@ +/******************************************************************************** +* ReactPhysics3D physics library, http://www.reactphysics3d.com * +* Copyright (c) 2010-2015 Daniel Chappuis * +********************************************************************************* +* * +* This software is provided 'as-is', without any express or implied warranty. * +* In no event will the authors be held liable for any damages arising from the * +* use of this software. * +* * +* Permission is granted to anyone to use this software for any purpose, * +* including commercial applications, and to alter it and redistribute it * +* freely, subject to the following restrictions: * +* * +* 1. The origin of this software must not be misrepresented; you must not claim * +* that you wrote the original software. If you use this software in a * +* product, an acknowledgment in the product documentation would be * +* appreciated but is not required. * +* * +* 2. Altered source versions must be plainly marked as such, and must not be * +* misrepresented as being the original software. * +* * +* 3. This notice may not be removed or altered from any source distribution. * +* * +********************************************************************************/ + +#ifndef PHYSICSOBJECT_H +#define PHYSICSOBJECT_H + +// Libraries +#include "openglframework.h" +#include "reactphysics3d.h" + +// Class PhysicsObject +class PhysicsObject { + + protected: + + /// Body used to simulate the dynamics of the box + rp3d::CollisionBody* mBody; + + /// Previous transform of the body (for interpolation) + rp3d::Transform mPreviousTransform; + + /// Main color of the box + openglframework::Color mColor; + + /// Sleeping color + openglframework::Color mSleepingColor; + + // Compute the new transform matrix + openglframework::Matrix4 computeTransform(float interpolationFactor, + const openglframework::Matrix4 &scalingMatrix); + + public: + + /// Constructor + PhysicsObject(); + + /// Update the transform matrix of the object + virtual void updateTransform(float interpolationFactor)=0; + + /// Set the color of the box + void setColor(const openglframework::Color& color); + + /// Set the sleeping color of the box + void setSleepingColor(const openglframework::Color& color); + + /// Return a pointer to the collision body of the box + reactphysics3d::CollisionBody* getCollisionBody(); + + /// Return a pointer to the rigid body of the box + reactphysics3d::RigidBody* getRigidBody(); +}; + +// Set the color of the box +inline void PhysicsObject::setColor(const openglframework::Color& color) { + mColor = color; +} + +// Set the sleeping color of the box +inline void PhysicsObject::setSleepingColor(const openglframework::Color& color) { + mSleepingColor = color; +} + +// Return a pointer to the collision body of the box +inline rp3d::CollisionBody* PhysicsObject::getCollisionBody() { + return mBody; +} + +// Return a pointer to the rigid body of the box (NULL if it's not a rigid body) +inline rp3d::RigidBody* PhysicsObject::getRigidBody() { + return dynamic_cast(mBody); +} + +#endif + diff --git a/examples/common/Sphere.cpp b/testbed/common/Sphere.cpp similarity index 57% rename from examples/common/Sphere.cpp rename to testbed/common/Sphere.cpp index 56882569..61fe9671 100644 --- a/examples/common/Sphere.cpp +++ b/testbed/common/Sphere.cpp @@ -26,6 +26,13 @@ // Libraries #include "Sphere.h" +openglframework::VertexBufferObject Sphere::mVBOVertices(GL_ARRAY_BUFFER); +openglframework::VertexBufferObject Sphere::mVBONormals(GL_ARRAY_BUFFER); +openglframework::VertexBufferObject Sphere::mVBOTextureCoords(GL_ARRAY_BUFFER); +openglframework::VertexBufferObject Sphere::mVBOIndices(GL_ELEMENT_ARRAY_BUFFER); +openglframework::VertexArrayObject Sphere::mVAO; +int Sphere::totalNbSpheres = 0; + // Constructor Sphere::Sphere(float radius, const openglframework::Vector3 &position, reactphysics3d::CollisionWorld* world, @@ -57,13 +64,22 @@ Sphere::Sphere(float radius, const openglframework::Vector3 &position, rp3d::Quaternion initOrientation = rp3d::Quaternion::identity(); rp3d::Transform transform(initPosition, initOrientation); + mPreviousTransform = transform; + // Create a rigid body corresponding to the sphere in the dynamics world - mRigidBody = world->createCollisionBody(transform); + mBody = world->createCollisionBody(transform); // Add a collision shape to the body and specify the mass of the shape - mRigidBody->addCollisionShape(collisionShape, rp3d::Transform::identity()); + mBody->addCollisionShape(collisionShape, rp3d::Transform::identity()); mTransformMatrix = mTransformMatrix * mScalingMatrix; + + // Create the VBOs and VAO + if (totalNbSpheres == 0) { + createVBOAndVAO(); + } + + totalNbSpheres++; } // Constructor @@ -103,16 +119,34 @@ Sphere::Sphere(float radius, const openglframework::Vector3 &position, // Add a collision shape to the body and specify the mass of the shape body->addCollisionShape(collisionShape, rp3d::Transform::identity(), mass); - mRigidBody = body; + mBody = body; mTransformMatrix = mTransformMatrix * mScalingMatrix; + + // Create the VBOs and VAO + if (totalNbSpheres == 0) { + createVBOAndVAO(); + } + + totalNbSpheres++; } // Destructor Sphere::~Sphere() { - // Destroy the mesh - destroy(); + if (totalNbSpheres == 1) { + // Destroy the mesh + destroy(); + + // Destroy the VBOs and VAO + mVBOIndices.destroy(); + mVBOVertices.destroy(); + mVBONormals.destroy(); + mVBOTextureCoords.destroy(); + mVAO.destroy(); + } + + totalNbSpheres--; } // Render the sphere at the correct position and with the correct orientation @@ -123,57 +157,125 @@ void Sphere::render(openglframework::Shader& shader, shader.bind(); // Set the model to camera matrix - const openglframework::Matrix4 localToCameraMatrix = worldToCameraMatrix * mTransformMatrix; - shader.setMatrix4x4Uniform("localToCameraMatrix", localToCameraMatrix); + shader.setMatrix4x4Uniform("localToWorldMatrix", mTransformMatrix); + shader.setMatrix4x4Uniform("worldToCameraMatrix", worldToCameraMatrix); // Set the normal matrix (inverse transpose of the 3x3 upper-left sub matrix of the // model-view matrix) + const openglframework::Matrix4 localToCameraMatrix = worldToCameraMatrix * mTransformMatrix; const openglframework::Matrix3 normalMatrix = localToCameraMatrix.getUpperLeft3x3Matrix().getInverse().getTranspose(); - shader.setMatrix3x3Uniform("normalMatrix", normalMatrix); + shader.setMatrix3x3Uniform("normalMatrix", normalMatrix, false); - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_NORMAL_ARRAY); - if (hasTexture()) { - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - } + // Set the vertex color + openglframework::Vector4 color(mColor.r, mColor.g, mColor.b, mColor.a); + shader.setVector4Uniform("vertexColor", color, false); - glVertexPointer(3, GL_FLOAT, 0, getVerticesPointer()); - glNormalPointer(GL_FLOAT, 0, getNormalsPointer()); - if(hasTexture()) { - glTexCoordPointer(2, GL_FLOAT, 0, getUVTextureCoordinatesPointer()); - } + // Bind the VAO + mVAO.bind(); + + mVBOVertices.bind(); + + // Get the location of shader attribute variables + GLint vertexPositionLoc = shader.getAttribLocation("vertexPosition"); + GLint vertexNormalLoc = shader.getAttribLocation("vertexNormal", false); + + glEnableVertexAttribArray(vertexPositionLoc); + glVertexAttribPointer(vertexPositionLoc, 3, GL_FLOAT, GL_FALSE, 0, (char*)NULL); + + mVBONormals.bind(); + + if (vertexNormalLoc != -1) glVertexAttribPointer(vertexNormalLoc, 3, GL_FLOAT, GL_FALSE, 0, (char*)NULL); + if (vertexNormalLoc != -1) glEnableVertexAttribArray(vertexNormalLoc); // For each part of the mesh for (unsigned int i=0; igetInterpolatedTransform(); + // Create the VBO for the vertices data + mVBOVertices.create(); + mVBOVertices.bind(); + size_t sizeVertices = mVertices.size() * sizeof(openglframework::Vector3); + mVBOVertices.copyDataIntoVBO(sizeVertices, getVerticesPointer(), GL_STATIC_DRAW); + mVBOVertices.unbind(); - // Compute the transform used for rendering the sphere - rp3d::decimal matrix[16]; - transform.getOpenGLMatrix(matrix); - openglframework::Matrix4 newMatrix(matrix[0], matrix[4], matrix[8], matrix[12], - matrix[1], matrix[5], matrix[9], matrix[13], - matrix[2], matrix[6], matrix[10], matrix[14], - matrix[3], matrix[7], matrix[11], matrix[15]); + // Create the VBO for the normals data + mVBONormals.create(); + mVBONormals.bind(); + size_t sizeNormals = mNormals.size() * sizeof(openglframework::Vector3); + mVBONormals.copyDataIntoVBO(sizeNormals, getNormalsPointer(), GL_STATIC_DRAW); + mVBONormals.unbind(); - // Apply the scaling matrix to have the correct sphere dimensions - mTransformMatrix = newMatrix * mScalingMatrix; + if (hasTexture()) { + // Create the VBO for the texture co data + mVBOTextureCoords.create(); + mVBOTextureCoords.bind(); + size_t sizeTextureCoords = mUVs.size() * sizeof(openglframework::Vector2); + mVBOTextureCoords.copyDataIntoVBO(sizeTextureCoords, getUVTextureCoordinatesPointer(), GL_STATIC_DRAW); + mVBOTextureCoords.unbind(); + } + + // Create th VBO for the indices data + mVBOIndices.create(); + mVBOIndices.bind(); + size_t sizeIndices = mIndices[0].size() * sizeof(uint); + mVBOIndices.copyDataIntoVBO(sizeIndices, getIndicesPointer(), GL_STATIC_DRAW); + mVBOIndices.unbind(); + + // Create the VAO for both VBOs + mVAO.create(); + mVAO.bind(); + + // Bind the VBO of vertices + mVBOVertices.bind(); + + // Bind the VBO of normals + mVBONormals.bind(); + + if (hasTexture()) { + // Bind the VBO of texture coords + mVBOTextureCoords.bind(); + } + + // Bind the VBO of indices + mVBOIndices.bind(); + + // Unbind the VAO + mVAO.unbind(); +} + +// Reset the transform +void Sphere::resetTransform(const rp3d::Transform& transform) { + + // Reset the transform + mBody->setTransform(transform); + + mBody->setIsSleeping(false); + + // Reset the velocity of the rigid body + rp3d::RigidBody* rigidBody = dynamic_cast(mBody); + if (rigidBody != NULL) { + rigidBody->setLinearVelocity(rp3d::Vector3(0, 0, 0)); + rigidBody->setAngularVelocity(rp3d::Vector3(0, 0, 0)); + } + + updateTransform(1.0f); } diff --git a/examples/common/Sphere.h b/testbed/common/Sphere.h similarity index 69% rename from examples/common/Sphere.h rename to testbed/common/Sphere.h index 08a335f7..99a27ecc 100644 --- a/examples/common/Sphere.h +++ b/testbed/common/Sphere.h @@ -29,9 +29,10 @@ // Libraries #include "openglframework.h" #include "reactphysics3d.h" +#include "PhysicsObject.h" // Class Sphere -class Sphere : public openglframework::Mesh { +class Sphere : public openglframework::Mesh, public PhysicsObject { private : @@ -40,14 +41,35 @@ class Sphere : public openglframework::Mesh { /// Radius of the sphere float mRadius; - /// Rigid body used to simulate the dynamics of the sphere - rp3d::CollisionBody* mRigidBody; - /// Scaling matrix (applied to a sphere to obtain the correct sphere dimensions) openglframework::Matrix4 mScalingMatrix; + /// Previous transform (for interpolation) + rp3d::Transform mPreviousTransform; + + /// Vertex Buffer Object for the vertices data + static openglframework::VertexBufferObject mVBOVertices; + + /// Vertex Buffer Object for the normals data + static openglframework::VertexBufferObject mVBONormals; + + /// Vertex Buffer Object for the texture coords + static openglframework::VertexBufferObject mVBOTextureCoords; + + /// Vertex Buffer Object for the indices + static openglframework::VertexBufferObject mVBOIndices; + + /// Vertex Array Object for the vertex data + static openglframework::VertexArrayObject mVAO; + + // Total number of capsules created + static int totalNbSpheres; + // -------------------- Methods -------------------- // + // Create the Vertex Buffer Objects used to render with OpenGL. + void createVBOAndVAO(); + public : // -------------------- Methods -------------------- // @@ -63,28 +85,20 @@ class Sphere : public openglframework::Mesh { /// Destructor ~Sphere(); - /// Return a pointer to the collision body of the box - reactphysics3d::CollisionBody* getCollisionBody(); - - /// Return a pointer to the rigid body of the box - reactphysics3d::RigidBody* getRigidBody(); - - /// Update the transform matrix of the sphere - void updateTransform(); - /// Render the sphere at the correct position and with the correct orientation void render(openglframework::Shader& shader, const openglframework::Matrix4& worldToCameraMatrix); + + /// Set the position of the box + void resetTransform(const rp3d::Transform& transform); + + /// Update the transform matrix of the object + virtual void updateTransform(float interpolationFactor); }; -// Return a pointer to the collision body of the box -inline rp3d::CollisionBody* Sphere::getCollisionBody() { - return mRigidBody; -} - -// Return a pointer to the rigid body of the box -inline rp3d::RigidBody* Sphere::getRigidBody() { - return dynamic_cast(mRigidBody); +// Update the transform matrix of the object +inline void Sphere::updateTransform(float interpolationFactor) { + mTransformMatrix = computeTransform(interpolationFactor, mScalingMatrix); } #endif diff --git a/examples/common/Viewer.cpp b/testbed/common/Viewer.cpp similarity index 100% rename from examples/common/Viewer.cpp rename to testbed/common/Viewer.cpp diff --git a/examples/common/Viewer.h b/testbed/common/Viewer.h similarity index 100% rename from examples/common/Viewer.h rename to testbed/common/Viewer.h diff --git a/testbed/common/VisualContactPoint.cpp b/testbed/common/VisualContactPoint.cpp new file mode 100644 index 00000000..b0d5351d --- /dev/null +++ b/testbed/common/VisualContactPoint.cpp @@ -0,0 +1,183 @@ +/******************************************************************************** +* ReactPhysics3D physics library, http://www.reactphysics3d.com * +* Copyright (c) 2010-2015 Daniel Chappuis * +********************************************************************************* +* * +* This software is provided 'as-is', without any express or implied warranty. * +* In no event will the authors be held liable for any damages arising from the * +* use of this software. * +* * +* Permission is granted to anyone to use this software for any purpose, * +* including commercial applications, and to alter it and redistribute it * +* freely, subject to the following restrictions: * +* * +* 1. The origin of this software must not be misrepresented; you must not claim * +* that you wrote the original software. If you use this software in a * +* product, an acknowledgment in the product documentation would be * +* appreciated but is not required. * +* * +* 2. Altered source versions must be plainly marked as such, and must not be * +* misrepresented as being the original software. * +* * +* 3. This notice may not be removed or altered from any source distribution. * +* * +********************************************************************************/ + +// Libraries +#include "VisualContactPoint.h" + +// Initialization of static variables +openglframework::VertexBufferObject VisualContactPoint::mVBOVertices(GL_ARRAY_BUFFER); +openglframework::VertexBufferObject VisualContactPoint::mVBONormals(GL_ARRAY_BUFFER); +openglframework::VertexBufferObject VisualContactPoint::mVBOIndices(GL_ELEMENT_ARRAY_BUFFER); +openglframework::VertexArrayObject VisualContactPoint::mVAO; +int VisualContactPoint::mNbTotalPoints = 0; +openglframework::Mesh VisualContactPoint::mMesh; +bool VisualContactPoint::mStaticDataCreated = false; + +// Constructor +VisualContactPoint::VisualContactPoint(const openglframework::Vector3& position, + const std::string& meshFolderPath) + : mColor(1.0f, 0.0f, 0.0f, 1.0f) { + + // Initialize the position where the mesh will be rendered + translateWorld(position); +} + +// Destructor +VisualContactPoint::~VisualContactPoint() { + +} + +// Load and initialize the mesh for all the contact points +void VisualContactPoint::createStaticData(const std::string& meshFolderPath) { + + if (mStaticDataCreated) return; + + // Load the mesh from a file + openglframework::MeshReaderWriter::loadMeshFromFile(meshFolderPath + "sphere.obj", mMesh); + + // Calculate the normals of the mesh + mMesh.calculateNormals(); + + mMesh.scaleVertices(VISUAL_CONTACT_POINT_RADIUS); + + createVBOAndVAO(); + + mStaticDataCreated = true; +} + +// Destroy the mesh for the contact points +void VisualContactPoint::destroyStaticData() { + + if (!mStaticDataCreated) return; + + // Destroy the VBOs and VAO + mVBOIndices.destroy(); + mVBOVertices.destroy(); + mVBONormals.destroy(); + mVAO.destroy(); + + mMesh.destroy(); + + mStaticDataCreated = false; +} + +// Render the sphere at the correct position and with the correct orientation +void VisualContactPoint::render(openglframework::Shader& shader, + const openglframework::Matrix4& worldToCameraMatrix) { + + // Bind the VAO + mVAO.bind(); + + // Bind the shader + shader.bind(); + + mVBOVertices.bind(); + + // Set the model to camera matrix + shader.setMatrix4x4Uniform("localToWorldMatrix", mTransformMatrix); + shader.setMatrix4x4Uniform("worldToCameraMatrix", worldToCameraMatrix); + + // Set the normal matrix (inverse transpose of the 3x3 upper-left sub matrix of the + // model-view matrix) + const openglframework::Matrix4 localToCameraMatrix = worldToCameraMatrix * mTransformMatrix; + const openglframework::Matrix3 normalMatrix = + localToCameraMatrix.getUpperLeft3x3Matrix().getInverse().getTranspose(); + shader.setMatrix3x3Uniform("normalMatrix", normalMatrix, false); + + // Set the vertex color + openglframework::Vector4 color(mColor.r, mColor.g, mColor.b, mColor.a); + shader.setVector4Uniform("vertexColor", color, false); + + // Get the location of shader attribute variables + GLint vertexPositionLoc = shader.getAttribLocation("vertexPosition"); + GLint vertexNormalLoc = shader.getAttribLocation("vertexNormal", false); + + glEnableVertexAttribArray(vertexPositionLoc); + glVertexAttribPointer(vertexPositionLoc, 3, GL_FLOAT, GL_FALSE, 0, (char*)NULL); + + mVBONormals.bind(); + + if (vertexNormalLoc != -1) glVertexAttribPointer(vertexNormalLoc, 3, GL_FLOAT, GL_FALSE, 0, (char*)NULL); + if (vertexNormalLoc != -1) glEnableVertexAttribArray(vertexNormalLoc); + + // For each part of the mesh + for (unsigned int i=0; i +#include +#define _USE_MATH_DEFINES +#include +#include "imgui.h" + +#ifdef _MSC_VER +# define snprintf _snprintf +#endif + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +static const unsigned TEXT_POOL_SIZE = 8000; +static char g_textPool[TEXT_POOL_SIZE]; +static unsigned g_textPoolSize = 0; +static const char* allocText(const char* text) +{ + unsigned len = strlen(text)+1; + if (g_textPoolSize + len >= TEXT_POOL_SIZE) + return 0; + char* dst = &g_textPool[g_textPoolSize]; + memcpy(dst, text, len); + g_textPoolSize += len; + return dst; +} + +static const unsigned GFXCMD_QUEUE_SIZE = 5000; +static imguiGfxCmd g_gfxCmdQueue[GFXCMD_QUEUE_SIZE]; +static unsigned g_gfxCmdQueueSize = 0; + +static void resetGfxCmdQueue() +{ + g_gfxCmdQueueSize = 0; + g_textPoolSize = 0; +} + +static void addGfxCmdScissor(int x, int y, int w, int h) +{ + if (g_gfxCmdQueueSize >= GFXCMD_QUEUE_SIZE) + return; + imguiGfxCmd& cmd = g_gfxCmdQueue[g_gfxCmdQueueSize++]; + cmd.type = IMGUI_GFXCMD_SCISSOR; + cmd.flags = x < 0 ? 0 : 1; // on/off flag. + cmd.col = 0; + cmd.rect.x = (short)x; + cmd.rect.y = (short)y; + cmd.rect.w = (short)w; + cmd.rect.h = (short)h; +} + +static void addGfxCmdRect(float x, float y, float w, float h, unsigned int color) +{ + if (g_gfxCmdQueueSize >= GFXCMD_QUEUE_SIZE) + return; + imguiGfxCmd& cmd = g_gfxCmdQueue[g_gfxCmdQueueSize++]; + cmd.type = IMGUI_GFXCMD_RECT; + cmd.flags = 0; + cmd.col = color; + cmd.rect.x = (short)(x*8.0f); + cmd.rect.y = (short)(y*8.0f); + cmd.rect.w = (short)(w*8.0f); + cmd.rect.h = (short)(h*8.0f); + cmd.rect.r = 0; +} + +static void addGfxCmdLine(float x0, float y0, float x1, float y1, float r, unsigned int color) +{ + if (g_gfxCmdQueueSize >= GFXCMD_QUEUE_SIZE) + return; + imguiGfxCmd& cmd = g_gfxCmdQueue[g_gfxCmdQueueSize++]; + cmd.type = IMGUI_GFXCMD_LINE; + cmd.flags = 0; + cmd.col = color; + cmd.line.x0 = (short)(x0*8.0f); + cmd.line.y0 = (short)(y0*8.0f); + cmd.line.x1 = (short)(x1*8.0f); + cmd.line.y1 = (short)(y1*8.0f); + cmd.line.r = (short)(r*8.0f); +} + +static void addGfxCmdRoundedRect(float x, float y, float w, float h, float r, unsigned int color) +{ + if (g_gfxCmdQueueSize >= GFXCMD_QUEUE_SIZE) + return; + imguiGfxCmd& cmd = g_gfxCmdQueue[g_gfxCmdQueueSize++]; + cmd.type = IMGUI_GFXCMD_RECT; + cmd.flags = 0; + cmd.col = color; + cmd.rect.x = (short)(x*8.0f); + cmd.rect.y = (short)(y*8.0f); + cmd.rect.w = (short)(w*8.0f); + cmd.rect.h = (short)(h*8.0f); + cmd.rect.r = (short)(r*8.0f); +} + +static void addGfxCmdTriangle(int x, int y, int w, int h, int flags, unsigned int color) +{ + if (g_gfxCmdQueueSize >= GFXCMD_QUEUE_SIZE) + return; + imguiGfxCmd& cmd = g_gfxCmdQueue[g_gfxCmdQueueSize++]; + cmd.type = IMGUI_GFXCMD_TRIANGLE; + cmd.flags = (char)flags; + cmd.col = color; + cmd.rect.x = (short)(x*8.0f); + cmd.rect.y = (short)(y*8.0f); + cmd.rect.w = (short)(w*8.0f); + cmd.rect.h = (short)(h*8.0f); +} + +static void addGfxCmdText(int x, int y, int align, const char* text, unsigned int color) +{ + if (g_gfxCmdQueueSize >= GFXCMD_QUEUE_SIZE) + return; + imguiGfxCmd& cmd = g_gfxCmdQueue[g_gfxCmdQueueSize++]; + cmd.type = IMGUI_GFXCMD_TEXT; + cmd.flags = 0; + cmd.col = color; + cmd.text.x = (short)x; + cmd.text.y = (short)y; + cmd.text.align = (short)align; + cmd.text.text = allocText(text); +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +struct GuiState +{ + GuiState() : + left(false), leftPressed(false), leftReleased(false), + mx(-1), my(-1), scroll(0), + active(0), hot(0), hotToBe(0), isHot(false), isActive(false), wentActive(false), + dragX(0), dragY(0), dragOrig(0), widgetX(0), widgetY(0), widgetW(100), + insideCurrentScroll(false), nextItemSameLine(false), newLinePreviousX(0), + areaId(0), widgetId(0) + { + } + + bool left; + bool leftPressed, leftReleased; + int mx,my; + int scroll; + unsigned int active; + unsigned int hot; + unsigned int hotToBe; + bool isHot; + bool isActive; + bool wentActive; + int dragX, dragY; + float dragOrig; + int widgetX, widgetY, widgetW; + bool insideCurrentScroll; + bool nextItemSameLine; + int newLinePreviousX; + + unsigned int areaId; + unsigned int widgetId; +}; + +static GuiState g_state; + +inline bool anyActive() +{ + return g_state.active != 0; +} + +inline bool isActive(unsigned int id) +{ + return g_state.active == id; +} + +inline bool isHot(unsigned int id) +{ + return g_state.hot == id; +} + +inline bool inRect(int x, int y, int w, int h, bool checkScroll = true) +{ + return (!checkScroll || g_state.insideCurrentScroll) && g_state.mx >= x && g_state.mx <= x+w && g_state.my >= y && g_state.my <= y+h; +} + +inline void clearInput() +{ + g_state.leftPressed = false; + g_state.leftReleased = false; + g_state.scroll = 0; +} + +inline void clearActive() +{ + g_state.active = 0; + // mark all UI for this frame as processed + clearInput(); +} + +inline void setActive(unsigned int id) +{ + g_state.active = id; + g_state.wentActive = true; +} + +inline void setHot(unsigned int id) +{ + g_state.hotToBe = id; +} + + +static bool buttonLogic(unsigned int id, bool over) +{ + bool res = false; + // process down + if (!anyActive()) + { + if (over) + setHot(id); + if (isHot(id) && g_state.leftPressed) + setActive(id); + } + + // if button is active, then react on left up + if (isActive(id)) + { + g_state.isActive = true; + if (over) + setHot(id); + if (g_state.leftReleased) + { + if (isHot(id)) + res = true; + clearActive(); + } + } + + if (isHot(id)) + g_state.isHot = true; + + return res; +} + +static void updateInput(int mx, int my, unsigned char mbut, int scroll) +{ + bool left = (mbut & IMGUI_MBUT_LEFT) != 0; + + g_state.mx = mx; + g_state.my = my; + g_state.leftPressed = !g_state.left && left; + g_state.leftReleased = g_state.left && !left; + g_state.left = left; + + g_state.scroll = scroll; +} + +void imguiBeginFrame(int mx, int my, unsigned char mbut, int scroll) +{ + updateInput(mx,my,mbut,scroll); + + g_state.hot = g_state.hotToBe; + g_state.hotToBe = 0; + + g_state.wentActive = false; + g_state.isActive = false; + g_state.isHot = false; + + g_state.widgetX = 0; + g_state.widgetY = 0; + g_state.widgetW = 0; + + g_state.areaId = 1; + g_state.widgetId = 1; + + resetGfxCmdQueue(); +} + +void imguiEndFrame() +{ + clearInput(); +} + +const imguiGfxCmd* imguiGetRenderQueue() +{ + return g_gfxCmdQueue; +} + +int imguiGetRenderQueueSize() +{ + return g_gfxCmdQueueSize; +} + + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +static int g_scrollTop = 0; +static int g_scrollBottom = 0; +static int g_scrollRight = 0; +static int g_scrollAreaTop = 0; +static int* g_scrollVal = 0; +static int g_focusTop = 0; +static int g_focusBottom = 0; +static unsigned int g_scrollId = 0; +static bool g_insideScrollArea = false; + +bool imguiBeginScrollArea(const char* name, int x, int y, int w, int h, int* scroll) +{ + g_state.areaId++; + g_state.widgetId = 0; + g_scrollId = (g_state.areaId<<16) | g_state.widgetId; + + const int areaHeading = name ? AREA_HEADER : 0; + + g_state.widgetX = x + SCROLL_AREA_PADDING; + g_state.widgetY = y+h-areaHeading + (*scroll) - SCROll_AREA_TOP_PADDING; + g_state.widgetW = w - SCROLL_AREA_PADDING*4; + g_scrollTop = y-areaHeading+h; + g_scrollBottom = y+SCROLL_AREA_PADDING; + g_scrollRight = x+w - SCROLL_AREA_PADDING*3; + g_scrollVal = scroll; + + g_scrollAreaTop = g_state.widgetY; + + g_focusTop = y-areaHeading; + g_focusBottom = y-areaHeading+h; + + g_insideScrollArea = inRect(x, y, w, h, false); + g_state.insideCurrentScroll = g_insideScrollArea; + + addGfxCmdRoundedRect((float)x, (float)y, (float)w, (float)h, 6, imguiRGBA(40,40,40,192)); + + if (name) addGfxCmdText(x+areaHeading/2, y+h-areaHeading/2-TEXT_HEIGHT/2, IMGUI_ALIGN_LEFT, name, imguiRGBA(255,255,255,128)); + + addGfxCmdScissor(x+SCROLL_AREA_PADDING, y+SCROLL_AREA_PADDING, w-SCROLL_AREA_PADDING*4, h-areaHeading-SCROLL_AREA_PADDING); + + return g_insideScrollArea; +} + +void imguiEndScrollArea() +{ + // Disable scissoring. + addGfxCmdScissor(-1,-1,-1,-1); + + // Draw scroll bar + int x = g_scrollRight+SCROLL_AREA_PADDING/2; + int y = g_scrollBottom; + int w = SCROLL_AREA_PADDING*2; + int h = g_scrollTop - g_scrollBottom; + + int stop = g_scrollAreaTop; + int sbot = g_state.widgetY; + int sh = stop - sbot; // The scrollable area height. + + float barHeight = (float)h/(float)sh; + + if (barHeight < 1) + { + float barY = (float)(y - sbot)/(float)sh; + if (barY < 0) barY = 0; + if (barY > 1) barY = 1; + + // Handle scroll bar logic. + unsigned int hid = g_scrollId; + int hx = x; + int hy = y + (int)(barY*h); + int hw = w; + int hh = (int)(barHeight*h); + + const int range = h - (hh-1); + bool over = inRect(hx, hy, hw, hh); + buttonLogic(hid, over); + if (isActive(hid)) + { + float u = (float)(hy-y) / (float)range; + if (g_state.wentActive) + { + g_state.dragY = g_state.my; + g_state.dragOrig = u; + } + if (g_state.dragY != g_state.my) + { + u = g_state.dragOrig + (g_state.my - g_state.dragY) / (float)range; + if (u < 0) u = 0; + if (u > 1) u = 1; + *g_scrollVal = (int)((1-u) * (sh - h)); + } + } + + // BG + addGfxCmdRoundedRect((float)x, (float)y, (float)w, (float)h, (float)w/2-1, imguiRGBA(0,0,0,196)); + // Bar + if (isActive(hid)) + addGfxCmdRoundedRect((float)hx, (float)hy, (float)hw, (float)hh, (float)w/2-1, imguiRGBA(255,196,0,196)); + else + addGfxCmdRoundedRect((float)hx, (float)hy, (float)hw, (float)hh, (float)w/2-1, isHot(hid) ? imguiRGBA(255,196,0,96) : imguiRGBA(255,255,255,64)); + + // Handle mouse scrolling. + if (g_insideScrollArea) // && !anyActive()) + { + if (g_state.scroll) + { + *g_scrollVal += 20*g_state.scroll; + if (*g_scrollVal < 0) *g_scrollVal = 0; + if (*g_scrollVal > (sh - h)) *g_scrollVal = (sh - h); + } + } + } + g_state.insideCurrentScroll = false; +} + +bool imguiButton(const char* text, bool enabled, int width) +{ + g_state.widgetId++; + unsigned int id = (g_state.areaId<<16) | g_state.widgetId; + + int x = g_state.widgetX; + int y = g_state.widgetY - BUTTON_HEIGHT; + int w = width > 0 ? width : g_state.widgetW; + int h = BUTTON_HEIGHT; + if (g_state.nextItemSameLine) { + g_state.widgetX += width; + } + else { + g_state.widgetY -= BUTTON_HEIGHT; + } + + bool over = enabled && inRect(x, y, w, h); + bool res = buttonLogic(id, over); + + addGfxCmdRoundedRect((float)x, (float)y, (float)w, (float)h, (float)BUTTON_HEIGHT/2-1, imguiRGBA(128,128,128, isActive(id)?196:96)); + if (enabled) + addGfxCmdText(x+w/2, y+BUTTON_HEIGHT/2-TEXT_HEIGHT/2, IMGUI_ALIGN_CENTER, text, isHot(id) ? imguiRGBA(255,196,0,255) : imguiRGBA(255,255,255,200)); + else + addGfxCmdText(x+w/2, y+BUTTON_HEIGHT/2-TEXT_HEIGHT/2, IMGUI_ALIGN_CENTER, text, imguiRGBA(128,128,128,200)); + + return res; +} + +bool imguiItem(const char* text, bool enabled) +{ + g_state.widgetId++; + unsigned int id = (g_state.areaId<<16) | g_state.widgetId; + + int x = g_state.widgetX; + int y = g_state.widgetY - BUTTON_HEIGHT; + int w = g_state.widgetW; + int h = BUTTON_HEIGHT; + g_state.widgetY -= BUTTON_HEIGHT + DEFAULT_VERTICAL_SPACING; + + bool over = enabled && inRect(x, y, w, h); + bool res = buttonLogic(id, over); + + if (isHot(id)) + addGfxCmdRoundedRect((float)x, (float)y, (float)w, (float)h, 2.0f, imguiRGBA(255,196,0,isActive(id)?196:96)); + + if (enabled) + addGfxCmdText(x+BUTTON_HEIGHT/2, y+BUTTON_HEIGHT/2-TEXT_HEIGHT/2, IMGUI_ALIGN_LEFT, text, imguiRGBA(255,255,255,200)); + else + addGfxCmdText(x+BUTTON_HEIGHT/2, y+BUTTON_HEIGHT/2-TEXT_HEIGHT/2, IMGUI_ALIGN_LEFT, text, imguiRGBA(128,128,128,200)); + + return res; +} + +bool imguiCheck(const char* text, bool checked, bool enabled) +{ + g_state.widgetId++; + unsigned int id = (g_state.areaId<<16) | g_state.widgetId; + + int x = g_state.widgetX; + int y = g_state.widgetY - BUTTON_HEIGHT; + int w = g_state.widgetW; + int h = BUTTON_HEIGHT; + if (g_state.nextItemSameLine) { + g_state.widgetX += w; + } + else { + g_state.widgetY -= BUTTON_HEIGHT; + } + + bool over = enabled && inRect(x, y, w, h); + bool res = buttonLogic(id, over); + + const int cx = x+BUTTON_HEIGHT/2-CHECK_SIZE/2; + const int cy = y+BUTTON_HEIGHT/2-CHECK_SIZE/2; + addGfxCmdRoundedRect((float)cx-3, (float)cy-3, (float)CHECK_SIZE+6, (float)CHECK_SIZE+6, 4, imguiRGBA(128,128,128, isActive(id)?196:96)); + if (checked) + { + if (enabled) + addGfxCmdRoundedRect((float)cx, (float)cy, (float)CHECK_SIZE, (float)CHECK_SIZE, (float)CHECK_SIZE/2-1, imguiRGBA(255,255,255,isActive(id)?255:200)); + else + addGfxCmdRoundedRect((float)cx, (float)cy, (float)CHECK_SIZE, (float)CHECK_SIZE, (float)CHECK_SIZE/2-1, imguiRGBA(128,128,128,200)); + } + + if (enabled) + addGfxCmdText(x+BUTTON_HEIGHT, y+BUTTON_HEIGHT/2-TEXT_HEIGHT/2, IMGUI_ALIGN_LEFT, text, isHot(id) ? imguiRGBA(255,196,0,255) : imguiRGBA(255,255,255,200)); + else + addGfxCmdText(x+BUTTON_HEIGHT, y+BUTTON_HEIGHT/2-TEXT_HEIGHT/2, IMGUI_ALIGN_LEFT, text, imguiRGBA(128,128,128,200)); + + return res; +} + +bool imguiCollapse(const char* text, const char* subtext, bool checked, bool enabled) +{ + g_state.widgetId++; + unsigned int id = (g_state.areaId<<16) | g_state.widgetId; + + int x = g_state.widgetX; + int y = g_state.widgetY - BUTTON_HEIGHT; + int w = g_state.widgetW; + int h = BUTTON_HEIGHT; + g_state.widgetY -= BUTTON_HEIGHT; // + DEFAULT_SPACING; + + const int cx = x+BUTTON_HEIGHT/2-CHECK_SIZE/2; + const int cy = y+BUTTON_HEIGHT/2-CHECK_SIZE/2; + + bool over = enabled && inRect(x, y, w, h); + bool res = buttonLogic(id, over); + + if (checked) + addGfxCmdTriangle(cx, cy, CHECK_SIZE, CHECK_SIZE, 2, imguiRGBA(255,255,255,isActive(id)?255:200)); + else + addGfxCmdTriangle(cx, cy, CHECK_SIZE, CHECK_SIZE, 1, imguiRGBA(255,255,255,isActive(id)?255:200)); + + if (enabled) + addGfxCmdText(x+BUTTON_HEIGHT, y+BUTTON_HEIGHT/2-TEXT_HEIGHT/2, IMGUI_ALIGN_LEFT, text, isHot(id) ? imguiRGBA(255,196,0,255) : imguiRGBA(255,255,255,200)); + else + addGfxCmdText(x+BUTTON_HEIGHT, y+BUTTON_HEIGHT/2-TEXT_HEIGHT/2, IMGUI_ALIGN_LEFT, text, imguiRGBA(128,128,128,200)); + + if (subtext) + addGfxCmdText(x+w-BUTTON_HEIGHT/2, y+BUTTON_HEIGHT/2-TEXT_HEIGHT/2, IMGUI_ALIGN_RIGHT, subtext, imguiRGBA(255,255,255,128)); + + return res; +} + +void imguiLabel(const char* text) +{ + int x = g_state.widgetX; + int y = g_state.widgetY - BUTTON_HEIGHT; + g_state.widgetY -= BUTTON_HEIGHT; + addGfxCmdText(x, y+BUTTON_HEIGHT/2-TEXT_HEIGHT/2, IMGUI_ALIGN_LEFT, text, imguiRGBA(255,255,255,255)); +} + +void imguiValue(const char* text) +{ + const int x = g_state.widgetX; + const int y = g_state.widgetY - BUTTON_HEIGHT; + const int w = g_state.widgetW; + g_state.widgetY -= BUTTON_HEIGHT; + + addGfxCmdText(x+w-BUTTON_HEIGHT/2, y+BUTTON_HEIGHT/2-TEXT_HEIGHT/2, IMGUI_ALIGN_RIGHT, text, imguiRGBA(255,255,255,200)); +} + +bool imguiSlider(const char* text, float* val, float vmin, float vmax, float vinc, bool enabled) +{ + g_state.widgetId++; + unsigned int id = (g_state.areaId<<16) | g_state.widgetId; + + int x = g_state.widgetX; + int y = g_state.widgetY - BUTTON_HEIGHT; + int w = g_state.widgetW; + int h = SLIDER_HEIGHT; + g_state.widgetY -= SLIDER_HEIGHT + DEFAULT_VERTICAL_SPACING; + + addGfxCmdRoundedRect((float)x, (float)y, (float)w, (float)h, 4.0f, imguiRGBA(0,0,0,128)); + + const int range = w - SLIDER_MARKER_WIDTH; + + float u = (*val - vmin) / (vmax-vmin); + if (u < 0) u = 0; + if (u > 1) u = 1; + int m = (int)(u * range); + + bool over = enabled && inRect(x+m, y, SLIDER_MARKER_WIDTH, SLIDER_HEIGHT); + bool res = buttonLogic(id, over); + bool valChanged = false; + + if (isActive(id)) + { + if (g_state.wentActive) + { + g_state.dragX = g_state.mx; + g_state.dragOrig = u; + } + if (g_state.dragX != g_state.mx) + { + u = g_state.dragOrig + (float)(g_state.mx - g_state.dragX) / (float)range; + if (u < 0) u = 0; + if (u > 1) u = 1; + *val = vmin + u*(vmax-vmin); + *val = floorf(*val/vinc+0.5f)*vinc; // Snap to vinc + m = (int)(u * range); + valChanged = true; + } + } + + if (isActive(id)) + addGfxCmdRoundedRect((float)(x+m), (float)y, (float)SLIDER_MARKER_WIDTH, (float)SLIDER_HEIGHT, 4.0f, imguiRGBA(255,255,255,255)); + else + addGfxCmdRoundedRect((float)(x+m), (float)y, (float)SLIDER_MARKER_WIDTH, (float)SLIDER_HEIGHT, 4.0f, isHot(id) ? imguiRGBA(255,196,0,128) : imguiRGBA(255,255,255,64)); + + // TODO: fix this, take a look at 'nicenum'. + int digits = (int)(ceilf(log10f(vinc))); + char fmt[16]; + snprintf(fmt, 16, "%%.%df", digits >= 0 ? 0 : -digits); + char msg[128]; + snprintf(msg, 128, fmt, *val); + + if (enabled) + { + addGfxCmdText(x+SLIDER_HEIGHT/2, y+SLIDER_HEIGHT/2-TEXT_HEIGHT/2, IMGUI_ALIGN_LEFT, text, isHot(id) ? imguiRGBA(255,196,0,255) : imguiRGBA(255,255,255,200)); + addGfxCmdText(x+w-SLIDER_HEIGHT/2, y+SLIDER_HEIGHT/2-TEXT_HEIGHT/2, IMGUI_ALIGN_RIGHT, msg, isHot(id) ? imguiRGBA(255,196,0,255) : imguiRGBA(255,255,255,200)); + } + else + { + addGfxCmdText(x+SLIDER_HEIGHT/2, y+SLIDER_HEIGHT/2-TEXT_HEIGHT/2, IMGUI_ALIGN_LEFT, text, imguiRGBA(128,128,128,200)); + addGfxCmdText(x+w-SLIDER_HEIGHT/2, y+SLIDER_HEIGHT/2-TEXT_HEIGHT/2, IMGUI_ALIGN_RIGHT, msg, imguiRGBA(128,128,128,200)); + } + + return res || valChanged; +} + +void imguiStartLine() { + g_state.nextItemSameLine = true; + g_state.newLinePreviousX = g_state.widgetX; +} + +void imguiEndLine() { + g_state.nextItemSameLine = false; + g_state.widgetX = g_state.newLinePreviousX; + g_state.newLinePreviousX = 0; +} + +void imguiVerticalSpace(int spaceY) +{ + g_state.widgetY -= spaceY; +} + +void imguiHorizontalSpace(int spaceX) { + g_state.widgetX += spaceX; +} + +void imguiSeparatorLine() +{ + int x = g_state.widgetX; + int y = g_state.widgetY; + int w = g_state.widgetW; + int h = SEPARATOR_LINE_WIDTH; + g_state.widgetY -= SEPARATOR_LINE_WIDTH; + + addGfxCmdRect((float)x, (float)y, (float)w, (float)h, imguiRGBA(255,255,255,70)); +} + +void imguiDrawText(int x, int y, int align, const char* text, unsigned int color) +{ + addGfxCmdText(x, y, align, text, color); +} + +void imguiDrawLine(float x0, float y0, float x1, float y1, float r, unsigned int color) +{ + addGfxCmdLine(x0, y0, x1, y1, r, color); +} + +void imguiDrawRect(float x, float y, float w, float h, unsigned int color) +{ + addGfxCmdRect(x, y, w, h, color); +} + +void imguiDrawRoundedRect(float x, float y, float w, float h, float r, unsigned int color) +{ + addGfxCmdRoundedRect(x, y, w, h, r, color); +} diff --git a/testbed/imgui/imgui.h b/testbed/imgui/imgui.h new file mode 100644 index 00000000..64f28109 --- /dev/null +++ b/testbed/imgui/imgui.h @@ -0,0 +1,128 @@ + +// +// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +// + +// Source altered and distributed from https://github.com/AdrienHerubel/imgui + +#ifndef IMGUI_H +#define IMGUI_H + +static const int BUTTON_HEIGHT = 40; +static const int SLIDER_HEIGHT = 30; +static const int SLIDER_MARKER_WIDTH = 10; +static const int CHECK_SIZE = 18; +static const int DEFAULT_VERTICAL_SPACING = 14; +static const int DEFAULT_HORIZONTAL_SPACING = 14; +static const int TEXT_HEIGHT = 16; +static const int FONT_HEIGHT = 30; +static const int SCROll_AREA_TOP_PADDING = 5; +static const int SCROLL_AREA_PADDING = 5; +static const int INDENT_SIZE = 16; +static const int AREA_HEADER = 20; +static const int SEPARATOR_LINE_WIDTH = 2; + +enum imguiMouseButton +{ + IMGUI_MBUT_LEFT = 0x01, + IMGUI_MBUT_RIGHT = 0x02, +}; + +enum imguiTextAlign +{ + IMGUI_ALIGN_LEFT, + IMGUI_ALIGN_CENTER, + IMGUI_ALIGN_RIGHT, +}; + +inline unsigned int imguiRGBA(unsigned char r, unsigned char g, unsigned char b, unsigned char a=255) +{ + return (r) | (g << 8) | (b << 16) | (a << 24); +} + +void imguiBeginFrame(int mx, int my, unsigned char mbut, int scroll); +void imguiEndFrame(); + +bool imguiBeginScrollArea(const char* name, int x, int y, int w, int h, int* scroll); +void imguiEndScrollArea(); + +void imguiStartLine(); +void imguiEndLine(); +void imguiIndent(); +void imguiUnindent(); +void imguiVerticalSpace(int spaceY); +void imguiHorizontalSpace(int spaceX); +void imguiSeparatorLine(); + +bool imguiButton(const char* text, bool enabled = true, int width = -1); +bool imguiItem(const char* text, bool enabled = true); +bool imguiCheck(const char* text, bool checked, bool enabled = true); +bool imguiCollapse(const char* text, const char* subtext, bool checked, bool enabled = true); +void imguiLabel(const char* text); +void imguiValue(const char* text); +bool imguiSlider(const char* text, float* val, float vmin, float vmax, float vinc, bool enabled = true); + +void imguiDrawText(int x, int y, int align, const char* text, unsigned int color); +void imguiDrawLine(float x0, float y0, float x1, float y1, float r, unsigned int color); +void imguiDrawRoundedRect(float x, float y, float w, float h, float r, unsigned int color); +void imguiDrawRect(float x, float y, float w, float h, unsigned int color); + +// Pull render interface. +enum imguiGfxCmdType +{ + IMGUI_GFXCMD_RECT, + IMGUI_GFXCMD_TRIANGLE, + IMGUI_GFXCMD_LINE, + IMGUI_GFXCMD_TEXT, + IMGUI_GFXCMD_SCISSOR, +}; + +struct imguiGfxRect +{ + short x,y,w,h,r; +}; + +struct imguiGfxText +{ + short x,y,align; + const char* text; +}; + +struct imguiGfxLine +{ + short x0,y0,x1,y1,r; +}; + +struct imguiGfxCmd +{ + char type; + char flags; + char pad[2]; + unsigned int col; + union + { + imguiGfxLine line; + imguiGfxRect rect; + imguiGfxText text; + }; +}; + +const imguiGfxCmd* imguiGetRenderQueue(); +int imguiGetRenderQueueSize(); + + +#endif // IMGUI_H diff --git a/testbed/imgui/imguiRenderGL2.cpp b/testbed/imgui/imguiRenderGL2.cpp new file mode 100644 index 00000000..21308219 --- /dev/null +++ b/testbed/imgui/imguiRenderGL2.cpp @@ -0,0 +1,489 @@ +// +// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +// + +// Source altered and distributed from https://github.com/AdrienHerubel/imguir + +#define _USE_MATH_DEFINES +#include +#include + +#include "imgui.h" + +#include +/*#ifdef __APPLE__ +#include +#else +#include +#endif +*/ + +// Some math headers don't have PI defined. +static const float PI = 3.14159265f; + +void imguifree(void* ptr, void* userptr); +void* imguimalloc(size_t size, void* userptr); + +#define STBTT_malloc(x,y) imguimalloc(x,y) +#define STBTT_free(x,y) imguifree(x,y) +#define STB_TRUETYPE_IMPLEMENTATION +#include "stb_truetype.h" + +void imguifree(void* ptr, void* /*userptr*/) +{ + free(ptr); +} + +void* imguimalloc(size_t size, void* /*userptr*/) +{ + return malloc(size); +} + +static const unsigned TEMP_COORD_COUNT = 100; +static float g_tempCoords[TEMP_COORD_COUNT*2]; +static float g_tempNormals[TEMP_COORD_COUNT*2]; + +static const int CIRCLE_VERTS = 8*4; +static float g_circleVerts[CIRCLE_VERTS*2]; + +static stbtt_bakedchar g_cdata[96]; // ASCII 32..126 is 95 glyphs +static GLuint g_ftex = 0; + +inline unsigned int RGBA(unsigned char r, unsigned char g, unsigned char b, unsigned char a) +{ + return (r) | (g << 8) | (b << 16) | (a << 24); +} + +static void drawPolygon(const float* coords, unsigned numCoords, float r, unsigned int col) +{ + if (numCoords > TEMP_COORD_COUNT) numCoords = TEMP_COORD_COUNT; + + for (unsigned i = 0, j = numCoords-1; i < numCoords; j=i++) + { + const float* v0 = &coords[j*2]; + const float* v1 = &coords[i*2]; + float dx = v1[0] - v0[0]; + float dy = v1[1] - v0[1]; + float d = sqrtf(dx*dx+dy*dy); + if (d > 0) + { + d = 1.0f/d; + dx *= d; + dy *= d; + } + g_tempNormals[j*2+0] = dy; + g_tempNormals[j*2+1] = -dx; + } + + for (unsigned i = 0, j = numCoords-1; i < numCoords; j=i++) + { + float dlx0 = g_tempNormals[j*2+0]; + float dly0 = g_tempNormals[j*2+1]; + float dlx1 = g_tempNormals[i*2+0]; + float dly1 = g_tempNormals[i*2+1]; + float dmx = (dlx0 + dlx1) * 0.5f; + float dmy = (dly0 + dly1) * 0.5f; + float dmr2 = dmx*dmx + dmy*dmy; + if (dmr2 > 0.000001f) + { + float scale = 1.0f / dmr2; + if (scale > 10.0f) scale = 10.0f; + dmx *= scale; + dmy *= scale; + } + g_tempCoords[i*2+0] = coords[i*2+0]+dmx*r; + g_tempCoords[i*2+1] = coords[i*2+1]+dmy*r; + } + + unsigned int colTrans = RGBA(col&0xff, (col>>8)&0xff, (col>>16)&0xff, 0); + + glBegin(GL_TRIANGLES); + + glColor4ubv((GLubyte*)&col); + + for (unsigned i = 0, j = numCoords-1; i < numCoords; j=i++) + { + glVertex2fv(&coords[i*2]); + glVertex2fv(&coords[j*2]); + glColor4ubv((GLubyte*)&colTrans); + glVertex2fv(&g_tempCoords[j*2]); + + glVertex2fv(&g_tempCoords[j*2]); + glVertex2fv(&g_tempCoords[i*2]); + + glColor4ubv((GLubyte*)&col); + glVertex2fv(&coords[i*2]); + } + + glColor4ubv((GLubyte*)&col); + for (unsigned i = 2; i < numCoords; ++i) + { + glVertex2fv(&coords[0]); + glVertex2fv(&coords[(i-1)*2]); + glVertex2fv(&coords[i*2]); + } + + glEnd(); +} + +static void drawRect(float x, float y, float w, float h, float fth, unsigned int col) +{ + float verts[4*2] = + { + x+0.5f, y+0.5f, + x+w-0.5f, y+0.5f, + x+w-0.5f, y+h-0.5f, + x+0.5f, y+h-0.5f, + }; + drawPolygon(verts, 4, fth, col); +} + +/* +static void drawEllipse(float x, float y, float w, float h, float fth, unsigned int col) +{ + float verts[CIRCLE_VERTS*2]; + const float* cverts = g_circleVerts; + float* v = verts; + + for (int i = 0; i < CIRCLE_VERTS; ++i) + { + *v++ = x + cverts[i*2]*w; + *v++ = y + cverts[i*2+1]*h; + } + + drawPolygon(verts, CIRCLE_VERTS, fth, col); +} +*/ + +static void drawRoundedRect(float x, float y, float w, float h, float r, float fth, unsigned int col) +{ + const unsigned n = CIRCLE_VERTS/4; + float verts[(n+1)*4*2]; + const float* cverts = g_circleVerts; + float* v = verts; + + for (unsigned i = 0; i <= n; ++i) + { + *v++ = x+w-r + cverts[i*2]*r; + *v++ = y+h-r + cverts[i*2+1]*r; + } + + for (unsigned i = n; i <= n*2; ++i) + { + *v++ = x+r + cverts[i*2]*r; + *v++ = y+h-r + cverts[i*2+1]*r; + } + + for (unsigned i = n*2; i <= n*3; ++i) + { + *v++ = x+r + cverts[i*2]*r; + *v++ = y+r + cverts[i*2+1]*r; + } + + for (unsigned i = n*3; i < n*4; ++i) + { + *v++ = x+w-r + cverts[i*2]*r; + *v++ = y+r + cverts[i*2+1]*r; + } + *v++ = x+w-r + cverts[0]*r; + *v++ = y+r + cverts[1]*r; + + drawPolygon(verts, (n+1)*4, fth, col); +} + + +static void drawLine(float x0, float y0, float x1, float y1, float r, float fth, unsigned int col) +{ + float dx = x1-x0; + float dy = y1-y0; + float d = sqrtf(dx*dx+dy*dy); + if (d > 0.0001f) + { + d = 1.0f/d; + dx *= d; + dy *= d; + } + float nx = dy; + float ny = -dx; + float verts[4*2]; + r -= fth; + r *= 0.5f; + if (r < 0.01f) r = 0.01f; + dx *= r; + dy *= r; + nx *= r; + ny *= r; + + verts[0] = x0-dx-nx; + verts[1] = y0-dy-ny; + + verts[2] = x0-dx+nx; + verts[3] = y0-dy+ny; + + verts[4] = x1+dx+nx; + verts[5] = y1+dy+ny; + + verts[6] = x1+dx-nx; + verts[7] = y1+dy-ny; + + drawPolygon(verts, 4, fth, col); +} + + +bool imguiRenderGLInit(const char* fontpath) +{ + for (int i = 0; i < CIRCLE_VERTS; ++i) + { + float a = (float)i/(float)CIRCLE_VERTS * PI*2; + g_circleVerts[i*2+0] = cosf(a); + g_circleVerts[i*2+1] = sinf(a); + } + + // Load font. + FILE* fp = fopen(fontpath, "rb"); + if (!fp) return false; + fseek(fp, 0, SEEK_END); + int size = ftell(fp); + fseek(fp, 0, SEEK_SET); + + unsigned char* ttfBuffer = (unsigned char*)malloc(size); + if (!ttfBuffer) + { + fclose(fp); + return false; + } + + fread(ttfBuffer, 1, size, fp); + fclose(fp); + fp = 0; + + unsigned char* bmap = (unsigned char*)malloc(512*512); + if (!bmap) + { + free(ttfBuffer); + return false; + } + + stbtt_BakeFontBitmap(ttfBuffer,0, 15.0f, bmap,512,512, 32,96, g_cdata); + + // can free ttf_buffer at this point + glGenTextures(1, &g_ftex); + glBindTexture(GL_TEXTURE_2D, g_ftex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, 512,512, 0, GL_ALPHA, GL_UNSIGNED_BYTE, bmap); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + free(ttfBuffer); + free(bmap); + + return true; +} + +void imguiRenderGLDestroy() +{ + if (g_ftex) + { + glDeleteTextures(1, &g_ftex); + g_ftex = 0; + } +} + +static void getBakedQuad(stbtt_bakedchar *chardata, int pw, int ph, int char_index, + float *xpos, float *ypos, stbtt_aligned_quad *q) +{ + stbtt_bakedchar *b = chardata + char_index; + int round_x = STBTT_ifloor(*xpos + b->xoff); + int round_y = STBTT_ifloor(*ypos - b->yoff); + + q->x0 = (float)round_x; + q->y0 = (float)round_y; + q->x1 = (float)round_x + b->x1 - b->x0; + q->y1 = (float)round_y - b->y1 + b->y0; + + q->s0 = b->x0 / (float)pw; + q->t0 = b->y0 / (float)pw; + q->s1 = b->x1 / (float)ph; + q->t1 = b->y1 / (float)ph; + + *xpos += b->xadvance; +} + +static const float g_tabStops[4] = {150, 210, 270, 330}; + +static float getTextLength(stbtt_bakedchar *chardata, const char* text) +{ + float xpos = 0; + float len = 0; + while (*text) + { + int c = (unsigned char)*text; + if (c == '\t') + { + for (int i = 0; i < 4; ++i) + { + if (xpos < g_tabStops[i]) + { + xpos = g_tabStops[i]; + break; + } + } + } + else if (c >= 32 && c < 128) + { + stbtt_bakedchar *b = chardata + c-32; + int round_x = STBTT_ifloor((xpos + b->xoff) + 0.5); + len = round_x + b->x1 - b->x0 + 0.5f; + xpos += b->xadvance; + } + ++text; + } + return len; +} + +static void drawText(float x, float y, const char *text, int align, unsigned int col) +{ + if (!g_ftex) return; + if (!text) return; + + if (align == IMGUI_ALIGN_CENTER) + x -= getTextLength(g_cdata, text)/2; + else if (align == IMGUI_ALIGN_RIGHT) + x -= getTextLength(g_cdata, text); + + glColor4ub(col&0xff, (col>>8)&0xff, (col>>16)&0xff, (col>>24)&0xff); + + glEnable(GL_TEXTURE_2D); + + // assume orthographic projection with units = screen pixels, origin at top left + glBindTexture(GL_TEXTURE_2D, g_ftex); + + glBegin(GL_TRIANGLES); + + const float ox = x; + + while (*text) + { + int c = (unsigned char)*text; + if (c == '\t') + { + for (int i = 0; i < 4; ++i) + { + if (x < g_tabStops[i]+ox) + { + x = g_tabStops[i]+ox; + break; + } + } + } + else if (c >= 32 && c < 128) + { + stbtt_aligned_quad q; + getBakedQuad(g_cdata, 512,512, c-32, &x,&y,&q); + + glTexCoord2f(q.s0, q.t0); + glVertex2f(q.x0, q.y0); + glTexCoord2f(q.s1, q.t1); + glVertex2f(q.x1, q.y1); + glTexCoord2f(q.s1, q.t0); + glVertex2f(q.x1, q.y0); + + glTexCoord2f(q.s0, q.t0); + glVertex2f(q.x0, q.y0); + glTexCoord2f(q.s0, q.t1); + glVertex2f(q.x0, q.y1); + glTexCoord2f(q.s1, q.t1); + glVertex2f(q.x1, q.y1); + } + ++text; + } + + glEnd(); + glDisable(GL_TEXTURE_2D); +} + +void imguiRenderGLDraw(int width, int height) +{ + const imguiGfxCmd* q = imguiGetRenderQueue(); + int nq = imguiGetRenderQueueSize(); + + const float s = 1.0f/8.0f; + + glDisable(GL_SCISSOR_TEST); + for (int i = 0; i < nq; ++i) + { + const imguiGfxCmd& cmd = q[i]; + if (cmd.type == IMGUI_GFXCMD_RECT) + { + if (cmd.rect.r == 0) + { + drawRect((float)cmd.rect.x*s+0.5f, (float)cmd.rect.y*s+0.5f, + (float)cmd.rect.w*s-1, (float)cmd.rect.h*s-1, + 1.0f, cmd.col); + } + else + { + drawRoundedRect((float)cmd.rect.x*s+0.5f, (float)cmd.rect.y*s+0.5f, + (float)cmd.rect.w*s-1, (float)cmd.rect.h*s-1, + (float)cmd.rect.r*s, 1.0f, cmd.col); + } + } + else if (cmd.type == IMGUI_GFXCMD_LINE) + { + drawLine(cmd.line.x0*s, cmd.line.y0*s, cmd.line.x1*s, cmd.line.y1*s, cmd.line.r*s, 1.0f, cmd.col); + } + else if (cmd.type == IMGUI_GFXCMD_TRIANGLE) + { + if (cmd.flags == 1) + { + const float verts[3*2] = + { + (float)cmd.rect.x*s+0.5f, (float)cmd.rect.y*s+0.5f, + (float)cmd.rect.x*s+0.5f+(float)cmd.rect.w*s-1, (float)cmd.rect.y*s+0.5f+(float)cmd.rect.h*s/2-0.5f, + (float)cmd.rect.x*s+0.5f, (float)cmd.rect.y*s+0.5f+(float)cmd.rect.h*s-1, + }; + drawPolygon(verts, 3, 1.0f, cmd.col); + } + if (cmd.flags == 2) + { + const float verts[3*2] = + { + (float)cmd.rect.x*s+0.5f, (float)cmd.rect.y*s+0.5f+(float)cmd.rect.h*s-1, + (float)cmd.rect.x*s+0.5f+(float)cmd.rect.w*s/2-0.5f, (float)cmd.rect.y*s+0.5f, + (float)cmd.rect.x*s+0.5f+(float)cmd.rect.w*s-1, (float)cmd.rect.y*s+0.5f+(float)cmd.rect.h*s-1, + }; + drawPolygon(verts, 3, 1.0f, cmd.col); + } + } + else if (cmd.type == IMGUI_GFXCMD_TEXT) + { + drawText(cmd.text.x, cmd.text.y, cmd.text.text, cmd.text.align, cmd.col); + } + else if (cmd.type == IMGUI_GFXCMD_SCISSOR) + { + if (cmd.flags) + { + glEnable(GL_SCISSOR_TEST); + glScissor(cmd.rect.x, cmd.rect.y, cmd.rect.w, cmd.rect.h); + } + else + { + glDisable(GL_SCISSOR_TEST); + } + } + } + glDisable(GL_SCISSOR_TEST); +} diff --git a/testbed/imgui/imguiRenderGL2.h b/testbed/imgui/imguiRenderGL2.h new file mode 100644 index 00000000..c36b8045 --- /dev/null +++ b/testbed/imgui/imguiRenderGL2.h @@ -0,0 +1,29 @@ +// +// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +// + +// Source altered and distributed from https://github.com/AdrienHerubel/imguir + + +#ifndef IMGUI_RENDER_GL_H +#define IMGUI_RENDER_GL_H + +bool imguiRenderGLInit(const char* fontpath); +void imguiRenderGLDestroy(); +void imguiRenderGLDraw(int width, int height); + +#endif // IMGUI_RENDER_GL_H \ No newline at end of file diff --git a/testbed/imgui/imguiRenderGL3.cpp b/testbed/imgui/imguiRenderGL3.cpp new file mode 100644 index 00000000..2a84a6fa --- /dev/null +++ b/testbed/imgui/imguiRenderGL3.cpp @@ -0,0 +1,695 @@ +// +// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +// + +// Source altered and distributed from https://github.com/AdrienHerubel/imgui + + +#define _USE_MATH_DEFINES +#include +#include + +#include +/*#ifdef __APPLE__ +#include +#else +#include +#endif +*/ + +#include "imgui.h" + +// Some math headers don't have PI defined. +static const float PI = 3.14159265f; + +void imguifree(void* ptr, void* userptr); +void* imguimalloc(size_t size, void* userptr); + +#define STBTT_malloc(x,y) imguimalloc(x,y) +#define STBTT_free(x,y) imguifree(x,y) +#define STB_TRUETYPE_IMPLEMENTATION +#include "stb_truetype.h" + +void imguifree(void* ptr, void* /*userptr*/) +{ + free(ptr); +} + +void* imguimalloc(size_t size, void* /*userptr*/) +{ + return malloc(size); +} + +static const unsigned TEMP_COORD_COUNT = 100; +static float g_tempCoords[TEMP_COORD_COUNT*2]; +static float g_tempNormals[TEMP_COORD_COUNT*2]; +static float g_tempVertices[TEMP_COORD_COUNT * 12 + (TEMP_COORD_COUNT - 2) * 6]; +static float g_tempTextureCoords[TEMP_COORD_COUNT * 12 + (TEMP_COORD_COUNT - 2) * 6]; +static float g_tempColors[TEMP_COORD_COUNT * 24 + (TEMP_COORD_COUNT - 2) * 12]; + +static const int CIRCLE_VERTS = 8*4; +static float g_circleVerts[CIRCLE_VERTS*2]; + +static stbtt_bakedchar g_cdata[96]; // ASCII 32..126 is 95 glyphs +static GLuint g_ftex = 0; +static GLuint g_whitetex = 0; +static GLuint g_vao = 0; +static GLuint g_vbos[3] = {0, 0, 0}; +static GLuint g_program = 0; +static GLuint g_programViewportLocation = 0; +static GLuint g_programTextureLocation = 0; + +inline unsigned int RGBA(unsigned char r, unsigned char g, unsigned char b, unsigned char a) +{ + return (r) | (g << 8) | (b << 16) | (a << 24); +} + +static void drawPolygon(const float* coords, unsigned numCoords, float r, unsigned int col) +{ + if (numCoords > TEMP_COORD_COUNT) numCoords = TEMP_COORD_COUNT; + + for (unsigned i = 0, j = numCoords-1; i < numCoords; j=i++) + { + const float* v0 = &coords[j*2]; + const float* v1 = &coords[i*2]; + float dx = v1[0] - v0[0]; + float dy = v1[1] - v0[1]; + float d = sqrtf(dx*dx+dy*dy); + if (d > 0) + { + d = 1.0f/d; + dx *= d; + dy *= d; + } + g_tempNormals[j*2+0] = dy; + g_tempNormals[j*2+1] = -dx; + } + + float colf[4] = { (float) (col&0xff) / 255.f, (float) ((col>>8)&0xff) / 255.f, (float) ((col>>16)&0xff) / 255.f, (float) ((col>>24)&0xff) / 255.f}; + float colTransf[4] = { (float) (col&0xff) / 255.f, (float) ((col>>8)&0xff) / 255.f, (float) ((col>>16)&0xff) / 255.f, 0}; + + for (unsigned i = 0, j = numCoords-1; i < numCoords; j=i++) + { + float dlx0 = g_tempNormals[j*2+0]; + float dly0 = g_tempNormals[j*2+1]; + float dlx1 = g_tempNormals[i*2+0]; + float dly1 = g_tempNormals[i*2+1]; + float dmx = (dlx0 + dlx1) * 0.5f; + float dmy = (dly0 + dly1) * 0.5f; + float dmr2 = dmx*dmx + dmy*dmy; + if (dmr2 > 0.000001f) + { + float scale = 1.0f / dmr2; + if (scale > 10.0f) scale = 10.0f; + dmx *= scale; + dmy *= scale; + } + g_tempCoords[i*2+0] = coords[i*2+0]+dmx*r; + g_tempCoords[i*2+1] = coords[i*2+1]+dmy*r; + } + + int vSize = numCoords * 12 + (numCoords - 2) * 6; + int uvSize = numCoords * 2 * 6 + (numCoords - 2) * 2 * 3; + int cSize = numCoords * 4 * 6 + (numCoords - 2) * 4 * 3; + float * v = g_tempVertices; + float * uv = g_tempTextureCoords; + memset(uv, 0, uvSize * sizeof(float)); + float * c = g_tempColors; + memset(c, 1, cSize * sizeof(float)); + + float * ptrV = v; + float * ptrC = c; + for (unsigned i = 0, j = numCoords-1; i < numCoords; j=i++) + { + *ptrV = coords[i*2]; + *(ptrV+1) = coords[i*2 + 1]; + ptrV += 2; + *ptrV = coords[j*2]; + *(ptrV+1) = coords[j*2 + 1]; + ptrV += 2; + *ptrV = g_tempCoords[j*2]; + *(ptrV+1) = g_tempCoords[j*2 + 1]; + ptrV += 2; + *ptrV = g_tempCoords[j*2]; + *(ptrV+1) = g_tempCoords[j*2 + 1]; + ptrV += 2; + *ptrV = g_tempCoords[i*2]; + *(ptrV+1) = g_tempCoords[i*2 + 1]; + ptrV += 2; + *ptrV = coords[i*2]; + *(ptrV+1) = coords[i*2 + 1]; + ptrV += 2; + + *ptrC = colf[0]; + *(ptrC+1) = colf[1]; + *(ptrC+2) = colf[2]; + *(ptrC+3) = colf[3]; + ptrC += 4; + *ptrC = colf[0]; + *(ptrC+1) = colf[1]; + *(ptrC+2) = colf[2]; + *(ptrC+3) = colf[3]; + ptrC += 4; + *ptrC = colTransf[0]; + *(ptrC+1) = colTransf[1]; + *(ptrC+2) = colTransf[2]; + *(ptrC+3) = colTransf[3]; + ptrC += 4; + *ptrC = colTransf[0]; + *(ptrC+1) = colTransf[1]; + *(ptrC+2) = colTransf[2]; + *(ptrC+3) = colTransf[3]; + ptrC += 4; + *ptrC = colTransf[0]; + *(ptrC+1) = colTransf[1]; + *(ptrC+2) = colTransf[2]; + *(ptrC+3) = colTransf[3]; + ptrC += 4; + *ptrC = colf[0]; + *(ptrC+1) = colf[1]; + *(ptrC+2) = colf[2]; + *(ptrC+3) = colf[3]; + ptrC += 4; + } + + for (unsigned i = 2; i < numCoords; ++i) + { + *ptrV = coords[0]; + *(ptrV+1) = coords[1]; + ptrV += 2; + *ptrV = coords[(i-1)*2]; + *(ptrV+1) = coords[(i-1)*2+1]; + ptrV += 2; + *ptrV = coords[i*2]; + *(ptrV+1) = coords[i*2 + 1]; + ptrV += 2; + + *ptrC = colf[0]; + *(ptrC+1) = colf[1]; + *(ptrC+2) = colf[2]; + *(ptrC+3) = colf[3]; + ptrC += 4; + *ptrC = colf[0]; + *(ptrC+1) = colf[1]; + *(ptrC+2) = colf[2]; + *(ptrC+3) = colf[3]; + ptrC += 4; + *ptrC = colf[0]; + *(ptrC+1) = colf[1]; + *(ptrC+2) = colf[2]; + *(ptrC+3) = colf[3]; + ptrC += 4; + } + glBindTexture(GL_TEXTURE_2D, g_whitetex); + + glBindVertexArray(g_vao); + glBindBuffer(GL_ARRAY_BUFFER, g_vbos[0]); + glBufferData(GL_ARRAY_BUFFER, vSize*sizeof(float), v, GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, g_vbos[1]); + glBufferData(GL_ARRAY_BUFFER, uvSize*sizeof(float), uv, GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, g_vbos[2]); + glBufferData(GL_ARRAY_BUFFER, cSize*sizeof(float), c, GL_STATIC_DRAW); + glDrawArrays(GL_TRIANGLES, 0, (numCoords * 2 + numCoords - 2)*3); + +} + +static void drawRect(float x, float y, float w, float h, float fth, unsigned int col) +{ + float verts[4*2] = + { + x+0.5f, y+0.5f, + x+w-0.5f, y+0.5f, + x+w-0.5f, y+h-0.5f, + x+0.5f, y+h-0.5f, + }; + drawPolygon(verts, 4, fth, col); +} + +/* +static void drawEllipse(float x, float y, float w, float h, float fth, unsigned int col) +{ + float verts[CIRCLE_VERTS*2]; + const float* cverts = g_circleVerts; + float* v = verts; + + for (int i = 0; i < CIRCLE_VERTS; ++i) + { + *v++ = x + cverts[i*2]*w; + *v++ = y + cverts[i*2+1]*h; + } + + drawPolygon(verts, CIRCLE_VERTS, fth, col); +} +*/ + +static void drawRoundedRect(float x, float y, float w, float h, float r, float fth, unsigned int col) +{ + const unsigned n = CIRCLE_VERTS/4; + float verts[(n+1)*4*2]; + const float* cverts = g_circleVerts; + float* v = verts; + + for (unsigned i = 0; i <= n; ++i) + { + *v++ = x+w-r + cverts[i*2]*r; + *v++ = y+h-r + cverts[i*2+1]*r; + } + + for (unsigned i = n; i <= n*2; ++i) + { + *v++ = x+r + cverts[i*2]*r; + *v++ = y+h-r + cverts[i*2+1]*r; + } + + for (unsigned i = n*2; i <= n*3; ++i) + { + *v++ = x+r + cverts[i*2]*r; + *v++ = y+r + cverts[i*2+1]*r; + } + + for (unsigned i = n*3; i < n*4; ++i) + { + *v++ = x+w-r + cverts[i*2]*r; + *v++ = y+r + cverts[i*2+1]*r; + } + *v++ = x+w-r + cverts[0]*r; + *v++ = y+r + cverts[1]*r; + + drawPolygon(verts, (n+1)*4, fth, col); +} + + +static void drawLine(float x0, float y0, float x1, float y1, float r, float fth, unsigned int col) +{ + float dx = x1-x0; + float dy = y1-y0; + float d = sqrtf(dx*dx+dy*dy); + if (d > 0.0001f) + { + d = 1.0f/d; + dx *= d; + dy *= d; + } + float nx = dy; + float ny = -dx; + float verts[4*2]; + r -= fth; + r *= 0.5f; + if (r < 0.01f) r = 0.01f; + dx *= r; + dy *= r; + nx *= r; + ny *= r; + + verts[0] = x0-dx-nx; + verts[1] = y0-dy-ny; + + verts[2] = x0-dx+nx; + verts[3] = y0-dy+ny; + + verts[4] = x1+dx+nx; + verts[5] = y1+dy+ny; + + verts[6] = x1+dx-nx; + verts[7] = y1+dy-ny; + + drawPolygon(verts, 4, fth, col); +} + + +bool imguiRenderGLInit(const char* fontpath) +{ + for (int i = 0; i < CIRCLE_VERTS; ++i) + { + float a = (float)i/(float)CIRCLE_VERTS * PI*2; + g_circleVerts[i*2+0] = cosf(a); + g_circleVerts[i*2+1] = sinf(a); + } + + // Load font. + FILE* fp = fopen(fontpath, "rb"); + if (!fp) return false; + fseek(fp, 0, SEEK_END); + int size = ftell(fp); + fseek(fp, 0, SEEK_SET); + + unsigned char* ttfBuffer = (unsigned char*)malloc(size); + if (!ttfBuffer) + { + fclose(fp); + return false; + } + + fread(ttfBuffer, 1, size, fp); + fclose(fp); + fp = 0; + + unsigned char* bmap = (unsigned char*)malloc(512*512); + if (!bmap) + { + free(ttfBuffer); + return false; + } + + stbtt_BakeFontBitmap(ttfBuffer,0, FONT_HEIGHT, bmap,512,512, 32,96, g_cdata); + + // can free ttf_buffer at this point + glGenTextures(1, &g_ftex); + glBindTexture(GL_TEXTURE_2D, g_ftex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, 512,512, 0, GL_RED, GL_UNSIGNED_BYTE, bmap); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + // can free ttf_buffer at this point + unsigned char white_alpha = 255; + glGenTextures(1, &g_whitetex); + glBindTexture(GL_TEXTURE_2D, g_whitetex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, 1, 1, 0, GL_RED, GL_UNSIGNED_BYTE, &white_alpha); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glGenVertexArrays(1, &g_vao); + glGenBuffers(3, g_vbos); + + glBindVertexArray(g_vao); + glEnableVertexAttribArray(0); + glEnableVertexAttribArray(1); + glEnableVertexAttribArray(2); + + glBindBuffer(GL_ARRAY_BUFFER, g_vbos[0]); + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(GL_FLOAT)*2, (void*)0); + glBufferData(GL_ARRAY_BUFFER, 0, 0, GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, g_vbos[1]); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(GL_FLOAT)*2, (void*)0); + glBufferData(GL_ARRAY_BUFFER, 0, 0, GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, g_vbos[2]); + glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, sizeof(GL_FLOAT)*4, (void*)0); + glBufferData(GL_ARRAY_BUFFER, 0, 0, GL_STATIC_DRAW); + g_program = glCreateProgram(); + + const char * vs = + "#version 150\n" + "uniform vec2 Viewport;\n" + "in vec2 VertexPosition;\n" + "in vec2 VertexTexCoord;\n" + "in vec4 VertexColor;\n" + "out vec2 texCoord;\n" + "out vec4 vertexColor;\n" + "void main(void)\n" + "{\n" + " vertexColor = VertexColor;\n" + " texCoord = VertexTexCoord;\n" + " gl_Position = vec4(VertexPosition * 2.0 / Viewport - 1.0, 0.f, 1.0);\n" + "}\n"; + GLuint vso = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(vso, 1, (const char **) &vs, NULL); + glCompileShader(vso); + glAttachShader(g_program, vso); + + const char * fs = + "#version 150\n" + "in vec2 texCoord;\n" + "in vec4 vertexColor;\n" + "uniform sampler2D Texture;\n" + "out vec4 Color;\n" + "void main(void)\n" + "{\n" + " float alpha = texture(Texture, texCoord).r;\n" + " Color = vec4(vertexColor.rgb, vertexColor.a * alpha);\n" + "}\n"; + GLuint fso = glCreateShader(GL_FRAGMENT_SHADER); + + glShaderSource(fso, 1, (const char **) &fs, NULL); + glCompileShader(fso); + glAttachShader(g_program, fso); + + glBindAttribLocation(g_program, 0, "VertexPosition"); + glBindAttribLocation(g_program, 1, "VertexTexCoord"); + glBindAttribLocation(g_program, 2, "VertexColor"); + glBindFragDataLocation(g_program, 0, "Color"); + glLinkProgram(g_program); + glDeleteShader(vso); + glDeleteShader(fso); + + glUseProgram(g_program); + g_programViewportLocation = glGetUniformLocation(g_program, "Viewport"); + g_programTextureLocation = glGetUniformLocation(g_program, "Texture"); + + glUseProgram(0); + + + free(ttfBuffer); + free(bmap); + + return true; +} + +void imguiRenderGLDestroy() +{ + if (g_ftex) + { + glDeleteTextures(1, &g_ftex); + g_ftex = 0; + } + + if (g_vao) + { + glDeleteVertexArrays(1, &g_vao); + glDeleteBuffers(3, g_vbos); + g_vao = 0; + } + + if (g_program) + { + glDeleteProgram(g_program); + g_program = 0; + } + +} + +static void getBakedQuad(stbtt_bakedchar *chardata, int pw, int ph, int char_index, + float *xpos, float *ypos, stbtt_aligned_quad *q) +{ + stbtt_bakedchar *b = chardata + char_index; + int round_x = STBTT_ifloor(*xpos + b->xoff); + int round_y = STBTT_ifloor(*ypos - b->yoff); + + q->x0 = (float)round_x; + q->y0 = (float)round_y; + q->x1 = (float)round_x + b->x1 - b->x0; + q->y1 = (float)round_y - b->y1 + b->y0; + + q->s0 = b->x0 / (float)pw; + q->t0 = b->y0 / (float)pw; + q->s1 = b->x1 / (float)ph; + q->t1 = b->y1 / (float)ph; + + *xpos += b->xadvance; +} + +static const float g_tabStops[4] = {150, 210, 270, 330}; + +static float getTextLength(stbtt_bakedchar *chardata, const char* text) +{ + float xpos = 0; + float len = 0; + while (*text) + { + int c = (unsigned char)*text; + if (c == '\t') + { + for (int i = 0; i < 4; ++i) + { + if (xpos < g_tabStops[i]) + { + xpos = g_tabStops[i]; + break; + } + } + } + else if (c >= 32 && c < 128) + { + stbtt_bakedchar *b = chardata + c-32; + int round_x = STBTT_ifloor((xpos + b->xoff) + 0.5); + len = round_x + b->x1 - b->x0 + 0.5f; + xpos += b->xadvance; + } + ++text; + } + return len; +} + +static void drawText(float x, float y, const char *text, int align, unsigned int col) +{ + if (!g_ftex) return; + if (!text) return; + + if (align == IMGUI_ALIGN_CENTER) + x -= getTextLength(g_cdata, text)/2; + else if (align == IMGUI_ALIGN_RIGHT) + x -= getTextLength(g_cdata, text); + + float r = (float) (col&0xff) / 255.f; + float g = (float) ((col>>8)&0xff) / 255.f; + float b = (float) ((col>>16)&0xff) / 255.f; + float a = (float) ((col>>24)&0xff) / 255.f; + + // assume orthographic projection with units = screen pixels, origin at top left + glBindTexture(GL_TEXTURE_2D, g_ftex); + + const float ox = x; + + while (*text) + { + int c = (unsigned char)*text; + if (c == '\t') + { + for (int i = 0; i < 4; ++i) + { + if (x < g_tabStops[i]+ox) + { + x = g_tabStops[i]+ox; + break; + } + } + } + else if (c >= 32 && c < 128) + { + stbtt_aligned_quad q; + getBakedQuad(g_cdata, 512,512, c-32, &x,&y,&q); + + float v[12] = { + q.x0, q.y0, + q.x1, q.y1, + q.x1, q.y0, + q.x0, q.y0, + q.x0, q.y1, + q.x1, q.y1, + }; + float uv[12] = { + q.s0, q.t0, + q.s1, q.t1, + q.s1, q.t0, + q.s0, q.t0, + q.s0, q.t1, + q.s1, q.t1, + }; + float c[24] = { + r, g, b, a, + r, g, b, a, + r, g, b, a, + r, g, b, a, + r, g, b, a, + r, g, b, a, + }; + glBindVertexArray(g_vao); + glBindBuffer(GL_ARRAY_BUFFER, g_vbos[0]); + glBufferData(GL_ARRAY_BUFFER, 12*sizeof(float), v, GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, g_vbos[1]); + glBufferData(GL_ARRAY_BUFFER, 12*sizeof(float), uv, GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, g_vbos[2]); + glBufferData(GL_ARRAY_BUFFER, 24*sizeof(float), c, GL_STATIC_DRAW); + glDrawArrays(GL_TRIANGLES, 0, 6); + + } + ++text; + } + + //glEnd(); + //glDisable(GL_TEXTURE_2D); +} + + +void imguiRenderGLDraw(int width, int height) +{ + const imguiGfxCmd* q = imguiGetRenderQueue(); + int nq = imguiGetRenderQueueSize(); + + const float s = 1.0f/8.0f; + + glViewport(0, 0, width, height); + glUseProgram(g_program); + glActiveTexture(GL_TEXTURE0); + glUniform2f(g_programViewportLocation, (float) width, (float) height); + glUniform1i(g_programTextureLocation, 0); + + + glDisable(GL_SCISSOR_TEST); + for (int i = 0; i < nq; ++i) + { + const imguiGfxCmd& cmd = q[i]; + if (cmd.type == IMGUI_GFXCMD_RECT) + { + if (cmd.rect.r == 0) + { + drawRect((float)cmd.rect.x*s+0.5f, (float)cmd.rect.y*s+0.5f, + (float)cmd.rect.w*s-1, (float)cmd.rect.h*s-1, + 1.0f, cmd.col); + } + else + { + drawRoundedRect((float)cmd.rect.x*s+0.5f, (float)cmd.rect.y*s+0.5f, + (float)cmd.rect.w*s-1, (float)cmd.rect.h*s-1, + (float)cmd.rect.r*s, 1.0f, cmd.col); + } + } + else if (cmd.type == IMGUI_GFXCMD_LINE) + { + drawLine(cmd.line.x0*s, cmd.line.y0*s, cmd.line.x1*s, cmd.line.y1*s, cmd.line.r*s, 1.0f, cmd.col); + } + else if (cmd.type == IMGUI_GFXCMD_TRIANGLE) + { + if (cmd.flags == 1) + { + const float verts[3*2] = + { + (float)cmd.rect.x*s+0.5f, (float)cmd.rect.y*s+0.5f, + (float)cmd.rect.x*s+0.5f+(float)cmd.rect.w*s-1, (float)cmd.rect.y*s+0.5f+(float)cmd.rect.h*s/2-0.5f, + (float)cmd.rect.x*s+0.5f, (float)cmd.rect.y*s+0.5f+(float)cmd.rect.h*s-1, + }; + drawPolygon(verts, 3, 1.0f, cmd.col); + } + if (cmd.flags == 2) + { + const float verts[3*2] = + { + (float)cmd.rect.x*s+0.5f, (float)cmd.rect.y*s+0.5f+(float)cmd.rect.h*s-1, + (float)cmd.rect.x*s+0.5f+(float)cmd.rect.w*s/2-0.5f, (float)cmd.rect.y*s+0.5f, + (float)cmd.rect.x*s+0.5f+(float)cmd.rect.w*s-1, (float)cmd.rect.y*s+0.5f+(float)cmd.rect.h*s-1, + }; + drawPolygon(verts, 3, 1.0f, cmd.col); + } + } + else if (cmd.type == IMGUI_GFXCMD_TEXT) + { + drawText(cmd.text.x, cmd.text.y, cmd.text.text, cmd.text.align, cmd.col); + } + else if (cmd.type == IMGUI_GFXCMD_SCISSOR) + { + if (cmd.flags) + { + glEnable(GL_SCISSOR_TEST); + glScissor(cmd.rect.x, cmd.rect.y, cmd.rect.w, cmd.rect.h); + } + else + { + glDisable(GL_SCISSOR_TEST); + } + } + } + glDisable(GL_SCISSOR_TEST); +} diff --git a/testbed/imgui/imguiRenderGL3.h b/testbed/imgui/imguiRenderGL3.h new file mode 100644 index 00000000..6c84a043 --- /dev/null +++ b/testbed/imgui/imguiRenderGL3.h @@ -0,0 +1,28 @@ +// +// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +// + +// Source altered and distributed from https://github.com/AdrienHerubel/imgui + +#ifndef IMGUI_RENDER_GL_H +#define IMGUI_RENDER_GL_H + +bool imguiRenderGLInit(const char* fontpath); +void imguiRenderGLDestroy(); +void imguiRenderGLDraw(int width, int height); + +#endif // IMGUI_RENDER_GL_H \ No newline at end of file diff --git a/testbed/imgui/stb_truetype.h b/testbed/imgui/stb_truetype.h new file mode 100644 index 00000000..9f07124a --- /dev/null +++ b/testbed/imgui/stb_truetype.h @@ -0,0 +1,2065 @@ +// stb_truetype.h - v0.6c - public domain +// authored from 2009-2012 by Sean Barrett / RAD Game Tools +// +// This library processes TrueType files: +// parse files +// extract glyph metrics +// extract glyph shapes +// render glyphs to one-channel bitmaps with antialiasing (box filter) +// +// Todo: +// non-MS cmaps +// crashproof on bad data +// hinting? (no longer patented) +// cleartype-style AA? +// optimize: use simple memory allocator for intermediates +// optimize: build edge-list directly from curves +// optimize: rasterize directly from curves? +// +// ADDITIONAL CONTRIBUTORS +// +// Mikko Mononen: compound shape support, more cmap formats +// Tor Andersson: kerning, subpixel rendering +// +// Bug/warning reports: +// "Zer" on mollyrocket (with fix) +// Cass Everitt +// stoiko (Haemimont Games) +// Brian Hook +// Walter van Niftrik +// +// VERSION HISTORY +// +// 0.6c (2012-07-24) improve documentation +// 0.6b (2012-07-20) fix a few more warnings +// 0.6 (2012-07-17) fix warnings; added stbtt_ScaleForMappingEmToPixels, +// stbtt_GetFontBoundingBox, stbtt_IsGlyphEmpty +// 0.5 (2011-12-09) bugfixes: +// subpixel glyph renderer computed wrong bounding box +// first vertex of shape can be off-curve (FreeSans) +// 0.4b (2011-12-03) fixed an error in the font baking example +// 0.4 (2011-12-01) kerning, subpixel rendering (tor) +// bugfixes for: +// codepoint-to-glyph conversion using table fmt=12 +// codepoint-to-glyph conversion using table fmt=4 +// stbtt_GetBakedQuad with non-square texture (Zer) +// updated Hello World! sample to use kerning and subpixel +// fixed some warnings +// 0.3 (2009-06-24) cmap fmt=12, compound shapes (MM) +// userdata, malloc-from-userdata, non-zero fill (STB) +// 0.2 (2009-03-11) Fix unsigned/signed char warnings +// 0.1 (2009-03-09) First public release +// +// LICENSE +// +// This software is in the public domain. Where that dedication is not +// recognized, you are granted a perpetual, irrevokable license to copy +// and modify this file as you see fit. +// +// USAGE +// +// Include this file in whatever places neeed to refer to it. In ONE C/C++ +// file, write: +// #define STB_TRUETYPE_IMPLEMENTATION +// before the #include of this file. This expands out the actual +// implementation into that C/C++ file. +// +// Simple 3D API (don't ship this, but it's fine for tools and quick start, +// and you can cut and paste from it to move to more advanced) +// stbtt_BakeFontBitmap() -- bake a font to a bitmap for use as texture +// stbtt_GetBakedQuad() -- compute quad to draw for a given char +// +// "Load" a font file from a memory buffer (you have to keep the buffer loaded) +// stbtt_InitFont() +// stbtt_GetFontOffsetForIndex() -- use for TTC font collections +// +// Render a unicode codepoint to a bitmap +// stbtt_GetCodepointBitmap() -- allocates and returns a bitmap +// stbtt_MakeCodepointBitmap() -- renders into bitmap you provide +// stbtt_GetCodepointBitmapBox() -- how big the bitmap must be +// +// Character advance/positioning +// stbtt_GetCodepointHMetrics() +// stbtt_GetFontVMetrics() +// stbtt_GetCodepointKernAdvance() +// +// ADDITIONAL DOCUMENTATION +// +// Immediately after this block comment are a series of sample programs. +// +// After the sample programs is the "header file" section. This section +// includes documentation for each API function. +// +// Some important concepts to understand to use this library: +// +// Codepoint +// Characters are defined by unicode codepoints, e.g. 65 is +// uppercase A, 231 is lowercase c with a cedilla, 0x7e30 is +// the hiragana for "ma". +// +// Glyph +// A visual character shape (every codepoint is rendered as +// some glyph) +// +// Glyph index +// A font-specific integer ID representing a glyph +// +// Baseline +// Glyph shapes are defined relative to a baseline, which is the +// bottom of uppercase characters. Characters extend both above +// and below the baseline. +// +// Current Point +// As you draw text to the screen, you keep track of a "current point" +// which is the origin of each character. The current point's vertical +// position is the baseline. Even "baked fonts" use this model. +// +// Vertical Font Metrics +// The vertical qualities of the font, used to vertically position +// and space the characters. See docs for stbtt_GetFontVMetrics. +// +// Font Size in Pixels or Points +// The preferred interface for specifying font sizes in stb_truetype +// is to specify how tall the font's vertical extent should be in pixels. +// If that sounds good enough, skip the next paragraph. +// +// Most font APIs instead use "points", which are a common typographic +// measurement for describing font size, defined as 72 points per inch. +// stb_truetype provides a point API for compatibility. However, true +// "per inch" conventions don't make much sense on computer displays +// since they different monitors have different number of pixels per +// inch. For example, Windows traditionally uses a convention that +// there are 96 pixels per inch, thus making 'inch' measurements have +// nothing to do with inches, and thus effectively defining a point to +// be 1.333 pixels. Additionally, the TrueType font data provides +// an explicit scale factor to scale a given font's glyphs to points, +// but the author has observed that this scale factor is often wrong +// for non-commercial fonts, thus making fonts scaled in points +// according to the TrueType spec incoherently sized in practice. +// +// ADVANCED USAGE +// +// Quality: +// +// - Use the functions with Subpixel at the end to allow your characters +// to have subpixel positioning. Since the font is anti-aliased, not +// hinted, this is very import for quality. (This is not possible with +// baked fonts.) +// +// - Kerning is now supported, and if you're supporting subpixel rendering +// then kerning is worth using to give your text a polished look. +// +// Performance: +// +// - Convert Unicode codepoints to glyph indexes and operate on the glyphs; +// if you don't do this, stb_truetype is forced to do the conversion on +// every call. +// +// - There are a lot of memory allocations. We should modify it to take +// a temp buffer and allocate from the temp buffer (without freeing), +// should help performance a lot. +// +// NOTES +// +// The system uses the raw data found in the .ttf file without changing it +// and without building auxiliary data structures. This is a bit inefficient +// on little-endian systems (the data is big-endian), but assuming you're +// caching the bitmaps or glyph shapes this shouldn't be a big deal. +// +// It appears to be very hard to programmatically determine what font a +// given file is in a general way. I provide an API for this, but I don't +// recommend it. +// +// +// SOURCE STATISTICS (based on v0.6c, 2050 LOC) +// +// Documentation & header file 520 LOC \___ 660 LOC documentation +// Sample code 140 LOC / +// Truetype parsing 620 LOC ---- 620 LOC TrueType +// Software rasterization 240 LOC \ . +// Curve tesselation 120 LOC \__ 550 LOC Bitmap creation +// Bitmap management 100 LOC / +// Baked bitmap interface 70 LOC / +// Font name matching & access 150 LOC ---- 150 +// C runtime library abstraction 60 LOC ---- 60 + + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +//// +//// SAMPLE PROGRAMS +//// +// +// Incomplete text-in-3d-api example, which draws quads properly aligned to be lossless +// +#if 0 +#define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation +#include "stb_truetype.h" + +char ttf_buffer[1<<20]; +unsigned char temp_bitmap[512*512]; + +stbtt_bakedchar cdata[96]; // ASCII 32..126 is 95 glyphs +GLstbtt_uint ftex; + +void my_stbtt_initfont(void) +{ + fread(ttf_buffer, 1, 1<<20, fopen("c:/windows/fonts/times.ttf", "rb")); + stbtt_BakeFontBitmap(data,0, 32.0, temp_bitmap,512,512, 32,96, cdata); // no guarantee this fits! + // can free ttf_buffer at this point + glGenTextures(1, &ftex); + glBindTexture(GL_TEXTURE_2D, ftex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, 512,512, 0, GL_ALPHA, GL_UNSIGNED_BYTE, temp_bitmap); + // can free temp_bitmap at this point + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); +} + +void my_stbtt_print(float x, float y, char *text) +{ + // assume orthographic projection with units = screen pixels, origin at top left + glBindTexture(GL_TEXTURE_2D, ftex); + glBegin(GL_QUADS); + while (*text) { + if (*text >= 32 && *text < 128) { + stbtt_aligned_quad q; + stbtt_GetBakedQuad(cdata, 512,512, *text-32, &x,&y,&q,1);//1=opengl,0=old d3d + glTexCoord2f(q.s0,q.t1); glVertex2f(q.x0,q.y0); + glTexCoord2f(q.s1,q.t1); glVertex2f(q.x1,q.y0); + glTexCoord2f(q.s1,q.t0); glVertex2f(q.x1,q.y1); + glTexCoord2f(q.s0,q.t0); glVertex2f(q.x0,q.y1); + } + ++text; + } + glEnd(); +} +#endif +// +// +////////////////////////////////////////////////////////////////////////////// +// +// Complete program (this compiles): get a single bitmap, print as ASCII art +// +#if 0 +#include +#define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation +#include "stb_truetype.h" + +char ttf_buffer[1<<25]; + +int main(int argc, char **argv) +{ + stbtt_fontinfo font; + unsigned char *bitmap; + int w,h,i,j,c = (argc > 1 ? atoi(argv[1]) : 'a'), s = (argc > 2 ? atoi(argv[2]) : 20); + + fread(ttf_buffer, 1, 1<<25, fopen(argc > 3 ? argv[3] : "c:/windows/fonts/arialbd.ttf", "rb")); + + stbtt_InitFont(&font, ttf_buffer, stbtt_GetFontOffsetForIndex(ttf_buffer,0)); + bitmap = stbtt_GetCodepointBitmap(&font, 0,stbtt_ScaleForPixelHeight(&font, s), c, &w, &h, 0,0); + + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) + putchar(" .:ioVM@"[bitmap[j*w+i]>>5]); + putchar('\n'); + } + return 0; +} +#endif +// +// Output: +// +// .ii. +// @@@@@@. +// V@Mio@@o +// :i. V@V +// :oM@@M +// :@@@MM@M +// @@o o@M +// :@@. M@M +// @@@o@@@@ +// :M@@V:@@. +// +////////////////////////////////////////////////////////////////////////////// +// +// Complete program: print "Hello World!" banner, with bugs +// +#if 0 +char buffer[24<<20]; +unsigned char screen[20][79]; + +int main(int arg, char **argv) +{ + stbtt_fontinfo font; + int i,j,ascent,baseline,ch=0; + float scale, xpos=0; + char *text = "Heljo World!"; + + fread(buffer, 1, 1000000, fopen("c:/windows/fonts/arialbd.ttf", "rb")); + stbtt_InitFont(&font, buffer, 0); + + scale = stbtt_ScaleForPixelHeight(&font, 15); + stbtt_GetFontVMetrics(&font, &ascent,0,0); + baseline = (int) (ascent*scale); + + while (text[ch]) { + int advance,lsb,x0,y0,x1,y1; + float x_shift = xpos - (float) floor(xpos); + stbtt_GetCodepointHMetrics(&font, text[ch], &advance, &lsb); + stbtt_GetCodepointBitmapBoxSubpixel(&font, text[ch], scale,scale,x_shift,0, &x0,&y0,&x1,&y1); + stbtt_MakeCodepointBitmapSubpixel(&font, &screen[baseline + y0][(int) xpos + x0], x1-x0,y1-y0, 79, scale,scale,x_shift,0, text[ch]); + // note that this stomps the old data, so where character boxes overlap (e.g. 'lj') it's wrong + // because this API is really for baking character bitmaps into textures. if you want to render + // a sequence of characters, you really need to render each bitmap to a temp buffer, then + // "alpha blend" that into the working buffer + xpos += (advance * scale); + if (text[ch+1]) + xpos += scale*stbtt_GetCodepointKernAdvance(&font, text[ch],text[ch+1]); + ++ch; + } + + for (j=0; j < 20; ++j) { + for (i=0; i < 78; ++i) + putchar(" .:ioVM@"[screen[j][i]>>5]); + putchar('\n'); + } + + return 0; +} +#endif + + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +//// +//// INTEGRATION WITH YOUR CODEBASE +//// +//// The following sections allow you to supply alternate definitions +//// of C library functions used by stb_truetype. + +#ifdef STB_TRUETYPE_IMPLEMENTATION + // #define your own (u)stbtt_int8/16/32 before including to override this + #ifndef stbtt_uint8 + typedef unsigned char stbtt_uint8; + typedef signed char stbtt_int8; + typedef unsigned short stbtt_uint16; + typedef signed short stbtt_int16; + typedef unsigned int stbtt_uint32; + typedef signed int stbtt_int32; + #endif + + typedef char stbtt__check_size32[sizeof(stbtt_int32)==4 ? 1 : -1]; + typedef char stbtt__check_size16[sizeof(stbtt_int16)==2 ? 1 : -1]; + + // #define your own STBTT_sort() to override this to avoid qsort + #ifndef STBTT_sort + #include + #define STBTT_sort(data,num_items,item_size,compare_func) qsort(data,num_items,item_size,compare_func) + #endif + + // #define your own STBTT_ifloor/STBTT_iceil() to avoid math.h + #ifndef STBTT_ifloor + #include + #define STBTT_ifloor(x) ((int) floor(x)) + #define STBTT_iceil(x) ((int) ceil(x)) + #endif + + // #define your own functions "STBTT_malloc" / "STBTT_free" to avoid malloc.h + #ifndef STBTT_malloc + #include + #define STBTT_malloc(x,u) malloc(x) + #define STBTT_free(x,u) free(x) + #endif + + #ifndef STBTT_assert + #include + #define STBTT_assert(x) assert(x) + #endif + + #ifndef STBTT_strlen + #include + #define STBTT_strlen(x) strlen(x) + #endif + + #ifndef STBTT_memcpy + #include + #define STBTT_memcpy memcpy + #define STBTT_memset memset + #endif +#endif + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +//// +//// INTERFACE +//// +//// + +#ifndef __STB_INCLUDE_STB_TRUETYPE_H__ +#define __STB_INCLUDE_STB_TRUETYPE_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// TEXTURE BAKING API +// +// If you use this API, you only have to call two functions ever. +// + +typedef struct +{ + unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap + float xoff,yoff,xadvance; +} stbtt_bakedchar; + +extern int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // font location (use offset=0 for plain .ttf) + float pixel_height, // height of font in pixels + unsigned char *pixels, int pw, int ph, // bitmap to be filled in + int first_char, int num_chars, // characters to bake + stbtt_bakedchar *chardata); // you allocate this, it's num_chars long +// if return is positive, the first unused row of the bitmap +// if return is negative, returns the negative of the number of characters that fit +// if return is 0, no characters fit and no rows were used +// This uses a very crappy packing. + +typedef struct +{ + float x0,y0,s0,t0; // top-left + float x1,y1,s1,t1; // bottom-right +} stbtt_aligned_quad; + +extern void stbtt_GetBakedQuad(stbtt_bakedchar *chardata, int pw, int ph, // same data as above + int char_index, // character to display + float *xpos, float *ypos, // pointers to current position in screen pixel space + stbtt_aligned_quad *q, // output: quad to draw + int opengl_fillrule); // true if opengl fill rule; false if DX9 or earlier +// Call GetBakedQuad with char_index = 'character - first_char', and it +// creates the quad you need to draw and advances the current position. +// +// The coordinate system used assumes y increases downwards. +// +// Characters will extend both above and below the current position; +// see discussion of "BASELINE" above. +// +// It's inefficient; you might want to c&p it and optimize it. + + +////////////////////////////////////////////////////////////////////////////// +// +// FONT LOADING +// +// + +extern int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index); +// Each .ttf/.ttc file may have more than one font. Each font has a sequential +// index number starting from 0. Call this function to get the font offset for +// a given index; it returns -1 if the index is out of range. A regular .ttf +// file will only define one font and it always be at offset 0, so it will +// return '0' for index 0, and -1 for all other indices. You can just skip +// this step if you know it's that kind of font. + + +// The following structure is defined publically so you can declare one on +// the stack or as a global or etc, but you should treat it as opaque. +typedef struct stbtt_fontinfo +{ + void * userdata; + unsigned char * data; // pointer to .ttf file + int fontstart; // offset of start of font + + int numGlyphs; // number of glyphs, needed for range checking + + int loca,head,glyf,hhea,hmtx,kern; // table locations as offset from start of .ttf + int index_map; // a cmap mapping for our chosen character encoding + int indexToLocFormat; // format needed to map from glyph index to glyph +} stbtt_fontinfo; + +extern int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset); +// Given an offset into the file that defines a font, this function builds +// the necessary cached info for the rest of the system. You must allocate +// the stbtt_fontinfo yourself, and stbtt_InitFont will fill it out. You don't +// need to do anything special to free it, because the contents are pure +// value data with no additional data structures. Returns 0 on failure. + + +////////////////////////////////////////////////////////////////////////////// +// +// CHARACTER TO GLYPH-INDEX CONVERSIOn + +int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint); +// If you're going to perform multiple operations on the same character +// and you want a speed-up, call this function with the character you're +// going to process, then use glyph-based functions instead of the +// codepoint-based functions. + + +////////////////////////////////////////////////////////////////////////////// +// +// CHARACTER PROPERTIES +// + +extern float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float pixels); +// computes a scale factor to produce a font whose "height" is 'pixels' tall. +// Height is measured as the distance from the highest ascender to the lowest +// descender; in other words, it's equivalent to calling stbtt_GetFontVMetrics +// and computing: +// scale = pixels / (ascent - descent) +// so if you prefer to measure height by the ascent only, use a similar calculation. + +extern float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels); +// computes a scale factor to produce a font whose EM size is mapped to +// 'pixels' tall. This is probably what traditional APIs compute, but +// I'm not positive. + +extern void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap); +// ascent is the coordinate above the baseline the font extends; descent +// is the coordinate below the baseline the font extends (i.e. it is typically negative) +// lineGap is the spacing between one row's descent and the next row's ascent... +// so you should advance the vertical position by "*ascent - *descent + *lineGap" +// these are expressed in unscaled coordinates, so you must multiply by +// the scale factor for a given size + +extern void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1); +// the bounding box around all possible characters + +extern void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing); +// leftSideBearing is the offset from the current horizontal position to the left edge of the character +// advanceWidth is the offset from the current horizontal position to the next horizontal position +// these are expressed in unscaled coordinates + +extern int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2); +// an additional amount to add to the 'advance' value between ch1 and ch2 +// @TODO; for now always returns 0! + +extern int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1); +// Gets the bounding box of the visible part of the glyph, in unscaled coordinates + +extern void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing); +extern int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2); +extern int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1); +// as above, but takes one or more glyph indices for greater efficiency + + +////////////////////////////////////////////////////////////////////////////// +// +// GLYPH SHAPES (you probably don't need these, but they have to go before +// the bitmaps for C declaration-order reasons) +// + +#ifndef STBTT_vmove // you can predefine these to use different values (but why?) + enum { + STBTT_vmove=1, + STBTT_vline, + STBTT_vcurve + }; +#endif + +#ifndef stbtt_vertex // you can predefine this to use different values + // (we share this with other code at RAD) + #define stbtt_vertex_type short // can't use stbtt_int16 because that's not visible in the header file + typedef struct + { + stbtt_vertex_type x,y,cx,cy; + unsigned char type,padding; + } stbtt_vertex; +#endif + +extern int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index); +// returns non-zero if nothing is drawn for this glyph + +extern int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices); +extern int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **vertices); +// returns # of vertices and fills *vertices with the pointer to them +// these are expressed in "unscaled" coordinates + +extern void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *vertices); +// frees the data allocated above + +////////////////////////////////////////////////////////////////////////////// +// +// BITMAP RENDERING +// + +extern void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata); +// frees the bitmap allocated below + +extern unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff); +// allocates a large-enough single-channel 8bpp bitmap and renders the +// specified character/glyph at the specified scale into it, with +// antialiasing. 0 is no coverage (transparent), 255 is fully covered (opaque). +// *width & *height are filled out with the width & height of the bitmap, +// which is stored left-to-right, top-to-bottom. +// +// xoff/yoff are the offset it pixel space from the glyph origin to the top-left of the bitmap + +extern unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff); +// the same as stbtt_GetCodepoitnBitmap, but you can specify a subpixel +// shift for the character + +extern void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint); +// the same as stbtt_GetCodepointBitmap, but you pass in storage for the bitmap +// in the form of 'output', with row spacing of 'out_stride' bytes. the bitmap +// is clipped to out_w/out_h bytes. Call stbtt_GetCodepointBitmapBox to get the +// width and height and positioning info for it first. + +extern void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint); +// same as stbtt_MakeCodepointBitmap, but you can specify a subpixel +// shift for the character + +extern void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1); +// get the bbox of the bitmap centered around the glyph origin; so the +// bitmap width is ix1-ix0, height is iy1-iy0, and location to place +// the bitmap top left is (leftSideBearing*scale,iy0). +// (Note that the bitmap uses y-increases-down, but the shape uses +// y-increases-up, so CodepointBitmapBox and CodepointBox are inverted.) + +extern void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1); +// same as stbtt_GetCodepointBitmapBox, but you can specify a subpixel +// shift for the character + +// the following functions are equivalent to the above functions, but operate +// on glyph indices instead of Unicode codepoints (for efficiency) +extern unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff); +extern unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff); +extern void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph); +extern void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph); +extern void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1); +extern void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1); + + +// @TODO: don't expose this structure +typedef struct +{ + int w,h,stride; + unsigned char *pixels; +} stbtt__bitmap; + +extern void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, float shift_x, float shift_y, int x_off, int y_off, int invert, void *userdata); + +////////////////////////////////////////////////////////////////////////////// +// +// Finding the right font... +// +// You should really just solve this offline, keep your own tables +// of what font is what, and don't try to get it out of the .ttf file. +// That's because getting it out of the .ttf file is really hard, because +// the names in the file can appear in many possible encodings, in many +// possible languages, and e.g. if you need a case-insensitive comparison, +// the details of that depend on the encoding & language in a complex way +// (actually underspecified in truetype, but also gigantic). +// +// But you can use the provided functions in two possible ways: +// stbtt_FindMatchingFont() will use *case-sensitive* comparisons on +// unicode-encoded names to try to find the font you want; +// you can run this before calling stbtt_InitFont() +// +// stbtt_GetFontNameString() lets you get any of the various strings +// from the file yourself and do your own comparisons on them. +// You have to have called stbtt_InitFont() first. + + +extern int stbtt_FindMatchingFont(const unsigned char *fontdata, const char *name, int flags); +// returns the offset (not index) of the font that matches, or -1 if none +// if you use STBTT_MACSTYLE_DONTCARE, use a font name like "Arial Bold". +// if you use any other flag, use a font name like "Arial"; this checks +// the 'macStyle' header field; i don't know if fonts set this consistently +#define STBTT_MACSTYLE_DONTCARE 0 +#define STBTT_MACSTYLE_BOLD 1 +#define STBTT_MACSTYLE_ITALIC 2 +#define STBTT_MACSTYLE_UNDERSCORE 4 +#define STBTT_MACSTYLE_NONE 8 // <= not same as 0, this makes us check the bitfield is 0 + +extern int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2); +// returns 1/0 whether the first string interpreted as utf8 is identical to +// the second string interpreted as big-endian utf16... useful for strings from next func + +extern const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID); +// returns the string (which may be big-endian double byte, e.g. for unicode) +// and puts the length in bytes in *length. +// +// some of the values for the IDs are below; for more see the truetype spec: +// http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6name.html +// http://www.microsoft.com/typography/otspec/name.htm + +enum { // platformID + STBTT_PLATFORM_ID_UNICODE =0, + STBTT_PLATFORM_ID_MAC =1, + STBTT_PLATFORM_ID_ISO =2, + STBTT_PLATFORM_ID_MICROSOFT =3 +}; + +enum { // encodingID for STBTT_PLATFORM_ID_UNICODE + STBTT_UNICODE_EID_UNICODE_1_0 =0, + STBTT_UNICODE_EID_UNICODE_1_1 =1, + STBTT_UNICODE_EID_ISO_10646 =2, + STBTT_UNICODE_EID_UNICODE_2_0_BMP=3, + STBTT_UNICODE_EID_UNICODE_2_0_FULL=4 +}; + +enum { // encodingID for STBTT_PLATFORM_ID_MICROSOFT + STBTT_MS_EID_SYMBOL =0, + STBTT_MS_EID_UNICODE_BMP =1, + STBTT_MS_EID_SHIFTJIS =2, + STBTT_MS_EID_UNICODE_FULL =10 +}; + +enum { // encodingID for STBTT_PLATFORM_ID_MAC; same as Script Manager codes + STBTT_MAC_EID_ROMAN =0, STBTT_MAC_EID_ARABIC =4, + STBTT_MAC_EID_JAPANESE =1, STBTT_MAC_EID_HEBREW =5, + STBTT_MAC_EID_CHINESE_TRAD =2, STBTT_MAC_EID_GREEK =6, + STBTT_MAC_EID_KOREAN =3, STBTT_MAC_EID_RUSSIAN =7 +}; + +enum { // languageID for STBTT_PLATFORM_ID_MICROSOFT; same as LCID... + // problematic because there are e.g. 16 english LCIDs and 16 arabic LCIDs + STBTT_MS_LANG_ENGLISH =0x0409, STBTT_MS_LANG_ITALIAN =0x0410, + STBTT_MS_LANG_CHINESE =0x0804, STBTT_MS_LANG_JAPANESE =0x0411, + STBTT_MS_LANG_DUTCH =0x0413, STBTT_MS_LANG_KOREAN =0x0412, + STBTT_MS_LANG_FRENCH =0x040c, STBTT_MS_LANG_RUSSIAN =0x0419, + STBTT_MS_LANG_GERMAN =0x0407, STBTT_MS_LANG_SPANISH =0x0409, + STBTT_MS_LANG_HEBREW =0x040d, STBTT_MS_LANG_SWEDISH =0x041D +}; + +enum { // languageID for STBTT_PLATFORM_ID_MAC + STBTT_MAC_LANG_ENGLISH =0 , STBTT_MAC_LANG_JAPANESE =11, + STBTT_MAC_LANG_ARABIC =12, STBTT_MAC_LANG_KOREAN =23, + STBTT_MAC_LANG_DUTCH =4 , STBTT_MAC_LANG_RUSSIAN =32, + STBTT_MAC_LANG_FRENCH =1 , STBTT_MAC_LANG_SPANISH =6 , + STBTT_MAC_LANG_GERMAN =2 , STBTT_MAC_LANG_SWEDISH =5 , + STBTT_MAC_LANG_HEBREW =10, STBTT_MAC_LANG_CHINESE_SIMPLIFIED =33, + STBTT_MAC_LANG_ITALIAN =3 , STBTT_MAC_LANG_CHINESE_TRAD =19 +}; + +#ifdef __cplusplus +} +#endif + +#endif // __STB_INCLUDE_STB_TRUETYPE_H__ + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +//// +//// IMPLEMENTATION +//// +//// + +#ifdef STB_TRUETYPE_IMPLEMENTATION + +////////////////////////////////////////////////////////////////////////// +// +// accessors to parse data from file +// + +// on platforms that don't allow misaligned reads, if we want to allow +// truetype fonts that aren't padded to alignment, define ALLOW_UNALIGNED_TRUETYPE + +#define ttBYTE(p) (* (stbtt_uint8 *) (p)) +#define ttCHAR(p) (* (stbtt_int8 *) (p)) +#define ttFixed(p) ttLONG(p) + +#if defined(STB_TRUETYPE_BIGENDIAN) && !defined(ALLOW_UNALIGNED_TRUETYPE) + + #define ttUSHORT(p) (* (stbtt_uint16 *) (p)) + #define ttSHORT(p) (* (stbtt_int16 *) (p)) + #define ttULONG(p) (* (stbtt_uint32 *) (p)) + #define ttLONG(p) (* (stbtt_int32 *) (p)) + +#else + + stbtt_uint16 ttUSHORT(const stbtt_uint8 *p) { return p[0]*256 + p[1]; } + stbtt_int16 ttSHORT(const stbtt_uint8 *p) { return p[0]*256 + p[1]; } + stbtt_uint32 ttULONG(const stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } + stbtt_int32 ttLONG(const stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } + +#endif + +#define stbtt_tag4(p,c0,c1,c2,c3) ((p)[0] == (c0) && (p)[1] == (c1) && (p)[2] == (c2) && (p)[3] == (c3)) +#define stbtt_tag(p,str) stbtt_tag4(p,str[0],str[1],str[2],str[3]) + +static int stbtt__isfont(const stbtt_uint8 *font) +{ + // check the version number + if (stbtt_tag4(font, '1',0,0,0)) return 1; // TrueType 1 + if (stbtt_tag(font, "typ1")) return 1; // TrueType with type 1 font -- we don't support this! + if (stbtt_tag(font, "OTTO")) return 1; // OpenType with CFF + if (stbtt_tag4(font, 0,1,0,0)) return 1; // OpenType 1.0 + return 0; +} + +// @OPTIMIZE: binary search +static stbtt_uint32 stbtt__find_table(stbtt_uint8 *data, stbtt_uint32 fontstart, const char *tag) +{ + stbtt_int32 num_tables = ttUSHORT(data+fontstart+4); + stbtt_uint32 tabledir = fontstart + 12; + stbtt_int32 i; + for (i=0; i < num_tables; ++i) { + stbtt_uint32 loc = tabledir + 16*i; + if (stbtt_tag(data+loc+0, tag)) + return ttULONG(data+loc+8); + } + return 0; +} + +int stbtt_GetFontOffsetForIndex(const unsigned char *font_collection, int index) +{ + // if it's just a font, there's only one valid index + if (stbtt__isfont(font_collection)) + return index == 0 ? 0 : -1; + + // check if it's a TTC + if (stbtt_tag(font_collection, "ttcf")) { + // version 1? + if (ttULONG(font_collection+4) == 0x00010000 || ttULONG(font_collection+4) == 0x00020000) { + stbtt_int32 n = ttLONG(font_collection+8); + if (index >= n) + return -1; + return ttULONG(font_collection+12+index*14); + } + } + return -1; +} + +int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data2, int fontstart) +{ + stbtt_uint8 *data = (stbtt_uint8 *) data2; + stbtt_uint32 cmap, t; + stbtt_int32 i,numTables; + + info->data = data; + info->fontstart = fontstart; + + cmap = stbtt__find_table(data, fontstart, "cmap"); // required + info->loca = stbtt__find_table(data, fontstart, "loca"); // required + info->head = stbtt__find_table(data, fontstart, "head"); // required + info->glyf = stbtt__find_table(data, fontstart, "glyf"); // required + info->hhea = stbtt__find_table(data, fontstart, "hhea"); // required + info->hmtx = stbtt__find_table(data, fontstart, "hmtx"); // required + info->kern = stbtt__find_table(data, fontstart, "kern"); // not required + if (!cmap || !info->loca || !info->head || !info->glyf || !info->hhea || !info->hmtx) + return 0; + + t = stbtt__find_table(data, fontstart, "maxp"); + if (t) + info->numGlyphs = ttUSHORT(data+t+4); + else + info->numGlyphs = 0xffff; + + // find a cmap encoding table we understand *now* to avoid searching + // later. (todo: could make this installable) + // the same regardless of glyph. + numTables = ttUSHORT(data + cmap + 2); + info->index_map = 0; + for (i=0; i < numTables; ++i) { + stbtt_uint32 encoding_record = cmap + 4 + 8 * i; + // find an encoding we understand: + switch(ttUSHORT(data+encoding_record)) { + case STBTT_PLATFORM_ID_MICROSOFT: + switch (ttUSHORT(data+encoding_record+2)) { + case STBTT_MS_EID_UNICODE_BMP: + case STBTT_MS_EID_UNICODE_FULL: + // MS/Unicode + info->index_map = cmap + ttULONG(data+encoding_record+4); + break; + } + break; + } + } + if (info->index_map == 0) + return 0; + + info->indexToLocFormat = ttUSHORT(data+info->head + 50); + return 1; +} + +int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint) +{ + stbtt_uint8 *data = info->data; + stbtt_uint32 index_map = info->index_map; + + stbtt_uint16 format = ttUSHORT(data + index_map + 0); + if (format == 0) { // apple byte encoding + stbtt_int32 bytes = ttUSHORT(data + index_map + 2); + if (unicode_codepoint < bytes-6) + return ttBYTE(data + index_map + 6 + unicode_codepoint); + return 0; + } else if (format == 6) { + stbtt_uint32 first = ttUSHORT(data + index_map + 6); + stbtt_uint32 count = ttUSHORT(data + index_map + 8); + if ((stbtt_uint32) unicode_codepoint >= first && (stbtt_uint32) unicode_codepoint < first+count) + return ttUSHORT(data + index_map + 10 + (unicode_codepoint - first)*2); + return 0; + } else if (format == 2) { + STBTT_assert(0); // @TODO: high-byte mapping for japanese/chinese/korean + return 0; + } else if (format == 4) { // standard mapping for windows fonts: binary search collection of ranges + stbtt_uint16 segcount = ttUSHORT(data+index_map+6) >> 1; + stbtt_uint16 searchRange = ttUSHORT(data+index_map+8) >> 1; + stbtt_uint16 entrySelector = ttUSHORT(data+index_map+10); + stbtt_uint16 rangeShift = ttUSHORT(data+index_map+12) >> 1; + stbtt_uint16 item, offset, start, end; + + // do a binary search of the segments + stbtt_uint32 endCount = index_map + 14; + stbtt_uint32 search = endCount; + + if (unicode_codepoint > 0xffff) + return 0; + + // they lie from endCount .. endCount + segCount + // but searchRange is the nearest power of two, so... + if (unicode_codepoint >= ttUSHORT(data + search + rangeShift*2)) + search += rangeShift*2; + + // now decrement to bias correctly to find smallest + search -= 2; + while (entrySelector) { + stbtt_uint16 start, end; + searchRange >>= 1; + start = ttUSHORT(data + search + 2 + segcount*2 + 2); + end = ttUSHORT(data + search + 2); + start = ttUSHORT(data + search + searchRange*2 + segcount*2 + 2); + end = ttUSHORT(data + search + searchRange*2); + if (unicode_codepoint > end) + search += searchRange*2; + --entrySelector; + } + search += 2; + + item = (stbtt_uint16) ((search - endCount) >> 1); + + STBTT_assert(unicode_codepoint <= ttUSHORT(data + endCount + 2*item)); + start = ttUSHORT(data + index_map + 14 + segcount*2 + 2 + 2*item); + end = ttUSHORT(data + index_map + 14 + 2 + 2*item); + if (unicode_codepoint < start) + return 0; + + offset = ttUSHORT(data + index_map + 14 + segcount*6 + 2 + 2*item); + if (offset == 0) + return (stbtt_uint16) (unicode_codepoint + ttSHORT(data + index_map + 14 + segcount*4 + 2 + 2*item)); + + return ttUSHORT(data + offset + (unicode_codepoint-start)*2 + index_map + 14 + segcount*6 + 2 + 2*item); + } else if (format == 12 || format == 13) { + stbtt_uint32 ngroups = ttULONG(data+index_map+12); + stbtt_int32 low,high; + low = 0; high = (stbtt_int32)ngroups; + // Binary search the right group. + while (low < high) { + stbtt_int32 mid = low + ((high-low) >> 1); // rounds down, so low <= mid < high + stbtt_uint32 start_char = ttULONG(data+index_map+16+mid*12); + stbtt_uint32 end_char = ttULONG(data+index_map+16+mid*12+4); + if ((stbtt_uint32) unicode_codepoint < start_char) + high = mid; + else if ((stbtt_uint32) unicode_codepoint > end_char) + low = mid+1; + else { + stbtt_uint32 start_glyph = ttULONG(data+index_map+16+mid*12+8); + if (format == 12) + return start_glyph + unicode_codepoint-start_char; + else // format == 13 + return start_glyph; + } + } + return 0; // not found + } + // @TODO + STBTT_assert(0); + return 0; +} + +int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices) +{ + return stbtt_GetGlyphShape(info, stbtt_FindGlyphIndex(info, unicode_codepoint), vertices); +} + +static void stbtt_setvertex(stbtt_vertex *v, stbtt_uint8 type, stbtt_int32 x, stbtt_int32 y, stbtt_int32 cx, stbtt_int32 cy) +{ + v->type = type; + v->x = (stbtt_int16) x; + v->y = (stbtt_int16) y; + v->cx = (stbtt_int16) cx; + v->cy = (stbtt_int16) cy; +} + +static int stbtt__GetGlyfOffset(const stbtt_fontinfo *info, int glyph_index) +{ + int g1,g2; + + if (glyph_index >= info->numGlyphs) return -1; // glyph index out of range + if (info->indexToLocFormat >= 2) return -1; // unknown index->glyph map format + + if (info->indexToLocFormat == 0) { + g1 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2) * 2; + g2 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2 + 2) * 2; + } else { + g1 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4); + g2 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4 + 4); + } + + return g1==g2 ? -1 : g1; // if length is 0, return -1 +} + +int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1) +{ + int g = stbtt__GetGlyfOffset(info, glyph_index); + if (g < 0) return 0; + + if (x0) *x0 = ttSHORT(info->data + g + 2); + if (y0) *y0 = ttSHORT(info->data + g + 4); + if (x1) *x1 = ttSHORT(info->data + g + 6); + if (y1) *y1 = ttSHORT(info->data + g + 8); + return 1; +} + +int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1) +{ + return stbtt_GetGlyphBox(info, stbtt_FindGlyphIndex(info,codepoint), x0,y0,x1,y1); +} + +int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index) +{ + stbtt_int16 numberOfContours; + int g = stbtt__GetGlyfOffset(info, glyph_index); + if (g < 0) return 1; + numberOfContours = ttSHORT(info->data + g); + return numberOfContours == 0; +} + +static int stbtt__close_shape(stbtt_vertex *vertices, int num_vertices, int was_off, int start_off, + stbtt_int32 sx, stbtt_int32 sy, stbtt_int32 scx, stbtt_int32 scy, stbtt_int32 cx, stbtt_int32 cy) +{ + if (start_off) { + if (was_off) + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+scx)>>1, (cy+scy)>>1, cx,cy); + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, sx,sy,scx,scy); + } else { + if (was_off) + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve,sx,sy,cx,cy); + else + stbtt_setvertex(&vertices[num_vertices++], STBTT_vline,sx,sy,0,0); + } + return num_vertices; +} + +int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices) +{ + stbtt_int16 numberOfContours; + stbtt_uint8 *endPtsOfContours; + stbtt_uint8 *data = info->data; + stbtt_vertex *vertices=0; + int num_vertices=0; + int g = stbtt__GetGlyfOffset(info, glyph_index); + + *pvertices = NULL; + + if (g < 0) return 0; + + numberOfContours = ttSHORT(data + g); + + if (numberOfContours > 0) { + stbtt_uint8 flags=0,flagcount; + stbtt_int32 ins, i,j=0,m,n, next_move, was_off=0, off, start_off=0; + stbtt_int32 x,y,cx,cy,sx,sy, scx,scy; + stbtt_uint8 *points; + endPtsOfContours = (data + g + 10); + ins = ttUSHORT(data + g + 10 + numberOfContours * 2); + points = data + g + 10 + numberOfContours * 2 + 2 + ins; + + n = 1+ttUSHORT(endPtsOfContours + numberOfContours*2-2); + + m = n + 2*numberOfContours; // a loose bound on how many vertices we might need + vertices = (stbtt_vertex *) STBTT_malloc(m * sizeof(vertices[0]), info->userdata); + if (vertices == 0) + return 0; + + next_move = 0; + flagcount=0; + + // in first pass, we load uninterpreted data into the allocated array + // above, shifted to the end of the array so we won't overwrite it when + // we create our final data starting from the front + + off = m - n; // starting offset for uninterpreted data, regardless of how m ends up being calculated + + // first load flags + + for (i=0; i < n; ++i) { + if (flagcount == 0) { + flags = *points++; + if (flags & 8) + flagcount = *points++; + } else + --flagcount; + vertices[off+i].type = flags; + } + + // now load x coordinates + x=0; + for (i=0; i < n; ++i) { + flags = vertices[off+i].type; + if (flags & 2) { + stbtt_int16 dx = *points++; + x += (flags & 16) ? dx : -dx; // ??? + } else { + if (!(flags & 16)) { + x = x + (stbtt_int16) (points[0]*256 + points[1]); + points += 2; + } + } + vertices[off+i].x = (stbtt_int16) x; + } + + // now load y coordinates + y=0; + for (i=0; i < n; ++i) { + flags = vertices[off+i].type; + if (flags & 4) { + stbtt_int16 dy = *points++; + y += (flags & 32) ? dy : -dy; // ??? + } else { + if (!(flags & 32)) { + y = y + (stbtt_int16) (points[0]*256 + points[1]); + points += 2; + } + } + vertices[off+i].y = (stbtt_int16) y; + } + + // now convert them to our format + num_vertices=0; + sx = sy = cx = cy = scx = scy = 0; + for (i=0; i < n; ++i) { + flags = vertices[off+i].type; + x = (stbtt_int16) vertices[off+i].x; + y = (stbtt_int16) vertices[off+i].y; + + if (next_move == i) { + if (i != 0) + num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); + + // now start the new one + start_off = !(flags & 1); + if (start_off) { + // if we start off with an off-curve point, then when we need to find a point on the curve + // where we can start, and we need to save some state for when we wraparound. + scx = x; + scy = y; + if (!(vertices[off+i+1].type & 1)) { + // next point is also a curve point, so interpolate an on-point curve + sx = (x + (stbtt_int32) vertices[off+i+1].x) >> 1; + sy = (y + (stbtt_int32) vertices[off+i+1].y) >> 1; + } else { + // otherwise just use the next point as our start point + sx = (stbtt_int32) vertices[off+i+1].x; + sy = (stbtt_int32) vertices[off+i+1].y; + ++i; // we're using point i+1 as the starting point, so skip it + } + } else { + sx = x; + sy = y; + } + stbtt_setvertex(&vertices[num_vertices++], STBTT_vmove,sx,sy,0,0); + was_off = 0; + next_move = 1 + ttUSHORT(endPtsOfContours+j*2); + ++j; + } else { + if (!(flags & 1)) { // if it's a curve + if (was_off) // two off-curve control points in a row means interpolate an on-curve midpoint + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+x)>>1, (cy+y)>>1, cx, cy); + cx = x; + cy = y; + was_off = 1; + } else { + if (was_off) + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, x,y, cx, cy); + else + stbtt_setvertex(&vertices[num_vertices++], STBTT_vline, x,y,0,0); + was_off = 0; + } + } + } + num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); + } else if (numberOfContours == -1) { + // Compound shapes. + int more = 1; + stbtt_uint8 *comp = data + g + 10; + num_vertices = 0; + vertices = 0; + while (more) { + stbtt_uint16 flags, gidx; + int comp_num_verts = 0, i; + stbtt_vertex *comp_verts = 0, *tmp = 0; + float mtx[6] = {1,0,0,1,0,0}, m, n; + + flags = ttSHORT(comp); comp+=2; + gidx = ttSHORT(comp); comp+=2; + + if (flags & 2) { // XY values + if (flags & 1) { // shorts + mtx[4] = ttSHORT(comp); comp+=2; + mtx[5] = ttSHORT(comp); comp+=2; + } else { + mtx[4] = ttCHAR(comp); comp+=1; + mtx[5] = ttCHAR(comp); comp+=1; + } + } + else { + // @TODO handle matching point + STBTT_assert(0); + } + if (flags & (1<<3)) { // WE_HAVE_A_SCALE + mtx[0] = mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[1] = mtx[2] = 0; + } else if (flags & (1<<6)) { // WE_HAVE_AN_X_AND_YSCALE + mtx[0] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[1] = mtx[2] = 0; + mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; + } else if (flags & (1<<7)) { // WE_HAVE_A_TWO_BY_TWO + mtx[0] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[1] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[2] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; + } + + // Find transformation scales. + m = (float) sqrt(mtx[0]*mtx[0] + mtx[1]*mtx[1]); + n = (float) sqrt(mtx[2]*mtx[2] + mtx[3]*mtx[3]); + + // Get indexed glyph. + comp_num_verts = stbtt_GetGlyphShape(info, gidx, &comp_verts); + if (comp_num_verts > 0) { + // Transform vertices. + for (i = 0; i < comp_num_verts; ++i) { + stbtt_vertex* v = &comp_verts[i]; + stbtt_vertex_type x,y; + x=v->x; y=v->y; + v->x = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4])); + v->y = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5])); + x=v->cx; y=v->cy; + v->cx = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4])); + v->cy = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5])); + } + // Append vertices. + tmp = (stbtt_vertex*)STBTT_malloc((num_vertices+comp_num_verts)*sizeof(stbtt_vertex), info->userdata); + if (!tmp) { + if (vertices) STBTT_free(vertices, info->userdata); + if (comp_verts) STBTT_free(comp_verts, info->userdata); + return 0; + } + if (num_vertices > 0) memcpy(tmp, vertices, num_vertices*sizeof(stbtt_vertex)); + memcpy(tmp+num_vertices, comp_verts, comp_num_verts*sizeof(stbtt_vertex)); + if (vertices) STBTT_free(vertices, info->userdata); + vertices = tmp; + STBTT_free(comp_verts, info->userdata); + num_vertices += comp_num_verts; + } + // More components ? + more = flags & (1<<5); + } + } else if (numberOfContours < 0) { + // @TODO other compound variations? + STBTT_assert(0); + } else { + // numberOfCounters == 0, do nothing + } + + *pvertices = vertices; + return num_vertices; +} + +void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing) +{ + stbtt_uint16 numOfLongHorMetrics = ttUSHORT(info->data+info->hhea + 34); + if (glyph_index < numOfLongHorMetrics) { + if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*glyph_index); + if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*glyph_index + 2); + } else { + if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*(numOfLongHorMetrics-1)); + if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*numOfLongHorMetrics + 2*(glyph_index - numOfLongHorMetrics)); + } +} + +int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2) +{ + stbtt_uint8 *data = info->data + info->kern; + stbtt_uint32 needle, straw; + int l, r, m; + + // we only look at the first table. it must be 'horizontal' and format 0. + if (!info->kern) + return 0; + if (ttUSHORT(data+2) < 1) // number of tables, need at least 1 + return 0; + if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format + return 0; + + l = 0; + r = ttUSHORT(data+10) - 1; + needle = glyph1 << 16 | glyph2; + while (l <= r) { + m = (l + r) >> 1; + straw = ttULONG(data+18+(m*6)); // note: unaligned read + if (needle < straw) + r = m - 1; + else if (needle > straw) + l = m + 1; + else + return ttSHORT(data+22+(m*6)); + } + return 0; +} + +int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2) +{ + if (!info->kern) // if no kerning table, don't waste time looking up both codepoint->glyphs + return 0; + return stbtt_GetGlyphKernAdvance(info, stbtt_FindGlyphIndex(info,ch1), stbtt_FindGlyphIndex(info,ch2)); +} + +void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing) +{ + stbtt_GetGlyphHMetrics(info, stbtt_FindGlyphIndex(info,codepoint), advanceWidth, leftSideBearing); +} + +void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap) +{ + if (ascent ) *ascent = ttSHORT(info->data+info->hhea + 4); + if (descent) *descent = ttSHORT(info->data+info->hhea + 6); + if (lineGap) *lineGap = ttSHORT(info->data+info->hhea + 8); +} + +void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1) +{ + *x0 = ttSHORT(info->data + info->head + 36); + *y0 = ttSHORT(info->data + info->head + 38); + *x1 = ttSHORT(info->data + info->head + 40); + *y1 = ttSHORT(info->data + info->head + 42); +} + +float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float height) +{ + int fheight = ttSHORT(info->data + info->hhea + 4) - ttSHORT(info->data + info->hhea + 6); + return (float) height / fheight; +} + +float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels) +{ + int unitsPerEm = ttUSHORT(info->data + info->head + 18); + return pixels / unitsPerEm; +} + +void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *v) +{ + STBTT_free(v, info->userdata); +} + +////////////////////////////////////////////////////////////////////////////// +// +// antialiasing software rasterizer +// + +void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1) +{ + int x0,y0,x1,y1; + if (!stbtt_GetGlyphBox(font, glyph, &x0,&y0,&x1,&y1)) + x0=y0=x1=y1=0; // e.g. space character + // now move to integral bboxes (treating pixels as little squares, what pixels get touched)? + if (ix0) *ix0 = STBTT_ifloor(x0 * scale_x + shift_x); + if (iy0) *iy0 = -STBTT_iceil (y1 * scale_y + shift_y); + if (ix1) *ix1 = STBTT_iceil (x1 * scale_x + shift_x); + if (iy1) *iy1 = -STBTT_ifloor(y0 * scale_y + shift_y); +} +void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1) +{ + stbtt_GetGlyphBitmapBoxSubpixel(font, glyph, scale_x, scale_y,0.0f,0.0f, ix0, iy0, ix1, iy1); +} + +void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1) +{ + stbtt_GetGlyphBitmapBoxSubpixel(font, stbtt_FindGlyphIndex(font,codepoint), scale_x, scale_y,shift_x,shift_y, ix0,iy0,ix1,iy1); +} + +void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1) +{ + stbtt_GetCodepointBitmapBoxSubpixel(font, codepoint, scale_x, scale_y,0.0f,0.0f, ix0,iy0,ix1,iy1); +} + +typedef struct stbtt__edge { + float x0,y0, x1,y1; + int invert; +} stbtt__edge; + +typedef struct stbtt__active_edge +{ + int x,dx; + float ey; + struct stbtt__active_edge *next; + int valid; +} stbtt__active_edge; + +#define FIXSHIFT 10 +#define FIX (1 << FIXSHIFT) +#define FIXMASK (FIX-1) + +static stbtt__active_edge *new_active(stbtt__edge *e, int off_x, float start_point, void *userdata) +{ + stbtt__active_edge *z = (stbtt__active_edge *) STBTT_malloc(sizeof(*z), userdata); // @TODO: make a pool of these!!! + float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); + STBTT_assert(e->y0 <= start_point); + if (!z) return z; + // round dx down to avoid going too far + if (dxdy < 0) + z->dx = -STBTT_ifloor(FIX * -dxdy); + else + z->dx = STBTT_ifloor(FIX * dxdy); + z->x = STBTT_ifloor(FIX * (e->x0 + dxdy * (start_point - e->y0))); + z->x -= off_x * FIX; + z->ey = e->y1; + z->next = 0; + z->valid = e->invert ? 1 : -1; + return z; +} + +// note: this routine clips fills that extend off the edges... ideally this +// wouldn't happen, but it could happen if the truetype glyph bounding boxes +// are wrong, or if the user supplies a too-small bitmap +static void stbtt__fill_active_edges(unsigned char *scanline, int len, stbtt__active_edge *e, int max_weight) +{ + // non-zero winding fill + int x0=0, w=0; + + while (e) { + if (w == 0) { + // if we're currently at zero, we need to record the edge start point + x0 = e->x; w += e->valid; + } else { + int x1 = e->x; w += e->valid; + // if we went to zero, we need to draw + if (w == 0) { + int i = x0 >> FIXSHIFT; + int j = x1 >> FIXSHIFT; + + if (i < len && j >= 0) { + if (i == j) { + // x0,x1 are the same pixel, so compute combined coverage + scanline[i] = scanline[i] + (stbtt_uint8) ((x1 - x0) * max_weight >> FIXSHIFT); + } else { + if (i >= 0) // add antialiasing for x0 + scanline[i] = scanline[i] + (stbtt_uint8) (((FIX - (x0 & FIXMASK)) * max_weight) >> FIXSHIFT); + else + i = -1; // clip + + if (j < len) // add antialiasing for x1 + scanline[j] = scanline[j] + (stbtt_uint8) (((x1 & FIXMASK) * max_weight) >> FIXSHIFT); + else + j = len; // clip + + for (++i; i < j; ++i) // fill pixels between x0 and x1 + scanline[i] = scanline[i] + (stbtt_uint8) max_weight; + } + } + } + } + + e = e->next; + } +} + +static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata) +{ + stbtt__active_edge *active = NULL; + int y,j=0; + int max_weight = (255 / vsubsample); // weight per vertical scanline + int s; // vertical subsample index + unsigned char scanline_data[512], *scanline; + + if (result->w > 512) + scanline = (unsigned char *) STBTT_malloc(result->w, userdata); + else + scanline = scanline_data; + + y = off_y * vsubsample; + e[n].y0 = (off_y + result->h) * (float) vsubsample + 1; + + while (j < result->h) { + STBTT_memset(scanline, 0, result->w); + for (s=0; s < vsubsample; ++s) { + // find center of pixel for this scanline + float scan_y = y + 0.5f; + stbtt__active_edge **step = &active; + + // update all active edges; + // remove all active edges that terminate before the center of this scanline + while (*step) { + stbtt__active_edge * z = *step; + if (z->ey <= scan_y) { + *step = z->next; // delete from list + STBTT_assert(z->valid); + z->valid = 0; + STBTT_free(z, userdata); + } else { + z->x += z->dx; // advance to position for current scanline + step = &((*step)->next); // advance through list + } + } + + // resort the list if needed + for(;;) { + int changed=0; + step = &active; + while (*step && (*step)->next) { + if ((*step)->x > (*step)->next->x) { + stbtt__active_edge *t = *step; + stbtt__active_edge *q = t->next; + + t->next = q->next; + q->next = t; + *step = q; + changed = 1; + } + step = &(*step)->next; + } + if (!changed) break; + } + + // insert all edges that start before the center of this scanline -- omit ones that also end on this scanline + while (e->y0 <= scan_y) { + if (e->y1 > scan_y) { + stbtt__active_edge *z = new_active(e, off_x, scan_y, userdata); + // find insertion point + if (active == NULL) + active = z; + else if (z->x < active->x) { + // insert at front + z->next = active; + active = z; + } else { + // find thing to insert AFTER + stbtt__active_edge *p = active; + while (p->next && p->next->x < z->x) + p = p->next; + // at this point, p->next->x is NOT < z->x + z->next = p->next; + p->next = z; + } + } + ++e; + } + + // now process all active edges in XOR fashion + if (active) + stbtt__fill_active_edges(scanline, result->w, active, max_weight); + + ++y; + } + STBTT_memcpy(result->pixels + j * result->stride, scanline, result->w); + ++j; + } + + while (active) { + stbtt__active_edge *z = active; + active = active->next; + STBTT_free(z, userdata); + } + + if (scanline != scanline_data) + STBTT_free(scanline, userdata); +} + +static int stbtt__edge_compare(const void *p, const void *q) +{ + stbtt__edge *a = (stbtt__edge *) p; + stbtt__edge *b = (stbtt__edge *) q; + + if (a->y0 < b->y0) return -1; + if (a->y0 > b->y0) return 1; + return 0; +} + +typedef struct +{ + float x,y; +} stbtt__point; + +static void stbtt__rasterize(stbtt__bitmap *result, stbtt__point *pts, int *wcount, int windings, float scale_x, float scale_y, float shift_x, float shift_y, int off_x, int off_y, int invert, void *userdata) +{ + float y_scale_inv = invert ? -scale_y : scale_y; + stbtt__edge *e; + int n,i,j,k,m; + int vsubsample = result->h < 8 ? 15 : 5; + // vsubsample should divide 255 evenly; otherwise we won't reach full opacity + + // now we have to blow out the windings into explicit edge lists + n = 0; + for (i=0; i < windings; ++i) + n += wcount[i]; + + e = (stbtt__edge *) STBTT_malloc(sizeof(*e) * (n+1), userdata); // add an extra one as a sentinel + if (e == 0) return; + n = 0; + + m=0; + for (i=0; i < windings; ++i) { + stbtt__point *p = pts + m; + m += wcount[i]; + j = wcount[i]-1; + for (k=0; k < wcount[i]; j=k++) { + int a=k,b=j; + // skip the edge if horizontal + if (p[j].y == p[k].y) + continue; + // add edge from j to k to the list + e[n].invert = 0; + if (invert ? p[j].y > p[k].y : p[j].y < p[k].y) { + e[n].invert = 1; + a=j,b=k; + } + e[n].x0 = p[a].x * scale_x + shift_x; + e[n].y0 = p[a].y * y_scale_inv * vsubsample + shift_y; + e[n].x1 = p[b].x * scale_x + shift_x; + e[n].y1 = p[b].y * y_scale_inv * vsubsample + shift_y; + ++n; + } + } + + // now sort the edges by their highest point (should snap to integer, and then by x) + STBTT_sort(e, n, sizeof(e[0]), stbtt__edge_compare); + + // now, traverse the scanlines and find the intersections on each scanline, use xor winding rule + stbtt__rasterize_sorted_edges(result, e, n, vsubsample, off_x, off_y, userdata); + + STBTT_free(e, userdata); +} + +static void stbtt__add_point(stbtt__point *points, int n, float x, float y) +{ + if (!points) return; // during first pass, it's unallocated + points[n].x = x; + points[n].y = y; +} + +// tesselate until threshhold p is happy... @TODO warped to compensate for non-linear stretching +static int stbtt__tesselate_curve(stbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float objspace_flatness_squared, int n) +{ + // midpoint + float mx = (x0 + 2*x1 + x2)/4; + float my = (y0 + 2*y1 + y2)/4; + // versus directly drawn line + float dx = (x0+x2)/2 - mx; + float dy = (y0+y2)/2 - my; + if (n > 16) // 65536 segments on one curve better be enough! + return 1; + if (dx*dx+dy*dy > objspace_flatness_squared) { // half-pixel error allowed... need to be smaller if AA + stbtt__tesselate_curve(points, num_points, x0,y0, (x0+x1)/2.0f,(y0+y1)/2.0f, mx,my, objspace_flatness_squared,n+1); + stbtt__tesselate_curve(points, num_points, mx,my, (x1+x2)/2.0f,(y1+y2)/2.0f, x2,y2, objspace_flatness_squared,n+1); + } else { + stbtt__add_point(points, *num_points,x2,y2); + *num_points = *num_points+1; + } + return 1; +} + +// returns number of contours +stbtt__point *stbtt_FlattenCurves(stbtt_vertex *vertices, int num_verts, float objspace_flatness, int **contour_lengths, int *num_contours, void *userdata) +{ + stbtt__point *points=0; + int num_points=0; + + float objspace_flatness_squared = objspace_flatness * objspace_flatness; + int i,n=0,start=0, pass; + + // count how many "moves" there are to get the contour count + for (i=0; i < num_verts; ++i) + if (vertices[i].type == STBTT_vmove) + ++n; + + *num_contours = n; + if (n == 0) return 0; + + *contour_lengths = (int *) STBTT_malloc(sizeof(**contour_lengths) * n, userdata); + + if (*contour_lengths == 0) { + *num_contours = 0; + return 0; + } + + // make two passes through the points so we don't need to realloc + for (pass=0; pass < 2; ++pass) { + float x=0,y=0; + if (pass == 1) { + points = (stbtt__point *) STBTT_malloc(num_points * sizeof(points[0]), userdata); + if (points == NULL) goto error; + } + num_points = 0; + n= -1; + for (i=0; i < num_verts; ++i) { + switch (vertices[i].type) { + case STBTT_vmove: + // start the next contour + if (n >= 0) + (*contour_lengths)[n] = num_points - start; + ++n; + start = num_points; + + x = vertices[i].x, y = vertices[i].y; + stbtt__add_point(points, num_points++, x,y); + break; + case STBTT_vline: + x = vertices[i].x, y = vertices[i].y; + stbtt__add_point(points, num_points++, x, y); + break; + case STBTT_vcurve: + stbtt__tesselate_curve(points, &num_points, x,y, + vertices[i].cx, vertices[i].cy, + vertices[i].x, vertices[i].y, + objspace_flatness_squared, 0); + x = vertices[i].x, y = vertices[i].y; + break; + } + } + (*contour_lengths)[n] = num_points - start; + } + + return points; +error: + STBTT_free(points, userdata); + STBTT_free(*contour_lengths, userdata); + *contour_lengths = 0; + *num_contours = 0; + return NULL; +} + +void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, float shift_x, float shift_y, int x_off, int y_off, int invert, void *userdata) +{ + float scale = scale_x > scale_y ? scale_y : scale_x; + int winding_count, *winding_lengths; + stbtt__point *windings = stbtt_FlattenCurves(vertices, num_verts, flatness_in_pixels / scale, &winding_lengths, &winding_count, userdata); + if (windings) { + stbtt__rasterize(result, windings, winding_lengths, winding_count, scale_x, scale_y, shift_x, shift_y, x_off, y_off, invert, userdata); + STBTT_free(winding_lengths, userdata); + STBTT_free(windings, userdata); + } +} + +void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata) +{ + STBTT_free(bitmap, userdata); +} + +unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff) +{ + int ix0,iy0,ix1,iy1; + stbtt__bitmap gbm; + stbtt_vertex *vertices; + int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); + + if (scale_x == 0) scale_x = scale_y; + if (scale_y == 0) { + if (scale_x == 0) return NULL; + scale_y = scale_x; + } + + stbtt_GetGlyphBitmapBox(info, glyph, scale_x, scale_y, &ix0,&iy0,&ix1,&iy1); + + // now we get the size + gbm.w = (ix1 - ix0); + gbm.h = (iy1 - iy0); + gbm.pixels = NULL; // in case we error + + if (width ) *width = gbm.w; + if (height) *height = gbm.h; + if (xoff ) *xoff = ix0; + if (yoff ) *yoff = iy0; + + if (gbm.w && gbm.h) { + gbm.pixels = (unsigned char *) STBTT_malloc(gbm.w * gbm.h, info->userdata); + if (gbm.pixels) { + gbm.stride = gbm.w; + + stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0, iy0, 1, info->userdata); + } + } + STBTT_free(vertices, info->userdata); + return gbm.pixels; +} + +unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff) +{ + return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y, 0.0f, 0.0f, glyph, width, height, xoff, yoff); +} + +void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph) +{ + int ix0,iy0; + stbtt_vertex *vertices; + int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); + stbtt__bitmap gbm; + + stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,0,0); + gbm.pixels = output; + gbm.w = out_w; + gbm.h = out_h; + gbm.stride = out_stride; + + if (gbm.w && gbm.h) + stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0,iy0, 1, info->userdata); + + STBTT_free(vertices, info->userdata); +} + +void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph) +{ + stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, glyph); +} + +unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff) +{ + return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y,shift_x,shift_y, stbtt_FindGlyphIndex(info,codepoint), width,height,xoff,yoff); +} + +void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint) +{ + stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, shift_x, shift_y, stbtt_FindGlyphIndex(info,codepoint)); +} + +unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff) +{ + return stbtt_GetCodepointBitmapSubpixel(info, scale_x, scale_y, 0.0f,0.0f, codepoint, width,height,xoff,yoff); +} + +void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint) +{ + stbtt_MakeCodepointBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, codepoint); +} + +////////////////////////////////////////////////////////////////////////////// +// +// bitmap baking +// +// This is SUPER-CRAPPY packing to keep source code small + +extern int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // font location (use offset=0 for plain .ttf) + float pixel_height, // height of font in pixels + unsigned char *pixels, int pw, int ph, // bitmap to be filled in + int first_char, int num_chars, // characters to bake + stbtt_bakedchar *chardata) +{ + float scale; + int x,y,bottom_y, i; + stbtt_fontinfo f; + stbtt_InitFont(&f, data, offset); + STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels + x=y=1; + bottom_y = 1; + + scale = stbtt_ScaleForPixelHeight(&f, pixel_height); + + for (i=0; i < num_chars; ++i) { + int advance, lsb, x0,y0,x1,y1,gw,gh; + int g = stbtt_FindGlyphIndex(&f, first_char + i); + stbtt_GetGlyphHMetrics(&f, g, &advance, &lsb); + stbtt_GetGlyphBitmapBox(&f, g, scale,scale, &x0,&y0,&x1,&y1); + gw = x1-x0; + gh = y1-y0; + if (x + gw + 1 >= pw) + y = bottom_y, x = 1; // advance to next row + if (y + gh + 1 >= ph) // check if it fits vertically AFTER potentially moving to next row + return -i; + STBTT_assert(x+gw < pw); + STBTT_assert(y+gh < ph); + stbtt_MakeGlyphBitmap(&f, pixels+x+y*pw, gw,gh,pw, scale,scale, g); + chardata[i].x0 = (stbtt_int16) x; + chardata[i].y0 = (stbtt_int16) y; + chardata[i].x1 = (stbtt_int16) (x + gw); + chardata[i].y1 = (stbtt_int16) (y + gh); + chardata[i].xadvance = scale * advance; + chardata[i].xoff = (float) x0; + chardata[i].yoff = (float) y0; + x = x + gw + 2; + if (y+gh+2 > bottom_y) + bottom_y = y+gh+2; + } + return bottom_y; +} + +void stbtt_GetBakedQuad(stbtt_bakedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int opengl_fillrule) +{ + float d3d_bias = opengl_fillrule ? 0 : -0.5f; + float ipw = 1.0f / pw, iph = 1.0f / ph; + stbtt_bakedchar *b = chardata + char_index; + int round_x = STBTT_ifloor((*xpos + b->xoff) + 0.5); + int round_y = STBTT_ifloor((*ypos + b->yoff) + 0.5); + + q->x0 = round_x + d3d_bias; + q->y0 = round_y + d3d_bias; + q->x1 = round_x + b->x1 - b->x0 + d3d_bias; + q->y1 = round_y + b->y1 - b->y0 + d3d_bias; + + q->s0 = b->x0 * ipw; + q->t0 = b->y0 * iph; + q->s1 = b->x1 * ipw; + q->t1 = b->y1 * iph; + + *xpos += b->xadvance; +} + +////////////////////////////////////////////////////////////////////////////// +// +// font name matching -- recommended not to use this +// + +// check if a utf8 string contains a prefix which is the utf16 string; if so return length of matching utf8 string +static stbtt_int32 stbtt__CompareUTF8toUTF16_bigendian_prefix(const stbtt_uint8 *s1, stbtt_int32 len1, const stbtt_uint8 *s2, stbtt_int32 len2) +{ + stbtt_int32 i=0; + + // convert utf16 to utf8 and compare the results while converting + while (len2) { + stbtt_uint16 ch = s2[0]*256 + s2[1]; + if (ch < 0x80) { + if (i >= len1) return -1; + if (s1[i++] != ch) return -1; + } else if (ch < 0x800) { + if (i+1 >= len1) return -1; + if (s1[i++] != 0xc0 + (ch >> 6)) return -1; + if (s1[i++] != 0x80 + (ch & 0x3f)) return -1; + } else if (ch >= 0xd800 && ch < 0xdc00) { + stbtt_uint32 c; + stbtt_uint16 ch2 = s2[2]*256 + s2[3]; + if (i+3 >= len1) return -1; + c = ((ch - 0xd800) << 10) + (ch2 - 0xdc00) + 0x10000; + if (s1[i++] != 0xf0 + (c >> 18)) return -1; + if (s1[i++] != 0x80 + ((c >> 12) & 0x3f)) return -1; + if (s1[i++] != 0x80 + ((c >> 6) & 0x3f)) return -1; + if (s1[i++] != 0x80 + ((c ) & 0x3f)) return -1; + s2 += 2; // plus another 2 below + len2 -= 2; + } else if (ch >= 0xdc00 && ch < 0xe000) { + return -1; + } else { + if (i+2 >= len1) return -1; + if (s1[i++] != 0xe0 + (ch >> 12)) return -1; + if (s1[i++] != 0x80 + ((ch >> 6) & 0x3f)) return -1; + if (s1[i++] != 0x80 + ((ch ) & 0x3f)) return -1; + } + s2 += 2; + len2 -= 2; + } + return i; +} + +int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2) +{ + return len1 == stbtt__CompareUTF8toUTF16_bigendian_prefix((const stbtt_uint8*) s1, len1, (const stbtt_uint8*) s2, len2); +} + +// returns results in whatever encoding you request... but note that 2-byte encodings +// will be BIG-ENDIAN... use stbtt_CompareUTF8toUTF16_bigendian() to compare +const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID) +{ + stbtt_int32 i,count,stringOffset; + stbtt_uint8 *fc = font->data; + stbtt_uint32 offset = font->fontstart; + stbtt_uint32 nm = stbtt__find_table(fc, offset, "name"); + if (!nm) return NULL; + + count = ttUSHORT(fc+nm+2); + stringOffset = nm + ttUSHORT(fc+nm+4); + for (i=0; i < count; ++i) { + stbtt_uint32 loc = nm + 6 + 12 * i; + if (platformID == ttUSHORT(fc+loc+0) && encodingID == ttUSHORT(fc+loc+2) + && languageID == ttUSHORT(fc+loc+4) && nameID == ttUSHORT(fc+loc+6)) { + *length = ttUSHORT(fc+loc+8); + return (const char *) (fc+stringOffset+ttUSHORT(fc+loc+10)); + } + } + return NULL; +} + +static int stbtt__matchpair(stbtt_uint8 *fc, stbtt_uint32 nm, stbtt_uint8 *name, stbtt_int32 nlen, stbtt_int32 target_id, stbtt_int32 next_id) +{ + stbtt_int32 i; + stbtt_int32 count = ttUSHORT(fc+nm+2); + stbtt_int32 stringOffset = nm + ttUSHORT(fc+nm+4); + + for (i=0; i < count; ++i) { + stbtt_uint32 loc = nm + 6 + 12 * i; + stbtt_int32 id = ttUSHORT(fc+loc+6); + if (id == target_id) { + // find the encoding + stbtt_int32 platform = ttUSHORT(fc+loc+0), encoding = ttUSHORT(fc+loc+2), language = ttUSHORT(fc+loc+4); + + // is this a Unicode encoding? + if (platform == 0 || (platform == 3 && encoding == 1) || (platform == 3 && encoding == 10)) { + stbtt_int32 slen = ttUSHORT(fc+loc+8), off = ttUSHORT(fc+loc+10); + + // check if there's a prefix match + stbtt_int32 matchlen = stbtt__CompareUTF8toUTF16_bigendian_prefix(name, nlen, fc+stringOffset+off,slen); + if (matchlen >= 0) { + // check for target_id+1 immediately following, with same encoding & language + if (i+1 < count && ttUSHORT(fc+loc+12+6) == next_id && ttUSHORT(fc+loc+12) == platform && ttUSHORT(fc+loc+12+2) == encoding && ttUSHORT(fc+loc+12+4) == language) { + stbtt_int32 slen = ttUSHORT(fc+loc+12+8), off = ttUSHORT(fc+loc+12+10); + if (slen == 0) { + if (matchlen == nlen) + return 1; + } else if (matchlen < nlen && name[matchlen] == ' ') { + ++matchlen; + if (stbtt_CompareUTF8toUTF16_bigendian((char*) (name+matchlen), nlen-matchlen, (char*)(fc+stringOffset+off),slen)) + return 1; + } + } else { + // if nothing immediately following + if (matchlen == nlen) + return 1; + } + } + } + + // @TODO handle other encodings + } + } + return 0; +} + +static int stbtt__matches(stbtt_uint8 *fc, stbtt_uint32 offset, stbtt_uint8 *name, stbtt_int32 flags) +{ + stbtt_int32 nlen = (stbtt_int32) STBTT_strlen((char *) name); + stbtt_uint32 nm,hd; + if (!stbtt__isfont(fc+offset)) return 0; + + // check italics/bold/underline flags in macStyle... + if (flags) { + hd = stbtt__find_table(fc, offset, "head"); + if ((ttUSHORT(fc+hd+44) & 7) != (flags & 7)) return 0; + } + + nm = stbtt__find_table(fc, offset, "name"); + if (!nm) return 0; + + if (flags) { + // if we checked the macStyle flags, then just check the family and ignore the subfamily + if (stbtt__matchpair(fc, nm, name, nlen, 16, -1)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 1, -1)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1; + } else { + if (stbtt__matchpair(fc, nm, name, nlen, 16, 17)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 1, 2)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1; + } + + return 0; +} + +int stbtt_FindMatchingFont(const unsigned char *font_collection, const char *name_utf8, stbtt_int32 flags) +{ + stbtt_int32 i; + for (i=0;;++i) { + stbtt_int32 off = stbtt_GetFontOffsetForIndex(font_collection, i); + if (off < 0) return off; + if (stbtt__matches((stbtt_uint8 *) font_collection, off, (stbtt_uint8*) name_utf8, flags)) + return off; + } +} + +#endif // STB_TRUETYPE_IMPLEMENTATION \ No newline at end of file diff --git a/examples/common/meshes/capsule.obj b/testbed/meshes/capsule.obj similarity index 100% rename from examples/common/meshes/capsule.obj rename to testbed/meshes/capsule.obj diff --git a/examples/common/meshes/cone.obj b/testbed/meshes/cone.obj similarity index 100% rename from examples/common/meshes/cone.obj rename to testbed/meshes/cone.obj diff --git a/examples/common/meshes/convexmesh.obj b/testbed/meshes/convexmesh.obj similarity index 100% rename from examples/common/meshes/convexmesh.obj rename to testbed/meshes/convexmesh.obj diff --git a/examples/common/meshes/cylinder.obj b/testbed/meshes/cylinder.obj similarity index 100% rename from examples/common/meshes/cylinder.obj rename to testbed/meshes/cylinder.obj diff --git a/examples/common/meshes/dumbbell.obj b/testbed/meshes/dumbbell.obj similarity index 100% rename from examples/common/meshes/dumbbell.obj rename to testbed/meshes/dumbbell.obj diff --git a/examples/common/meshes/sphere.obj b/testbed/meshes/sphere.obj similarity index 100% rename from examples/common/meshes/sphere.obj rename to testbed/meshes/sphere.obj diff --git a/examples/common/opengl-framework/CMakeLists.txt b/testbed/opengl-framework/CMakeLists.txt similarity index 96% rename from examples/common/opengl-framework/CMakeLists.txt rename to testbed/opengl-framework/CMakeLists.txt index e10ae781..56a108a1 100644 --- a/examples/common/opengl-framework/CMakeLists.txt +++ b/testbed/opengl-framework/CMakeLists.txt @@ -71,6 +71,8 @@ SET(OPENGL_FRAMEWORK_SOURCES "src/TextureReaderWriter.cpp" "src/VertexBufferObject.h" "src/VertexBufferObject.cpp" + "src/VertexArrayObject.h" + "src/VertexArrayObject.cpp" ) IF(USE_JPEG_TEXTURES) diff --git a/examples/common/opengl-framework/cmake/FindFREEGLUT.cmake b/testbed/opengl-framework/cmake/FindFREEGLUT.cmake similarity index 100% rename from examples/common/opengl-framework/cmake/FindFREEGLUT.cmake rename to testbed/opengl-framework/cmake/FindFREEGLUT.cmake diff --git a/examples/common/opengl-framework/cmake/FindGLEW.cmake b/testbed/opengl-framework/cmake/FindGLEW.cmake similarity index 100% rename from examples/common/opengl-framework/cmake/FindGLEW.cmake rename to testbed/opengl-framework/cmake/FindGLEW.cmake diff --git a/examples/common/opengl-framework/src/Camera.cpp b/testbed/opengl-framework/src/Camera.cpp similarity index 100% rename from examples/common/opengl-framework/src/Camera.cpp rename to testbed/opengl-framework/src/Camera.cpp diff --git a/examples/common/opengl-framework/src/Camera.h b/testbed/opengl-framework/src/Camera.h similarity index 100% rename from examples/common/opengl-framework/src/Camera.h rename to testbed/opengl-framework/src/Camera.h diff --git a/examples/common/opengl-framework/src/FrameBufferObject.cpp b/testbed/opengl-framework/src/FrameBufferObject.cpp similarity index 99% rename from examples/common/opengl-framework/src/FrameBufferObject.cpp rename to testbed/opengl-framework/src/FrameBufferObject.cpp index 5f67be23..247873c3 100644 --- a/examples/common/opengl-framework/src/FrameBufferObject.cpp +++ b/testbed/opengl-framework/src/FrameBufferObject.cpp @@ -37,7 +37,7 @@ FrameBufferObject::FrameBufferObject() : mFrameBufferID(0), mRenderBufferID (0) // Destructor FrameBufferObject::~FrameBufferObject() { - + destroy(); } // Create the frame buffer object diff --git a/examples/common/opengl-framework/src/FrameBufferObject.h b/testbed/opengl-framework/src/FrameBufferObject.h similarity index 94% rename from examples/common/opengl-framework/src/FrameBufferObject.h rename to testbed/opengl-framework/src/FrameBufferObject.h index a76951c7..6194510a 100644 --- a/examples/common/opengl-framework/src/FrameBufferObject.h +++ b/testbed/opengl-framework/src/FrameBufferObject.h @@ -65,7 +65,7 @@ class FrameBufferObject { void attachTexture(uint position, uint textureID); // Bind the FBO - void bind(uint position) const; + void bind() const; // Unbind the FBO void unbind() const; @@ -78,18 +78,14 @@ class FrameBufferObject { }; // Bind the FBO -inline void FrameBufferObject::bind(uint position) const { +inline void FrameBufferObject::bind() const { assert(mFrameBufferID != 0); glBindFramebuffer(GL_FRAMEBUFFER, mFrameBufferID); - glDrawBuffer(position); - glReadBuffer(position); } // Unbind the FBO inline void FrameBufferObject::unbind() const { assert(mFrameBufferID != 0); - glDrawBuffer(GL_NONE); - glReadBuffer(GL_NONE); glBindFramebuffer(GL_FRAMEBUFFER, 0); } diff --git a/examples/common/opengl-framework/src/shaders/phong.vert b/testbed/opengl-framework/src/Light.cpp similarity index 68% rename from examples/common/opengl-framework/src/shaders/phong.vert rename to testbed/opengl-framework/src/Light.cpp index 620344a9..9bffc0c6 100644 --- a/examples/common/opengl-framework/src/shaders/phong.vert +++ b/testbed/opengl-framework/src/Light.cpp @@ -23,28 +23,40 @@ * * ********************************************************************************/ -// Uniform variables -uniform mat4 localToCameraMatrix; // Local-space to camera-space matrix -uniform mat4 projectionMatrix; // Projection matrix -uniform mat3 normalMatrix; // Normal matrix +// Libraries +#include "Light.h" -// Varying variables -varying vec3 vertexPosCameraSpace; // Camera-space position of the vertex -varying vec3 vertexNormalCameraSpace; // Vertex normal in camera-space -varying vec2 texCoords; // Texture coordinates +// Namespaces +using namespace openglframework; -void main() { +// Constructor +Light::Light(GLuint id) + : mLightID(id), mDiffuseColor(Color::white()), + mSpecularColor(Color::white()), mIsActive(false) { - // Compute the vertex position - vec4 positionCameraSpace = localToCameraMatrix * gl_Vertex; - vertexPosCameraSpace = positionCameraSpace.xyz; - - // Compute the world surface normal - vertexNormalCameraSpace = normalMatrix * gl_Normal; - - // Get the texture coordinates - texCoords = gl_MultiTexCoord0.xy; - - // Compute the clip-space vertex coordinates - gl_Position = projectionMatrix * positionCameraSpace; +} + +// Constructor +Light::Light(GLuint id, Color diffuseColor, Color specularColor) + : mLightID(id), mDiffuseColor(diffuseColor), + mSpecularColor(specularColor), mIsActive(false) { + +} + +// Destructor +Light::~Light() { + +} + +// Initialize the light +void Light::init() { + + // Enable the light + enable(); + + // Set the diffuse and specular color + GLfloat diffuseColor[] = {mDiffuseColor.r, mDiffuseColor.g, mDiffuseColor.b, mDiffuseColor.a}; + GLfloat specularColor[] = {mSpecularColor.r,mSpecularColor.g,mSpecularColor.b,mSpecularColor.a}; + glLightfv(mLightID, GL_DIFFUSE, diffuseColor); + glLightfv(mLightID, GL_SPECULAR, specularColor); } diff --git a/examples/common/opengl-framework/src/Light.h b/testbed/opengl-framework/src/Light.h similarity index 76% rename from examples/common/opengl-framework/src/Light.h rename to testbed/opengl-framework/src/Light.h index 8f8323d9..9ecc63b9 100644 --- a/examples/common/opengl-framework/src/Light.h +++ b/testbed/opengl-framework/src/Light.h @@ -31,7 +31,6 @@ #include "maths/Vector3.h" #include "Object3D.h" #include "Texture2D.h" -#include "FrameBufferObject.h" #include "Shader.h" #include @@ -56,15 +55,6 @@ class Light : public Object3D { // True if the light is active bool mIsActive; - // Shadow map associated with this light - Texture2D mShadowMap; - - // Framebuffer object to render the shadow map - FrameBufferObject mFBOShadowMap; - - // Shader to render the depth of the scene into a texture - Shader mDepthShader; - public: // -------------------- Methods -------------------- // @@ -101,18 +91,6 @@ class Light : public Object3D { // Disable the light void disable(); - - // Create a shadow map associated with this light - bool createShadowMap(uint width, uint height); - - // Call this method before rendering the scene for the shadow map computation - void startRenderingShadowMap(); - - // Call this method after having rendered the scene for the shadow map computation - void stopRenderingShadowMap(); - - // Destroy the shadow map associated with this light - void destroyShadowMap(); }; // Return the diffuse color @@ -159,23 +137,6 @@ inline void Light::disable() { glDisable(GL_LIGHT0 + mLightID); } -// Destroy the shadow map associated with this light -inline void Light::destroyShadowMap() { - mShadowMap.destroy(); - mFBOShadowMap.destroy(); - mDepthShader.destroy(); -} - -// Call this method before rendering the scene for the shadow map computation -inline void Light::startRenderingShadowMap() { - assert(mShadowMap.getID()); -} - -// Call this method after having rendered the scene for the shadow map computation -inline void Light::stopRenderingShadowMap() { - assert(mShadowMap.getID()); -} - } #endif diff --git a/examples/common/opengl-framework/src/Mesh.cpp b/testbed/opengl-framework/src/Mesh.cpp similarity index 100% rename from examples/common/opengl-framework/src/Mesh.cpp rename to testbed/opengl-framework/src/Mesh.cpp diff --git a/examples/common/opengl-framework/src/Mesh.h b/testbed/opengl-framework/src/Mesh.h similarity index 98% rename from examples/common/opengl-framework/src/Mesh.h rename to testbed/opengl-framework/src/Mesh.h index ed15ed04..92e5d144 100644 --- a/examples/common/opengl-framework/src/Mesh.h +++ b/testbed/opengl-framework/src/Mesh.h @@ -140,10 +140,10 @@ class Mesh : public Object3D { void setNormal(uint i, const Vector3& normal); // Return the color of a given vertex - const Color& getColor(uint i) const; + const Color& getVertexColor(uint i) const; // Set the color of a given vertex - void setColor(uint i, const Color& color); + void setVertexColor(uint i, const Color& color); // Set a color to all the vertices void setColorToAllVertices(const Color& color); @@ -282,13 +282,13 @@ inline void Mesh::setNormal(uint i, const Vector3& normal) { } // Return the color of a given vertex -inline const Color& Mesh::getColor(uint i) const { +inline const Color& Mesh::getVertexColor(uint i) const { assert(i < getNbVertices()); return mColors[i]; } // Set the color of a given vertex -inline void Mesh::setColor(uint i, const Color& color) { +inline void Mesh::setVertexColor(uint i, const Color& color) { // If the color array does not have the same size as // the vertices array diff --git a/examples/common/opengl-framework/src/MeshReaderWriter.cpp b/testbed/opengl-framework/src/MeshReaderWriter.cpp similarity index 100% rename from examples/common/opengl-framework/src/MeshReaderWriter.cpp rename to testbed/opengl-framework/src/MeshReaderWriter.cpp diff --git a/examples/common/opengl-framework/src/MeshReaderWriter.h b/testbed/opengl-framework/src/MeshReaderWriter.h similarity index 100% rename from examples/common/opengl-framework/src/MeshReaderWriter.h rename to testbed/opengl-framework/src/MeshReaderWriter.h diff --git a/examples/common/opengl-framework/src/Object3D.cpp b/testbed/opengl-framework/src/Object3D.cpp similarity index 100% rename from examples/common/opengl-framework/src/Object3D.cpp rename to testbed/opengl-framework/src/Object3D.cpp diff --git a/examples/common/opengl-framework/src/Object3D.h b/testbed/opengl-framework/src/Object3D.h similarity index 100% rename from examples/common/opengl-framework/src/Object3D.h rename to testbed/opengl-framework/src/Object3D.h diff --git a/examples/common/opengl-framework/src/Shader.cpp b/testbed/opengl-framework/src/Shader.cpp similarity index 97% rename from examples/common/opengl-framework/src/Shader.cpp rename to testbed/opengl-framework/src/Shader.cpp index 137669f7..aab00154 100644 --- a/examples/common/opengl-framework/src/Shader.cpp +++ b/testbed/opengl-framework/src/Shader.cpp @@ -48,7 +48,7 @@ Shader::Shader(const std::string vertexShaderFilename, const std::string fragmen // Destructor Shader::~Shader() { - + destroy(); } // Create the shader @@ -112,7 +112,7 @@ bool Shader::create(const std::string vertexShaderFilename, glGetShaderInfoLog(vertexShaderID, lengthLog, NULL, str); // Display the log of the compilation - std::cerr << "Vertex Shader Error : " << str << std::endl; + std::cerr << "Vertex Shader Error (in " << vertexShaderFilename << ") : " << str << std::endl; delete[] str; assert(false); return false; @@ -165,7 +165,7 @@ bool Shader::create(const std::string vertexShaderFilename, glGetShaderInfoLog(fragmentShaderID, lengthLog, NULL, str); // Display the log of the compilation - std::cerr << "Fragment Shader Error : " << str << std::endl; + std::cerr << "Fragment Shader Error (in " << fragmentShaderFilename << ") : " << str << std::endl; delete[] str; assert(false); return false; diff --git a/examples/common/opengl-framework/src/Shader.h b/testbed/opengl-framework/src/Shader.h similarity index 71% rename from examples/common/opengl-framework/src/Shader.h rename to testbed/opengl-framework/src/Shader.h index 52a206bc..8019b645 100644 --- a/examples/common/opengl-framework/src/Shader.h +++ b/testbed/opengl-framework/src/Shader.h @@ -34,6 +34,7 @@ #include "maths/Vector4.h" #include #include +#include #include namespace openglframework { @@ -78,54 +79,60 @@ class Shader { void unbind() const; // Return the location of a uniform variable inside a shader program - int getUniformLocation(const std::string& variableName) const; + GLint getUniformLocation(const std::string& variableName, bool errorIfMissing = true) const; + + // Return the location of an attribute variable inside a shader program + GLint getAttribLocation(const std::string& variableName, bool errorIfMissing = true) const; // Set a float uniform value to this shader (be careful if the uniform is not // used in the shader, the compiler will remove it, then when you will try // to set it, an assert will occur) - void setFloatUniform(const std::string& variableName, float value) const; + void setFloatUniform(const std::string& variableName, float value, bool errorIfMissing = true) const; // Set an int uniform value to this shader (be careful if the uniform is not // used in the shader, the compiler will remove it, then when you will try // to set it, an assert will occur) - void setIntUniform(const std::string& variableName, int value) const; + void setIntUniform(const std::string& variableName, int value, bool errorIfMissing = true) const; // Set a vector 2 uniform value to this shader (be careful if the uniform is not // used in the shader, the compiler will remove it, then when you will try // to set it, an assert will occur) - void setVector2Uniform(const std::string& variableName, const Vector2& v) const; + void setVector2Uniform(const std::string& variableName, const Vector2& v, bool errorIfMissing = true) const; // Set a vector 3 uniform value to this shader (be careful if the uniform is not // used in the shader, the compiler will remove it, then when you will try // to set it, an assert will occur) - void setVector3Uniform(const std::string& variableName, const Vector3& v) const; + void setVector3Uniform(const std::string& variableName, const Vector3& v, bool errorIfMissing = true) const; // Set a vector 4 uniform value to this shader (be careful if the uniform is not // used in the shader, the compiler will remove it, then when you will try // to set it, an assert will occur) - void setVector4Uniform(const std::string& variableName, const Vector4 &v) const; + void setVector4Uniform(const std::string& variableName, const Vector4 &v, bool errorIfMissing = true) const; // Set a 3x3 matrix uniform value to this shader (be careful if the uniform is not // used in the shader, the compiler will remove it, then when you will try // to set it, an assert will occur) void setMatrix3x3Uniform(const std::string& variableName, const float* matrix, - bool transpose = false) const; + bool transpose = false, bool errorIfMissing = true) const; // Set a 3x3 matrix uniform value to this shader (be careful if the uniform is not // used in the shader, the compiler will remove it, then when you will try // to set it, an assert will occur) - void setMatrix3x3Uniform(const std::string& variableName, const Matrix3& matrix) const; + void setMatrix3x3Uniform(const std::string& variableName, const Matrix3& matrix, bool errorIfMissing = true) const; // Set a 4x4 matrix uniform value to this shader (be careful if the uniform is not // used in the shader, the compiler will remove it, then when you will try // to set it, an assert will occur) void setMatrix4x4Uniform(const std::string& variableName, const float* matrix, - bool transpose = false) const; + bool transpose = false, bool errorIfMissing = true) const; // Set a 4x4 matrix uniform value to this shader (be careful if the uniform is not // used in the shader, the compiler will remove it, then when you will try // to set it, an assert will occur) - void setMatrix4x4Uniform(const std::string& variableName, const Matrix4& matrix) const; + void setMatrix4x4Uniform(const std::string& variableName, const Matrix4& matrix, bool errorIfMissing = true) const; + + // Return the shader object program ID + GLuint getProgramObjectId() const; // Return true if the needed OpenGL extensions are available static bool checkOpenGLExtensions(); @@ -144,77 +151,112 @@ inline void Shader::unbind() const { } // Return the location of a uniform variable inside a shader program -inline int Shader::getUniformLocation(const std::string& variableName) const { +inline GLint Shader::getUniformLocation(const std::string& variableName, bool errorIfMissing) const { assert(mProgramObjectID != 0); - int location = glGetUniformLocation(mProgramObjectID, variableName.c_str()); - if (location == -1) { + GLint location = glGetUniformLocation(mProgramObjectID, variableName.c_str()); + if (location == -1 && errorIfMissing) { std::cerr << "Error in vertex shader " << mFilenameVertexShader << " or in fragment shader" << mFilenameFragmentShader << " : No Uniform variable : " << variableName << std::endl; + throw std::logic_error("Error in Shader"); } - assert(location != -1); + + return location; +} + +// Return the location of an attribute variable inside a shader program +inline GLint Shader::getAttribLocation(const std::string& variableName, bool errorIfMissing) const { + assert(mProgramObjectID != 0); + GLint location = glGetAttribLocation(mProgramObjectID, variableName.c_str()); + if (location == -1 && errorIfMissing) { + std::cerr << "Error in vertex shader " << mFilenameVertexShader << " or in fragment shader" + << mFilenameFragmentShader << " : No Uniform variable : " << variableName + << std::endl; + throw std::logic_error("Error in Shader"); + } + return location; } // Clear the shader inline void Shader::destroy() { - glDeleteProgram(mProgramObjectID); - mProgramObjectID = 0; + if (mProgramObjectID != 0) { + glDeleteProgram(mProgramObjectID); + mProgramObjectID = 0; + } } // Set a float uniform value to this shader (be careful if the uniform is not // used in the shader, the compiler will remove it, then when you will try // to set it, an assert will occur) -inline void Shader::setFloatUniform(const std::string& variableName, float value) const { +inline void Shader::setFloatUniform(const std::string& variableName, float value, bool errorIfMissing) const { assert(mProgramObjectID != 0); - glUniform1f(getUniformLocation(variableName), value); + GLint location = getUniformLocation(variableName, errorIfMissing); + if (location != -1) { + glUniform1f(location, value); + } } // Set an int uniform value to this shader (be careful if the uniform is not // used in the shader, the compiler will remove it, then when you will try // to set it, an assert will occur) -inline void Shader::setIntUniform(const std::string& variableName, int value) const { +inline void Shader::setIntUniform(const std::string& variableName, int value, bool errorIfMissing) const { assert(mProgramObjectID != 0); - glUniform1i(getUniformLocation(variableName), value); + GLint location = getUniformLocation(variableName, errorIfMissing); + if (location != -1) { + glUniform1i(location, value); + } } // Set a vector 2 uniform value to this shader (be careful if the uniform is not // used in the shader, the compiler will remove it, then when you will try // to set it, an assert will occur) -inline void Shader::setVector2Uniform(const std::string& variableName, const Vector2& v) const { +inline void Shader::setVector2Uniform(const std::string& variableName, const Vector2& v, bool errorIfMissing) const { assert(mProgramObjectID != 0); - glUniform2f(getUniformLocation(variableName), v.x, v.y); + GLint location = getUniformLocation(variableName, errorIfMissing); + if (location != -1) { + glUniform2f(location, v.x, v.y); + } } // Set a vector 3 uniform value to this shader (be careful if the uniform is not // used in the shader, the compiler will remove it, then when you will try // to set it, an assert will occur) -inline void Shader::setVector3Uniform(const std::string& variableName, const Vector3 &v) const { +inline void Shader::setVector3Uniform(const std::string& variableName, const Vector3 &v, bool errorIfMissing) const { assert(mProgramObjectID != 0); - glUniform3f(getUniformLocation(variableName), v.x, v.y, v.z); + GLint location = getUniformLocation(variableName, errorIfMissing); + if (location != -1) { + glUniform3f(location, v.x, v.y, v.z); + } } // Set a vector 4 uniform value to this shader (be careful if the uniform is not // used in the shader, the compiler will remove it, then when you will try // to set it, an assert will occur) -inline void Shader::setVector4Uniform(const std::string& variableName, const Vector4& v) const { +inline void Shader::setVector4Uniform(const std::string& variableName, const Vector4& v, bool errorIfMissing) const { assert(mProgramObjectID != 0); - glUniform4f(getUniformLocation(variableName), v.x, v.y, v.z, v.w); + GLint location = getUniformLocation(variableName, errorIfMissing); + if (location != -1) { + glUniform4f(location, v.x, v.y, v.z, v.w); + } } // Set a 4x4 matrix uniform value to this shader (be careful if the uniform is not // used in the shader, the compiler will remove it, then when you will try // to set it, an assert will occur) inline void Shader::setMatrix3x3Uniform(const std::string& variableName, const float* matrix, - bool transpose) const { + bool transpose, bool errorIfMissing) const { assert(mProgramObjectID != 0); - glUniformMatrix3fv(getUniformLocation(variableName), 1, transpose, matrix); + GLint location = getUniformLocation(variableName, errorIfMissing); + if (location != -1) { + glUniformMatrix3fv(location, 1, transpose, matrix); + } } // Set a 3x3 matrix uniform value to this shader (be careful if the uniform is not // used in the shader, the compiler will remove it, then when you will try // to set it, an assert will occur) -inline void Shader::setMatrix3x3Uniform(const std::string& variableName, const Matrix3& matrix) const { +inline void Shader::setMatrix3x3Uniform(const std::string& variableName, const Matrix3& matrix, bool errorIfMissing) const { assert(mProgramObjectID != 0); GLfloat mat[9]; for (int i=0; i<3; i++) { @@ -222,22 +264,28 @@ inline void Shader::setMatrix3x3Uniform(const std::string& variableName, const M mat[i*3 + j] = matrix.getValue(i, j); } } - glUniformMatrix3fv(getUniformLocation(variableName), 1, true, mat); + GLint location = getUniformLocation(variableName, errorIfMissing); + if (location != -1) { + glUniformMatrix3fv(location, 1, true, mat); + } } // Set a 4x4 matrix uniform value to this shader (be careful if the uniform is not // used in the shader, the compiler will remove it, then when you will try // to set it, an assert will occur) inline void Shader::setMatrix4x4Uniform(const std::string& variableName, const float* matrix, - bool transpose) const { + bool transpose, bool errorIfMissing) const { assert(mProgramObjectID != 0); - glUniformMatrix4fv(getUniformLocation(variableName), 1, transpose, matrix); + GLint location = getUniformLocation(variableName, errorIfMissing); + if (location != -1) { + glUniformMatrix4fv(location, 1, transpose, matrix); + } } // Set a 4x4 matrix uniform value to this shader (be careful if the uniform is not // used in the shader, the compiler will remove it, then when you will try // to set it, an assert will occur) -inline void Shader::setMatrix4x4Uniform(const std::string& variableName, const Matrix4& matrix) const { +inline void Shader::setMatrix4x4Uniform(const std::string& variableName, const Matrix4& matrix, bool errorIfMissing) const { assert(mProgramObjectID != 0); GLfloat mat[16]; for (int i=0; i<4; i++) { @@ -245,7 +293,15 @@ inline void Shader::setMatrix4x4Uniform(const std::string& variableName, const M mat[i*4 + j] = matrix.m[i][j]; } } - glUniformMatrix4fv(getUniformLocation(variableName), 1, true, mat); + GLint location = getUniformLocation(variableName, errorIfMissing); + if (location != -1) { + glUniformMatrix4fv(location, 1, true, mat); + } +} + +// Return the shader object program ID +inline GLuint Shader::getProgramObjectId() const { + return mProgramObjectID; } // Return true if the needed OpenGL extensions are available for shaders diff --git a/examples/common/opengl-framework/src/Texture2D.cpp b/testbed/opengl-framework/src/Texture2D.cpp similarity index 76% rename from examples/common/opengl-framework/src/Texture2D.cpp rename to testbed/opengl-framework/src/Texture2D.cpp index 1b316b73..2bc8f0a5 100644 --- a/examples/common/opengl-framework/src/Texture2D.cpp +++ b/testbed/opengl-framework/src/Texture2D.cpp @@ -33,13 +33,13 @@ using namespace openglframework; // Constructor -Texture2D::Texture2D() : mID(0), mLayer(0), mWidth(0), mHeight(0) { +Texture2D::Texture2D() : mID(0), mUnit(0), mWidth(0), mHeight(0) { } // Constructor Texture2D::Texture2D(uint width, uint height, uint internalFormat, uint format, uint type) - : mID(0), mLayer(0), mWidth(0), mHeight(0){ + : mID(0), mUnit(0), mWidth(0), mHeight(0) { // Create the texture create(width, height, internalFormat, format, type); @@ -47,12 +47,12 @@ Texture2D::Texture2D(uint width, uint height, uint internalFormat, uint format, // Destructor Texture2D::~Texture2D() { - + destroy(); } // Create the texture void Texture2D::create(uint width, uint height, uint internalFormat, uint format, uint type, - void* data) { + void* data) { // Destroy the current texture destroy(); @@ -72,12 +72,34 @@ void Texture2D::create(uint width, uint height, uint internalFormat, uint format glBindTexture(GL_TEXTURE_2D, 0); } +// Create the texture +void Texture2D::create(uint width, uint height, uint internalFormat, uint format, uint type, + uint minFilter, uint maxFilter, uint wrapS, uint wrapT, void* data) { + + // Destroy the current texture + destroy(); + + mWidth = width; + mHeight = height; + + // Create the OpenGL texture + glGenTextures(1, &mID); + assert(mID != 0); + glBindTexture(GL_TEXTURE_2D, mID); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapS); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, maxFilter); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilter); + glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, mWidth, mHeight, 0, format, type, data); + glBindTexture(GL_TEXTURE_2D, 0); +} + // Destroy the texture void Texture2D::destroy() { if (mID != 0) { glDeleteTextures(1, &mID); mID = 0; - mLayer = 0; + mUnit = 0; mWidth = 0; mHeight = 0; } diff --git a/examples/common/opengl-framework/src/Texture2D.h b/testbed/opengl-framework/src/Texture2D.h similarity index 84% rename from examples/common/opengl-framework/src/Texture2D.h rename to testbed/opengl-framework/src/Texture2D.h index 9c6fa1c0..5f8ce3ae 100644 --- a/examples/common/opengl-framework/src/Texture2D.h +++ b/testbed/opengl-framework/src/Texture2D.h @@ -45,8 +45,8 @@ class Texture2D { // OpenGL texture ID GLuint mID; - // Layer of the texture - GLuint mLayer; + // Current texture unit for this texture + GLuint mUnit; // Width uint mWidth; @@ -71,6 +71,10 @@ class Texture2D { void create(uint width, uint height, uint internalFormat, uint format, uint type, void* data = NULL); + // Create the texture + void create(uint width, uint height, uint internalFormat, uint format, uint type, + uint minFilter, uint maxFilter, uint wrapS, uint wrapT, void* data); + // Destroy the texture void destroy(); @@ -83,11 +87,11 @@ class Texture2D { // Get the OpenGL texture ID uint getID() const; - // Get the layer of the texture - uint getLayer() const; + // Get the unit of the texture + uint getUnit() const; - // Set the layer of the texture - void setLayer(uint layer); + // Set the unit of the texture + void setUnit(uint unit); // Get the width uint getWidth() const; @@ -99,17 +103,15 @@ class Texture2D { // Bind the texture inline void Texture2D::bind() const { assert(mID != 0); - glEnable(GL_TEXTURE_2D); - glActiveTexture(GL_TEXTURE0 + mLayer); + glActiveTexture(GL_TEXTURE0 + mUnit); glBindTexture(GL_TEXTURE_2D, mID); } // Unbind the texture inline void Texture2D::unbind() const { assert(mID != 0); - glActiveTexture(GL_TEXTURE0 + mLayer); + glActiveTexture(GL_TEXTURE0 + mUnit); glBindTexture(GL_TEXTURE_2D, 0); - glDisable(GL_TEXTURE_2D); } // Get the OpenGL texture ID @@ -117,14 +119,14 @@ inline uint Texture2D::getID() const { return mID; } -// Get the layer of the texture -inline uint Texture2D::getLayer() const { - return mLayer; +// Get the unit of the texture +inline uint Texture2D::getUnit() const { + return mUnit; } -// Set the layer of the texture -inline void Texture2D::setLayer(uint layer) { - mLayer = layer; +// Set the unit of the texture +inline void Texture2D::setUnit(uint unit) { + mUnit = unit; } // Get the width diff --git a/examples/common/opengl-framework/src/TextureReaderWriter.cpp b/testbed/opengl-framework/src/TextureReaderWriter.cpp similarity index 100% rename from examples/common/opengl-framework/src/TextureReaderWriter.cpp rename to testbed/opengl-framework/src/TextureReaderWriter.cpp diff --git a/examples/common/opengl-framework/src/TextureReaderWriter.h b/testbed/opengl-framework/src/TextureReaderWriter.h similarity index 100% rename from examples/common/opengl-framework/src/TextureReaderWriter.h rename to testbed/opengl-framework/src/TextureReaderWriter.h diff --git a/testbed/opengl-framework/src/VertexArrayObject.cpp b/testbed/opengl-framework/src/VertexArrayObject.cpp new file mode 100644 index 00000000..b30e2e4b --- /dev/null +++ b/testbed/opengl-framework/src/VertexArrayObject.cpp @@ -0,0 +1,70 @@ +/******************************************************************************** +* OpenGL-Framework * +* Copyright (c) 2015 Daniel Chappuis * +********************************************************************************* +* * +* This software is provided 'as-is', without any express or implied warranty. * +* In no event will the authors be held liable for any damages arising from the * +* use of this software. * +* * +* Permission is granted to anyone to use this software for any purpose, * +* including commercial applications, and to alter it and redistribute it * +* freely, subject to the following restrictions: * +* * +* 1. The origin of this software must not be misrepresented; you must not claim * +* that you wrote the original software. If you use this software in a * +* product, an acknowledgment in the product documentation would be * +* appreciated but is not required. * +* * +* 2. Altered source versions must be plainly marked as such, and must not be * +* misrepresented as being the original software. * +* * +* 3. This notice may not be removed or altered from any source distribution. * +* * +********************************************************************************/ + +// Libraries +#include "VertexArrayObject.h" + +using namespace openglframework; + +// Constructor +VertexArrayObject::VertexArrayObject() : mVertexArrayID(0) { + +} + +// Destructor +VertexArrayObject::~VertexArrayObject() { + destroy(); +} + +// Create the vertex buffer object +bool VertexArrayObject::create() { + + // Destroy the current VAO + destroy(); + + // Check that the needed OpenGL extensions are available + bool isExtensionOK = checkOpenGLExtensions(); + if (!isExtensionOK) { + std::cerr << "Error : Impossible to use Vertex Array Object on this platform" << std::endl; + assert(false); + return false; + } + + // Generate a new VAO + glGenVertexArrays(1, &mVertexArrayID); + assert(mVertexArrayID != 0); + + return true; +} + +// Destroy the VAO +void VertexArrayObject::destroy() { + + // Delete the vertex buffer object + if (mVertexArrayID != 0) { + glDeleteVertexArrays(1, &mVertexArrayID); + mVertexArrayID = 0; + } +} diff --git a/testbed/opengl-framework/src/VertexArrayObject.h b/testbed/opengl-framework/src/VertexArrayObject.h new file mode 100644 index 00000000..2f7ed1b0 --- /dev/null +++ b/testbed/opengl-framework/src/VertexArrayObject.h @@ -0,0 +1,99 @@ +/******************************************************************************** +* OpenGL-Framework * +* Copyright (c) 2013 Daniel Chappuis * +********************************************************************************* +* * +* This software is provided 'as-is', without any express or implied warranty. * +* In no event will the authors be held liable for any damages arising from the * +* use of this software. * +* * +* Permission is granted to anyone to use this software for any purpose, * +* including commercial applications, and to alter it and redistribute it * +* freely, subject to the following restrictions: * +* * +* 1. The origin of this software must not be misrepresented; you must not claim * +* that you wrote the original software. If you use this software in a * +* product, an acknowledgment in the product documentation would be * +* appreciated but is not required. * +* * +* 2. Altered source versions must be plainly marked as such, and must not be * +* misrepresented as being the original software. * +* * +* 3. This notice may not be removed or altered from any source distribution. * +* * +********************************************************************************/ + +#ifndef VERTEX_ARRAY_OBJECT_H +#define VERTEX_ARRAY_OBJECT_H + +// Libraries +#include +#include +#include + +namespace openglframework { + + +// Class VertexArrayObject +class VertexArrayObject { + + private : + + // -------------------- Attributes -------------------- // + + /// ID of the Vertex Array Object + GLuint mVertexArrayID; + + public : + + // -------------------- Methods -------------------- // + + /// Constructor + VertexArrayObject(); + + /// Destructor + ~VertexArrayObject(); + + /// Create the vertex buffer object + bool create(); + + /// Bind the VAO + void bind() const; + + /// Unbind the VAO + void unbind() const; + + /// Return true if the needed OpenGL extensions are available for VAO + static bool checkOpenGLExtensions(); + + /// Destroy the VAO + void destroy(); +}; + +// Bind the VAO +inline void VertexArrayObject::bind() const { + assert(mVertexArrayID != 0); + + // Bind the VAO + glBindVertexArray(mVertexArrayID); +} + +// Unbind the VAO +inline void VertexArrayObject::unbind() const { + assert(mVertexArrayID != 0); + + // Unbind the VAO + glBindVertexArray(0); +} + +// Return true if the needed OpenGL extensions are available for VAO +inline bool VertexArrayObject::checkOpenGLExtensions() { + + // Check that OpenGL version is at least 3.0 or there the vertex array object extension exists + return (GLEW_VERSION_3_0 || GL_ARB_vertex_array_object); +} + +} + +#endif + diff --git a/examples/common/opengl-framework/src/VertexBufferObject.cpp b/testbed/opengl-framework/src/VertexBufferObject.cpp similarity index 97% rename from examples/common/opengl-framework/src/VertexBufferObject.cpp rename to testbed/opengl-framework/src/VertexBufferObject.cpp index 8afd1daf..f5fe3b0c 100644 --- a/examples/common/opengl-framework/src/VertexBufferObject.cpp +++ b/testbed/opengl-framework/src/VertexBufferObject.cpp @@ -36,7 +36,7 @@ VertexBufferObject::VertexBufferObject(GLenum targetData) // Destructor VertexBufferObject::~VertexBufferObject() { - + destroy(); } // Create the vertex buffer object @@ -63,21 +63,15 @@ bool VertexBufferObject::create() { // Copy data into the VBO void VertexBufferObject::copyDataIntoVBO(GLsizei size, const void* data, GLenum usage) { - // Bind the VBO - bind(); - // Copy the data into the VBO glBufferData(mTargetData, size, data, usage); - - // Unbind the VBO - unbind(); } // Destroy the VBO void VertexBufferObject::destroy() { // Delete the vertex buffer object - if (mVertexBufferID) { + if (mVertexBufferID != 0) { glDeleteFramebuffers(1, &mVertexBufferID); mVertexBufferID = 0; } diff --git a/examples/common/opengl-framework/src/VertexBufferObject.h b/testbed/opengl-framework/src/VertexBufferObject.h similarity index 100% rename from examples/common/opengl-framework/src/VertexBufferObject.h rename to testbed/opengl-framework/src/VertexBufferObject.h diff --git a/examples/common/opengl-framework/src/definitions.h b/testbed/opengl-framework/src/definitions.h similarity index 100% rename from examples/common/opengl-framework/src/definitions.h rename to testbed/opengl-framework/src/definitions.h diff --git a/examples/common/opengl-framework/src/maths/Color.h b/testbed/opengl-framework/src/maths/Color.h similarity index 100% rename from examples/common/opengl-framework/src/maths/Color.h rename to testbed/opengl-framework/src/maths/Color.h diff --git a/examples/common/opengl-framework/src/maths/Matrix3.h b/testbed/opengl-framework/src/maths/Matrix3.h similarity index 98% rename from examples/common/opengl-framework/src/maths/Matrix3.h rename to testbed/opengl-framework/src/maths/Matrix3.h index 5460f616..8439388f 100644 --- a/examples/common/opengl-framework/src/maths/Matrix3.h +++ b/testbed/opengl-framework/src/maths/Matrix3.h @@ -272,6 +272,13 @@ class Matrix3 { m[2][0] *= nb; m[2][1] *= nb; m[2][2] *= nb; return *this; } + + // Return the identity matrix + static Matrix3 identity() { + return Matrix3(1, 0, 0, + 0, 1, 0, + 0, 0, 1); + } }; } diff --git a/examples/common/opengl-framework/src/maths/Matrix4.h b/testbed/opengl-framework/src/maths/Matrix4.h similarity index 88% rename from examples/common/opengl-framework/src/maths/Matrix4.h rename to testbed/opengl-framework/src/maths/Matrix4.h index bcfbcebd..9463b15b 100644 --- a/examples/common/opengl-framework/src/maths/Matrix4.h +++ b/testbed/opengl-framework/src/maths/Matrix4.h @@ -27,7 +27,7 @@ #define MATRIX4_H // Libraries -#include +#include #include #include #include "Vector3.h" @@ -376,6 +376,18 @@ class Matrix4 { // Return a 4x4 rotation matrix static Matrix4 rotationMatrix(const Vector3& axis, float angle); + + // Return a 4x4 perspective projection matrix + static Matrix4 perspectiveProjectionMatrix(float near, float far, + int width, int height, + float fieldOfView); + + // Return a 4x4 orthographic projection matrix + static Matrix4 orthoProjectionMatrix(float near, float far, int width, + int height); + + // Return the identity matrix + static Matrix4 identity(); }; // * operator @@ -422,6 +434,61 @@ inline Matrix4 Matrix4::rotationMatrix(const Vector3& axis, float angle) { return rotationMatrix; } +// Return a 4x4 perspective projection matrix +inline Matrix4 Matrix4::perspectiveProjectionMatrix(float near, float far, int width, int height, + float fieldOfView) { + + // Compute the aspect ratio + float aspect = float(width) / float(height); + + float top = near * tan((fieldOfView / 2.0f) * (float(M_PI) / 180.0f)); + float bottom = -top; + float left = bottom * aspect; + float right = top * aspect; + + float fx = 2.0f * near / (right - left); + float fy = 2.0f * near / (top - bottom); + float fz = -(far + near) / (far - near); + float fw = -2.0f * far * near / (far - near); + + // Compute the projection matrix + return Matrix4(fx, 0, 0, 0, + 0, fy, 0, 0, + 0, 0, fz, fw, + 0, 0, -1, 0); +} + +// Return a 4x4 orthographic projection matrix +inline Matrix4 Matrix4::orthoProjectionMatrix(float near, float far, int width, int height) { + + // Compute the aspect ratio + float aspect = float(width) / float(height); + + float top = height * 0.5f; + float bottom = -top; + float left = bottom * aspect; + float right = top * aspect; + + float fx = 2.0f / (right - left); + float fy = 2.0f / (top - bottom); + float fz = -2.0f / (far - near); + float fw = -(far + near) / (far - near); + + // Compute the projection matrix + return Matrix4(fx, 0, 0, 0, + 0, fy, 0, 0, + 0, 0, fz, fw, + 0, 0, 0, 1); +} + +// Return the identity matrix +inline Matrix4 Matrix4::identity() { + return Matrix4(1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1); +} + } #endif //_MATRIX4_H diff --git a/examples/common/opengl-framework/src/maths/Vector2.h b/testbed/opengl-framework/src/maths/Vector2.h similarity index 100% rename from examples/common/opengl-framework/src/maths/Vector2.h rename to testbed/opengl-framework/src/maths/Vector2.h diff --git a/examples/common/opengl-framework/src/maths/Vector3.h b/testbed/opengl-framework/src/maths/Vector3.h similarity index 100% rename from examples/common/opengl-framework/src/maths/Vector3.h rename to testbed/opengl-framework/src/maths/Vector3.h diff --git a/examples/common/opengl-framework/src/maths/Vector4.h b/testbed/opengl-framework/src/maths/Vector4.h similarity index 100% rename from examples/common/opengl-framework/src/maths/Vector4.h rename to testbed/opengl-framework/src/maths/Vector4.h diff --git a/examples/common/opengl-framework/src/openglframework.h b/testbed/opengl-framework/src/openglframework.h similarity index 98% rename from examples/common/opengl-framework/src/openglframework.h rename to testbed/opengl-framework/src/openglframework.h index 4ac735d8..f9d1fd97 100644 --- a/examples/common/opengl-framework/src/openglframework.h +++ b/testbed/opengl-framework/src/openglframework.h @@ -36,6 +36,7 @@ #include "Texture2D.h" #include "FrameBufferObject.h" #include "VertexBufferObject.h" +#include "VertexArrayObject.h" #include "Shader.h" #include "maths/Color.h" #include "maths/Vector2.h" diff --git a/examples/collisionshapes/Scene.cpp b/testbed/scenes/collisionshapes/CollisionShapesScene.cpp similarity index 53% rename from examples/collisionshapes/Scene.cpp rename to testbed/scenes/collisionshapes/CollisionShapesScene.cpp index 740a48e6..b63cb03c 100644 --- a/examples/collisionshapes/Scene.cpp +++ b/testbed/scenes/collisionshapes/CollisionShapesScene.cpp @@ -24,26 +24,23 @@ ********************************************************************************/ // Libraries -#include "Scene.h" +#include "CollisionShapesScene.h" // Namespaces using namespace openglframework; +using namespace collisionshapesscene; // Constructor -Scene::Scene(Viewer* viewer, const std::string& shaderFolderPath, const std::string& meshFolderPath) - : mViewer(viewer), mLight0(0), - mPhongShader(shaderFolderPath + "phong.vert", - shaderFolderPath +"phong.frag"), mIsRunning(false) { +CollisionShapesScene::CollisionShapesScene(const std::string& name) + : SceneDemo(name, SCENE_RADIUS) { - // Move the light 0 - mLight0.translateWorld(Vector3(50, 50, 50)); + std::string meshFolderPath("meshes/"); // Compute the radius and the center of the scene - float radiusScene = 30.0f; openglframework::Vector3 center(0, 5, 0); // Set the center of the scene - mViewer->setScenePosition(center, radiusScene); + setScenePosition(center, SCENE_RADIUS); // Gravity vector in the dynamics world rp3d::Vector3 gravity(0, -9.81, 0); @@ -52,14 +49,11 @@ Scene::Scene(Viewer* viewer, const std::string& shaderFolderPath, const std::str rp3d::decimal timeStep = 1.0f / 60.0f; // Create the dynamics world for the physics simulation - mDynamicsWorld = new rp3d::DynamicsWorld(gravity, timeStep); + mDynamicsWorld = new rp3d::DynamicsWorld(gravity); // Set the number of iterations of the constraint solver mDynamicsWorld->setNbIterationsVelocitySolver(15); - // Create the static data for the visual contact points - VisualContactPoint::createStaticData(meshFolderPath); - float radius = 3.0f; for (int i=0; isetColor(mDemoColors[i % mNbDemoColors]); + dumbbell->setSleepingColor(mRedColorDemo); + // Change the material properties of the rigid body rp3d::Material& material = dumbbell->getRigidBody()->getMaterial(); material.setBounciness(rp3d::decimal(0.2)); @@ -93,6 +91,10 @@ Scene::Scene(Viewer* viewer, const std::string& shaderFolderPath, const std::str // Create a sphere and a corresponding rigid in the dynamics world Box* box = new Box(BOX_SIZE, position , BOX_MASS, mDynamicsWorld); + // Set the box color + box->setColor(mDemoColors[i % mNbDemoColors]); + box->setSleepingColor(mRedColorDemo); + // Change the material properties of the rigid body rp3d::Material& material = box->getRigidBody()->getMaterial(); material.setBounciness(rp3d::decimal(0.2)); @@ -114,6 +116,10 @@ Scene::Scene(Viewer* viewer, const std::string& shaderFolderPath, const std::str Sphere* sphere = new Sphere(SPHERE_RADIUS, position , BOX_MASS, mDynamicsWorld, meshFolderPath); + // Set the box color + sphere->setColor(mDemoColors[i % mNbDemoColors]); + sphere->setSleepingColor(mRedColorDemo); + // Change the material properties of the rigid body rp3d::Material& material = sphere->getRigidBody()->getMaterial(); material.setBounciness(rp3d::decimal(0.2)); @@ -135,6 +141,10 @@ Scene::Scene(Viewer* viewer, const std::string& shaderFolderPath, const std::str Cone* cone = new Cone(CONE_RADIUS, CONE_HEIGHT, position, CONE_MASS, mDynamicsWorld, meshFolderPath); + // Set the box color + cone->setColor(mDemoColors[i % mNbDemoColors]); + cone->setSleepingColor(mRedColorDemo); + // Change the material properties of the rigid body rp3d::Material& material = cone->getRigidBody()->getMaterial(); material.setBounciness(rp3d::decimal(0.2)); @@ -156,6 +166,10 @@ Scene::Scene(Viewer* viewer, const std::string& shaderFolderPath, const std::str Cylinder* cylinder = new Cylinder(CYLINDER_RADIUS, CYLINDER_HEIGHT, position , CYLINDER_MASS, mDynamicsWorld, meshFolderPath); + // Set the box color + cylinder->setColor(mDemoColors[i % mNbDemoColors]); + cylinder->setSleepingColor(mRedColorDemo); + // Change the material properties of the rigid body rp3d::Material& material = cylinder->getRigidBody()->getMaterial(); material.setBounciness(rp3d::decimal(0.2)); @@ -177,6 +191,10 @@ Scene::Scene(Viewer* viewer, const std::string& shaderFolderPath, const std::str Capsule* capsule = new Capsule(CAPSULE_RADIUS, CAPSULE_HEIGHT, position , CAPSULE_MASS, mDynamicsWorld, meshFolderPath); + // Set the box color + capsule->setColor(mDemoColors[i % mNbDemoColors]); + capsule->setSleepingColor(mRedColorDemo); + // Change the material properties of the rigid body rp3d::Material& material = capsule->getRigidBody()->getMaterial(); material.setBounciness(rp3d::decimal(0.2)); @@ -197,6 +215,10 @@ Scene::Scene(Viewer* viewer, const std::string& shaderFolderPath, const std::str // Create a convex mesh and a corresponding rigid in the dynamics world ConvexMesh* mesh = new ConvexMesh(position, MESH_MASS, mDynamicsWorld, meshFolderPath); + // Set the box color + mesh->setColor(mDemoColors[i % mNbDemoColors]); + mesh->setSleepingColor(mRedColorDemo); + // Change the material properties of the rigid body rp3d::Material& material = mesh->getRigidBody()->getMaterial(); material.setBounciness(rp3d::decimal(0.2)); @@ -209,6 +231,10 @@ Scene::Scene(Viewer* viewer, const std::string& shaderFolderPath, const std::str openglframework::Vector3 floorPosition(0, 0, 0); mFloor = new Box(FLOOR_SIZE, floorPosition, FLOOR_MASS, mDynamicsWorld); + // Set the box color + mFloor->setColor(mGreyColorDemo); + mFloor->setSleepingColor(mGreyColorDemo); + // The floor must be a static rigid body mFloor->getRigidBody()->setType(rp3d::STATIC); @@ -216,18 +242,20 @@ Scene::Scene(Viewer* viewer, const std::string& shaderFolderPath, const std::str rp3d::Material& material = mFloor->getRigidBody()->getMaterial(); material.setBounciness(rp3d::decimal(0.2)); - // Start the simulation - startSimulation(); + // Get the physics engine parameters + mEngineSettings.isGravityEnabled = mDynamicsWorld->isGravityEnabled(); + rp3d::Vector3 gravityVector = mDynamicsWorld->getGravity(); + mEngineSettings.gravity = openglframework::Vector3(gravityVector.x, gravityVector.y, gravityVector.z); + mEngineSettings.isSleepingEnabled = mDynamicsWorld->isSleepingEnabled(); + mEngineSettings.sleepLinearVelocity = mDynamicsWorld->getSleepLinearVelocity(); + mEngineSettings.sleepAngularVelocity = mDynamicsWorld->getSleepAngularVelocity(); + mEngineSettings.nbPositionSolverIterations = mDynamicsWorld->getNbIterationsPositionSolver(); + mEngineSettings.nbVelocitySolverIterations = mDynamicsWorld->getNbIterationsVelocitySolver(); + mEngineSettings.timeBeforeSleep = mDynamicsWorld->getTimeBeforeSleep(); } // Destructor -Scene::~Scene() { - - // Stop the physics simulation - stopSimulation(); - - // Destroy the shader - mPhongShader.destroy(); +CollisionShapesScene::~CollisionShapesScene() { // Destroy all the boxes of the scene for (std::vector::iterator it = mBoxes.begin(); it != mBoxes.end(); ++it) { @@ -311,134 +339,262 @@ Scene::~Scene() { delete mDynamicsWorld; } +// Update the physics world (take a simulation step) +void CollisionShapesScene::updatePhysics() { + + // Update the physics engine parameters + mDynamicsWorld->setIsGratityEnabled(mEngineSettings.isGravityEnabled); + rp3d::Vector3 gravity(mEngineSettings.gravity.x, mEngineSettings.gravity.y, + mEngineSettings.gravity.z); + mDynamicsWorld->setGravity(gravity); + mDynamicsWorld->enableSleeping(mEngineSettings.isSleepingEnabled); + mDynamicsWorld->setSleepLinearVelocity(mEngineSettings.sleepLinearVelocity); + mDynamicsWorld->setSleepAngularVelocity(mEngineSettings.sleepAngularVelocity); + mDynamicsWorld->setNbIterationsPositionSolver(mEngineSettings.nbPositionSolverIterations); + mDynamicsWorld->setNbIterationsVelocitySolver(mEngineSettings.nbVelocitySolverIterations); + mDynamicsWorld->setTimeBeforeSleep(mEngineSettings.timeBeforeSleep); + + // Take a simulation step + mDynamicsWorld->update(mEngineSettings.timeStep); +} + // Take a step for the simulation -void Scene::simulate() { +void CollisionShapesScene::update() { - // If the physics simulation is running - if (mIsRunning) { + SceneDemo::update(); - // Take a simulation step - mDynamicsWorld->update(); + // Update the position and orientation of the boxes + for (std::vector::iterator it = mBoxes.begin(); it != mBoxes.end(); ++it) { - // Update the position and orientation of the boxes - for (std::vector::iterator it = mBoxes.begin(); it != mBoxes.end(); ++it) { - - // Update the transform used for the rendering - (*it)->updateTransform(); - } - - // Update the position and orientation of the sphere - for (std::vector::iterator it = mSpheres.begin(); it != mSpheres.end(); ++it) { - - // Update the transform used for the rendering - (*it)->updateTransform(); - } - - // Update the position and orientation of the cones - for (std::vector::iterator it = mCones.begin(); it != mCones.end(); ++it) { - - // Update the transform used for the rendering - (*it)->updateTransform(); - } - - // Update the position and orientation of the cylinders - for (std::vector::iterator it = mCylinders.begin(); it != mCylinders.end(); ++it) { - - // Update the transform used for the rendering - (*it)->updateTransform(); - } - - // Update the position and orientation of the capsules - for (std::vector::iterator it = mCapsules.begin(); it != mCapsules.end(); ++it) { - - // Update the transform used for the rendering - (*it)->updateTransform(); - } - - // Update the position and orientation of the convex meshes - for (std::vector::iterator it = mConvexMeshes.begin(); - it != mConvexMeshes.end(); ++it) { - - // Update the transform used for the rendering - (*it)->updateTransform(); - } - - // Update the position and orientation of the dumbbells - for (std::vector::iterator it = mDumbbells.begin(); - it != mDumbbells.end(); ++it) { - - // Update the transform used for the rendering - (*it)->updateTransform(); - } - - mFloor->updateTransform(); + // Update the transform used for the rendering + (*it)->updateTransform(mInterpolationFactor); } + + // Update the position and orientation of the sphere + for (std::vector::iterator it = mSpheres.begin(); it != mSpheres.end(); ++it) { + + // Update the transform used for the rendering + (*it)->updateTransform(mInterpolationFactor); + } + + // Update the position and orientation of the cones + for (std::vector::iterator it = mCones.begin(); it != mCones.end(); ++it) { + + // Update the transform used for the rendering + (*it)->updateTransform(mInterpolationFactor); + } + + // Update the position and orientation of the cylinders + for (std::vector::iterator it = mCylinders.begin(); it != mCylinders.end(); ++it) { + + // Update the transform used for the rendering + (*it)->updateTransform(mInterpolationFactor); + } + + // Update the position and orientation of the capsules + for (std::vector::iterator it = mCapsules.begin(); it != mCapsules.end(); ++it) { + + // Update the transform used for the rendering + (*it)->updateTransform(mInterpolationFactor); + } + + // Update the position and orientation of the convex meshes + for (std::vector::iterator it = mConvexMeshes.begin(); + it != mConvexMeshes.end(); ++it) { + + // Update the transform used for the rendering + (*it)->updateTransform(mInterpolationFactor); + } + + // Update the position and orientation of the dumbbells + for (std::vector::iterator it = mDumbbells.begin(); + it != mDumbbells.end(); ++it) { + + // Update the transform used for the rendering + (*it)->updateTransform(mInterpolationFactor); + } + + mFloor->updateTransform(mInterpolationFactor); } // Render the scene -void Scene::render() { - - glEnable(GL_DEPTH_TEST); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - glEnable(GL_CULL_FACE); - - // Get the world-space to camera-space matrix - const Camera& camera = mViewer->getCamera(); - const openglframework::Matrix4 worldToCameraMatrix = camera.getTransformMatrix().getInverse(); +void CollisionShapesScene::renderSinglePass(openglframework::Shader& shader, + const openglframework::Matrix4& worldToCameraMatrix) { // Bind the shader - mPhongShader.bind(); - - // Set the variables of the shader - mPhongShader.setMatrix4x4Uniform("projectionMatrix", camera.getProjectionMatrix()); - mPhongShader.setVector3Uniform("light0PosCameraSpace", worldToCameraMatrix * mLight0.getOrigin()); - mPhongShader.setVector3Uniform("lightAmbientColor", Vector3(0.3f, 0.3f, 0.3f)); - const Color& diffColLight0 = mLight0.getDiffuseColor(); - const Color& specColLight0 = mLight0.getSpecularColor(); - mPhongShader.setVector3Uniform("light0DiffuseColor", Vector3(diffColLight0.r, diffColLight0.g, diffColLight0.b)); - mPhongShader.setVector3Uniform("light0SpecularColor", Vector3(specColLight0.r, specColLight0.g, specColLight0.b)); - mPhongShader.setFloatUniform("shininess", 200.0f); + shader.bind(); // Render all the boxes of the scene for (std::vector::iterator it = mBoxes.begin(); it != mBoxes.end(); ++it) { - (*it)->render(mPhongShader, worldToCameraMatrix); + (*it)->render(shader, worldToCameraMatrix); } // Render all the sphere of the scene for (std::vector::iterator it = mSpheres.begin(); it != mSpheres.end(); ++it) { - (*it)->render(mPhongShader, worldToCameraMatrix); + (*it)->render(shader, worldToCameraMatrix); } // Render all the cones of the scene for (std::vector::iterator it = mCones.begin(); it != mCones.end(); ++it) { - (*it)->render(mPhongShader, worldToCameraMatrix); + (*it)->render(shader, worldToCameraMatrix); } // Render all the cylinders of the scene for (std::vector::iterator it = mCylinders.begin(); it != mCylinders.end(); ++it) { - (*it)->render(mPhongShader, worldToCameraMatrix); + (*it)->render(shader, worldToCameraMatrix); } // Render all the capsules of the scene for (std::vector::iterator it = mCapsules.begin(); it != mCapsules.end(); ++it) { - (*it)->render(mPhongShader, worldToCameraMatrix); + (*it)->render(shader, worldToCameraMatrix); } // Render all the convex meshes of the scene for (std::vector::iterator it = mConvexMeshes.begin(); it != mConvexMeshes.end(); ++it) { - (*it)->render(mPhongShader, worldToCameraMatrix); + (*it)->render(shader, worldToCameraMatrix); } // Render all the dumbbells of the scene for (std::vector::iterator it = mDumbbells.begin(); it != mDumbbells.end(); ++it) { - (*it)->render(mPhongShader, worldToCameraMatrix); + (*it)->render(shader, worldToCameraMatrix); } // Render the floor - mFloor->render(mPhongShader, worldToCameraMatrix); + mFloor->render(shader, worldToCameraMatrix); // Unbind the shader - mPhongShader.unbind(); + shader.unbind(); +} + +/// Reset the scene +void CollisionShapesScene::reset() { + + float radius = 3.0f; + + for (int i=0; iresetTransform(transform); + } + + // Create all the boxes of the scene + for (int i=0; iresetTransform(transform); + } + + // Create all the spheres of the scene + for (int i=0; iresetTransform(transform); + } + + // Create all the cones of the scene + for (int i=0; iresetTransform(transform); + } + + // Create all the cylinders of the scene + for (int i=0; iresetTransform(transform); + } + + // Create all the capsules of the scene + for (int i=0; iresetTransform(transform); + } + + // Create all the convex meshes of the scene + for (int i=0; iresetTransform(transform); + } } diff --git a/examples/collisionshapes/Scene.h b/testbed/scenes/collisionshapes/CollisionShapesScene.h similarity index 72% rename from examples/collisionshapes/Scene.h rename to testbed/scenes/collisionshapes/CollisionShapesScene.h index 61a2e7a7..d7f41726 100644 --- a/examples/collisionshapes/Scene.h +++ b/testbed/scenes/collisionshapes/CollisionShapesScene.h @@ -23,12 +23,13 @@ * * ********************************************************************************/ -#ifndef SCENE_H -#define SCENE_H +#ifndef COLLISION_SHAPES_SCENE_H +#define COLLISION_SHAPES_SCENE_H // Libraries #include "openglframework.h" #include "reactphysics3d.h" +#include "SceneDemo.h" #include "Sphere.h" #include "Box.h" #include "Cone.h" @@ -39,14 +40,18 @@ #include "VisualContactPoint.h" #include "../common/Viewer.h" +namespace collisionshapesscene { + + // Constants -const int NB_BOXES = 2; -const int NB_CUBES = 1; -const int NB_CONES = 3; -const int NB_CYLINDERS = 2; -const int NB_CAPSULES = 1; -const int NB_MESHES = 2; -const int NB_COMPOUND_SHAPES = 2; +const float SCENE_RADIUS = 30.0f; +const int NB_BOXES = 4; +const int NB_CUBES = 6; +const int NB_CONES = 6; +const int NB_CYLINDERS = 5; +const int NB_CAPSULES = 4; +const int NB_MESHES = 5; +const int NB_COMPOUND_SHAPES = 4; const openglframework::Vector3 BOX_SIZE(2, 2, 2); const float SPHERE_RADIUS = 1.5f; const float CONE_RADIUS = 2.0f; @@ -56,7 +61,7 @@ const float CYLINDER_HEIGHT = 5.0f; const float CAPSULE_RADIUS = 1.0f; const float CAPSULE_HEIGHT = 1.0f; const float DUMBBELL_HEIGHT = 1.0f; -const openglframework::Vector3 FLOOR_SIZE(20, 0.5f, 20); // Floor dimensions in meters +const openglframework::Vector3 FLOOR_SIZE(50, 0.5f, 50); // Floor dimensions in meters const float BOX_MASS = 1.0f; const float CONE_MASS = 1.0f; const float CYLINDER_MASS = 1.0f; @@ -64,22 +69,13 @@ const float CAPSULE_MASS = 1.0f; const float MESH_MASS = 1.0f; const float FLOOR_MASS = 100.0f; // Floor mass in kilograms -// Class Scene -class Scene { +// Class CollisionShapesScene +class CollisionShapesScene : public SceneDemo { private : // -------------------- Attributes -------------------- // - /// Pointer to the viewer - Viewer* mViewer; - - /// Light 0 - openglframework::Light mLight0; - - /// Phong shader - openglframework::Shader mPhongShader; - /// All the spheres of the scene std::vector mBoxes; @@ -103,56 +99,39 @@ class Scene { /// Dynamics world used for the physics simulation rp3d::DynamicsWorld* mDynamicsWorld; - /// True if the physics simulation is running - bool mIsRunning; - public: // -------------------- Methods -------------------- // /// Constructor - Scene(Viewer* viewer, const std::string& shaderFolderPath, - const std::string& meshFolderPath); + CollisionShapesScene(const std::string& name); /// Destructor - ~Scene(); + virtual ~CollisionShapesScene(); + + /// Update the physics world (take a simulation step) + /// Can be called several times per frame + virtual void updatePhysics(); /// Take a step for the simulation - void simulate(); + virtual void update(); - /// Stop the simulation - void stopSimulation(); + /// Render the scene in a single pass + virtual void renderSinglePass(openglframework::Shader& shader, + const openglframework::Matrix4& worldToCameraMatrix); - /// Start the simulation - void startSimulation(); + /// Reset the scene + virtual void reset(); - /// Pause or continue simulation - void pauseContinueSimulation(); - - /// Render the scene - void render(); + /// Return all the contact points of the scene + virtual std::vector getContactPoints() const; }; -// Stop the simulation -inline void Scene::stopSimulation() { - mDynamicsWorld->stop(); - mIsRunning = false; +// Return all the contact points of the scene +inline std::vector CollisionShapesScene::getContactPoints() const { + return computeContactPointsOfWorld(mDynamicsWorld); } -// Start the simulation -inline void Scene::startSimulation() { - mDynamicsWorld->start(); - mIsRunning = true; -} - -// Pause or continue simulation -inline void Scene::pauseContinueSimulation() { - if (mIsRunning) { - stopSimulation(); - } - else { - startSimulation(); - } } #endif diff --git a/examples/cubes/Scene.cpp b/testbed/scenes/cubes/CubesScene.cpp similarity index 55% rename from examples/cubes/Scene.cpp rename to testbed/scenes/cubes/CubesScene.cpp index 073ab02b..c4652972 100644 --- a/examples/cubes/Scene.cpp +++ b/testbed/scenes/cubes/CubesScene.cpp @@ -24,35 +24,30 @@ ********************************************************************************/ // Libraries -#include "Scene.h" +#include "CubesScene.h" // Namespaces using namespace openglframework; +using namespace cubesscene; // Constructor -Scene::Scene(Viewer* viewer, const std::string& shaderFolderPath) - : mViewer(viewer), mLight0(0), - mPhongShader(shaderFolderPath + "phong.vert", shaderFolderPath + "phong.frag"), - mIsRunning(false) { - - // Move the light 0 - mLight0.translateWorld(Vector3(7, 15, 15)); +CubesScene::CubesScene(const std::string& name) + : SceneDemo(name, SCENE_RADIUS) { // Compute the radius and the center of the scene - float radiusScene = 30.0f; openglframework::Vector3 center(0, 5, 0); // Set the center of the scene - mViewer->setScenePosition(center, radiusScene); + setScenePosition(center, SCENE_RADIUS); // Gravity vector in the dynamics world - rp3d::Vector3 gravity(0, rp3d::decimal(-5.81), 0); + rp3d::Vector3 gravity(0, rp3d::decimal(-9.81), 0); // Time step for the physics simulation rp3d::decimal timeStep = 1.0f / 60.0f; // Create the dynamics world for the physics simulation - mDynamicsWorld = new rp3d::DynamicsWorld(gravity, timeStep); + mDynamicsWorld = new rp3d::DynamicsWorld(gravity); // Set the number of iterations of the constraint solver mDynamicsWorld->setNbIterationsVelocitySolver(15); @@ -65,12 +60,16 @@ Scene::Scene(Viewer* viewer, const std::string& shaderFolderPath) // Position of the cubes float angle = i * 30.0f; openglframework::Vector3 position(radius * cos(angle), - 10 + i * (BOX_SIZE.y + 0.3f), + 30 + i * (BOX_SIZE.y + 0.3f), 0); // Create a cube and a corresponding rigid in the dynamics world Box* cube = new Box(BOX_SIZE, position , BOX_MASS, mDynamicsWorld); + // Set the box color + cube->setColor(mDemoColors[i % mNbDemoColors]); + cube->setSleepingColor(mRedColorDemo); + // Change the material properties of the rigid body rp3d::Material& material = cube->getRigidBody()->getMaterial(); material.setBounciness(rp3d::decimal(0.4)); @@ -82,6 +81,8 @@ Scene::Scene(Viewer* viewer, const std::string& shaderFolderPath) // Create the floor openglframework::Vector3 floorPosition(0, 0, 0); mFloor = new Box(FLOOR_SIZE, floorPosition, FLOOR_MASS, mDynamicsWorld); + mFloor->setColor(mGreyColorDemo); + mFloor->setSleepingColor(mGreyColorDemo); // The floor must be a static rigid body mFloor->getRigidBody()->setType(rp3d::STATIC); @@ -90,20 +91,20 @@ Scene::Scene(Viewer* viewer, const std::string& shaderFolderPath) rp3d::Material& material = mFloor->getRigidBody()->getMaterial(); material.setBounciness(rp3d::decimal(0.3)); - // Start the simulation - startSimulation(); - - counter=0; + // Get the physics engine parameters + mEngineSettings.isGravityEnabled = mDynamicsWorld->isGravityEnabled(); + rp3d::Vector3 gravityVector = mDynamicsWorld->getGravity(); + mEngineSettings.gravity = openglframework::Vector3(gravityVector.x, gravityVector.y, gravityVector.z); + mEngineSettings.isSleepingEnabled = mDynamicsWorld->isSleepingEnabled(); + mEngineSettings.sleepLinearVelocity = mDynamicsWorld->getSleepLinearVelocity(); + mEngineSettings.sleepAngularVelocity = mDynamicsWorld->getSleepAngularVelocity(); + mEngineSettings.nbPositionSolverIterations = mDynamicsWorld->getNbIterationsPositionSolver(); + mEngineSettings.nbVelocitySolverIterations = mDynamicsWorld->getNbIterationsVelocitySolver(); + mEngineSettings.timeBeforeSleep = mDynamicsWorld->getTimeBeforeSleep(); } // Destructor -Scene::~Scene() { - - // Stop the physics simulation - stopSimulation(); - - // Destroy the shader - mPhongShader.destroy(); +CubesScene::~CubesScene() { // Destroy all the cubes of the scene for (std::vector::iterator it = mBoxes.begin(); it != mBoxes.end(); ++it) { @@ -125,73 +126,76 @@ Scene::~Scene() { delete mDynamicsWorld; } -// Take a step for the simulation -void Scene::simulate() { +// Update the physics world (take a simulation step) +void CubesScene::updatePhysics() { - // If the physics simulation is running - if (mIsRunning) { + // Update the physics engine parameters + mDynamicsWorld->setIsGratityEnabled(mEngineSettings.isGravityEnabled); + rp3d::Vector3 gravity(mEngineSettings.gravity.x, mEngineSettings.gravity.y, + mEngineSettings.gravity.z); + mDynamicsWorld->setGravity(gravity); + mDynamicsWorld->enableSleeping(mEngineSettings.isSleepingEnabled); + mDynamicsWorld->setSleepLinearVelocity(mEngineSettings.sleepLinearVelocity); + mDynamicsWorld->setSleepAngularVelocity(mEngineSettings.sleepAngularVelocity); + mDynamicsWorld->setNbIterationsPositionSolver(mEngineSettings.nbPositionSolverIterations); + mDynamicsWorld->setNbIterationsVelocitySolver(mEngineSettings.nbVelocitySolverIterations); + mDynamicsWorld->setTimeBeforeSleep(mEngineSettings.timeBeforeSleep); - counter++; - if (counter == 400) { - //mIsRunning = false; - } - - // Take a simulation step - mDynamicsWorld->update(); - - // Update the position and orientation of the boxes - for (std::vector::iterator it = mBoxes.begin(); it != mBoxes.end(); ++it) { - - // Update the transform used for the rendering - (*it)->updateTransform(); - } - - mFloor->updateTransform(); - - // Set the color of the awake/sleeping bodies - for (uint i=0; igetRigidBody()->isSleeping()) { - mBoxes[i]->setColor(Color(1, 0, 0, 1)); - } - else { - mBoxes[i]->setColor(Color(0, 1, 0, 1)); - } - } - } + // Take a simulation step + mDynamicsWorld->update(mEngineSettings.timeStep); } -// Render the scene -void Scene::render() { +// Update the scene +void CubesScene::update() { - glEnable(GL_DEPTH_TEST); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - glEnable(GL_CULL_FACE); + SceneDemo::update(); - // Get the world-space to camera-space matrix - const Camera& camera = mViewer->getCamera(); - const openglframework::Matrix4 worldToCameraMatrix = camera.getTransformMatrix().getInverse(); + // Update the position and orientation of the boxes + for (std::vector::iterator it = mBoxes.begin(); it != mBoxes.end(); ++it) { + + // Update the transform used for the rendering + (*it)->updateTransform(mInterpolationFactor); + } + + mFloor->updateTransform(mInterpolationFactor); +} + +// Render the scene in a single pass +void CubesScene::renderSinglePass(Shader& shader, const openglframework::Matrix4& worldToCameraMatrix) { // Bind the shader - mPhongShader.bind(); - - // Set the variables of the shader - mPhongShader.setMatrix4x4Uniform("projectionMatrix", camera.getProjectionMatrix()); - mPhongShader.setVector3Uniform("light0PosCameraSpace",worldToCameraMatrix * mLight0.getOrigin()); - mPhongShader.setVector3Uniform("lightAmbientColor", Vector3(0.3f, 0.3f, 0.3f)); - const Color& diffCol = mLight0.getDiffuseColor(); - const Color& specCol = mLight0.getSpecularColor(); - mPhongShader.setVector3Uniform("light0DiffuseColor", Vector3(diffCol.r, diffCol.g, diffCol.b)); - mPhongShader.setVector3Uniform("light0SpecularColor", Vector3(specCol.r, specCol.g, specCol.b)); - mPhongShader.setFloatUniform("shininess", 60.0f); + shader.bind(); // Render all the cubes of the scene for (std::vector::iterator it = mBoxes.begin(); it != mBoxes.end(); ++it) { - (*it)->render(mPhongShader, worldToCameraMatrix); + (*it)->render(shader, worldToCameraMatrix); } // Render the floor - mFloor->render(mPhongShader, worldToCameraMatrix); + mFloor->render(shader, worldToCameraMatrix); // Unbind the shader - mPhongShader.unbind(); + shader.unbind(); +} + +// Reset the scene +void CubesScene::reset() { + + float radius = 2.0f; + + for (int i=0; iresetTransform(transform); + } } diff --git a/examples/cubes/Scene.h b/testbed/scenes/cubes/CubesScene.h similarity index 68% rename from examples/cubes/Scene.h rename to testbed/scenes/cubes/CubesScene.h index 2f79daed..57dfb99f 100644 --- a/examples/cubes/Scene.h +++ b/testbed/scenes/cubes/CubesScene.h @@ -23,40 +23,32 @@ * * ********************************************************************************/ -#ifndef SCENE_H -#define SCENE_H +#ifndef CUBES_SCENE_H +#define CUBES_SCENE_H // Libraries #include "openglframework.h" #include "reactphysics3d.h" #include "Box.h" -#include "Viewer.h" +#include "SceneDemo.h" + +namespace cubesscene { // Constants -const int NB_CUBES = 200; // Number of boxes in the scene +const float SCENE_RADIUS = 30.0f; // Radius of the scene in meters +const int NB_CUBES = 30; // Number of boxes in the scene const openglframework::Vector3 BOX_SIZE(2, 2, 2); // Box dimensions in meters const openglframework::Vector3 FLOOR_SIZE(50, 0.5f, 50); // Floor dimensions in meters const float BOX_MASS = 1.0f; // Box mass in kilograms const float FLOOR_MASS = 100.0f; // Floor mass in kilograms -// Class Scene -class Scene { +// Class CubesScene +class CubesScene : public SceneDemo { - private : + protected : // -------------------- Attributes -------------------- // - int counter; - - /// Pointer to the viewer - Viewer* mViewer; - - /// Light 0 - openglframework::Light mLight0; - - /// Phong shader - openglframework::Shader mPhongShader; - /// All the boxes of the scene std::vector mBoxes; @@ -66,55 +58,39 @@ class Scene { /// Dynamics world used for the physics simulation rp3d::DynamicsWorld* mDynamicsWorld; - /// True if the physics simulation is running - bool mIsRunning; - public: // -------------------- Methods -------------------- // /// Constructor - Scene(Viewer* viewer, const std::string& shaderFolderPath); + CubesScene(const std::string& name); /// Destructor - ~Scene(); + virtual ~CubesScene(); - /// Take a step for the simulation - void simulate(); + /// Update the physics world (take a simulation step) + /// Can be called several times per frame + virtual void updatePhysics(); - /// Stop the simulation - void stopSimulation(); + /// Update the scene (take a simulation step) + virtual void update(); - /// Start the simulation - void startSimulation(); + /// Render the scene in a single pass + virtual void renderSinglePass(openglframework::Shader& shader, + const openglframework::Matrix4& worldToCameraMatrix); - /// Pause or continue simulation - void pauseContinueSimulation(); + /// Reset the scene + virtual void reset(); - /// Render the scene - void render(); + /// Return all the contact points of the scene + virtual std::vector getContactPoints() const; }; -// Stop the simulation -inline void Scene::stopSimulation() { - mDynamicsWorld->stop(); - mIsRunning = false; +// Return all the contact points of the scene +inline std::vector CubesScene::getContactPoints() const { + return computeContactPointsOfWorld(mDynamicsWorld); } -// Start the simulation -inline void Scene::startSimulation() { - mDynamicsWorld->start(); - mIsRunning = true; -} - -// Pause or continue simulation -inline void Scene::pauseContinueSimulation() { - if (mIsRunning) { - stopSimulation(); - } - else { - startSimulation(); - } } #endif diff --git a/examples/joints/Scene.cpp b/testbed/scenes/joints/JointsScene.cpp similarity index 62% rename from examples/joints/Scene.cpp rename to testbed/scenes/joints/JointsScene.cpp index def294fa..dd3255d4 100644 --- a/examples/joints/Scene.cpp +++ b/testbed/scenes/joints/JointsScene.cpp @@ -24,27 +24,22 @@ ********************************************************************************/ // Libraries -#include "Scene.h" +#include "JointsScene.h" #include // Namespaces using namespace openglframework; +using namespace jointsscene; // Constructor -Scene::Scene(Viewer *viewer, const std::string& shaderFolderPath) - : mViewer(viewer), mLight0(0), mPhongShader(shaderFolderPath + "phong.vert", - shaderFolderPath + "phong.frag"), - mIsRunning(false) { - - // Move the light 0 - mLight0.translateWorld(Vector3(7, 15, 15)); +JointsScene::JointsScene(const std::string& name) + : SceneDemo(name, SCENE_RADIUS) { // Compute the radius and the center of the scene - float radiusScene = 30.0f; openglframework::Vector3 center(0, 5, 0); // Set the center of the scene - mViewer->setScenePosition(center, radiusScene); + setScenePosition(center, SCENE_RADIUS); // Gravity vector in the dynamics world rp3d::Vector3 gravity(0, rp3d::decimal(-9.81), 0); @@ -53,7 +48,7 @@ Scene::Scene(Viewer *viewer, const std::string& shaderFolderPath) rp3d::decimal timeStep = 1.0f / 60.0f; // Create the dynamics world for the physics simulation - mDynamicsWorld = new rp3d::DynamicsWorld(gravity, timeStep); + mDynamicsWorld = new rp3d::DynamicsWorld(gravity); // Set the number of iterations of the constraint solver mDynamicsWorld->setNbIterationsVelocitySolver(15); @@ -73,18 +68,20 @@ Scene::Scene(Viewer *viewer, const std::string& shaderFolderPath) // Create the floor createFloor(); - // Start the simulation - startSimulation(); + // Get the physics engine parameters + mEngineSettings.isGravityEnabled = mDynamicsWorld->isGravityEnabled(); + rp3d::Vector3 gravityVector = mDynamicsWorld->getGravity(); + mEngineSettings.gravity = openglframework::Vector3(gravityVector.x, gravityVector.y, gravityVector.z); + mEngineSettings.isSleepingEnabled = mDynamicsWorld->isSleepingEnabled(); + mEngineSettings.sleepLinearVelocity = mDynamicsWorld->getSleepLinearVelocity(); + mEngineSettings.sleepAngularVelocity = mDynamicsWorld->getSleepAngularVelocity(); + mEngineSettings.nbPositionSolverIterations = mDynamicsWorld->getNbIterationsPositionSolver(); + mEngineSettings.nbVelocitySolverIterations = mDynamicsWorld->getNbIterationsVelocitySolver(); + mEngineSettings.timeBeforeSleep = mDynamicsWorld->getTimeBeforeSleep(); } // Destructor -Scene::~Scene() { - - // Stop the physics simulation - stopSimulation(); - - // Destroy the shader - mPhongShader.destroy(); +JointsScene::~JointsScene() { // Destroy the joints mDynamicsWorld->destroyJoint(mSliderJoint); @@ -122,77 +119,145 @@ Scene::~Scene() { delete mDynamicsWorld; } +// Update the physics world (take a simulation step) +void JointsScene::updatePhysics() { + + // Update the physics engine parameters + mDynamicsWorld->setIsGratityEnabled(mEngineSettings.isGravityEnabled); + rp3d::Vector3 gravity(mEngineSettings.gravity.x, mEngineSettings.gravity.y, + mEngineSettings.gravity.z); + mDynamicsWorld->setGravity(gravity); + mDynamicsWorld->enableSleeping(mEngineSettings.isSleepingEnabled); + mDynamicsWorld->setSleepLinearVelocity(mEngineSettings.sleepLinearVelocity); + mDynamicsWorld->setSleepAngularVelocity(mEngineSettings.sleepAngularVelocity); + mDynamicsWorld->setNbIterationsPositionSolver(mEngineSettings.nbPositionSolverIterations); + mDynamicsWorld->setNbIterationsVelocitySolver(mEngineSettings.nbVelocitySolverIterations); + mDynamicsWorld->setTimeBeforeSleep(mEngineSettings.timeBeforeSleep); + + // Update the motor speed of the Slider Joint (to move up and down) + long double motorSpeed = 2 * cos(mEngineSettings.elapsedTime * 1.5); + mSliderJoint->setMotorSpeed(rp3d::decimal(motorSpeed)); + + // Take a simulation step + mDynamicsWorld->update(mEngineSettings.timeStep); +} + // Take a step for the simulation -void Scene::simulate() { +void JointsScene::update() { - // If the physics simulation is running - if (mIsRunning) { + SceneDemo::update(); - // Update the motor speed of the Slider Joint (to move up and down) - long double motorSpeed = 2 * cos(mDynamicsWorld->getPhysicsTime() * 1.5); - mSliderJoint->setMotorSpeed(rp3d::decimal(motorSpeed)); - - // Take a simulation step - mDynamicsWorld->update(); - - // Update the position and orientation of the boxes - mSliderJointBottomBox->updateTransform(); - mSliderJointTopBox->updateTransform(); - mPropellerBox->updateTransform(); - mFixedJointBox1->updateTransform(); - mFixedJointBox2->updateTransform(); - for (int i=0; iupdateTransform(); - } - - // Update the position and orientation of the floor - mFloor->updateTransform(); + // Update the position and orientation of the boxes + mSliderJointBottomBox->updateTransform(mInterpolationFactor); + mSliderJointTopBox->updateTransform(mInterpolationFactor); + mPropellerBox->updateTransform(mInterpolationFactor); + mFixedJointBox1->updateTransform(mInterpolationFactor); + mFixedJointBox2->updateTransform(mInterpolationFactor); + for (int i=0; iupdateTransform(mInterpolationFactor); } + + // Update the position and orientation of the floor + mFloor->updateTransform(mInterpolationFactor); } // Render the scene -void Scene::render() { - - glEnable(GL_DEPTH_TEST); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - glEnable(GL_CULL_FACE); - - // Get the world-space to camera-space matrix - const Camera& camera = mViewer->getCamera(); - const openglframework::Matrix4 worldToCameraMatrix = camera.getTransformMatrix().getInverse(); +void JointsScene::renderSinglePass(openglframework::Shader& shader, + const openglframework::Matrix4& worldToCameraMatrix) { // Bind the shader - mPhongShader.bind(); - - // Set the variables of the shader - mPhongShader.setVector3Uniform("light0PosCameraSpace",worldToCameraMatrix * mLight0.getOrigin()); - mPhongShader.setMatrix4x4Uniform("projectionMatrix", camera.getProjectionMatrix()); - mPhongShader.setVector3Uniform("lightAmbientColor", Vector3(0.3f, 0.3f, 0.3f)); - const Color& diffCol = mLight0.getDiffuseColor(); - const Color& specCol = mLight0.getSpecularColor(); - mPhongShader.setVector3Uniform("light0DiffuseColor", Vector3(diffCol.r, diffCol.g, diffCol.b)); - mPhongShader.setVector3Uniform("light0SpecularColor", Vector3(specCol.r, specCol.g, specCol.b)); - mPhongShader.setFloatUniform("shininess", 60.0f); + shader.bind(); // Render all the boxes - mSliderJointBottomBox->render(mPhongShader, worldToCameraMatrix); - mSliderJointTopBox->render(mPhongShader, worldToCameraMatrix); - mPropellerBox->render(mPhongShader, worldToCameraMatrix); - mFixedJointBox1->render(mPhongShader, worldToCameraMatrix); - mFixedJointBox2->render(mPhongShader, worldToCameraMatrix); + mSliderJointBottomBox->render(shader, worldToCameraMatrix); + mSliderJointTopBox->render(shader, worldToCameraMatrix); + mPropellerBox->render(shader, worldToCameraMatrix); + mFixedJointBox1->render(shader, worldToCameraMatrix); + mFixedJointBox2->render(shader, worldToCameraMatrix); for (int i=0; irender(mPhongShader, worldToCameraMatrix); + mBallAndSocketJointChainBoxes[i]->render(shader, worldToCameraMatrix); } // Render the floor - mFloor->render(mPhongShader, worldToCameraMatrix); + mFloor->render(shader, worldToCameraMatrix); // Unbind the shader - mPhongShader.unbind(); + shader.unbind(); +} + +// Reset the scene +void JointsScene::reset() { + + openglframework::Vector3 positionBox(0, 15, 5); + openglframework::Vector3 boxDimension(1, 1, 1); + + for (int i=0; iresetTransform(transform); + + positionBox.y -= boxDimension.y + 0.5f; + } + + // --------------- Slider Joint --------------- // + + // Position of the box + openglframework::Vector3 positionBox1(0, 2.1f, 0); + rp3d::Vector3 initPosition(positionBox1.x, positionBox1.y, positionBox1.z); + rp3d::Quaternion initOrientation = rp3d::Quaternion::identity(); + rp3d::Transform transformBottomBox(initPosition, initOrientation); + + // Create a box and a corresponding rigid in the dynamics world + mSliderJointBottomBox->resetTransform(transformBottomBox); + + // Position of the box + openglframework::Vector3 positionBox2(0, 4.2f, 0); + initPosition = rp3d::Vector3(positionBox2.x, positionBox2.y, positionBox2.z); + initOrientation = rp3d::Quaternion::identity(); + rp3d::Transform transformTopBox(initPosition, initOrientation); + + // Create a box and a corresponding rigid in the dynamics world + mSliderJointTopBox->resetTransform(transformTopBox); + + // --------------- Propeller Hinge joint --------------- // + + // Position of the box + positionBox1 = openglframework::Vector3(0, 7, 0); + initPosition = rp3d::Vector3(positionBox1.x, positionBox1.y, positionBox1.z); + initOrientation = rp3d::Quaternion::identity(); + rp3d::Transform transformHingeBox(initPosition, initOrientation); + + // Create a box and a corresponding rigid in the dynamics world + mPropellerBox->resetTransform(transformHingeBox); + + // --------------- Fixed joint --------------- // + + // Position of the box + positionBox1 = openglframework::Vector3(5, 7, 0); + initPosition = rp3d::Vector3(positionBox1.x, positionBox1.y, positionBox1.z); + initOrientation = rp3d::Quaternion::identity(); + rp3d::Transform transformFixedBox1(initPosition, initOrientation); + + // Create a box and a corresponding rigid in the dynamics world + mFixedJointBox1->resetTransform(transformFixedBox1); + + // Position of the box + positionBox2 = openglframework::Vector3(-5, 7, 0); + initPosition = rp3d::Vector3(positionBox2.x, positionBox2.y, positionBox2.z); + initOrientation = rp3d::Quaternion::identity(); + rp3d::Transform transformFixedBox2(initPosition, initOrientation); + + // Create a box and a corresponding rigid in the dynamics world + mFixedJointBox2->resetTransform(transformFixedBox2); } // Create the boxes and joints for the Ball-and-Socket joint example -void Scene::createBallAndSocketJoints() { +void JointsScene::createBallAndSocketJoints() { // --------------- Create the boxes --------------- // @@ -206,6 +271,10 @@ void Scene::createBallAndSocketJoints() { mBallAndSocketJointChainBoxes[i] = new Box(boxDimension, positionBox , boxMass, mDynamicsWorld); + // Set the box color + mBallAndSocketJointChainBoxes[i]->setColor(mDemoColors[i % mNbDemoColors]); + mBallAndSocketJointChainBoxes[i]->setSleepingColor(mRedColorDemo); + // The fist box cannot move (static body) if (i == 0) { mBallAndSocketJointChainBoxes[i]->getRigidBody()->setType(rp3d::STATIC); @@ -240,7 +309,7 @@ void Scene::createBallAndSocketJoints() { } /// Create the boxes and joint for the Slider joint example -void Scene::createSliderJoint() { +void JointsScene::createSliderJoint() { // --------------- Create the first box --------------- // @@ -251,6 +320,10 @@ void Scene::createSliderJoint() { openglframework::Vector3 box1Dimension(2, 4, 2); mSliderJointBottomBox = new Box(box1Dimension, positionBox1 , BOX_MASS, mDynamicsWorld); + // Set the box color + mSliderJointBottomBox->setColor(mBlueColorDemo); + mSliderJointBottomBox->setSleepingColor(mRedColorDemo); + // The fist box cannot move mSliderJointBottomBox->getRigidBody()->setType(rp3d::STATIC); @@ -265,7 +338,11 @@ void Scene::createSliderJoint() { // Create a box and a corresponding rigid in the dynamics world openglframework::Vector3 box2Dimension(1.5f, 4, 1.5f); - mSliderJointTopBox = new Box(box2Dimension, positionBox2 , BOX_MASS, mDynamicsWorld); + mSliderJointTopBox = new Box(box2Dimension, positionBox2, BOX_MASS, mDynamicsWorld); + + // Set the box color + mSliderJointTopBox->setColor(mOrangeColorDemo); + mSliderJointTopBox->setSleepingColor(mRedColorDemo); // Change the material properties of the rigid body rp3d::Material& material2 = mSliderJointTopBox->getRigidBody()->getMaterial(); @@ -292,7 +369,7 @@ void Scene::createSliderJoint() { } /// Create the boxes and joint for the Hinge joint example -void Scene::createPropellerHingeJoint() { +void JointsScene::createPropellerHingeJoint() { // --------------- Create the propeller box --------------- // @@ -303,6 +380,10 @@ void Scene::createPropellerHingeJoint() { openglframework::Vector3 boxDimension(10, 1, 1); mPropellerBox = new Box(boxDimension, positionBox1 , BOX_MASS, mDynamicsWorld); + // Set the box color + mPropellerBox->setColor(mYellowColorDemo); + mPropellerBox->setSleepingColor(mRedColorDemo); + // Change the material properties of the rigid body rp3d::Material& material = mPropellerBox->getRigidBody()->getMaterial(); material.setBounciness(rp3d::decimal(0.4)); @@ -327,7 +408,7 @@ void Scene::createPropellerHingeJoint() { } /// Create the boxes and joints for the fixed joints -void Scene::createFixedJoints() { +void JointsScene::createFixedJoints() { // --------------- Create the first box --------------- // @@ -338,6 +419,10 @@ void Scene::createFixedJoints() { openglframework::Vector3 boxDimension(1.5, 1.5, 1.5); mFixedJointBox1 = new Box(boxDimension, positionBox1 , BOX_MASS, mDynamicsWorld); + // Set the box color + mFixedJointBox1->setColor(mPinkColorDemo); + mFixedJointBox1->setSleepingColor(mRedColorDemo); + // Change the material properties of the rigid body rp3d::Material& material1 = mFixedJointBox1->getRigidBody()->getMaterial(); material1.setBounciness(rp3d::decimal(0.4)); @@ -350,6 +435,10 @@ void Scene::createFixedJoints() { // Create a box and a corresponding rigid in the dynamics world mFixedJointBox2 = new Box(boxDimension, positionBox2 , BOX_MASS, mDynamicsWorld); + // Set the box color + mFixedJointBox2->setColor(mBlueColorDemo); + mFixedJointBox2->setSleepingColor(mRedColorDemo); + // Change the material properties of the rigid body rp3d::Material& material2 = mFixedJointBox2->getRigidBody()->getMaterial(); material2.setBounciness(rp3d::decimal(0.4)); @@ -379,12 +468,16 @@ void Scene::createFixedJoints() { } // Create the floor -void Scene::createFloor() { +void JointsScene::createFloor() { // Create the floor openglframework::Vector3 floorPosition(0, 0, 0); mFloor = new Box(FLOOR_SIZE, floorPosition, FLOOR_MASS, mDynamicsWorld); + // Set the box color + mFloor->setColor(mGreyColorDemo); + mFloor->setSleepingColor(mGreyColorDemo); + // The floor must be a static rigid body mFloor->getRigidBody()->setType(rp3d::STATIC); diff --git a/examples/joints/Scene.h b/testbed/scenes/joints/JointsScene.h similarity index 79% rename from examples/joints/Scene.h rename to testbed/scenes/joints/JointsScene.h index 4467f3af..39ceecb2 100644 --- a/examples/joints/Scene.h +++ b/testbed/scenes/joints/JointsScene.h @@ -23,39 +23,33 @@ * * ********************************************************************************/ -#ifndef SCENE_H -#define SCENE_H +#ifndef JOINTS_SCENE_H +#define JOINTS_SCENE_H // Libraries #include "openglframework.h" #include "reactphysics3d.h" #include "Box.h" -#include "../common/Viewer.h" +#include "SceneDemo.h" + +namespace jointsscene { // Constants +const float SCENE_RADIUS = 30.0f; const openglframework::Vector3 BOX_SIZE(2, 2, 2); // Box dimensions in meters -const openglframework::Vector3 FLOOR_SIZE(20, 0.5f, 20); // Floor dimensions in meters +const openglframework::Vector3 FLOOR_SIZE(50, 0.5f, 50); // Floor dimensions in meters const float BOX_MASS = 1.0f; // Box mass in kilograms const float FLOOR_MASS = 100.0f; // Floor mass in kilograms const int NB_BALLSOCKETJOINT_BOXES = 7; // Number of Ball-And-Socket chain boxes const int NB_HINGE_BOXES = 7; // Number of Hinge chain boxes -// Class Scene -class Scene { +// Class JointsScene +class JointsScene : public SceneDemo { - private : + protected : // -------------------- Attributes -------------------- // - /// Pointer to the viewer - Viewer* mViewer; - - /// Light 0 - openglframework::Light mLight0; - - /// Phong shader - openglframework::Shader mPhongShader; - /// Boxes of Ball-And-Socket joint chain Box* mBallAndSocketJointChainBoxes[NB_BALLSOCKETJOINT_BOXES]; @@ -101,9 +95,6 @@ class Scene { /// Dynamics world used for the physics simulation rp3d::DynamicsWorld* mDynamicsWorld; - /// True if the physics simulation is running - bool mIsRunning; - // -------------------- Methods -------------------- // /// Create the boxes and joints for the Ball-and-Socket joint example @@ -126,47 +117,34 @@ class Scene { // -------------------- Methods -------------------- // /// Constructor - Scene(Viewer* viewer, const std::string& shaderFolderPath); + JointsScene(const std::string& name); /// Destructor - ~Scene(); + virtual ~JointsScene(); + + /// Update the physics world (take a simulation step) + /// Can be called several times per frame + virtual void updatePhysics(); /// Take a step for the simulation - void simulate(); + virtual void update(); - /// Stop the simulation - void stopSimulation(); + /// Render the scene in a single pass + virtual void renderSinglePass(openglframework::Shader& shader, + const openglframework::Matrix4& worldToCameraMatrix); - /// Start the simulation - void startSimulation(); + /// Reset the scene + virtual void reset(); - /// Pause or continue simulation - void pauseContinueSimulation(); - - /// Render the scene - void render(); + /// Return all the contact points of the scene + virtual std::vector getContactPoints() const; }; -// Stop the simulation -inline void Scene::stopSimulation() { - mDynamicsWorld->stop(); - mIsRunning = false; +// Return all the contact points of the scene +inline std::vector JointsScene::getContactPoints() const { + return computeContactPointsOfWorld(mDynamicsWorld); } -// Start the simulation -inline void Scene::startSimulation() { - mDynamicsWorld->start(); - mIsRunning = true; -} - -// Pause or continue simulation -inline void Scene::pauseContinueSimulation() { - if (mIsRunning) { - stopSimulation(); - } - else { - startSimulation(); - } } #endif diff --git a/examples/raycast/Scene.cpp b/testbed/scenes/raycast/RaycastScene.cpp similarity index 64% rename from examples/raycast/Scene.cpp rename to testbed/scenes/raycast/RaycastScene.cpp index da6423a3..c437df5b 100644 --- a/examples/raycast/Scene.cpp +++ b/testbed/scenes/raycast/RaycastScene.cpp @@ -24,38 +24,38 @@ ********************************************************************************/ // Libraries -#include "Scene.h" +#include "RaycastScene.h" // Namespaces using namespace openglframework; +using namespace raycastscene; // Constructor -Scene::Scene(Viewer* viewer, const std::string& shaderFolderPath, const std::string& meshFolderPath) - : mViewer(viewer), mLight0(0), mCurrentBodyIndex(-1), mAreNormalsDisplayed(false), - mPhongShader(shaderFolderPath + "phong.vert", - shaderFolderPath +"phong.frag") { +RaycastScene::RaycastScene(const std::string& name) + : SceneDemo(name, SCENE_RADIUS, false), mCurrentBodyIndex(-1), mAreNormalsDisplayed(false), + mMeshFolderPath("meshes/"), mRaycastManager(mPhongShader, mMeshFolderPath), + mVBOVertices(GL_ARRAY_BUFFER) { - // Move the light 0 - mLight0.translateWorld(Vector3(50, 50, 50)); + mIsContactPointsDisplayed = true; // Compute the radius and the center of the scene - float radiusScene = 30.0f; openglframework::Vector3 center(0, 0, 0); // Set the center of the scene - mViewer->setScenePosition(center, radiusScene); + setScenePosition(center, SCENE_RADIUS); // Create the dynamics world for the physics simulation mCollisionWorld = new rp3d::CollisionWorld(); - // Create the static data for the visual contact points - VisualContactPoint::createStaticData(meshFolderPath); - // ---------- Dumbbell ---------- // openglframework::Vector3 position1(0, 0, 0); // Create a convex mesh and a corresponding collision body in the dynamics world - mDumbbell = new Dumbbell(position1, mCollisionWorld, meshFolderPath); + mDumbbell = new Dumbbell(position1, mCollisionWorld, mMeshFolderPath); + + // Set the box color + mDumbbell->setColor(mGreyColorDemo); + mDumbbell->setSleepingColor(mRedColorDemo); // ---------- Box ---------- // openglframework::Vector3 position2(0, 0, 0); @@ -64,48 +64,75 @@ Scene::Scene(Viewer* viewer, const std::string& shaderFolderPath, const std::str mBox = new Box(BOX_SIZE, position2, mCollisionWorld); mBox->getCollisionBody()->setIsActive(false); + // Set the box color + mBox->setColor(mGreyColorDemo); + mBox->setSleepingColor(mRedColorDemo); + // ---------- Sphere ---------- // openglframework::Vector3 position3(0, 0, 0); // Create a sphere and a corresponding collision body in the dynamics world mSphere = new Sphere(SPHERE_RADIUS, position3, mCollisionWorld, - meshFolderPath); + mMeshFolderPath); + + // Set the color + mSphere->setColor(mGreyColorDemo); + mSphere->setSleepingColor(mRedColorDemo); // ---------- Cone ---------- // openglframework::Vector3 position4(0, 0, 0); // Create a cone and a corresponding collision body in the dynamics world mCone = new Cone(CONE_RADIUS, CONE_HEIGHT, position4, mCollisionWorld, - meshFolderPath); + mMeshFolderPath); + + // Set the color + mCone->setColor(mGreyColorDemo); + mCone->setSleepingColor(mRedColorDemo); // ---------- Cylinder ---------- // openglframework::Vector3 position5(0, 0, 0); // Create a cylinder and a corresponding collision body in the dynamics world mCylinder = new Cylinder(CYLINDER_RADIUS, CYLINDER_HEIGHT, position5, - mCollisionWorld, meshFolderPath); + mCollisionWorld, mMeshFolderPath); + + // Set the color + mCylinder->setColor(mGreyColorDemo); + mCylinder->setSleepingColor(mRedColorDemo); // ---------- Capsule ---------- // openglframework::Vector3 position6(0, 0, 0); // Create a cylinder and a corresponding collision body in the dynamics world mCapsule = new Capsule(CAPSULE_RADIUS, CAPSULE_HEIGHT, position6 , - mCollisionWorld, meshFolderPath); + mCollisionWorld, mMeshFolderPath); + + // Set the color + mCapsule->setColor(mGreyColorDemo); + mCapsule->setSleepingColor(mRedColorDemo); // ---------- Convex Mesh ---------- // openglframework::Vector3 position7(0, 0, 0); // Create a convex mesh and a corresponding collision body in the dynamics world - mConvexMesh = new ConvexMesh(position7, mCollisionWorld, meshFolderPath); + mConvexMesh = new ConvexMesh(position7, mCollisionWorld, mMeshFolderPath); + + // Set the color + mConvexMesh->setColor(mGreyColorDemo); + mConvexMesh->setSleepingColor(mRedColorDemo); // Create the lines that will be used for raycasting createLines(); + // Create the VBO and VAO to render the lines + createVBOAndVAO(mPhongShader); + changeBody(); } // Create the raycast lines -void Scene::createLines() { +void RaycastScene::createLines() { int nbRaysOneDimension = std::sqrt(float(NB_RAYS)); @@ -126,12 +153,15 @@ void Scene::createLines() { openglframework::Vector3 point2(0.0f, 0.0f, 0.0f); Line* line = new Line(point1, point2); mLines.push_back(line); + + mLinePoints.push_back(point1); + mLinePoints.push_back(point2); } } } // Change the body to raycast and to display -void Scene::changeBody() { +void RaycastScene::changeBody() { mCurrentBodyIndex++; if (mCurrentBodyIndex >= NB_BODIES) mCurrentBodyIndex = 0; @@ -163,8 +193,13 @@ void Scene::changeBody() { } } +// Reset the scene +void RaycastScene::reset() { + +} + // Destructor -Scene::~Scene() { +RaycastScene::~RaycastScene() { // Destroy the shader mPhongShader.destroy(); @@ -218,10 +253,20 @@ Scene::~Scene() { ++it) { delete (*it); } + + // Destroy the VBOs and VAO + mVBOVertices.destroy(); + mVAO.destroy(); +} + +// Update the physics world (take a simulation step) +void RaycastScene::updatePhysics() { + + } // Take a step for the simulation -void Scene::simulate() { +void RaycastScene::update() { mRaycastManager.resetPoints(); @@ -243,65 +288,101 @@ void Scene::simulate() { // callback class in argument. mCollisionWorld->raycast(ray, &mRaycastManager); } + + SceneDemo::update(); } // Render the scene -void Scene::render() { +void RaycastScene::renderSinglePass(openglframework::Shader& shader, + const openglframework::Matrix4& worldToCameraMatrix) { - glEnable(GL_DEPTH_TEST); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - glEnable(GL_CULL_FACE); - - // Get the world-space to camera-space matrix - const Camera& camera = mViewer->getCamera(); - const openglframework::Matrix4 worldToCameraMatrix = camera.getTransformMatrix().getInverse(); + // Bind the VAO + mVAO.bind(); // Bind the shader - mPhongShader.bind(); + shader.bind(); - openglframework::Vector4 grey(0.7, 0.7, 0.7, 1); - mPhongShader.setVector4Uniform("vertexColor", grey); + mVBOVertices.bind(); - // Set the variables of the shader - mPhongShader.setMatrix4x4Uniform("projectionMatrix", camera.getProjectionMatrix()); - mPhongShader.setVector3Uniform("light0PosCameraSpace", worldToCameraMatrix * mLight0.getOrigin()); - mPhongShader.setVector3Uniform("lightAmbientColor", Vector3(0.3f, 0.3f, 0.3f)); - const Color& diffColLight0 = mLight0.getDiffuseColor(); - const Color& specColLight0 = mLight0.getSpecularColor(); - mPhongShader.setVector3Uniform("light0DiffuseColor", Vector3(diffColLight0.r, diffColLight0.g, diffColLight0.b)); - mPhongShader.setVector3Uniform("light0SpecularColor", Vector3(specColLight0.r, specColLight0.g, specColLight0.b)); - mPhongShader.setFloatUniform("shininess", 200.0f); + // Set the model to camera matrix + const Matrix4 localToCameraMatrix = Matrix4::identity(); + shader.setMatrix4x4Uniform("localToWorldMatrix", localToCameraMatrix); + shader.setMatrix4x4Uniform("worldToCameraMatrix", worldToCameraMatrix); - if (mBox->getCollisionBody()->isActive()) mBox->render(mPhongShader, worldToCameraMatrix); - if (mSphere->getCollisionBody()->isActive()) mSphere->render(mPhongShader, worldToCameraMatrix); - if (mCone->getCollisionBody()->isActive()) mCone->render(mPhongShader, worldToCameraMatrix); - if (mCylinder->getCollisionBody()->isActive()) mCylinder->render(mPhongShader, worldToCameraMatrix); - if (mCapsule->getCollisionBody()->isActive()) mCapsule->render(mPhongShader, worldToCameraMatrix); - if (mConvexMesh->getCollisionBody()->isActive()) mConvexMesh->render(mPhongShader, worldToCameraMatrix); - if (mDumbbell->getCollisionBody()->isActive()) mDumbbell->render(mPhongShader, worldToCameraMatrix); + // Set the normal matrix (inverse transpose of the 3x3 upper-left sub matrix of the + // model-view matrix) + const openglframework::Matrix3 normalMatrix = + localToCameraMatrix.getUpperLeft3x3Matrix().getInverse().getTranspose(); + shader.setMatrix3x3Uniform("normalMatrix", normalMatrix, false); - mPhongShader.unbind(); - mPhongShader.bind(); + // Set the vertex color + openglframework::Vector4 color(1, 0, 0, 1); + shader.setVector4Uniform("vertexColor", color, false); - mPhongShader.setVector3Uniform("light0SpecularColor", Vector3(0, 0, 0)); - openglframework::Vector4 redColor(1, 0, 0, 1); - mPhongShader.setVector4Uniform("vertexColor", redColor); + // Get the location of shader attribute variables + GLint vertexPositionLoc = shader.getAttribLocation("vertexPosition"); - // Render all the raycast hit points - mRaycastManager.render(mPhongShader, worldToCameraMatrix, mAreNormalsDisplayed); + glEnableVertexAttribArray(vertexPositionLoc); + glVertexAttribPointer(vertexPositionLoc, 3, GL_FLOAT, GL_FALSE, 0, (char*)NULL); - mPhongShader.unbind(); - mPhongShader.bind(); + // Draw the lines + glDrawArrays(GL_LINES, 0, NB_RAYS); - openglframework::Vector4 blueColor(0, 0.62, 0.92, 1); - mPhongShader.setVector4Uniform("vertexColor", blueColor); + glDisableVertexAttribArray(vertexPositionLoc); - // Render the lines - for (std::vector::iterator it = mLines.begin(); it != mLines.end(); - ++it) { - (*it)->render(mPhongShader, worldToCameraMatrix); - } + mVBOVertices.unbind(); + + // Unbind the VAO + mVAO.unbind(); + + shader.unbind(); + + // Render the shapes + if (mBox->getCollisionBody()->isActive()) mBox->render(shader, worldToCameraMatrix); + if (mSphere->getCollisionBody()->isActive()) mSphere->render(shader, worldToCameraMatrix); + if (mCone->getCollisionBody()->isActive()) mCone->render(shader, worldToCameraMatrix); + if (mCylinder->getCollisionBody()->isActive()) mCylinder->render(shader, worldToCameraMatrix); + if (mCapsule->getCollisionBody()->isActive()) mCapsule->render(shader, worldToCameraMatrix); + if (mConvexMesh->getCollisionBody()->isActive()) mConvexMesh->render(shader, worldToCameraMatrix); + if (mDumbbell->getCollisionBody()->isActive()) mDumbbell->render(shader, worldToCameraMatrix); + + //mPhongShader.unbind(); + shader.unbind(); +} + +// Create the Vertex Buffer Objects used to render with OpenGL. +/// We create two VBOs (one for vertices and one for indices) +void RaycastScene::createVBOAndVAO(openglframework::Shader& shader) { + + // Bind the shader + shader.bind(); + + // Create the VBO for the vertices data + mVBOVertices.create(); + mVBOVertices.bind(); + size_t sizeVertices = mLinePoints.size() * sizeof(openglframework::Vector3); + mVBOVertices.copyDataIntoVBO(sizeVertices, &mLinePoints[0], GL_STATIC_DRAW); + mVBOVertices.unbind(); + + // Create the VAO for both VBOs + mVAO.create(); + mVAO.bind(); + + // Bind the VBO of vertices + mVBOVertices.bind(); + + // Unbind the VAO + mVAO.unbind(); // Unbind the shader - mPhongShader.unbind(); + shader.unbind(); +} + +// Called when a keyboard event occurs +void RaycastScene::keyboardEvent(int key, int scancode, int action, int mods) { + + // If the space key has been pressed + if (key == GLFW_KEY_SPACE && action == GLFW_PRESS) { + changeBody(); + } } diff --git a/examples/raycast/Scene.h b/testbed/scenes/raycast/RaycastScene.h similarity index 64% rename from examples/raycast/Scene.h rename to testbed/scenes/raycast/RaycastScene.h index 9b044e06..84cb0702 100644 --- a/examples/raycast/Scene.h +++ b/testbed/scenes/raycast/RaycastScene.h @@ -23,14 +23,15 @@ * * ********************************************************************************/ -#ifndef SCENE_H -#define SCENE_H +#ifndef RAYCAST_SCENE_H +#define RAYCAST_SCENE_H // Libraries #define _USE_MATH_DEFINES #include #include "openglframework.h" #include "reactphysics3d.h" +#include "SceneDemo.h" #include "Sphere.h" #include "Box.h" #include "Cone.h" @@ -42,7 +43,10 @@ #include "VisualContactPoint.h" #include "../common/Viewer.h" +namespace raycastscene { + // Constants +const float SCENE_RADIUS = 30.0f; const openglframework::Vector3 BOX_SIZE(4, 2, 1); const float SPHERE_RADIUS = 3.0f; const float CONE_RADIUS = 3.0f; @@ -62,18 +66,29 @@ class RaycastManager : public rp3d::RaycastCallback { private: /// All the visual contact points - std::vector mHitPoints; + std::vector mHitPoints; /// All the normals at hit points std::vector mNormals; + /// Shader + openglframework::Shader& mShader; + + /// Contact point mesh folder path + std::string mMeshFolderPath; + public: + RaycastManager(openglframework::Shader& shader, + const std::string& meshFolderPath) + : mShader(shader), mMeshFolderPath(meshFolderPath) { + + } + virtual rp3d::decimal notifyRaycastHit(const rp3d::RaycastInfo& raycastInfo) { rp3d::Vector3 hitPos = raycastInfo.worldPoint; openglframework::Vector3 position(hitPos.x, hitPos.y, hitPos.z); - VisualContactPoint* point = new VisualContactPoint(position); - mHitPoints.push_back(point); + mHitPoints.push_back(ContactPoint(position)); // Create a line to display the normal at hit point rp3d::Vector3 n = raycastInfo.worldNormal; @@ -84,33 +99,8 @@ class RaycastManager : public rp3d::RaycastCallback { return raycastInfo.hitFraction; } - void render(openglframework::Shader& shader, - const openglframework::Matrix4& worldToCameraMatrix, - bool showNormals) { - - // Render all the raycast hit points - for (std::vector::iterator it = mHitPoints.begin(); - it != mHitPoints.end(); ++it) { - (*it)->render(shader, worldToCameraMatrix); - } - - if (showNormals) { - - // Render all the normals at hit points - for (std::vector::iterator it = mNormals.begin(); - it != mNormals.end(); ++it) { - (*it)->render(shader, worldToCameraMatrix); - } - } - } - void resetPoints() { - // Destroy all the visual contact points - for (std::vector::iterator it = mHitPoints.begin(); - it != mHitPoints.end(); ++it) { - delete (*it); - } mHitPoints.clear(); // Destroy all the normals @@ -120,27 +110,25 @@ class RaycastManager : public rp3d::RaycastCallback { } mNormals.clear(); } + + std::vector getHitPoints() const { + return mHitPoints; + } }; -// Class Scene -class Scene { +// Class RaycastScene +class RaycastScene : public SceneDemo { private : // -------------------- Attributes -------------------- // - /// Pointer to the viewer - Viewer* mViewer; + /// Contact point mesh folder path + std::string mMeshFolderPath; /// Raycast manager RaycastManager mRaycastManager; - /// Light 0 - openglframework::Light mLight0; - - /// Phong shader - openglframework::Shader mPhongShader; - /// All the raycast lines std::vector mLines; @@ -164,37 +152,85 @@ class Scene { /// Collision world used for the physics simulation rp3d::CollisionWorld* mCollisionWorld; + /// All the points to render the lines + std::vector mLinePoints; + + /// Vertex Buffer Object for the vertices data + openglframework::VertexBufferObject mVBOVertices; + + /// Vertex Array Object for the vertex data + openglframework::VertexArrayObject mVAO; + /// Create the raycast lines void createLines(); + // Create the Vertex Buffer Objects used to render with OpenGL. + void createVBOAndVAO(openglframework::Shader& shader); + + public: // -------------------- Methods -------------------- // /// Constructor - Scene(Viewer* viewer, const std::string& shaderFolderPath, - const std::string& meshFolderPath); + RaycastScene(const std::string& name); /// Destructor - ~Scene(); + virtual ~RaycastScene(); + + /// Update the physics world (take a simulation step) + /// Can be called several times per frame + virtual void updatePhysics(); /// Take a step for the simulation - void simulate(); + virtual void update(); - /// Render the scene - void render(); + /// Render the scene in a single pass + virtual void renderSinglePass(openglframework::Shader& shader, + const openglframework::Matrix4& worldToCameraMatrix); + + /// Reset the scene + virtual void reset(); /// Change the body to raycast void changeBody(); /// Display or not the surface normals at hit points void showHideNormals(); + + /// Called when a keyboard event occurs + virtual void keyboardEvent(int key, int scancode, int action, int mods); + + /// Enabled/Disable the shadow mapping + void virtual setIsShadowMappingEnabled(bool isShadowMappingEnabled); + + /// Display/Hide the contact points + void virtual setIsContactPointsDisplayed(bool display); + + /// Return all the contact points of the scene + virtual std::vector getContactPoints() const; }; // Display or not the surface normals at hit points -inline void Scene::showHideNormals() { +inline void RaycastScene::showHideNormals() { mAreNormalsDisplayed = !mAreNormalsDisplayed; } +// Enabled/Disable the shadow mapping +inline void RaycastScene::setIsShadowMappingEnabled(bool isShadowMappingEnabled) { + SceneDemo::setIsShadowMappingEnabled(false); +} + +// Display/Hide the contact points +inline void RaycastScene::setIsContactPointsDisplayed(bool display) { + SceneDemo::setIsContactPointsDisplayed(true); +} + +// Return all the contact points of the scene +inline std::vector RaycastScene::getContactPoints() const { + return mRaycastManager.getHitPoints(); +} + +} #endif diff --git a/examples/common/opengl-framework/src/shaders/depth.frag b/testbed/shaders/depth.frag similarity index 88% rename from examples/common/opengl-framework/src/shaders/depth.frag rename to testbed/shaders/depth.frag index 4ef97b32..75c12abe 100644 --- a/examples/common/opengl-framework/src/shaders/depth.frag +++ b/testbed/shaders/depth.frag @@ -1,12 +1,14 @@ +#version 330 + /******************************************************************************** * OpenGL-Framework * -* Copyright (c) 2013 Daniel Chappuis * +* Copyright (c) 2015 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * * In no event will the authors be held liable for any damages arising from the * * use of this software. * -* * +* * * Permission is granted to anyone to use this software for any purpose, * * including commercial applications, and to alter it and redistribute it * * freely, subject to the following restrictions: * @@ -23,10 +25,11 @@ * * ********************************************************************************/ -void main(void) { +// Out variable +out vec4 color; // Output color - // Compute the depth of the pixel - float depth = +void main() { - gl_FragColor = vec4(depth, depth, depth, 1); + // Compute the final color + color = vec4(1, 1, 1, 1); } diff --git a/examples/common/opengl-framework/src/shaders/depth.vert b/testbed/shaders/depth.vert similarity index 84% rename from examples/common/opengl-framework/src/shaders/depth.vert rename to testbed/shaders/depth.vert index f320cc72..c4af178a 100644 --- a/examples/common/opengl-framework/src/shaders/depth.vert +++ b/testbed/shaders/depth.vert @@ -1,6 +1,8 @@ +#version 330 + /******************************************************************************** * OpenGL-Framework * -* Copyright (c) 2013 Daniel Chappuis * +* Copyright (c) 2015 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -24,13 +26,15 @@ ********************************************************************************/ // Uniform variables -uniform mat4 modelToWorldMatrix; // Model too world coordinates matrix -uniform mat4 worldToCameraMatrix; // World to camera coordinates matrix +uniform mat4 localToWorldMatrix; // Local-space to world-space matrix +uniform mat4 worldToCameraMatrix; // World-space to camera-space matrix uniform mat4 projectionMatrix; // Projection matrix -void main(void) { +// In variables +in vec4 vertexPosition; + +void main() { // Compute the clip-space vertex coordinates - gl_Position = projectionMatrix * worldToCameraMatrix * - modelToWorldMatrix * gl_Vertex; + gl_Position = projectionMatrix * worldToCameraMatrix * localToWorldMatrix * vertexPosition; } diff --git a/examples/common/opengl-framework/src/shaders/phong.frag b/testbed/shaders/phong.frag similarity index 57% rename from examples/common/opengl-framework/src/shaders/phong.frag rename to testbed/shaders/phong.frag index f09777da..73c76ba6 100644 --- a/examples/common/opengl-framework/src/shaders/phong.frag +++ b/testbed/shaders/phong.frag @@ -1,6 +1,8 @@ +#version 330 + /******************************************************************************** * OpenGL-Framework * -* Copyright (c) 2013 Daniel Chappuis * +* Copyright (c) 2015 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * @@ -27,16 +29,28 @@ uniform vec3 lightAmbientColor; // Lights ambient color uniform vec3 light0PosCameraSpace; // Camera-space position of the light uniform vec3 light0DiffuseColor; // Light 0 diffuse color -uniform vec3 light0SpecularColor; // Light 0 specular color -uniform float shininess; // Shininess -uniform sampler2D texture; // Texture +uniform sampler2D textureSampler; // Texture +uniform sampler2D shadowMapSampler; // Shadow map texture sampler uniform bool isTexture; // True if we need to use the texture uniform vec4 vertexColor; // Vertex color +uniform bool isShadowEnabled; // True if shadow mapping is enabled +uniform vec2 shadowMapDimension; // Shadow map dimension -// Varying variables -varying vec3 vertexPosCameraSpace; // Camera-space position of the vertex -varying vec3 vertexNormalCameraSpace; // Vertex normal in camera-space -varying vec2 texCoords; // Texture coordinates +// In variables +in vec3 vertexPosCameraSpace; // Camera-space position of the vertex +in vec3 vertexNormalCameraSpace; // Vertex normal in camera-space +in vec2 texCoords; // Texture coordinates +in vec4 shadowMapCoords; // Shadow map texture coords + +// Out variable +out vec4 color; // Output color + +// Texture for PCF Shadow mapping +float textureLookupPCF(sampler2D map, vec2 texCoords, vec2 offset) +{ + vec2 shadowMapScale = vec2(1.0, 1.0) / shadowMapDimension; + return texture(map, texCoords.xy + offset * shadowMapScale).r; +} void main() { @@ -45,7 +59,7 @@ void main() { // Get the texture color vec3 textureColor = vertexColor.rgb; - if (isTexture) textureColor = texture2D(texture, texCoords).rgb; + if (isTexture) textureColor = texture(textureSampler, texCoords).rgb; // Compute the surface normal vector vec3 N = normalize(vertexNormalCameraSpace); @@ -55,13 +69,35 @@ void main() { float diffuseFactor = max(dot(N, L0), 0.0); vec3 diffuse = light0DiffuseColor * diffuseFactor * textureColor; - // Compute the specular term of light 0 - vec3 V = normalize(-vertexPosCameraSpace); - vec3 H0 = normalize(V + L0); - float specularFactor = pow(max(dot(N, H0), 0.0), shininess); - if (diffuseFactor < 0.0) specularFactor = 0.0; - vec3 specular = light0SpecularColor * specularFactor; + // Compute shadow factor + float shadow = 1.0; + if (isShadowEnabled) { + shadow = 0.0; + float bias = 0.0003; + float shadowBias = -0.000; + vec4 shadowMapUV = shadowMapCoords; + shadowMapUV.z -= shadowBias; + vec4 shadowMapCoordsOverW = shadowMapUV / shadowMapUV.w; + + // PCF Shadow Mapping + for (float i=-1; i<=1; i++) { + for (float j=-1; j<=1; j++) { + float distInShadowMap = textureLookupPCF(shadowMapSampler, shadowMapCoordsOverW.xy, vec2(i, j)) + bias; + if (shadowMapCoords.w > 0) { + shadow += distInShadowMap < shadowMapCoordsOverW.z ? 0.5 : 1.0; + } + } + } + shadow /= 9.0; + + /* + float distanceInShadowMap = texture(shadowMapSampler, shadowMapCoordsOverW.xy).r + bias; + if (shadowMapCoords.w > 0) { + shadow = distanceInShadowMap < shadowMapCoordsOverW.z ? 0.5 : 1.0; + } + */ + } // Compute the final color - gl_FragColor = vec4(ambient + diffuse + specular, 1.0); + color = vec4(ambient + shadow * diffuse, 1.0); } diff --git a/testbed/shaders/phong.vert b/testbed/shaders/phong.vert new file mode 100644 index 00000000..491558fb --- /dev/null +++ b/testbed/shaders/phong.vert @@ -0,0 +1,64 @@ +#version 330 + +/******************************************************************************** +* OpenGL-Framework * +* Copyright (c) 2015 Daniel Chappuis * +********************************************************************************* +* * +* This software is provided 'as-is', without any express or implied warranty. * +* In no event will the authors be held liable for any damages arising from the * +* use of this software. * +* * +* Permission is granted to anyone to use this software for any purpose, * +* including commercial applications, and to alter it and redistribute it * +* freely, subject to the following restrictions: * +* * +* 1. The origin of this software must not be misrepresented; you must not claim * +* that you wrote the original software. If you use this software in a * +* product, an acknowledgment in the product documentation would be * +* appreciated but is not required. * +* * +* 2. Altered source versions must be plainly marked as such, and must not be * +* misrepresented as being the original software. * +* * +* 3. This notice may not be removed or altered from any source distribution. * +* * +********************************************************************************/ + +// Uniform variables +uniform mat4 localToWorldMatrix; // Local-space to world-space matrix +uniform mat4 worldToCameraMatrix; // World-space to camera-space matrix +uniform mat4 worldToLight0CameraMatrix; // World-space to light0 camera-space matrix (for shadow mapping) +uniform mat4 projectionMatrix; // Projection matrix +uniform mat3 normalMatrix; // Normal matrix +uniform mat4 shadowMapProjectionMatrix; // Shadow map projection matrix + +// In variables +in vec4 vertexPosition; +in vec3 vertexNormal; +in vec2 textureCoords; + +// Out variables +out vec3 vertexPosCameraSpace; // Camera-space position of the vertex +out vec3 vertexNormalCameraSpace; // Vertex normal in camera-space +out vec2 texCoords; // Texture coordinates +out vec4 shadowMapCoords; // Shadow map texture coords + +void main() { + + // Compute the vertex position + vec4 positionCameraSpace = worldToCameraMatrix * localToWorldMatrix * vertexPosition; + vertexPosCameraSpace = positionCameraSpace.xyz; + + // Compute the world surface normal + vertexNormalCameraSpace = normalMatrix * vertexNormal; + + // Get the texture coordinates + texCoords = textureCoords; + + // Compute the texture coords of the vertex in the shadow map + shadowMapCoords = shadowMapProjectionMatrix * worldToLight0CameraMatrix * localToWorldMatrix * vertexPosition; + + // Compute the clip-space vertex coordinates + gl_Position = projectionMatrix * positionCameraSpace; +} diff --git a/testbed/shaders/quad.frag b/testbed/shaders/quad.frag new file mode 100644 index 00000000..a6a842ba --- /dev/null +++ b/testbed/shaders/quad.frag @@ -0,0 +1,42 @@ +#version 330 + +/******************************************************************************** +* OpenGL-Framework * +* Copyright (c) 2015 Daniel Chappuis * +********************************************************************************* +* * +* This software is provided 'as-is', without any express or implied warranty. * +* In no event will the authors be held liable for any damages arising from the * +* use of this software. * +* * +* Permission is granted to anyone to use this software for any purpose, * +* including commercial applications, and to alter it and redistribute it * +* freely, subject to the following restrictions: * +* * +* 1. The origin of this software must not be misrepresented; you must not claim * +* that you wrote the original software. If you use this software in a * +* product, an acknowledgment in the product documentation would be * +* appreciated but is not required. * +* * +* 2. Altered source versions must be plainly marked as such, and must not be * +* misrepresented as being the original software. * +* * +* 3. This notice may not be removed or altered from any source distribution. * +* * +********************************************************************************/ + +// Uniform variables +uniform sampler2D textureSampler; // Texture + +// In variables +in vec2 texCoords; // Texture coordinates + +// Out variable +out vec4 color; // Output color + +void main() { + + // Get the texture color + float depthColor = texture(textureSampler, texCoords).r; + color = vec4(depthColor, depthColor, depthColor, 1); +} diff --git a/testbed/shaders/quad.vert b/testbed/shaders/quad.vert new file mode 100644 index 00000000..81c6ee9e --- /dev/null +++ b/testbed/shaders/quad.vert @@ -0,0 +1,41 @@ +#version 330 + +/******************************************************************************** +* OpenGL-Framework * +* Copyright (c) 2015 Daniel Chappuis * +********************************************************************************* +* * +* This software is provided 'as-is', without any express or implied warranty. * +* In no event will the authors be held liable for any damages arising from the * +* use of this software. * +* * +* Permission is granted to anyone to use this software for any purpose, * +* including commercial applications, and to alter it and redistribute it * +* freely, subject to the following restrictions: * +* * +* 1. The origin of this software must not be misrepresented; you must not claim * +* that you wrote the original software. If you use this software in a * +* product, an acknowledgment in the product documentation would be * +* appreciated but is not required. * +* * +* 2. Altered source versions must be plainly marked as such, and must not be * +* misrepresented as being the original software. * +* * +* 3. This notice may not be removed or altered from any source distribution. * +* * +********************************************************************************/ + +// In variables +in vec4 vertexPosition; + +// Out variables +out vec2 texCoords; // Texture coordinates + +void main() { + + // Compute the texture coordinates + texCoords = (vertexPosition.xy + vec2(1, 1)) / 2.0; + + // Compute the clip-space vertex coordinates + gl_Position = vec4(vertexPosition.xyz, 1); +} diff --git a/testbed/src/Gui.cpp b/testbed/src/Gui.cpp new file mode 100644 index 00000000..a80ed525 --- /dev/null +++ b/testbed/src/Gui.cpp @@ -0,0 +1,406 @@ +/******************************************************************************** +* ReactPhysics3D physics library, http://www.reactphysics3d.com * +* Copyright (c) 2010-2015 Daniel Chappuis * +********************************************************************************* +* * +* This software is provided 'as-is', without any express or implied warranty. * +* In no event will the authors be held liable for any damages arising from the * +* use of this software. * +* * +* Permission is granted to anyone to use this software for any purpose, * +* including commercial applications, and to alter it and redistribute it * +* freely, subject to the following restrictions: * +* * +* 1. The origin of this software must not be misrepresented; you must not claim * +* that you wrote the original software. If you use this software in a * +* product, an acknowledgment in the product documentation would be * +* appreciated but is not required. * +* * +* 2. Altered source versions must be plainly marked as such, and must not be * +* misrepresented as being the original software. * +* * +* 3. This notice may not be removed or altered from any source distribution. * +* * +********************************************************************************/ + +// Libraries +#include "Gui.h" +#include +#include +#include +#include "TestbedApplication.h" + +GLFWwindow* Gui::mWindow = NULL; +double Gui::g_Time = 0.0f; +bool Gui::g_MousePressed[3] = {false, false, false}; +float Gui::g_MouseWheel = 0.0f; +GLuint Gui::g_FontTexture = 0; +size_t Gui::g_VboSize = 0; +int Gui::g_AttribLocationTex = 0, Gui::g_AttribLocationProjMtx = 0; +int Gui::g_AttribLocationPosition = 0, Gui::g_AttribLocationUV = 0, Gui::g_AttribLocationColor = 0; +Shader Gui::mShader; +openglframework::VertexBufferObject Gui::mVBO(GL_ARRAY_BUFFER); +openglframework::VertexArrayObject Gui::mVAO; +Gui::LeftPane Gui::mLeftPane = SCENES; +double Gui::mScrollX = 0.0; +double Gui::mScrollY = 0.0; +double Gui::mTimeSinceLastProfilingDisplay = 0; +double Gui::mCachedFPS = 0; +double Gui::mCachedUpdateTime = 0; +double Gui::mCachedPhysicsUpdateTime = 0; + +// Constructor +Gui::Gui() { + + g_Time = 0.0f; + g_MousePressed[0] = false; + g_MousePressed[1] = false; + g_MousePressed[2] = false; + g_MouseWheel = 0.0f; + g_FontTexture = 0; + g_VboSize = 0; +} + +// Destructor +Gui::~Gui() { + + mVAO.destroy(); + mVBO.destroy(); + + mShader.destroy(); + + imguiRenderGLDestroy(); +} + +// Create and return the singleton instance of this class +Gui& Gui::getInstance() { + static Gui instance; + return instance; +} + +/// Initialize the GUI +void Gui::init() { + + // Init UI + if (!imguiRenderGLInit("DroidSans.ttf")) { + fprintf(stderr, "Could not init GUI renderer.\n"); + exit(EXIT_FAILURE); + } + + mTimeSinceLastProfilingDisplay = glfwGetTime(); +} + +void Gui::displayLeftPane() { + + TestbedApplication& app = TestbedApplication::getInstance(); + + int windowWidth, windowHeight; + glfwGetWindowSize(mWindow, &windowWidth, &windowHeight); + + int scrollarea = 0; + imguiBeginScrollArea(NULL, 0, app.mWindowToFramebufferRatio.y * (windowHeight - LEFT_PANE_HEADER_HEIGHT), + app.mWindowToFramebufferRatio.x * LEFT_PANE_WIDTH, + app.mWindowToFramebufferRatio.y * LEFT_PANE_HEADER_HEIGHT, &scrollarea); + + // ------ Header ----- // + + imguiHorizontalSpace(10); + imguiVerticalSpace(20); + imguiStartLine(); + + const int button_width = 150; + + // Play/Pause + if (imguiButton(app.mTimer.isRunning() ? "Pause" : "Play", true, button_width)) { + app.togglePlayPauseSimulation(); + } + imguiHorizontalSpace(5); + + // Step + if (imguiButton("Step", !app.mTimer.isRunning(), button_width)) { + app.toggleTakeSinglePhysicsStep(); + } + imguiHorizontalSpace(5); + + // Restart + if (imguiButton("Restart", true, button_width)) { + app.restartSimulation(); + } + + imguiEndLine(); + imguiVerticalSpace(70); + + imguiSeparatorLine(); + imguiVerticalSpace(5); + imguiStartLine(); + + // ----- Left Pane Tabs ----- // + + int widthButton = app.mWindowToFramebufferRatio.x * LEFT_PANE_WIDTH / 4.3; + if (imguiButton("Scenes", true, widthButton)) { + mLeftPane = SCENES; + } + imguiHorizontalSpace(5); + + if (imguiButton("Physics", true, widthButton)) { + mLeftPane = PHYSICS; + } + imguiHorizontalSpace(5); + + if (imguiButton("Rendering", true, widthButton)) { + mLeftPane = RENDERING; + } + imguiHorizontalSpace(5); + + if (imguiButton("Profiling", true, widthButton)) { + mLeftPane = PROFILING; + } + + imguiEndLine(); + imguiVerticalSpace(BUTTON_HEIGHT + 8); + imguiSeparatorLine(); + imguiEndScrollArea(); + + // Display the left pane content + switch(mLeftPane) { + case SCENES: displayScenesPane(); break; + case PHYSICS: displayPhysicsPane(); break; + case RENDERING: displayRenderingPane(); break; + case PROFILING: displayProfilingPane(); break; + } +} + +// Display the list of scenes +void Gui::displayScenesPane() { + + TestbedApplication& app = TestbedApplication::getInstance(); + + static int choice = 0; + int startChoice = choice; + int scrollarea = 1; + int windowWidth, windowHeight; + glfwGetWindowSize(mWindow, &windowWidth, &windowHeight); + + std::vector scenes = app.getScenes(); + + imguiBeginScrollArea("Scenes", 0, 0, + app.mWindowToFramebufferRatio.x * LEFT_PANE_WIDTH, + app.mWindowToFramebufferRatio.y * (windowHeight - LEFT_PANE_HEADER_HEIGHT), + &scrollarea); + + imguiVerticalSpace(15); + + // For each scene + for (int i=0; igetName().c_str(), choice == i)) { + choice = i; + } + } + if (choice != startChoice) { + app.switchScene(scenes[choice]); + } + + imguiEndScrollArea(); +} + +void Gui::displayPhysicsPane() { + + TestbedApplication& app = TestbedApplication::getInstance(); + + int windowWidth, windowHeight; + glfwGetWindowSize(mWindow, &windowWidth, &windowHeight); + + int scrollarea = 2; + imguiBeginScrollArea("Physics Engine Parameters", 0, 0, + app.mWindowToFramebufferRatio.x * LEFT_PANE_WIDTH, + app.mWindowToFramebufferRatio.y * (windowHeight - LEFT_PANE_HEADER_HEIGHT), + &scrollarea); + + imguiVerticalSpace(15); + + // Enabled/Disable Sleeping + bool toggle = imguiCheck("Sleeping enabled", app.mEngineSettings.isSleepingEnabled); + if (toggle) { + app.mEngineSettings.isSleepingEnabled = !app.mEngineSettings.isSleepingEnabled; + } + + // Enabled/Disable Gravity + toggle = imguiCheck("Gravity enabled", app.mEngineSettings.isGravityEnabled); + if (toggle) { + app.mEngineSettings.isGravityEnabled = !app.mEngineSettings.isGravityEnabled; + } + + // Timestep + float timeStep = app.mEngineSettings.timeStep; + if (imguiSlider("Timestep", &timeStep, 0.001f, 1.0f, 0.001f)) { + app.mEngineSettings.timeStep = timeStep; + } + + // Nb velocity solver iterations + float nbVelocityIterations = static_cast(app.mEngineSettings.nbVelocitySolverIterations); + if (imguiSlider("Velocity Solver Iterations", &nbVelocityIterations, 1.0f, 100.0f, 1.0f)) { + app.mEngineSettings.nbVelocitySolverIterations = static_cast(nbVelocityIterations); + } + + // Nb position solver iterations + float nbPositionIterations = static_cast(app.mEngineSettings.nbPositionSolverIterations); + if (imguiSlider("Position Solver Iterations", &nbPositionIterations, 1.0f, 100.0f, 1.0f)) { + app.mEngineSettings.nbPositionSolverIterations = static_cast(nbPositionIterations); + } + + // Time before sleep + float timeBeforeSleep = app.mEngineSettings.timeBeforeSleep; + if (imguiSlider("Time before sleep", &timeBeforeSleep, 0.0f, 60.0f, 0.5f)) { + app.mEngineSettings.timeBeforeSleep = timeBeforeSleep; + } + + // Sleep linear velocity + float sleepLinearVelocity = app.mEngineSettings.sleepLinearVelocity; + if (imguiSlider("Sleep linear velocity", &sleepLinearVelocity, 0.0f, 30.0f, 0.5f)) { + app.mEngineSettings.sleepLinearVelocity = sleepLinearVelocity; + } + + // Sleep angular velocity + float sleepAngularVelocity = app.mEngineSettings.sleepAngularVelocity; + if (imguiSlider("Sleep angular velocity", &sleepAngularVelocity, 0.0f, 30.0f, 0.5f)) { + app.mEngineSettings.sleepAngularVelocity = sleepAngularVelocity; + } + + // Gravity vector + openglframework::Vector3 gravity = app.mEngineSettings.gravity; + float gravityX = gravity.x, gravityY = gravity.y, gravityZ = gravity.z; + if (imguiSlider("Gravity X", &gravityX, -50.0f, 50.0f, 0.5f)) { + app.mEngineSettings.gravity.x = gravityX; + } + if (imguiSlider("Gravity Y", &gravityY, -50.0f, 50.0f, 0.5f)) { + app.mEngineSettings.gravity.y = gravityY; + } + if (imguiSlider("Gravity Z", &gravityZ, -50.0f, 50.0f, 0.5f)) { + app.mEngineSettings.gravity.z = gravityZ; + } + + imguiEndScrollArea(); +} + +void Gui::displayRenderingPane() { + + TestbedApplication& app = TestbedApplication::getInstance(); + + int windowWidth, windowHeight; + glfwGetWindowSize(mWindow, &windowWidth, &windowHeight); + + int scrollarea = 2; + imguiBeginScrollArea("Rendering Parameters", 0, 0, + app.mWindowToFramebufferRatio.x * LEFT_PANE_WIDTH, + app.mWindowToFramebufferRatio.y * (windowHeight - LEFT_PANE_HEADER_HEIGHT), + &scrollarea); + + imguiVerticalSpace(15); + + + // Display/Hide contact points + bool toggleContactPoints = imguiCheck("Contacts", app.mIsContactPointsDisplayed); + if (toggleContactPoints) { + app.displayContactPoints(!app.mIsContactPointsDisplayed); + } + + // Enabled/Disable VSync + bool toggleVSync = imguiCheck("V Sync", app.mIsVSyncEnabled); + if (toggleVSync) { + app.enableVSync(!app.mIsVSyncEnabled); + } + + // Enabled/Disable Shadows + bool toggleShadows = imguiCheck("Shadows", app.mIsShadowMappingEnabled); + if (toggleShadows) { + app.enableShadows(!app.mIsShadowMappingEnabled); + } + + imguiEndScrollArea(); +} + +void Gui::displayProfilingPane() { + + TestbedApplication& app = TestbedApplication::getInstance(); + + double currentTime = glfwGetTime(); + if ((currentTime - mTimeSinceLastProfilingDisplay) > TIME_INTERVAL_DISPLAY_PROFILING_INFO) { + mTimeSinceLastProfilingDisplay = currentTime; + mCachedFPS = app.mFPS; + mCachedUpdateTime = app.mUpdateTime; + mCachedPhysicsUpdateTime = app.mPhysicsUpdateTime; + } + + int windowWidth, windowHeight; + glfwGetWindowSize(mWindow, &windowWidth, &windowHeight); + + int scrollarea = 2; + imguiBeginScrollArea("Profiling", 0, 0, + app.mWindowToFramebufferRatio.x * LEFT_PANE_WIDTH, + app.mWindowToFramebufferRatio.y * (windowHeight - LEFT_PANE_HEADER_HEIGHT), + &scrollarea); + + imguiVerticalSpace(15); + + // Framerate (FPS) + std::stringstream ss; + ss << std::setprecision(4) << mCachedFPS; + std::string fps = std::string("FPS : ") + ss.str(); + imguiItem(fps.c_str()); + + // Update time + std::stringstream ss1; + double updateTime = mCachedUpdateTime * 1000.0; + ss1 << std::setprecision(4) << updateTime; + std::string updateTimeStr = std::string("Update time (ms) : ") + ss1.str(); + imguiItem(updateTimeStr.c_str()); + + // Update time (physics) + std::stringstream ss2; + ss2 << std::setprecision(4) << (mCachedPhysicsUpdateTime * 1000.0); + std::string updatePhysicsTimeStr = std::string("Update physics time (ms) : ") + ss2.str(); + imguiItem(updatePhysicsTimeStr.c_str()); + + imguiEndScrollArea(); +} + +// Display the GUI +void Gui::render() { + + TestbedApplication& app = TestbedApplication::getInstance(); + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDisable(GL_DEPTH_TEST); + + int display_w, display_h; + glfwGetFramebufferSize(mWindow, &display_w, &display_h); + + int windowWidth, windowHeight; + glfwGetWindowSize(mWindow, &windowWidth, &windowHeight); + + // Mouse position + double mouseX, mouseY; + glfwGetCursorPos(mWindow, &mouseX, &mouseY); + + // Mouse buttons + int leftButton = glfwGetMouseButton(mWindow, GLFW_MOUSE_BUTTON_LEFT); + unsigned char mousebutton = 0; + if(leftButton == GLFW_PRESS ) { + mousebutton |= IMGUI_MBUT_LEFT; + } + + imguiBeginFrame(app.mWindowToFramebufferRatio.x * mouseX, + app.mWindowToFramebufferRatio.y * (windowHeight - mouseY), mousebutton, + - app.mWindowToFramebufferRatio.y * mScrollY); + resetScroll(); + + //displayHeader(); + displayLeftPane(); + + imguiEndFrame(); + + imguiRenderGLDraw(display_w, display_h); +} diff --git a/testbed/src/Gui.h b/testbed/src/Gui.h new file mode 100644 index 00000000..19d5dc07 --- /dev/null +++ b/testbed/src/Gui.h @@ -0,0 +1,140 @@ +/******************************************************************************** +* ReactPhysics3D physics library, http://www.reactphysics3d.com * +* Copyright (c) 2010-2015 Daniel Chappuis * +********************************************************************************* +* * +* This software is provided 'as-is', without any express or implied warranty. * +* In no event will the authors be held liable for any damages arising from the * +* use of this software. * +* * +* Permission is granted to anyone to use this software for any purpose, * +* including commercial applications, and to alter it and redistribute it * +* freely, subject to the following restrictions: * +* * +* 1. The origin of this software must not be misrepresented; you must not claim * +* that you wrote the original software. If you use this software in a * +* product, an acknowledgment in the product documentation would be * +* appreciated but is not required. * +* * +* 2. Altered source versions must be plainly marked as such, and must not be * +* misrepresented as being the original software. * +* * +* 3. This notice may not be removed or altered from any source distribution. * +* * +********************************************************************************/ + +#ifndef GUI_H +#define GUI_H + +// Libraries +#include "imgui.h" +#include "imguiRenderGL3.h" +#include +#include +#include "openglframework.h" + +// Constants +const float GUI_SCALING = 2.0f; +const int LEFT_PANE_WIDTH = 300; +const int LEFT_PANE_HEADER_HEIGHT = 90; +const double TIME_INTERVAL_DISPLAY_PROFILING_INFO = 0.3; + +using namespace openglframework; + +// Declarations +class TestbedApplication; + +// Class Gui +class Gui { + + protected : + + enum LeftPane {SCENES, PHYSICS, RENDERING, PROFILING}; + + // -------------------- Constants -------------------- // + + + // -------------------- Attributes -------------------- // + + // TODO : Delete this + static GLFWwindow* mWindow; + + static Shader mShader; + static double g_Time; + static bool g_MousePressed[3]; + static float g_MouseWheel; + static GLuint g_FontTexture; + static int g_AttribLocationTex, g_AttribLocationProjMtx; + static int g_AttribLocationPosition, g_AttribLocationUV, g_AttribLocationColor; + static size_t g_VboSize; + static openglframework::VertexBufferObject mVBO; + static openglframework::VertexArrayObject mVAO; + static LeftPane mLeftPane; + + static double mScrollX, mScrollY; + + // -------------------- Methods -------------------- // + + static void displayLeftPane(); + + /// Display the list of scenes + static void displayScenesPane(); + + static void displayPhysicsPane(); + static void displayRenderingPane(); + static void displayProfilingPane(); + + static void resetScroll(); + + /// Current time (in seconds) from last profiling time display + static double mTimeSinceLastProfilingDisplay; + + /// Cached Framerate + static double mCachedFPS; + + /// Cached update time + static double mCachedUpdateTime; + + // Cached update physics time + static double mCachedPhysicsUpdateTime; + + + public : + + // -------------------- Methods -------------------- // + + /// Constructor + Gui(); + + /// Destructor + ~Gui(); + + /// Create and return the singleton instance of this class + static Gui& getInstance(); + + /// Initialize the GUI + void init(); + + /// Display the GUI + void render(); + + static void setWindow(GLFWwindow* window); + + static void setScroll(double scrollX, double scrollY); +}; + +inline void Gui::setWindow(GLFWwindow* window) { + mWindow = window; +} + +inline void Gui::resetScroll() { + mScrollX = 0.0; + mScrollY = 0.0; +} + +inline void Gui::setScroll(double scrollX, double scrollY) { + mScrollX = scrollX; + mScrollY = scrollY; +} + +#endif diff --git a/testbed/src/Main.cpp b/testbed/src/Main.cpp new file mode 100644 index 00000000..39fd038f --- /dev/null +++ b/testbed/src/Main.cpp @@ -0,0 +1,38 @@ +/******************************************************************************** +* ReactPhysics3D physics library, http://www.reactphysics3d.com * +* Copyright (c) 2010-2015 Daniel Chappuis * +********************************************************************************* +* * +* This software is provided 'as-is', without any express or implied warranty. * +* In no event will the authors be held liable for any damages arising from the * +* use of this software. * +* * +* Permission is granted to anyone to use this software for any purpose, * +* including commercial applications, and to alter it and redistribute it * +* freely, subject to the following restrictions: * +* * +* 1. The origin of this software must not be misrepresented; you must not claim * +* that you wrote the original software. If you use this software in a * +* product, an acknowledgment in the product documentation would be * +* appreciated but is not required. * +* * +* 2. Altered source versions must be plainly marked as such, and must not be * +* misrepresented as being the original software. * +* * +* 3. This notice may not be removed or altered from any source distribution. * +* * +********************************************************************************/ + +// Libraries +#include "TestbedApplication.h" + +// Main function +int main(int argc, char** argv) { + + // Create and start the testbed application + TestbedApplication& application = TestbedApplication::getInstance(); + application.init(); + application.startMainLoop(); + + return 0; +} diff --git a/testbed/src/Scene.cpp b/testbed/src/Scene.cpp new file mode 100644 index 00000000..7e71968b --- /dev/null +++ b/testbed/src/Scene.cpp @@ -0,0 +1,181 @@ +/******************************************************************************** +* ReactPhysics3D physics library, http://www.reactphysics3d.com * +* Copyright (c) 2010-2015 Daniel Chappuis * +********************************************************************************* +* * +* This software is provided 'as-is', without any express or implied warranty. * +* In no event will the authors be held liable for any damages arising from the * +* use of this software. * +* * +* Permission is granted to anyone to use this software for any purpose, * +* including commercial applications, and to alter it and redistribute it * +* freely, subject to the following restrictions: * +* * +* 1. The origin of this software must not be misrepresented; you must not claim * +* that you wrote the original software. If you use this software in a * +* product, an acknowledgment in the product documentation would be * +* appreciated but is not required. * +* * +* 2. Altered source versions must be plainly marked as such, and must not be * +* misrepresented as being the original software. * +* * +* 3. This notice may not be removed or altered from any source distribution. * +* * +********************************************************************************/ + +// Libraries +#include "Scene.h" +#include + +using namespace openglframework; + +// Constructor +Scene::Scene(const std::string& name, bool isShadowMappingEnabled) + : mName(name), mInterpolationFactor(0.0f), mViewportX(0), mViewportY(0), + mViewportWidth(0), mViewportHeight(0), mIsShadowMappingEnabled(isShadowMappingEnabled), + mIsContactPointsDisplayed(true) { + +} + +// Destructor +Scene::~Scene() { + +} + +// Set the scene position (where the camera needs to look at) +void Scene::setScenePosition(const openglframework::Vector3& position, float sceneRadius) { + + // Set the position and radius of the scene + mCenterScene = position; + mCamera.setSceneRadius(sceneRadius); + + // Reset the camera position and zoom in order to view all the scene + resetCameraToViewAll(); +} + +// Set the camera so that we can view the whole scene +void Scene::resetCameraToViewAll() { + + // Move the camera to the origin of the scene + mCamera.translateWorld(-mCamera.getOrigin()); + + // Move the camera to the center of the scene + mCamera.translateWorld(mCenterScene); + + // Set the zoom of the camera so that the scene center is + // in negative view direction of the camera + mCamera.setZoom(1.0); +} + +// Map the mouse x,y coordinates to a point on a sphere +bool Scene::mapMouseCoordinatesToSphere(double xMouse, double yMouse, + Vector3& spherePoint) const { + + if ((xMouse >= 0) && (xMouse <= mWindowWidth) && (yMouse >= 0) && (yMouse <= mWindowHeight)) { + float x = float(xMouse - 0.5f * mWindowWidth) / float(mWindowWidth); + float y = float(0.5f * mWindowHeight - yMouse) / float(mWindowHeight); + float sinx = sin(PI * x * 0.5f); + float siny = sin(PI * y * 0.5f); + float sinx2siny2 = sinx * sinx + siny * siny; + + // Compute the point on the sphere + spherePoint.x = sinx; + spherePoint.y = siny; + spherePoint.z = (sinx2siny2 < 1.0) ? sqrt(1.0f - sinx2siny2) : 0.0f; + + return true; + } + + return false; +} + +// Called when a mouse button event occurs +void Scene::mouseButtonEvent(int button, int action, int mods, + double mousePosX, double mousePosY) { + + // If the mouse button is pressed + if (action == GLFW_PRESS) { + mLastMouseX = mousePosX; + mLastMouseY = mousePosY; + mIsLastPointOnSphereValid = mapMouseCoordinatesToSphere(mousePosX, mousePosY, mLastPointOnSphere); + } + else { // If the mouse button is released + mIsLastPointOnSphereValid = false; + } +} + +// Called when a mouse motion event occurs +void Scene::mouseMotionEvent(double xMouse, double yMouse, int leftButtonState, + int rightButtonState, int middleButtonState, + int altKeyState) { + + // Zoom + if (leftButtonState == GLFW_PRESS && altKeyState == GLFW_PRESS) { + + float dy = static_cast(yMouse - mLastMouseY); + float h = static_cast(mWindowHeight); + + // Zoom the camera + zoom(-dy / h); + } + // Translation + else if (middleButtonState == GLFW_PRESS || rightButtonState == GLFW_PRESS || + (leftButtonState == GLFW_PRESS && altKeyState == GLFW_PRESS)) { + translate(xMouse, yMouse); + } + // Rotation + else if (leftButtonState == GLFW_PRESS) { + rotate(xMouse, yMouse); + } + + // Remember the mouse position + mLastMouseX = xMouse; + mLastMouseY = yMouse; + mIsLastPointOnSphereValid = mapMouseCoordinatesToSphere(xMouse, yMouse, mLastPointOnSphere); +} + +// Called when a scrolling event occurs +void Scene::scrollingEvent(float xAxis, float yAxis, float scrollSensitivy) { + zoom(yAxis * scrollSensitivy); +} + +// Zoom the camera +void Scene::zoom(float zoomDiff) { + + // Zoom the camera + mCamera.setZoom(zoomDiff); +} + +// Translate the camera +void Scene::translate(int xMouse, int yMouse) { + float dx = static_cast(xMouse - mLastMouseX); + float dy = static_cast(yMouse - mLastMouseY); + + // Translate the camera + mCamera.translateCamera(-dx / float(mCamera.getWidth()), + -dy / float(mCamera.getHeight()), mCenterScene); +} + +// Rotate the camera +void Scene::rotate(int xMouse, int yMouse) { + + if (mIsLastPointOnSphereValid) { + + Vector3 newPoint3D; + bool isNewPointOK = mapMouseCoordinatesToSphere(xMouse, yMouse, newPoint3D); + + if (isNewPointOK) { + Vector3 axis = mLastPointOnSphere.cross(newPoint3D); + float cosAngle = mLastPointOnSphere.dot(newPoint3D); + + float epsilon = std::numeric_limits::epsilon(); + if (fabs(cosAngle) < 1.0f && axis.length() > epsilon) { + axis.normalize(); + float angle = 2.0f * acos(cosAngle); + + // Rotate the camera around the center of the scene + mCamera.rotateAroundLocalPoint(axis, -angle, mCenterScene); + } + } + } +} diff --git a/testbed/src/Scene.h b/testbed/src/Scene.h new file mode 100644 index 00000000..2091caac --- /dev/null +++ b/testbed/src/Scene.h @@ -0,0 +1,277 @@ +/******************************************************************************** +* ReactPhysics3D physics library, http://www.reactphysics3d.com * +* Copyright (c) 2010-2015 Daniel Chappuis * +********************************************************************************* +* * +* This software is provided 'as-is', without any express or implied warranty. * +* In no event will the authors be held liable for any damages arising from the * +* use of this software. * +* * +* Permission is granted to anyone to use this software for any purpose, * +* including commercial applications, and to alter it and redistribute it * +* freely, subject to the following restrictions: * +* * +* 1. The origin of this software must not be misrepresented; you must not claim * +* that you wrote the original software. If you use this software in a * +* product, an acknowledgment in the product documentation would be * +* appreciated but is not required. * +* * +* 2. Altered source versions must be plainly marked as such, and must not be * +* misrepresented as being the original software. * +* * +* 3. This notice may not be removed or altered from any source distribution. * +* * +********************************************************************************/ + +#ifndef SCENE_H +#define SCENE_H + +// Libraries +#include "openglframework.h" + +// Structure ContactPoint +struct ContactPoint { + + public: + openglframework::Vector3 point; + + /// Constructor + ContactPoint(const openglframework::Vector3& pointWorld) : point(pointWorld) { + + } +}; + +/// Structure EngineSettings +/// This structure contains several physics engine parameters +struct EngineSettings { + + public: + + float elapsedTime; // Elapsed time (in seconds) + float timeStep; // Current time step (in seconds) + int nbVelocitySolverIterations; // Nb of velocity solver iterations + int nbPositionSolverIterations; // Nb of position solver iterations + bool isSleepingEnabled; // True if sleeping technique is enabled + float timeBeforeSleep; // Time of inactivity before a body sleep + float sleepLinearVelocity; // Sleep linear velocity + float sleepAngularVelocity; // Sleep angular velocity + bool isGravityEnabled; // True if gravity is enabled + openglframework::Vector3 gravity; // Gravity vector + + /// Constructor + EngineSettings() : elapsedTime(0.0f), timeStep(0.0f) { + + } +}; + +// Class Scene +// Abstract class that represents a 3D scene. +class Scene { + + protected: + + // -------------------- Attributes -------------------- // + + /// Scene name + std::string mName; + + /// Physics engine settings + EngineSettings mEngineSettings; + + /// Camera + openglframework::Camera mCamera; + + /// Center of the scene + openglframework::Vector3 mCenterScene; + + /// Last mouse coordinates on the windows + double mLastMouseX, mLastMouseY; + + /// Window dimension + int mWindowWidth, mWindowHeight; + + /// Last point computed on a sphere (for camera rotation) + openglframework::Vector3 mLastPointOnSphere; + + /// True if the last point computed on a sphere (for camera rotation) is valid + bool mIsLastPointOnSphereValid; + + /// Interpolation factor for the bodies in the current frame + float mInterpolationFactor; + + /// Viewport x,y, width and height values + int mViewportX, mViewportY, mViewportWidth, mViewportHeight; + + /// True if the shadow mapping is enabled + bool mIsShadowMappingEnabled; + + /// True if contact points are displayed + bool mIsContactPointsDisplayed; + + // -------------------- Methods -------------------- // + + /// Set the scene position (where the camera needs to look at) + void setScenePosition(const openglframework::Vector3& position, float sceneRadius); + + /// Set the camera so that we can view the whole scene + void resetCameraToViewAll(); + + /// Map mouse coordinates to coordinates on the sphere + bool mapMouseCoordinatesToSphere(double xMouse, double yMouse, + openglframework::Vector3& spherePoint) const; + + /// Zoom the camera + void zoom(float zoomDiff); + + /// Translate the camera + void translate(int xMouse, int yMouse); + + /// Rotate the camera + void rotate(int xMouse, int yMouse); + + public: + + // -------------------- Methods -------------------- // + + /// Constructor + Scene(const std::string& name, bool isShadowMappingEnabled = false); + + /// Destructor + virtual ~Scene(); + + /// Reshape the view + virtual void reshape(int width, int height); + + /// Update the physics world (take a simulation step) + /// Can be called several times per frame + virtual void updatePhysics()=0; + + /// Update the scene + virtual void update()=0; + + /// Render the scene + virtual void render()=0; + + /// Reset the scene + virtual void reset()=0; + + /// Called when a keyboard event occurs + virtual void keyboardEvent(int key, int scancode, int action, int mods); + + /// Called when a mouse button event occurs + virtual void mouseButtonEvent(int button, int action, int mods, + double mousePosX, double mousePosY); + + /// Called when a mouse motion event occurs + virtual void mouseMotionEvent(double xMouse, double yMouse, int leftButtonState, + int rightButtonState, int middleButtonState, int altKeyState); + + /// Called when a scrolling event occurs + virtual void scrollingEvent(float xAxis, float yAxis, float scrollSensitivy); + + /// Set the window dimension + void setWindowDimension(int width, int height); + + /// Set the viewport to render the scene + void setViewport(int x, int y, int width, int height); + + /// Return a reference to the camera + const openglframework::Camera& getCamera() const; + + /// Get the engine settings + EngineSettings getEngineSettings() const; + + /// Set the engine settings + void setEngineSettings(const EngineSettings& settings); + + /// Set the interpolation factor + void setInterpolationFactor(float interpolationFactor); + + /// Return the name of the scene + std::string getName() const; + + /// Return true if the shadow mapping is enabled + bool getIsShadowMappingEnabled() const; + + /// Enabled/Disable the shadow mapping + void virtual setIsShadowMappingEnabled(bool isShadowMappingEnabled); + + /// Display/Hide the contact points + void virtual setIsContactPointsDisplayed(bool display); + + /// Return all the contact points of the scene + std::vector virtual getContactPoints() const; +}; + +// Called when a keyboard event occurs +inline void Scene::keyboardEvent(int key, int scancode, int action, int mods) { + +} + +/// Reshape the view +inline void Scene::reshape(int width, int height) { + mCamera.setDimensions(width, height); +} + +// Return a reference to the camera +inline const openglframework::Camera& Scene::getCamera() const { + return mCamera; +} + +// Set the window dimension +inline void Scene::setWindowDimension(int width, int height) { + mWindowWidth = width; + mWindowHeight = height; +} + +// Set the viewport to render the scene +inline void Scene::setViewport(int x, int y, int width, int height) { + mViewportX = x; + mViewportY = y; + mViewportWidth = width; + mViewportHeight = height; +} + +// Get the engine settings +inline EngineSettings Scene::getEngineSettings() const { + return mEngineSettings; +} + +// Set the engine settings +inline void Scene::setEngineSettings(const EngineSettings& settings) { + mEngineSettings = settings; +} + +// Set the interpolation factor +inline void Scene::setInterpolationFactor(float interpolationFactor) { + mInterpolationFactor = interpolationFactor; +} + +// Return the name of the scene +inline std::string Scene::getName() const { + return mName; +} + +// Return true if the shadow mapping is enabled +inline bool Scene::getIsShadowMappingEnabled() const { + return mIsShadowMappingEnabled; +} + +// Enabled/Disable the shadow mapping +inline void Scene::setIsShadowMappingEnabled(bool isShadowMappingEnabled) { + mIsShadowMappingEnabled = isShadowMappingEnabled; +} + +// Display/Hide the contact points +inline void Scene::setIsContactPointsDisplayed(bool display) { + mIsContactPointsDisplayed = display; +} + +// Return all the contact points of the scene +inline std::vector Scene::getContactPoints() const { + + // Return an empty list of contact points + return std::vector(); +} + +#endif diff --git a/testbed/src/SceneDemo.cpp b/testbed/src/SceneDemo.cpp new file mode 100644 index 00000000..7fe881b9 --- /dev/null +++ b/testbed/src/SceneDemo.cpp @@ -0,0 +1,346 @@ +/******************************************************************************** +* ReactPhysics3D physics library, http://www.reactphysics3d.com * +* Copyright (c) 2010-2015 Daniel Chappuis * +********************************************************************************* +* * +* This software is provided 'as-is', without any express or implied warranty. * +* In no event will the authors be held liable for any damages arising from the * +* use of this software. * +* * +* Permission is granted to anyone to use this software for any purpose, * +* including commercial applications, and to alter it and redistribute it * +* freely, subject to the following restrictions: * +* * +* 1. The origin of this software must not be misrepresented; you must not claim * +* that you wrote the original software. If you use this software in a * +* product, an acknowledgment in the product documentation would be * +* appreciated but is not required. * +* * +* 2. Altered source versions must be plainly marked as such, and must not be * +* misrepresented as being the original software. * +* * +* 3. This notice may not be removed or altered from any source distribution. * +* * +********************************************************************************/ + +// Libraries +#include "SceneDemo.h" +#include + +using namespace openglframework; + +int SceneDemo::shadowMapTextureLevel = 0; +openglframework::Color SceneDemo::mGreyColorDemo = Color(0.70f, 0.70f, 0.7f, 1.0); +openglframework::Color SceneDemo::mYellowColorDemo = Color(0.9, 0.88, 0.145, 1.0); +openglframework::Color SceneDemo::mBlueColorDemo = Color(0, 0.66, 0.95, 1.0); +openglframework::Color SceneDemo::mOrangeColorDemo = Color(0.9, 0.35, 0, 1.0); +openglframework::Color SceneDemo::mPinkColorDemo = Color(0.83, 0.48, 0.64, 1.0); +openglframework::Color SceneDemo::mRedColorDemo = Color(0.95, 0, 0, 1.0); +int SceneDemo::mNbDemoColors = 4; +openglframework::Color SceneDemo::mDemoColors[] = {SceneDemo::mYellowColorDemo, SceneDemo::mBlueColorDemo, + SceneDemo::mOrangeColorDemo, SceneDemo::mPinkColorDemo}; + +// Constructor +SceneDemo::SceneDemo(const std::string& name, float sceneRadius, bool isShadowMappingEnabled) + : Scene(name, isShadowMappingEnabled), + mLight0(0), mIsShadowMappingInitialized(false), + mDepthShader("shaders/depth.vert", "shaders/depth.frag"), + mPhongShader("shaders/phong.vert", "shaders/phong.frag"), + mQuadShader("shaders/quad.vert", "shaders/quad.frag"), + mVBOQuad(GL_ARRAY_BUFFER), mMeshFolderPath("meshes/") { + + shadowMapTextureLevel++; + + // Move the light0 + mLight0.translateWorld(Vector3(-2, 35, 40)); + + // Camera at light0 postion for the shadow map + mShadowMapLightCamera.translateWorld(mLight0.getOrigin()); + mShadowMapLightCamera.rotateLocal(Vector3(1, 0, 0), -PI / 4.0f); + mShadowMapLightCamera.rotateWorld(Vector3(0, 1, 0), PI / 8.0f); + + mShadowMapLightCamera.setDimensions(SHADOWMAP_WIDTH, SHADOWMAP_HEIGHT); + mShadowMapLightCamera.setFieldOfView(80.0f); + mShadowMapLightCamera.setSceneRadius(100); + + mShadowMapBiasMatrix.setAllValues(0.5, 0.0, 0.0, 0.5, + 0.0, 0.5, 0.0, 0.5, + 0.0, 0.0, 0.5, 0.5, + 0.0, 0.0, 0.0, 1.0); + + // Create the Shadow map FBO and texture + if (mIsShadowMappingEnabled) { + createShadowMapFBOAndTexture(); + } + + createQuadVBO(); + + VisualContactPoint::createStaticData(mMeshFolderPath); +} + +// Destructor +SceneDemo::~SceneDemo() { + + mShadowMapTexture.destroy(); + mFBOShadowMap.destroy(); + mVBOQuad.destroy(); + + // Destroy the contact points + removeAllContactPoints(); + + VisualContactPoint::destroyStaticData(); +} + +// Update the scene +void SceneDemo::update() { + + // Update the contact points + updateContactPoints(); +} + +// Render the scene (in multiple passes for shadow mapping) +void SceneDemo::render() { + + const Color& diffCol = mLight0.getDiffuseColor(); + + glEnable(GL_DEPTH_TEST); + glEnable(GL_CULL_FACE); + + // ---------- Render the scene to generate the shadow map (first pass) ----------- // + + const Matrix4 shadowMapProjMatrix = mShadowMapLightCamera.getProjectionMatrix(); + const openglframework::Matrix4 worldToLightCameraMatrix = mShadowMapLightCamera.getTransformMatrix().getInverse(); + + // If Shadow Mapping is enabled + if (mIsShadowMappingEnabled) { + + // Culling switching, rendering only backface, this is done to avoid self-shadowing + glCullFace(GL_BACK); + + mFBOShadowMap.bind(); + + // Bind the shader + mDepthShader.bind(); + + // Set the variables of the shader + mDepthShader.setMatrix4x4Uniform("projectionMatrix", shadowMapProjMatrix); + + // Set the viewport to render into the shadow map texture + glViewport(0, 0, SHADOWMAP_WIDTH, SHADOWMAP_HEIGHT); + + // Clear previous frame values + glClear(GL_DEPTH_BUFFER_BIT); + + // Disable color rendering, we only want to write to the Z-Buffer + glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); + + // Render the objects of the scene + renderSinglePass(mDepthShader, worldToLightCameraMatrix); + + // Unbind the shader + mDepthShader.unbind(); + + mFBOShadowMap.unbind(); + + glDisable(GL_POLYGON_OFFSET_FILL); + } + + // ---------- Render the scene for final rendering (second pass) ----------- // + + glCullFace(GL_BACK); + + // Get the world-space to camera-space matrix + const openglframework::Matrix4 worldToCameraMatrix = mCamera.getTransformMatrix().getInverse(); + + mPhongShader.bind(); + + if (mIsShadowMappingEnabled) mShadowMapTexture.bind(); + const GLuint textureUnit = 0; + + // Set the variables of the shader + mPhongShader.setMatrix4x4Uniform("projectionMatrix", mCamera.getProjectionMatrix()); + mPhongShader.setMatrix4x4Uniform("shadowMapProjectionMatrix", mShadowMapBiasMatrix * shadowMapProjMatrix); + mPhongShader.setMatrix4x4Uniform("worldToLight0CameraMatrix", worldToLightCameraMatrix); + mPhongShader.setVector3Uniform("light0PosCameraSpace", worldToCameraMatrix * mLight0.getOrigin()); + mPhongShader.setVector3Uniform("lightAmbientColor", Vector3(0.4f, 0.4f, 0.4f)); + mPhongShader.setVector3Uniform("light0DiffuseColor", Vector3(diffCol.r, diffCol.g, diffCol.b)); + mPhongShader.setIntUniform("shadowMapSampler", textureUnit); + mPhongShader.setIntUniform("isShadowEnabled", mIsShadowMappingEnabled); + mPhongShader.setVector2Uniform("shadowMapDimension", Vector2(SHADOWMAP_WIDTH, SHADOWMAP_HEIGHT)); + + // Set the viewport to render the scene + glViewport(mViewportX, mViewportY, mViewportWidth, mViewportHeight); + + //Enabling color write (previously disabled for light POV z-buffer rendering) + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + + // Clear previous frame values + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + // Render the objects of the scene + renderSinglePass(mPhongShader, worldToCameraMatrix); + + // Render the contact points + if (mIsContactPointsDisplayed) { + renderContactPoints(mPhongShader, worldToCameraMatrix); + } + + if (mIsShadowMappingEnabled) mShadowMapTexture.unbind(); + mPhongShader.unbind(); + + //drawTextureQuad(); +} + +// Create the Shadow map FBO and texture +void SceneDemo::createShadowMapFBOAndTexture() { + + // Create the texture for the depth values + mShadowMapTexture.create(SHADOWMAP_WIDTH, SHADOWMAP_HEIGHT, GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT, + GL_UNSIGNED_BYTE, GL_NEAREST, GL_NEAREST, GL_CLAMP, GL_CLAMP, NULL); + + // Create the FBO for the shadow map + mFBOShadowMap.create(0, 0, false); + mFBOShadowMap.bind(); + + // Tell OpenGL that we won't bind a color texture with the currently binded FBO + glDrawBuffer(GL_NONE); + glReadBuffer(GL_NONE); + + mFBOShadowMap.attachTexture(GL_DEPTH_ATTACHMENT_EXT, mShadowMapTexture.getID()); + mFBOShadowMap.unbind(); + + mIsShadowMappingInitialized = true; +} + +// Used for debugging shadow maps +void SceneDemo::createQuadVBO() { + + mVAOQuad.create(); + mVAOQuad.bind(); + + static const GLfloat quadVertexData[] = { + -1.0f, -1.0f, 0.0f, + 1.0f, -1.0f, 0.0f, + -1.0f, 1.0f, 0.0f, + -1.0f, 1.0f, 0.0f, + 1.0f, -1.0f, 0.0f, + 1.0f, 1.0f, 0.0f, + }; + + mVBOQuad.create(); + mVBOQuad.bind(); + mVBOQuad.copyDataIntoVBO(sizeof(quadVertexData), quadVertexData, GL_STATIC_DRAW); + mVBOQuad.unbind(); + + mVAOQuad.unbind(); +} + +void SceneDemo::drawTextureQuad() { + + glViewport(mViewportX, mViewportY, mViewportWidth, mViewportHeight); + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + + // Clear previous frame values + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + const GLuint textureUnit = 0; + + mVAOQuad.bind(); + mQuadShader.bind(); + mShadowMapTexture.bind(); + mQuadShader.setIntUniform("textureSampler", textureUnit); + mVBOQuad.bind(); + + GLint vertexPositionLoc = mQuadShader.getAttribLocation("vertexPosition"); + glEnableVertexAttribArray(vertexPositionLoc); + + glVertexAttribPointer( + vertexPositionLoc, // attribute 0. No particular reason for 0, but must match the layout in the shader. + 3, // size + GL_FLOAT, // type + GL_FALSE, // normalized? + 0, // stride + (void*)0 // array buffer offset + ); + + // Draw the triangles ! + glDrawArrays(GL_TRIANGLES, 0, 6); // 2*3 indices starting at 0 -> 2 triangles + + glDisableVertexAttribArray(vertexPositionLoc); + + mVBOQuad.unbind(); + mShadowMapTexture.unbind(); + mQuadShader.unbind(); + mVAOQuad.unbind(); +} + +// Gather and create contact points +void SceneDemo::updateContactPoints() { + + // Remove the previous contact points + removeAllContactPoints(); + + if (mIsContactPointsDisplayed) { + + // Get the current contact points of the scene + std::vector contactPoints = getContactPoints(); + + // For each contact point + std::vector::const_iterator it; + for (it = contactPoints.begin(); it != contactPoints.end(); ++it) { + + // Create a visual contact point for rendering + VisualContactPoint* point = new VisualContactPoint(it->point, mMeshFolderPath); + mContactPoints.push_back(point); + } + } +} + +// Render the contact points +void SceneDemo::renderContactPoints(openglframework::Shader& shader, + const openglframework::Matrix4& worldToCameraMatrix) { + + // Render all the raycast hit points + for (std::vector::iterator it = mContactPoints.begin(); + it != mContactPoints.end(); ++it) { + (*it)->render(shader, worldToCameraMatrix); + } +} + +void SceneDemo::removeAllContactPoints() { + + // Destroy all the visual contact points + for (std::vector::iterator it = mContactPoints.begin(); + it != mContactPoints.end(); ++it) { + delete (*it); + } + mContactPoints.clear(); +} + +// Return all the contact points of the scene +std::vector SceneDemo::computeContactPointsOfWorld(const rp3d::DynamicsWorld* world) const { + + std::vector contactPoints; + + // Get the list of contact manifolds from the world + std::vector manifolds = world->getContactsList(); + + // For each contact manifold + std::vector::const_iterator it; + for (it = manifolds.begin(); it != manifolds.end(); ++it) { + + const rp3d::ContactManifold* manifold = *it; + + // For each contact point of the manifold + for (uint i=0; igetNbContactPoints(); i++) { + + rp3d::ContactPoint* contactPoint = manifold->getContactPoint(i); + rp3d::Vector3 point = contactPoint->getWorldPointOnBody1(); + ContactPoint contact(openglframework::Vector3(point.x, point.y, point.z)); + contactPoints.push_back(contact); + } + + } + + return contactPoints; +} diff --git a/testbed/src/SceneDemo.h b/testbed/src/SceneDemo.h new file mode 100644 index 00000000..5ec37b45 --- /dev/null +++ b/testbed/src/SceneDemo.h @@ -0,0 +1,154 @@ +/******************************************************************************** +* ReactPhysics3D physics library, http://www.reactphysics3d.com * +* Copyright (c) 2010-2015 Daniel Chappuis * +********************************************************************************* +* * +* This software is provided 'as-is', without any express or implied warranty. * +* In no event will the authors be held liable for any damages arising from the * +* use of this software. * +* * +* Permission is granted to anyone to use this software for any purpose, * +* including commercial applications, and to alter it and redistribute it * +* freely, subject to the following restrictions: * +* * +* 1. The origin of this software must not be misrepresented; you must not claim * +* that you wrote the original software. If you use this software in a * +* product, an acknowledgment in the product documentation would be * +* appreciated but is not required. * +* * +* 2. Altered source versions must be plainly marked as such, and must not be * +* misrepresented as being the original software. * +* * +* 3. This notice may not be removed or altered from any source distribution. * +* * +********************************************************************************/ + +#ifndef SCENEDEMO_H +#define SCENEDEMO_H + +// Libraries +#include "Scene.h" +#include "VisualContactPoint.h" +#include "reactphysics3d.h" + +// Constants +const int SHADOWMAP_WIDTH = 2048; +const int SHADOWMAP_HEIGHT = 2048; + +// Class SceneDemo +// Abstract class that represents a 3D scene for the ReactPhysics3D examples. +// This scene has a single light source with shadow mapping. +class SceneDemo : public Scene { + + protected: + + // -------------------- Attributes -------------------- // + + /// Light 0 + openglframework::Light mLight0; + + /// True if the shadows FBO, textures have been created + bool mIsShadowMappingInitialized; + + /// FBO for the shadow map + openglframework::FrameBufferObject mFBOShadowMap; + + /// Shadow map texture + openglframework::Texture2D mShadowMapTexture; + + static int shadowMapTextureLevel; + + /// All the visual contact points + std::vector mContactPoints; + + /// Shadow map bias matrix + openglframework::Matrix4 mShadowMapBiasMatrix; + + /// Camera at light0 position for the shadow map + openglframework::Camera mShadowMapLightCamera; + + /// Depth shader to render the shadow map + openglframework::Shader mDepthShader; + + /// Phong shader + openglframework::Shader mPhongShader; + + // TODO : Delete this + openglframework::Shader mQuadShader; + + // TODO : Delete this + openglframework::VertexArrayObject mVAOQuad; + + openglframework::VertexBufferObject mVBOQuad; + + static openglframework::Color mGreyColorDemo; + static openglframework::Color mYellowColorDemo; + static openglframework::Color mBlueColorDemo; + static openglframework::Color mOrangeColorDemo; + static openglframework::Color mPinkColorDemo; + static openglframework::Color mRedColorDemo; + static openglframework::Color mDemoColors[]; + static int mNbDemoColors; + + std::string mMeshFolderPath; + + // -------------------- Methods -------------------- // + + // Create the Shadow map FBO and texture + void createShadowMapFBOAndTexture(); + + // Used for debugging shadow maps + void createQuadVBO(); + + // TODO : Delete this + void drawTextureQuad(); + + // Update the contact points + void updateContactPoints(); + + // Render the contact points + void renderContactPoints(openglframework::Shader& shader, + const openglframework::Matrix4& worldToCameraMatrix); + + void removeAllContactPoints(); + + public: + + // -------------------- Methods -------------------- // + + /// Constructor + SceneDemo(const std::string& name, float sceneRadius, bool isShadowMappingEnabled = true); + + /// Destructor + virtual ~SceneDemo(); + + /// Update the scene + virtual void update(); + + /// Render the scene (possibly in multiple passes for shadow mapping) + virtual void render(); + + /// Render the scene in a single pass + virtual void renderSinglePass(openglframework::Shader& shader, + const openglframework::Matrix4& worldToCameraMatrix)=0; + + /// Enabled/Disable the shadow mapping + void virtual setIsShadowMappingEnabled(bool isShadowMappingEnabled); + + /// Return all the contact points of the scene + std::vector computeContactPointsOfWorld(const rp3d::DynamicsWorld* world) const; +}; + +// Enabled/Disable the shadow mapping +inline void SceneDemo::setIsShadowMappingEnabled(bool isShadowMappingEnabled) { + + Scene::setIsShadowMappingEnabled(isShadowMappingEnabled); + + if (mIsShadowMappingEnabled && !mIsShadowMappingInitialized) { + createShadowMapFBOAndTexture(); + } +} + +#endif + + diff --git a/testbed/src/TestbedApplication.cpp b/testbed/src/TestbedApplication.cpp new file mode 100644 index 00000000..fc958671 --- /dev/null +++ b/testbed/src/TestbedApplication.cpp @@ -0,0 +1,453 @@ +/******************************************************************************** +* ReactPhysics3D physics library, http://www.reactphysics3d.com * +* Copyright (c) 2010-2015 Daniel Chappuis * +********************************************************************************* +* * +* This software is provided 'as-is', without any express or implied warranty. * +* In no event will the authors be held liable for any damages arising from the * +* use of this software. * +* * +* Permission is granted to anyone to use this software for any purpose, * +* including commercial applications, and to alter it and redistribute it * +* freely, subject to the following restrictions: * +* * +* 1. The origin of this software must not be misrepresented; you must not claim * +* that you wrote the original software. If you use this software in a * +* product, an acknowledgment in the product documentation would be * +* appreciated but is not required. * +* * +* 2. Altered source versions must be plainly marked as such, and must not be * +* misrepresented as being the original software. * +* * +* 3. This notice may not be removed or altered from any source distribution. * +* * +********************************************************************************/ + +// Libraries +#include "TestbedApplication.h" +#include "openglframework.h" +#include +#include +#include +#include "cubes/CubesScene.h" +#include "joints/JointsScene.h" +#include "collisionshapes/CollisionShapesScene.h" +#include "raycast/RaycastScene.h" + +using namespace openglframework; +using namespace jointsscene; +using namespace cubesscene; +using namespace raycastscene; +using namespace collisionshapesscene; + +// Initialization of static variables +const float TestbedApplication::SCROLL_SENSITIVITY = 0.02f; + +// Create and return the singleton instance of this class +TestbedApplication& TestbedApplication::getInstance() { + static TestbedApplication instance; + return instance; +} + +// Constructor +TestbedApplication::TestbedApplication() + : mFPS(0), mNbFrames(0), mPreviousTime(0), + mUpdateTime(0), mPhysicsUpdateTime(0) { + + mCurrentScene = NULL; + mIsMultisamplingActive = true; + mWidth = 1280; + mHeight = 720; + mSinglePhysicsStepEnabled = false; + mSinglePhysicsStepDone = false; + mWindowToFramebufferRatio = Vector2(1, 1); + mIsShadowMappingEnabled = true; + mIsVSyncEnabled = true; + mIsContactPointsDisplayed = false; +} + +// Destructor +TestbedApplication::~TestbedApplication() { + + // Destroy all the scenes + destroyScenes(); + + // Destroy the window + glfwDestroyWindow(mWindow); + + // Terminate GLFW + glfwTerminate(); +} + +// Initialize the viewer +void TestbedApplication::init() { + + // Set the GLFW error callback method + glfwSetErrorCallback(error_callback); + + // Initialize the GLFW library + if (!glfwInit()) { + std::exit(EXIT_FAILURE); + } + + // OpenGL version required + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); + + // Active the multi-sampling by default + if (mIsMultisamplingActive) { + glfwWindowHint(GLFW_SAMPLES, 4); + } + + // Create the GLFW window + mWindow = glfwCreateWindow(mWidth, mHeight, + "ReactPhysics3D Testbed", NULL, NULL); + if (!mWindow) { + glfwTerminate(); + std::exit(EXIT_FAILURE); + } + glfwMakeContextCurrent(mWindow); + + // Vertical Synchronization + enableVSync(mIsVSyncEnabled); + + // Initialize the GLEW library + glewExperimental = GL_TRUE; + GLenum errorGLEW = glewInit(); + if (errorGLEW != GLEW_OK) { + + // Problem: glewInit failed, something is wrong + std::cerr << "GLEW Error : " << glewGetErrorString(errorGLEW) << std::endl; + assert(false); + std::exit(EXIT_FAILURE); + } + + if (mIsMultisamplingActive) { + glEnable(GL_MULTISAMPLE); + } + + glfwSetKeyCallback(mWindow, keyboard); + glfwSetMouseButtonCallback(mWindow, mouseButton); + glfwSetCursorPosCallback(mWindow, mouseMotion); + glfwSetScrollCallback(mWindow, scroll); + + // Define the background color (black) + glClearColor(0, 0, 0, 1.0); + + // Create all the scenes + createScenes(); + + Gui::getInstance().setWindow(mWindow); + + // Init the GUI + Gui::getInstance().init(); + + mTimer.start(); +} + +// Create all the scenes +void TestbedApplication::createScenes() { + + // Cubes scene + CubesScene* cubeScene = new CubesScene("Cubes"); + mScenes.push_back(cubeScene); + + // Joints scene + JointsScene* jointsScene = new JointsScene("Joints"); + mScenes.push_back(jointsScene); + + // Collision shapes scene + CollisionShapesScene* collisionShapesScene = new CollisionShapesScene("Collision Shapes"); + mScenes.push_back(collisionShapesScene); + + // Raycast scene + RaycastScene* raycastScene = new RaycastScene("Raycast"); + mScenes.push_back(raycastScene); + + assert(mScenes.size() > 0); + mCurrentScene = mScenes[0]; + + // Get the engine settings from the scene + mEngineSettings = mCurrentScene->getEngineSettings(); + mEngineSettings.timeStep = DEFAULT_TIMESTEP; +} + +// Remove all the scenes +void TestbedApplication::destroyScenes() { + + for (int i=0; iupdatePhysics(); +} + +// Update the physics of the current scene +void TestbedApplication::updatePhysics() { + + // Set the engine settings + mEngineSettings.elapsedTime = mTimer.getPhysicsTime(); + mCurrentScene->setEngineSettings(mEngineSettings); + + if (mTimer.isRunning()) { + + // Compute the time since the last update() call and update the timer + mTimer.update(); + + // While the time accumulator is not empty + while(mTimer.isPossibleToTakeStep(mEngineSettings.timeStep)) { + + // Take a physics simulation step + mCurrentScene->updatePhysics(); + + // Update the timer + mTimer.nextStep(mEngineSettings.timeStep); + } + } +} + +void TestbedApplication::update() { + + double currentTime = glfwGetTime(); + + // Update the physics + if (mSinglePhysicsStepEnabled && !mSinglePhysicsStepDone) { + updateSinglePhysicsStep(); + mSinglePhysicsStepDone = true; + } + else { + updatePhysics(); + } + + // Compute the physics update time + mPhysicsUpdateTime = glfwGetTime() - currentTime; + + // Compute the interpolation factor + float factor = mTimer.computeInterpolationFactor(mEngineSettings.timeStep); + assert(factor >= 0.0f && factor <= 1.0f); + + // Notify the scene about the interpolation factor + mCurrentScene->setInterpolationFactor(factor); + + // Enable/Disable shadow mapping + mCurrentScene->setIsShadowMappingEnabled(mIsShadowMappingEnabled); + + // Display/Hide contact points + mCurrentScene->setIsContactPointsDisplayed(mIsContactPointsDisplayed); + + // Update the scene + mCurrentScene->update(); +} + +// Render +void TestbedApplication::render() { + + int bufferWidth, bufferHeight; + glfwGetFramebufferSize(mWindow, &bufferWidth, &bufferHeight); + + int windowWidth, windowHeight; + glfwGetWindowSize(mWindow, &windowWidth, &windowHeight); + + // Compute the window to framebuffer ratio + mWindowToFramebufferRatio.x = float(bufferWidth) / float(windowWidth); + mWindowToFramebufferRatio.y = float(bufferHeight) / float(windowHeight); + + // Set the viewport of the scene + mCurrentScene->setViewport(mWindowToFramebufferRatio.x * LEFT_PANE_WIDTH, + 0, + bufferWidth - mWindowToFramebufferRatio.x * LEFT_PANE_WIDTH, + bufferHeight); + + // Render the scene + mCurrentScene->render(); + + // Display the GUI + Gui::getInstance().render(); + + // Check the OpenGL errors + checkOpenGLErrors(); +} + +// Set the dimension of the camera viewport +void TestbedApplication::reshape() { + + // Get the framebuffer dimension + int width, height; + glfwGetFramebufferSize(mWindow, &width, &height); + + // Resize the camera viewport + mCurrentScene->reshape(width - LEFT_PANE_WIDTH, height); + + // Update the window size of the scene + int windowWidth, windowHeight; + glfwGetWindowSize(mWindow, &windowWidth, &windowHeight); + mCurrentScene->setWindowDimension(windowWidth, windowHeight); +} + +// Start the main loop where rendering occur +void TestbedApplication::startMainLoop() { + + // Loop until the user closes the window + while (!glfwWindowShouldClose(mWindow)) { + + checkOpenGLErrors(); + + // Reshape the viewport + reshape(); + + // Call the update function + update(); + + // Render the application + render(); + + // Swap front and back buffers + glfwSwapBuffers(mWindow); + + // Process events + glfwPollEvents(); + + // Compute the current framerate + computeFPS(); + + checkOpenGLErrors(); + } +} + +// Change the current scene +void TestbedApplication::switchScene(Scene* newScene) { + + if (newScene == mCurrentScene) return; + + mCurrentScene = newScene; + + // Get the engine settings of the scene + float currentTimeStep = mEngineSettings.timeStep; + mEngineSettings = mCurrentScene->getEngineSettings(); + mEngineSettings.timeStep = currentTimeStep; + + // Reset the scene + mCurrentScene->reset(); +} + +// Check the OpenGL errors +void TestbedApplication::checkOpenGLErrors() { + GLenum glError; + + // Get the OpenGL errors + glError = glGetError(); + + // While there are errors + while (glError != GL_NO_ERROR) { + + // Get the error string + const GLubyte* stringError = gluErrorString(glError); + + // Display the error + if (stringError) + std::cerr << "OpenGL Error #" << glError << "(" << gluErrorString(glError) << ")" << std::endl; + else + std::cerr << "OpenGL Error #" << glError << " (no message available)" << std::endl; + + // Get the next error + glError = glGetError(); + } +} + + +// Compute the FPS +void TestbedApplication::computeFPS() { + + mNbFrames++; + + // Get the number of seconds since start + mCurrentTime = glfwGetTime(); + + // Calculate time passed + mUpdateTime = mCurrentTime - mPreviousTime; + double timeInterval = mUpdateTime * 1000.0; + + // Update the FPS counter each second + if(timeInterval > 0.0001) { + + // calculate the number of frames per second + mFPS = static_cast(mNbFrames) / timeInterval; + mFPS *= 1000.0; + + // Set time + mPreviousTime = mCurrentTime; + + // Reset frame count + mNbFrames = 0; + } +} + +// GLFW error callback method +void TestbedApplication::error_callback(int error, const char* description) { + fputs(description, stderr); +} + +// Display the GUI +void TestbedApplication::displayGUI() { + + // Display the FPS + displayFPS(); +} + +// Display the FPS +void TestbedApplication::displayFPS() { + + /* + std::stringstream ss; + ss << mFPS; + std::string fpsString = ss.str(); + std::string windowTitle = mWindowTitle + " | FPS : " + fpsString; + glfwSetWindowTitle(mWindow, windowTitle.c_str()); + */ +} + +// Callback method to receive keyboard events +void TestbedApplication::keyboard(GLFWwindow* window, int key, int scancode, + int action, int mods) { + getInstance().mCurrentScene->keyboardEvent(key, scancode, action, mods); +} + +// Callback method to receive scrolling events +void TestbedApplication::scroll(GLFWwindow* window, double xAxis, double yAxis) { + + // Update scroll on the GUI + Gui::getInstance().setScroll(xAxis, yAxis); + + getInstance().mCurrentScene->scrollingEvent(xAxis, yAxis, SCROLL_SENSITIVITY); +} + +// Called when a mouse button event occurs +void TestbedApplication::mouseButton(GLFWwindow* window, int button, int action, int mods) { + + // Get the mouse cursor position + double x, y; + glfwGetCursorPos(window, &x, &y); + + getInstance().mCurrentScene->mouseButtonEvent(button, action, mods, x, y); +} + +// Called when a mouse motion event occurs +void TestbedApplication::mouseMotion(GLFWwindow* window, double x, double y) { + + int leftButtonState = glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_LEFT); + int rightButtonState = glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_RIGHT); + int middleButtonState = glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_MIDDLE); + int altKeyState = glfwGetKey(window, GLFW_KEY_LEFT_ALT); + + getInstance().mCurrentScene->mouseMotionEvent(x, y, leftButtonState, rightButtonState, + middleButtonState, altKeyState); +} diff --git a/testbed/src/TestbedApplication.h b/testbed/src/TestbedApplication.h new file mode 100644 index 00000000..6abd7b8a --- /dev/null +++ b/testbed/src/TestbedApplication.h @@ -0,0 +1,264 @@ +/******************************************************************************** +* ReactPhysics3D physics library, http://www.reactphysics3d.com * +* Copyright (c) 2010-2015 Daniel Chappuis * +********************************************************************************* +* * +* This software is provided 'as-is', without any express or implied warranty. * +* In no event will the authors be held liable for any damages arising from the * +* use of this software. * +* * +* Permission is granted to anyone to use this software for any purpose, * +* including commercial applications, and to alter it and redistribute it * +* freely, subject to the following restrictions: * +* * +* 1. The origin of this software must not be misrepresented; you must not claim * +* that you wrote the original software. If you use this software in a * +* product, an acknowledgment in the product documentation would be * +* appreciated but is not required. * +* * +* 2. Altered source versions must be plainly marked as such, and must not be * +* misrepresented as being the original software. * +* * +* 3. This notice may not be removed or altered from any source distribution. * +* * +********************************************************************************/ + +#ifndef TESTBED_APPLICATION_H +#define TESTBED_APPLICATION_H + +// Libraries +#include "openglframework.h" +#include "GUI.h" +#include "Scene.h" +#include "Timer.h" +#include + +// Constants +const float DEFAULT_TIMESTEP = 1.0f / 60.0f; + +/// Class TestbedApplication +/// Singleton class representing the application. +class TestbedApplication { + + private : + + // -------------------- Constants -------------------- // + + static const float SCROLL_SENSITIVITY; + + // -------------------- Attributes -------------------- // + + /// GLFW window + GLFWwindow* mWindow; + + /// Timer + Timer mTimer; + + /// List of 3D scenes + std::vector mScenes; + + /// Current 3D scene + Scene* mCurrentScene; + + /// Physics engine settings + EngineSettings mEngineSettings; + + /// Current number of frames per seconds + double mFPS; + + /// Number of frames during the last second + int mNbFrames; + + /// Current time for fps computation (in seconds) + double mCurrentTime; + + /// Previous time for fps computation (in seconds) + double mPreviousTime; + + /// Update time (in seconds) + double mUpdateTime; + + /// Physics update time (in seconds) + double mPhysicsUpdateTime; + + /// True if multisampling is active + bool mIsMultisamplingActive; + + /// Width and height of the window + int mWidth, mHeight; + + /// True if the next simulation update is a single physics step + bool mSinglePhysicsStepEnabled; + + /// True if the single physics step has been taken already + bool mSinglePhysicsStepDone; + + openglframework::Vector2 mWindowToFramebufferRatio; + + /// True if shadow mapping is enabled + bool mIsShadowMappingEnabled; + + /// True if contact points are displayed + bool mIsContactPointsDisplayed; + + /// True if vsync is enabled + bool mIsVSyncEnabled; + + // -------------------- Methods -------------------- // + + /// Private constructor (for the singleton class) + TestbedApplication(); + + /// Private copy-constructor (for the singleton class) + TestbedApplication(TestbedApplication const&); + + /// Private assignment operator (for the singleton class) + void operator=(TestbedApplication const&); + + /// Update the physics of the current scene + void updatePhysics(); + + /// Update + void update(); + + /// Update the simulation by taking a single physics step + void updateSinglePhysicsStep(); + + /// Called when the windows is reshaped + void reshape(); + + /// Render + void render(); + + /// Check the OpenGL errors + static void checkOpenGLErrors(); + + /// Display the FPS + void displayFPS(); + + /// Compute the FPS + void computeFPS(); + + /// Display the GUI + void displayGUI(); + + /// GLFW error callback method + static void error_callback(int error, const char* description); + + /// Callback method to receive keyboard events + static void keyboard(GLFWwindow* window, int key, int scancode, int action, int mods); + + /// Callback method to receive scrolling events + static void scroll(GLFWwindow* window, double xAxis, double yAxis); + + /// Called when a mouse button event occurs + static void mouseButton(GLFWwindow* window, int button, int action, int mods); + + /// Called when a mouse motion event occurs + static void mouseMotion(GLFWwindow* window, double x, double y); + + /// Initialize all the scenes + void createScenes(); + + /// Remove all the scenes + void destroyScenes(); + + /// Return the list of the scenes + std::vector getScenes(); + + /// Start/stop the simulation + void togglePlayPauseSimulation(); + + /// Restart the simulation + void restartSimulation(); + + /// Set the variable to know if we need to take a single physics step + void toggleTakeSinglePhysicsStep(); + + /// Enable/Disable shadow mapping + void enableShadows(bool enable); + + /// Display/Hide contact points + void displayContactPoints(bool display); + + public : + + // -------------------- Methods -------------------- // + + /// Create and return the singleton instance of this class + static TestbedApplication& getInstance(); + + /// Destructor + ~TestbedApplication(); + + /// Initialize the application + void init(); + + /// Start the main loop where rendering occur + void startMainLoop(); + + /// Change the current scene + void switchScene(Scene* newScene); + + /// Enable/Disable Vertical synchronization + void enableVSync(bool enable); + + // -------------------- Friendship -------------------- // + + friend class Gui; +}; + +// Return the list of the scenes +inline std::vector TestbedApplication::getScenes() { + return mScenes; +} + +// Start the simulation +inline void TestbedApplication::togglePlayPauseSimulation() { + + if (mTimer.isRunning()) { + mTimer.stop(); + } + else { + mTimer.start(); + } +} + +// Restart the simulation +inline void TestbedApplication::restartSimulation() { + mCurrentScene->reset(); + mTimer.start(); +} + +// Take a single step of simulation +inline void TestbedApplication::toggleTakeSinglePhysicsStep() { + mSinglePhysicsStepEnabled = true; + mSinglePhysicsStepDone = false; + + if (mTimer.isRunning()) { + mSinglePhysicsStepEnabled = false; + } +} + +// Enable/Disable shadow mapping +inline void TestbedApplication::enableShadows(bool enable) { + mIsShadowMappingEnabled = enable; +} + +/// Display/Hide contact points +inline void TestbedApplication::displayContactPoints(bool display) { + mIsContactPointsDisplayed = display; +} + +// Enable/Disable Vertical synchronization +inline void TestbedApplication::enableVSync(bool enable) { + mIsVSyncEnabled = enable; + if (mIsVSyncEnabled) { + glfwSwapInterval(1); + } + else { + glfwSwapInterval(0); + } +} + +#endif diff --git a/testbed/src/Timer.cpp b/testbed/src/Timer.cpp new file mode 100644 index 00000000..a7849534 --- /dev/null +++ b/testbed/src/Timer.cpp @@ -0,0 +1,64 @@ +/******************************************************************************** +* ReactPhysics3D physics library, http://www.reactphysics3d.com * +* Copyright (c) 2010-2015 Daniel Chappuis * +********************************************************************************* +* * +* This software is provided 'as-is', without any express or implied warranty. * +* In no event will the authors be held liable for any damages arising from the * +* use of this software. * +* * +* Permission is granted to anyone to use this software for any purpose, * +* including commercial applications, and to alter it and redistribute it * +* freely, subject to the following restrictions: * +* * +* 1. The origin of this software must not be misrepresented; you must not claim * +* that you wrote the original software. If you use this software in a * +* product, an acknowledgment in the product documentation would be * +* appreciated but is not required. * +* * +* 2. Altered source versions must be plainly marked as such, and must not be * +* misrepresented as being the original software. * +* * +* 3. This notice may not be removed or altered from any source distribution. * +* * +********************************************************************************/ + +// Libraries +#include "Timer.h" + + +// Constructor +Timer::Timer() : mIsRunning(false) { + +} + +// Destructor +Timer::~Timer() { + +} + +// Return the current time of the system in seconds +long double Timer::getCurrentSystemTime() { + + #if defined(WINDOWS_OS) + LARGE_INTEGER ticksPerSecond; + LARGE_INTEGER ticks; + QueryPerformanceFrequency(&ticksPerSecond); + QueryPerformanceCounter(&ticks); + return (long double(ticks.QuadPart) / long double(ticksPerSecond.QuadPart)); + #else + // Initialize the lastUpdateTime with the current time in seconds + timeval timeValue; + gettimeofday(&timeValue, NULL); + return (timeValue.tv_sec + (timeValue.tv_usec / 1000000.0)); + #endif +} + + + + + + + + + diff --git a/testbed/src/Timer.h b/testbed/src/Timer.h new file mode 100644 index 00000000..0a75d010 --- /dev/null +++ b/testbed/src/Timer.h @@ -0,0 +1,174 @@ +/******************************************************************************** +* ReactPhysics3D physics library, http://www.reactphysics3d.com * +* Copyright (c) 2010-2015 Daniel Chappuis * +********************************************************************************* +* * +* This software is provided 'as-is', without any express or implied warranty. * +* In no event will the authors be held liable for any damages arising from the * +* use of this software. * +* * +* Permission is granted to anyone to use this software for any purpose, * +* including commercial applications, and to alter it and redistribute it * +* freely, subject to the following restrictions: * +* * +* 1. The origin of this software must not be misrepresented; you must not claim * +* that you wrote the original software. If you use this software in a * +* product, an acknowledgment in the product documentation would be * +* appreciated but is not required. * +* * +* 2. Altered source versions must be plainly marked as such, and must not be * +* misrepresented as being the original software. * +* * +* 3. This notice may not be removed or altered from any source distribution. * +* * +********************************************************************************/ + +#ifndef TIMER_H +#define TIMER_H + +// Libraries +#include +#include +#include +#include +#include "configuration.h" + +#if defined(WINDOWS_OS) // For Windows platform + #define NOMINMAX // This is used to avoid definition of max() and min() macros + #include +#else // For Mac OS or Linux platform + #include +#endif + +// Class Timer +/** + * This class will take care of the time in the physics engine. It + * uses functions that depend on the current platform to get the + * current time. + */ +class Timer { + + private : + + // -------------------- Attributes -------------------- // + + /// Last time the timer has been updated + long double mLastUpdateTime; + + /// Time difference between the two last timer update() calls + long double mDeltaTime; + + /// Used to fix the time step and avoid strange time effects + double mAccumulator; + + /// True if the timer is running + bool mIsRunning; + + // -------------------- Methods -------------------- // + + /// Private copy-constructor + Timer(const Timer& timer); + + /// Private assignment operator + Timer& operator=(const Timer& timer); + + public : + + // -------------------- Methods -------------------- // + + /// Constructor + Timer(); + + /// Destructor + virtual ~Timer(); + + /// Return the current time of the physics engine + long double getPhysicsTime() const; + + /// Start the timer + void start(); + + /// Stop the timer + void stop(); + + /// Return true if the timer is running + bool isRunning() const; + + /// True if it's possible to take a new step + bool isPossibleToTakeStep(float timeStep) const; + + /// Compute the time since the last update() call and add it to the accumulator + void update(); + + /// Take a new step => update the timer by adding the timeStep value to the current time + void nextStep(float timeStep); + + /// Compute the interpolation factor + float computeInterpolationFactor(float timeStep); + + /// Return the current time of the system in seconds + static long double getCurrentSystemTime(); +}; + +// Return the current time +inline long double Timer::getPhysicsTime() const { + return mLastUpdateTime; +} + +// Return if the timer is running +inline bool Timer::isRunning() const { + return mIsRunning; +} + +// Start the timer +inline void Timer::start() { + if (!mIsRunning) { + + // Get the current system time + mLastUpdateTime = getCurrentSystemTime(); + + mAccumulator = 0.0; + mIsRunning = true; + } +} + +// Stop the timer +inline void Timer::stop() { + mIsRunning = false; +} + +// True if it's possible to take a new step +inline bool Timer::isPossibleToTakeStep(float timeStep) const { + return (mAccumulator >= timeStep); +} + +// Take a new step => update the timer by adding the timeStep value to the current time +inline void Timer::nextStep(float timeStep) { + assert(mIsRunning); + + // Update the accumulator value + mAccumulator -= timeStep; +} + +// Compute the interpolation factor +inline float Timer::computeInterpolationFactor(float timeStep) { + return (float(mAccumulator) / timeStep); +} + +// Compute the time since the last update() call and add it to the accumulator +inline void Timer::update() { + + // Get the current system time + long double currentTime = getCurrentSystemTime(); + + // Compute the delta display time between two display frames + mDeltaTime = currentTime - mLastUpdateTime; + + // Update the current display time + mLastUpdateTime = currentTime; + + // Update the accumulator value + mAccumulator += mDeltaTime; +} + + #endif