├── .gitignore ├── README.md ├── buildall.pro ├── csb.png ├── csb.uxf ├── csb ├── csb.pro ├── include │ ├── BendingConstraint.h │ ├── ContinuousCollisionConstraint.h │ ├── DistanceConstraint.h │ ├── Edge.h │ ├── Particle.h │ ├── PinConstraint.h │ ├── PositionConstraint.h │ ├── SelfCollisionRaysConstraint.h │ ├── SelfCollisionSpheresConstraint.h │ ├── SimulatedMesh.h │ ├── Solver.h │ ├── SpatialHash.h │ ├── SphereCollisionConstraint.h │ ├── StaticCollisionConstraint.h │ └── TriMesh.h └── src │ ├── BendingConstraint.cpp │ ├── ContinuousCollisionConstraint.cpp │ ├── DistanceConstraint.cpp │ ├── PinConstraint.cpp │ ├── PositionConstraint.cpp │ ├── SelfCollisionRaysConstraint.cpp │ ├── SelfCollisionSpheresConstraint.cpp │ ├── SimulatedMesh.cpp │ ├── Solver.cpp │ ├── SpatialHash.cpp │ ├── SphereCollisionConstraint.cpp │ ├── StaticCollisionConstraint.cpp │ └── TriMesh.cpp ├── demo ├── Demo.pro ├── images │ ├── gloss.png │ ├── sky_xneg.png │ ├── sky_xpos.png │ ├── sky_yneg.png │ ├── sky_ypos.png │ ├── sky_zneg.png │ └── sky_zpos.png ├── include │ ├── Camera.h │ ├── CameraStates.h │ ├── DemoScene.h │ ├── MainWindow.h │ ├── Material.h │ ├── MaterialCSBpbr.h │ ├── MaterialEnvMap.h │ ├── MaterialFractal.h │ ├── MaterialPBR.h │ ├── MaterialPhong.h │ ├── MaterialWireframe.h │ ├── MeshVBO.h │ ├── Scene.h │ ├── ShaderLib.h │ └── TrackballCamera.h ├── models │ ├── Asteroid.obj │ ├── Face.obj │ ├── Face2.obj │ ├── Sphere.obj │ ├── Suzanne.obj │ ├── bigPlane.obj │ ├── bigSphere.obj │ ├── cube.obj │ ├── hdCube.obj │ ├── hdPlane.obj │ ├── hdxPlane.obj │ ├── mandarin.obj │ ├── rayTest.obj │ ├── smallSphere.obj │ ├── test2.obj │ ├── uhdPlane.obj │ └── xyplane.obj ├── shaderPrograms │ ├── csbPBR.json │ ├── fractal.json │ └── wireframe.json ├── shaders │ ├── FractalFragment.glsl │ ├── FractalVertex.glsl │ ├── WireframeFragment.glsl │ ├── WireframeGeometry.glsl │ ├── WireframeVertex.glsl │ ├── csb_hard_normals.glsl │ ├── csb_pbr_frag.glsl │ └── csb_pbr_vertex.glsl ├── src │ ├── Camera.cpp │ ├── CameraStates.cpp │ ├── DemoScene.cpp │ ├── MainWindow.cpp │ ├── Material.cpp │ ├── MaterialCSBpbr.cpp │ ├── MaterialEnvMap.cpp │ ├── MaterialFractal.cpp │ ├── MaterialPBR.cpp │ ├── MaterialPhong.cpp │ ├── MaterialWireframe.cpp │ ├── MeshVBO.cpp │ ├── Scene.cpp │ ├── ShaderLib.cpp │ ├── TrackballCamera.cpp │ └── main.cpp └── ui │ ├── mainwindow.ui │ └── ui_mainwindow.h ├── feedback ├── README.md └── UML.png ├── jack_diver_initial_design.pdf ├── notes └── tests ├── src ├── BendingConstraintTests.cpp ├── DistanceConstraintTests.cpp ├── EdgeTests.cpp ├── ParticleTests.cpp ├── PinConstraintTests.cpp ├── SelfCollisionRaysTests.cpp ├── SelfCollisionSpheresTests.cpp ├── SimulatedMeshTests.cpp ├── SolverTests.cpp ├── SpatialHashTests.cpp ├── SphereCollisionConstraintTests.cpp ├── TriMeshTests.cpp ├── UtilMacros.h └── main.cpp └── tests.pro /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.user 3 | moc/* 4 | obj/* 5 | ui/*.h 6 | *Makefile* 7 | .qmake.stash 8 | Project -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Cloth & Soft Bodies 2 | 3 | A cloth and soft bodies simulation library, using Position Based Dynamics. 4 | Uses triangle mesh .obj files, and offers a constraint based system for simulating cloth like behaviour. 5 | 6 | # Installation 7 | ## Dependencies 8 | 9 | - C++14 10 | - OpenGL 4.1 11 | - GLM (9.8.5 was used) 12 | - Assimp 13 | - Qt 5.9 (used for OpenGL functions) 14 | - gtest 15 | 16 | ## Building and running 17 | 18 | The top level directory contains three sub directories, csb, demo and tests. 19 | In the csb folder you will find the library code. Each project can be build individually, however tests and demo are dependent on the library. 20 | The top level directory also contains a buildall.pro which will build all three, 21 | sub directories in the correct order. 22 | 23 | The simplest way to view demos is to: 24 | 1. Go to the top level directory. 25 | 2. qmake 26 | 3. make -j 27 | 4. Change into the demos/ directory 28 | 5. ./Demo 29 | 30 | ## Tests 31 | 32 | The tests have been written with gtest and can be found in the tests/ sub directory. 33 | If you performed the steps above, you can run ./CSBTests, otherwise you will need to first, build the library then build the tests. 34 | 35 | # User Instructions 36 | 37 | ## Demos 38 | Within the demo's project you can see multiple demo's set up in functions of the DemoScene class. You are required to manually change the currently set demo in the code 39 | by changing the call to initDemoX(), in the init function, where X is the demo you want to run. 40 | 41 | ### Controls 42 | 43 | The demo makes use of Qt's gui system and the widgets should be clear. 44 | The demo does have mouse controls: 45 | 46 | - Right click and drag - Zoom camera 47 | - Left click and drag - Orbit camera 48 | 49 | # Creating a simulation 50 | 51 | To set up a simulation using the library we first require a Solver. 52 | 53 | ```c++ 54 | #include "Solver.h" 55 | 56 | //... 57 | 58 | csb::Solver solver; 59 | ``` 60 | 61 | Next you will want to load up the meshes that you want to simulate as cloth. The solver uses shared pointers to meshes to ensure that the lifetimes do not fall short. 62 | The init call is separate from the load function as you are able to load meshes from multiple files into the same mesh object, so multiple load calls are permitted, however the mesh should be reset before call init more than once. 63 | 64 | ```c++ 65 | #include "SimulatedMesh.h" 66 | 67 | //... 68 | 69 | std::shared_ptr mesh = std::make_shared(); 70 | mesh->load("path/to/mesh.obj"); 71 | mesh->init(); 72 | ``` 73 | 74 | You can then add constraints to the mesh, the library includes some presets such as ```generateStructuralConstraints()```, ```generateBendingConstraints(float)``` and ```generateClothConstraints(float)``` which is a wrapper for the first two. These can be used to automatically generate constraints across all edges of the mesh and neighbouring vertices to easily create a cloth mesh. You can also set the particle masses. It is important to set particle masses before creating constraints. 75 | 76 | 77 | ```c++ 78 | //The mesh will hang from it's first vertex 79 | mesh->setParticleInverseMass(0, 0.f); 80 | // Generate cloth constraints, where the bending stiffness is 0.05f 81 | mesh->generateClothConstraints(0.05f); 82 | ``` 83 | 84 | Then you should add the mesh to the simulation by giving it to the solver. 85 | ```c++ 86 | solver.addSimulatedMesh(mesh); 87 | ``` 88 | 89 | The solver also has constraints which can be set, these constraints will be global to the simulation and affect all meshes. 90 | 91 | ```c++ 92 | // Adds a self collision constraint that uses ray triangle intersection checks 93 | solver.addContinuousCollision(new csb::SelfCollisionRaysConstraint); 94 | // Adds a static sphere obstacle to the simulation 95 | solver.addStaticCollision(new csb::SphereCollisionConstraint({0.f,-1.f,0.f}, 0.5f)); 96 | ``` 97 | 98 | You can also set some simulation parameters. 99 | 100 | ```c++ 101 | // Adds a gravity to the simulation 102 | solver.addForce({0.f, -9.81f, 0.f}); 103 | // Drag can be simulated with slight damping 104 | solver.setDamping(0.1f); 105 | // The mesh constraints will be solved 60 times per step 106 | solver.setPositionConstraintIterations(60); 107 | ``` 108 | 109 | Finally you are ready to simulate. The solver has an in-built timer that manages a fixed time-step. This timer is used internally when you call the ```update()``` function. If you prefer to write your own timer you can call the ```step(float dt)``` function directly. 110 | 111 | ```c++ 112 | solver.update(); 113 | // or with your own timer 114 | solver.step(TIME_STEP); 115 | ``` 116 | 117 | The last thing you will want to do is draw your simulated meshes. The meshes are stored using indexed vertices, and so you are required to use glDrawElements rather than arrays. The meshes also store indices independently of each-other and so you must use the BaseVertex gl draw calls. If you have an array of meshes an example would be: 118 | 119 | ```c++ 120 | glMultiDrawElementsBaseVertex( 121 | GL_TRIANGLES, 122 | // This would store mesh[i-1]->getNIndices() for every ith mesh 123 | m_numIndicesPerMesh.data(), 124 | GL_UNSIGNED_SHORT, 125 | // This would store the addresses into your vbo that every mesh's indices begin 126 | m_meshIndexOffsets.data(), 127 | // Number of meshes to draw 128 | static_cast(meshes.size()), 129 | // This would store mesh[i-1]->getNVerts() for every ith mesh 130 | m_meshBaseVert.data() 131 | ); 132 | ``` 133 | 134 | # Limitations 135 | 136 | The systems self collision detection is not perfect and can create tangled cloth in some cases, so if self collision will not be extreme, you may get better results when not using a SelfCollisionRaysConstraint. -------------------------------------------------------------------------------- /buildall.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = subdirs 2 | SUBDIRS = csb demo tests 3 | 4 | demo.depend = csb 5 | tests.depend = csb -------------------------------------------------------------------------------- /csb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitronoid/csb/c3d67051410c67e784003db0df4f7c81a702d248/csb.png -------------------------------------------------------------------------------- /csb.uxf: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 4 | 5 | UMLClass 6 | 7 | 492 8 | 582 9 | 180 10 | 114 11 | 12 | PinConstraint 13 | -- 14 | /+ project(const bool _equality, / 15 | / const float _stiffness, / 16 | / const float _invMass / 17 | / ) : virtual void/ 18 | -- 19 | - m_pos : vec3& 20 | - m_pinPos : vec3 21 | 22 | 23 | 24 | UMLClass 25 | 26 | 864 27 | 582 28 | 180 29 | 114 30 | 31 | DistanceConstraint 32 | -- 33 | /+ project(const bool _equality, / 34 | / const float _stiffness, / 35 | / const float _invMass / 36 | / ) : virtual void/ 37 | -- 38 | - m_posA : vec3& 39 | - m_posB : vec3& 40 | - m_restLength : float 41 | 42 | 43 | 44 | UMLClass 45 | 46 | 684 47 | 582 48 | 168 49 | 114 50 | 51 | BendingConstraint 52 | -- 53 | /+ project(const bool _equality, / 54 | / const float _stiffness, / 55 | / const float _invMass / 56 | / ) : virtual void/ 57 | -- 58 | - m_posA : vec3& 59 | - m_posB : vec3& 60 | - m_posC : vec3& 61 | - m_posD : vec3& 62 | - m_minAngle : float 63 | 64 | 65 | 66 | UMLClass 67 | 68 | 684 69 | 456 70 | 168 71 | 78 72 | 73 | *<<Interface>>* 74 | /Constraint/ 75 | -- 76 | /+ project(const bool _equality, / 77 | / const float _stiffness, / 78 | / const float _invMass / 79 | / ) : virtual void/ 80 | 81 | 82 | 83 | 84 | UMLClass 85 | 86 | 912 87 | 456 88 | 132 89 | 78 90 | 91 | Point 92 | -- 93 | + Point(const vec3 &_pos, 94 | const vec3 &_vel 95 | ) 96 | -- 97 | + m_pos : vec3 98 | + m_vel : vec3 99 | 100 | 101 | 102 | UMLClass 103 | 104 | 660 105 | 234 106 | 216 107 | 180 108 | 109 | CSBScene 110 | -- 111 | + CSBScene(const float _mass, 112 | const float _stiffness 113 | ) 114 | + init() : void 115 | + loadMesh(const string &_path) : void 116 | + loadMesh(const vector<vec3> &_verts) : void 117 | + update() : void 118 | + draw() : void 119 | - initGL() : void 120 | - initBodies() : void 121 | -- 122 | - m_time : float 123 | - m_invMass : float 124 | - m_stiffness : float 125 | - m_points : vector<Point> 126 | - m_normals : vector<vec3> 127 | - m_constraints : vector<unique_ptr<Constraint>> 128 | 129 | 130 | 131 | 132 | 133 | 134 | Relation 135 | 136 | 762 137 | 528 138 | 18 139 | 66 140 | 141 | lt=<<- 142 | 10.0;10.0;10.0;90.0 143 | 144 | 145 | Relation 146 | 147 | 762 148 | 552 149 | 210 150 | 42 151 | 152 | lt=- 153 | 10.0;10.0;330.0;10.0;330.0;50.0 154 | 155 | 156 | Relation 157 | 158 | 576 159 | 552 160 | 204 161 | 42 162 | 163 | lt=- 164 | 320.0;10.0;10.0;10.0;10.0;50.0 165 | 166 | 167 | Relation 168 | 169 | 762 170 | 408 171 | 18 172 | 60 173 | 174 | lt=<<<<<- 175 | 10.0;10.0;10.0;80.0 176 | 177 | 178 | Relation 179 | 180 | 870 181 | 354 182 | 120 183 | 114 184 | 185 | lt=<<<<<- 186 | 10.0;10.0;180.0;10.0;180.0;170.0 187 | 188 | 189 | -------------------------------------------------------------------------------- /csb/csb.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = lib 2 | TARGET = csb 3 | 4 | DEFINES += CSB 5 | 6 | OBJECTS_DIR = obj 7 | UI_DIR = ui 8 | 9 | QT += opengl core gui 10 | CONFIG += console c++14 11 | CONFIG -= app_bundle 12 | DESTDIR = $$PWD/lib 13 | 14 | SOURCES += \ 15 | src/TriMesh.cpp \ 16 | src/SimulatedMesh.cpp \ 17 | src/Solver.cpp \ 18 | src/DistanceConstraint.cpp \ 19 | src/BendingConstraint.cpp \ 20 | src/PinConstraint.cpp \ 21 | src/PositionConstraint.cpp \ 22 | src/StaticCollisionConstraint.cpp \ 23 | src/SphereCollisionConstraint.cpp \ 24 | src/SpatialHash.cpp \ 25 | src/ContinuousCollisionConstraint.cpp \ 26 | src/SelfCollisionSpheresConstraint.cpp \ 27 | src/SelfCollisionRaysConstraint.cpp 28 | 29 | HEADERS += \ 30 | include/TriMesh.h \ 31 | include/SimulatedMesh.h \ 32 | include/Particle.h \ 33 | include/Solver.h \ 34 | include/Edge.h \ 35 | include/DistanceConstraint.h \ 36 | include/BendingConstraint.h \ 37 | include/PinConstraint.h \ 38 | include/PositionConstraint.h \ 39 | include/StaticCollisionConstraint.h \ 40 | include/SphereCollisionConstraint.h \ 41 | include/SpatialHash.h \ 42 | include/ContinuousCollisionConstraint.h \ 43 | include/SelfCollisionSpheresConstraint.h \ 44 | include/SelfCollisionRaysConstraint.h 45 | 46 | INCLUDEPATH += \ 47 | /usr/local/include/glm/glm \ 48 | /usr/local/include/glm \ 49 | /usr/local/include \ 50 | $$PWD/include 51 | 52 | 53 | 54 | QMAKE_CXXFLAGS += -O3 -std=c++14 -msse -msse2 -msse3 55 | 56 | linux:{ 57 | LIBS += -lGL -lGLU -lGLEW -lassimp 58 | } 59 | 60 | mac:{ 61 | LIBS+= -L/usr/local/lib -lassimp 62 | QMAKE_CXXFLAGS += -arch x86_64 63 | } 64 | -------------------------------------------------------------------------------- /csb/include/ContinuousCollisionConstraint.h: -------------------------------------------------------------------------------- 1 | #ifndef CONTINUOUSCOLLISIONCONSTRAINT_H 2 | #define CONTINUOUSCOLLISIONCONSTRAINT_H 3 | 4 | #include 5 | #include 6 | #include "SimulatedMesh.h" 7 | #include "SpatialHash.h" 8 | 9 | namespace csb 10 | { 11 | 12 | class ContinuousCollisionConstraint 13 | { 14 | public: 15 | //----------------------------------------------------------------------------------------------------- 16 | /// @brief Default constructor. 17 | //----------------------------------------------------------------------------------------------------- 18 | ContinuousCollisionConstraint() = default; 19 | //----------------------------------------------------------------------------------------------------- 20 | /// @brief Default copy constructor. 21 | //----------------------------------------------------------------------------------------------------- 22 | ContinuousCollisionConstraint(const ContinuousCollisionConstraint&) = default; 23 | //----------------------------------------------------------------------------------------------------- 24 | /// @brief Default copy assignment operator. 25 | //----------------------------------------------------------------------------------------------------- 26 | ContinuousCollisionConstraint& operator=(const ContinuousCollisionConstraint&) = default; 27 | //----------------------------------------------------------------------------------------------------- 28 | /// @brief Default move constructor. 29 | //----------------------------------------------------------------------------------------------------- 30 | ContinuousCollisionConstraint(ContinuousCollisionConstraint&&) = default; 31 | //----------------------------------------------------------------------------------------------------- 32 | /// @brief Default move assignment operator. 33 | //----------------------------------------------------------------------------------------------------- 34 | ContinuousCollisionConstraint& operator=(ContinuousCollisionConstraint&&) = default; 35 | //----------------------------------------------------------------------------------------------------- 36 | /// @brief Virtual default destructor. 37 | //----------------------------------------------------------------------------------------------------- 38 | virtual ~ContinuousCollisionConstraint(); 39 | //----------------------------------------------------------------------------------------------------- 40 | /// @brief This projects the constraint onto the particles. 41 | /// @param io_particles is a reference to the particles that this constraint is to be projected on to. 42 | /// @param _spatialHash is a reference to the solver's hash table which can be used to make the collision, 43 | /// test more efficient. 44 | //----------------------------------------------------------------------------------------------------- 45 | virtual void project( 46 | const size_t &_meshIndex, 47 | std::vector> &_meshes, 48 | const SpatialHash::SpatialHashTable &_spatialHash 49 | ) = 0; 50 | //----------------------------------------------------------------------------------------------------- 51 | /// @brief This is used by classes that contain constraints to implement copy constructors. 52 | /// @return A raw pointer to a newly allocated clone of the derived class. 53 | //----------------------------------------------------------------------------------------------------- 54 | virtual ContinuousCollisionConstraint* clone() const = 0; 55 | 56 | protected: 57 | //----------------------------------------------------------------------------------------------------- 58 | /// @brief This gives derived classes access to a meshes particles. 59 | //----------------------------------------------------------------------------------------------------- 60 | std::vector& getParticles(SimulatedMesh&_mesh) const noexcept; 61 | }; 62 | 63 | } 64 | 65 | 66 | #endif // CONTINUOUSCOLLISIONCONSTRAINT_H 67 | -------------------------------------------------------------------------------- /csb/include/DistanceConstraint.h: -------------------------------------------------------------------------------- 1 | #ifndef DISTANCECONSTRAINT_H 2 | #define DISTANCECONSTRAINT_H 3 | 4 | #include "PositionConstraint.h" 5 | 6 | namespace csb 7 | { 8 | 9 | class DistanceConstraint : public PositionConstraint 10 | { 11 | public: 12 | //----------------------------------------------------------------------------------------------------- 13 | /// @brief Default constructor. 14 | //----------------------------------------------------------------------------------------------------- 15 | DistanceConstraint() = default; 16 | //----------------------------------------------------------------------------------------------------- 17 | /// @brief Constructor. 18 | /// @param _p1 is the first particle index that this constraint will be projected onto. 19 | /// @param _p2 is the second particle index that this constraint will be projected onto. 20 | /// @param _rest is distance from _p1 to _p2 that satisfies the constraint. 21 | //----------------------------------------------------------------------------------------------------- 22 | DistanceConstraint(const size_t _p1, const size_t _p2, float _rest) : 23 | m_rest(_rest), 24 | m_p1(_p1), 25 | m_p2(_p2) 26 | {} 27 | //----------------------------------------------------------------------------------------------------- 28 | /// @brief Default copy constructor. 29 | //----------------------------------------------------------------------------------------------------- 30 | DistanceConstraint(const DistanceConstraint&) = default; 31 | //----------------------------------------------------------------------------------------------------- 32 | /// @brief Default copy assignment operator. 33 | //----------------------------------------------------------------------------------------------------- 34 | DistanceConstraint& operator=(const DistanceConstraint&) = default; 35 | //----------------------------------------------------------------------------------------------------- 36 | /// @brief Default move constructor. 37 | //----------------------------------------------------------------------------------------------------- 38 | DistanceConstraint(DistanceConstraint&&) = default; 39 | //----------------------------------------------------------------------------------------------------- 40 | /// @brief Default move assignment operator. 41 | //----------------------------------------------------------------------------------------------------- 42 | DistanceConstraint& operator=(DistanceConstraint&&) = default; 43 | //----------------------------------------------------------------------------------------------------- 44 | /// @brief Virtual default destructor. 45 | //----------------------------------------------------------------------------------------------------- 46 | virtual ~DistanceConstraint() override = default; 47 | //----------------------------------------------------------------------------------------------------- 48 | /// @brief This projects the constraint onto the particles. Attempts to make the distance between the, 49 | /// points equal to the rest distance. 50 | /// @param io_particles is a reference to the particles that this constraint is to be projected on to. 51 | //----------------------------------------------------------------------------------------------------- 52 | virtual void project(std::vector &io_particles) override; 53 | //----------------------------------------------------------------------------------------------------- 54 | /// @brief This is used by classes that contain constraints to implement copy constructors. 55 | /// @return A raw pointer to a newly allocated clone of the derived class. 56 | //----------------------------------------------------------------------------------------------------- 57 | virtual PositionConstraint* clone() const override; 58 | //----------------------------------------------------------------------------------------------------- 59 | /// @brief Gets the rest distance between the two particles. 60 | /// @return The rest distance between the two particles. 61 | //----------------------------------------------------------------------------------------------------- 62 | float getRest() const noexcept; 63 | //----------------------------------------------------------------------------------------------------- 64 | /// @brief Gets the particle index of the first particle. 65 | /// @return The particle index m_p1. 66 | //----------------------------------------------------------------------------------------------------- 67 | size_t getParticleIndex1() const noexcept; 68 | //----------------------------------------------------------------------------------------------------- 69 | /// @brief Gets the particle index of the second particle. 70 | /// @return The particle index m_p2. 71 | //----------------------------------------------------------------------------------------------------- 72 | size_t getParticleIndex2() const noexcept; 73 | //----------------------------------------------------------------------------------------------------- 74 | /// @brief Sets the rest distance between the two particles. 75 | /// @param _rest is the new rest distance between the two particles. 76 | //----------------------------------------------------------------------------------------------------- 77 | void setRest(const float _rest); 78 | //----------------------------------------------------------------------------------------------------- 79 | /// @brief Sets the particle index of the first particle. 80 | /// @param _p1 is the new particle index for m_p1. 81 | //----------------------------------------------------------------------------------------------------- 82 | void setParticleIndex1(const size_t &_p1); 83 | //----------------------------------------------------------------------------------------------------- 84 | /// @brief Sets the particle index of the second particle. 85 | /// @param _p2 is the new particle index for m_p2. 86 | //----------------------------------------------------------------------------------------------------- 87 | void setParticleIndex2(const size_t &_p2); 88 | 89 | private: 90 | //----------------------------------------------------------------------------------------------------- 91 | /// @brief The rest distance between m_p1 and m_p2. 92 | //----------------------------------------------------------------------------------------------------- 93 | float m_rest = 0.f; 94 | //----------------------------------------------------------------------------------------------------- 95 | /// @brief The index of the first particle. 96 | //----------------------------------------------------------------------------------------------------- 97 | size_t m_p1 = 0; 98 | //----------------------------------------------------------------------------------------------------- 99 | /// @brief The index of the second particle. 100 | //----------------------------------------------------------------------------------------------------- 101 | size_t m_p2 = 0; 102 | }; 103 | 104 | } 105 | 106 | #endif // DISTANCECONSTRAINT_H 107 | -------------------------------------------------------------------------------- /csb/include/Edge.h: -------------------------------------------------------------------------------- 1 | #ifndef EDGE_H 2 | #define EDGE_H 3 | 4 | #include 5 | 6 | namespace csb 7 | { 8 | 9 | struct Edge 10 | { 11 | //----------------------------------------------------------------------------------------------------- 12 | /// @brief Default constructor. 13 | //----------------------------------------------------------------------------------------------------- 14 | Edge() = default; 15 | //----------------------------------------------------------------------------------------------------- 16 | /// @brief Default copy constructor. 17 | //----------------------------------------------------------------------------------------------------- 18 | Edge(const Edge&) = default; 19 | //----------------------------------------------------------------------------------------------------- 20 | /// @brief Default copy assignment operator. 21 | //----------------------------------------------------------------------------------------------------- 22 | Edge& operator=(const Edge&) = default; 23 | //----------------------------------------------------------------------------------------------------- 24 | /// @brief Default move constructor. 25 | //----------------------------------------------------------------------------------------------------- 26 | Edge(Edge&&) = default; 27 | //----------------------------------------------------------------------------------------------------- 28 | /// @brief Default move assignment operator. 29 | //----------------------------------------------------------------------------------------------------- 30 | Edge& operator=(Edge&&) = default; 31 | //----------------------------------------------------------------------------------------------------- 32 | /// @brief Default destructor. 33 | //----------------------------------------------------------------------------------------------------- 34 | ~Edge() = default; 35 | //----------------------------------------------------------------------------------------------------- 36 | /// @brief Constructor that takes two vertex indices. 37 | /// @param _a is a vertex index representing one of the vertices that contributes to this edge. 38 | /// @param _b is a vertex index representing one of the vertices that contributes to this edge. 39 | //----------------------------------------------------------------------------------------------------- 40 | Edge(const GLushort _a, const GLushort _b) : 41 | p(std::min(_a, _b), std::max(_a, _b)) 42 | {} 43 | //----------------------------------------------------------------------------------------------------- 44 | /// @brief Used for hashing. 45 | //----------------------------------------------------------------------------------------------------- 46 | friend bool operator==(const Edge &_a, const Edge &_b) 47 | { 48 | return _a.p == _b.p; 49 | } 50 | //----------------------------------------------------------------------------------------------------- 51 | /// @brief Internally stores a pair, which is sorted on construction. 52 | //----------------------------------------------------------------------------------------------------- 53 | std::pair p; 54 | }; 55 | 56 | } 57 | 58 | //----------------------------------------------------------------------------------------------------- 59 | /// @brief Define the hash struct for this Edge so we can create unordered_set's using it. 60 | //----------------------------------------------------------------------------------------------------- 61 | namespace std 62 | { 63 | template <> 64 | struct hash 65 | { 66 | size_t operator()(const csb::Edge &_key) const 67 | { 68 | return std::hash()(std::hash()(_key.p.first)) ^ std::hash()(_key.p.second); 69 | } 70 | }; 71 | } 72 | 73 | #endif // EDGE_H 74 | -------------------------------------------------------------------------------- /csb/include/Particle.h: -------------------------------------------------------------------------------- 1 | #ifndef CSBPOINT_H 2 | #define CSBPOINT_H 3 | 4 | #include "vec3.hpp" 5 | 6 | namespace csb 7 | { 8 | 9 | struct Particle 10 | { 11 | //----------------------------------------------------------------------------------------------------- 12 | /// @brief Default constructor. 13 | //----------------------------------------------------------------------------------------------------- 14 | Particle() = default; 15 | //----------------------------------------------------------------------------------------------------- 16 | /// @brief Default copy constructor. 17 | //----------------------------------------------------------------------------------------------------- 18 | Particle(const Particle&) = default; 19 | //----------------------------------------------------------------------------------------------------- 20 | /// @brief Default copy assignment operator. 21 | //----------------------------------------------------------------------------------------------------- 22 | Particle& operator=(const Particle&) = default; 23 | //----------------------------------------------------------------------------------------------------- 24 | /// @brief Default move constructor. 25 | //----------------------------------------------------------------------------------------------------- 26 | Particle(Particle&&) = default; 27 | //----------------------------------------------------------------------------------------------------- 28 | /// @brief Default move assignment operator. 29 | //----------------------------------------------------------------------------------------------------- 30 | Particle& operator=(Particle&&) = default; 31 | //----------------------------------------------------------------------------------------------------- 32 | /// @brief Default destructor. 33 | //----------------------------------------------------------------------------------------------------- 34 | ~Particle() = default; 35 | //----------------------------------------------------------------------------------------------------- 36 | /// @brief Constructor. 37 | /// @param _pos is a reference to the position of a vertex, that this particle will manipulate. 38 | /// @param _invMass is the inverse mass of the particle which is used by constraints. 39 | //----------------------------------------------------------------------------------------------------- 40 | Particle(glm::vec3 &_pos, const float &_invMass) : 41 | m_pos(&_pos), 42 | m_prevPos(_pos), 43 | m_invMass(_invMass) 44 | {} 45 | 46 | //----------------------------------------------------------------------------------------------------- 47 | /// @brief A pointer to a vertex position, used by the particle to move that vertex. 48 | //----------------------------------------------------------------------------------------------------- 49 | glm::vec3* m_pos = nullptr; 50 | //----------------------------------------------------------------------------------------------------- 51 | /// @brief The previous position of the vertex, used for calculation of the next position. 52 | //----------------------------------------------------------------------------------------------------- 53 | glm::vec3 m_prevPos{0.f}; 54 | //----------------------------------------------------------------------------------------------------- 55 | /// @brief The inverse mass of the particle which is used by constraints. 56 | //----------------------------------------------------------------------------------------------------- 57 | float m_invMass{0.f}; 58 | }; 59 | 60 | } 61 | 62 | #endif // CSBPOINT_H 63 | -------------------------------------------------------------------------------- /csb/include/PinConstraint.h: -------------------------------------------------------------------------------- 1 | #ifndef PINCONSTRAINT_H 2 | #define PINCONSTRAINT_H 3 | 4 | #include "PositionConstraint.h" 5 | 6 | namespace csb 7 | { 8 | 9 | class PinConstraint : public PositionConstraint 10 | { 11 | public: 12 | //----------------------------------------------------------------------------------------------------- 13 | /// @brief Default constructor. 14 | //----------------------------------------------------------------------------------------------------- 15 | PinConstraint() = default; 16 | //----------------------------------------------------------------------------------------------------- 17 | /// @brief Constructor. 18 | /// @param _p is the particle index that will be pinned. 19 | /// @param _pin is the position to pin the particle to. 20 | //----------------------------------------------------------------------------------------------------- 21 | PinConstraint(const size_t &_p, const glm::vec3 &_pin) : 22 | m_pin(_pin), 23 | m_p(_p) 24 | {} 25 | //----------------------------------------------------------------------------------------------------- 26 | /// @brief Default copy constructor. 27 | //----------------------------------------------------------------------------------------------------- 28 | PinConstraint(const PinConstraint&) = default; 29 | //----------------------------------------------------------------------------------------------------- 30 | /// @brief Default copy assignment operator. 31 | //----------------------------------------------------------------------------------------------------- 32 | PinConstraint& operator=(const PinConstraint&) = default; 33 | //----------------------------------------------------------------------------------------------------- 34 | /// @brief Default move constructor. 35 | //----------------------------------------------------------------------------------------------------- 36 | PinConstraint(PinConstraint&&) = default; 37 | //----------------------------------------------------------------------------------------------------- 38 | /// @brief Default move assignment operator. 39 | //----------------------------------------------------------------------------------------------------- 40 | PinConstraint& operator=(PinConstraint&&) = default; 41 | //----------------------------------------------------------------------------------------------------- 42 | /// @brief Virtual default destructor. 43 | //----------------------------------------------------------------------------------------------------- 44 | virtual ~PinConstraint() override = default; 45 | //----------------------------------------------------------------------------------------------------- 46 | /// @brief This projects the constraint onto the particles. Sets the particle's position to m_pin. 47 | /// @param io_particles is a reference to the particles that this constraint is to be projected on to. 48 | //----------------------------------------------------------------------------------------------------- 49 | virtual void project(std::vector &io_particles) override; 50 | //----------------------------------------------------------------------------------------------------- 51 | /// @brief This is used by classes that contain constraints to implement copy constructors. 52 | /// @return A raw pointer to a newly allocated clone of the derived class. 53 | //----------------------------------------------------------------------------------------------------- 54 | virtual PositionConstraint* clone() const override; 55 | //----------------------------------------------------------------------------------------------------- 56 | /// @brief Gets the index of the particle that is pinned. 57 | /// @return The index of the pinned particle. 58 | //----------------------------------------------------------------------------------------------------- 59 | size_t getParticleIndex() const noexcept; 60 | //----------------------------------------------------------------------------------------------------- 61 | /// @brief Gets the position that the particle is pinned to. 62 | /// @return The pin position. 63 | //----------------------------------------------------------------------------------------------------- 64 | glm::vec3 getPinPosition() const noexcept; 65 | //----------------------------------------------------------------------------------------------------- 66 | /// @brief Sets the index so that a new particle is pinned. 67 | /// @param _p is the index of the new particle to pin. 68 | //----------------------------------------------------------------------------------------------------- 69 | void setParticleIndex(const size_t &_p); 70 | //----------------------------------------------------------------------------------------------------- 71 | /// @brief Sets the position that the particle should be pinned to. 72 | /// @param The new pin position. 73 | //----------------------------------------------------------------------------------------------------- 74 | void setPinPosition(const glm::vec3 &_pin); 75 | 76 | private: 77 | //----------------------------------------------------------------------------------------------------- 78 | /// @brief The position that the particle is pinned to. 79 | //----------------------------------------------------------------------------------------------------- 80 | glm::vec3 m_pin; 81 | //----------------------------------------------------------------------------------------------------- 82 | /// @brief The index of the pinned particle. 83 | //----------------------------------------------------------------------------------------------------- 84 | size_t m_p; 85 | }; 86 | 87 | } 88 | 89 | #endif // PINCONSTRAINT_H 90 | -------------------------------------------------------------------------------- /csb/include/PositionConstraint.h: -------------------------------------------------------------------------------- 1 | #ifndef CSBCONSTRAINT_H 2 | #define CSBCONSTRAINT_H 3 | 4 | #include 5 | #include "Particle.h" 6 | 7 | namespace csb 8 | { 9 | 10 | class PositionConstraint 11 | { 12 | public: 13 | //----------------------------------------------------------------------------------------------------- 14 | /// @brief Default constructor. 15 | //----------------------------------------------------------------------------------------------------- 16 | PositionConstraint() = default; 17 | //----------------------------------------------------------------------------------------------------- 18 | /// @brief Default copy constructor. 19 | //----------------------------------------------------------------------------------------------------- 20 | PositionConstraint(const PositionConstraint&) = default; 21 | //----------------------------------------------------------------------------------------------------- 22 | /// @brief Default copy assignment operator. 23 | //----------------------------------------------------------------------------------------------------- 24 | PositionConstraint& operator=(const PositionConstraint&) = default; 25 | //----------------------------------------------------------------------------------------------------- 26 | /// @brief Default move constructor. 27 | //----------------------------------------------------------------------------------------------------- 28 | PositionConstraint(PositionConstraint&&) = default; 29 | //----------------------------------------------------------------------------------------------------- 30 | /// @brief Default move assignment operator. 31 | //----------------------------------------------------------------------------------------------------- 32 | PositionConstraint& operator=(PositionConstraint&&) = default; 33 | //----------------------------------------------------------------------------------------------------- 34 | /// @brief Virtual default destructor. 35 | //----------------------------------------------------------------------------------------------------- 36 | virtual ~PositionConstraint(); 37 | //----------------------------------------------------------------------------------------------------- 38 | /// @brief This projects the constraint onto the particles. 39 | /// @param io_particles is a reference to the particles that this constraint is to be projected on to. 40 | //----------------------------------------------------------------------------------------------------- 41 | virtual void project(std::vector &io_particles) = 0; 42 | //----------------------------------------------------------------------------------------------------- 43 | /// @brief This is used by classes that contain constraints to implement copy constructors. 44 | /// @return A raw pointer to a newly allocated clone of the derived class. 45 | //----------------------------------------------------------------------------------------------------- 46 | virtual PositionConstraint* clone() const = 0; 47 | }; 48 | 49 | } 50 | 51 | #endif // CSBCONSTRAINT_H 52 | -------------------------------------------------------------------------------- /csb/include/SelfCollisionRaysConstraint.h: -------------------------------------------------------------------------------- 1 | #ifndef SELFCOLLISIONCONSTRAINTRAYS_H 2 | #define SELFCOLLISIONCONSTRAINTRAYS_H 3 | 4 | 5 | #include "ContinuousCollisionConstraint.h" 6 | 7 | namespace csb 8 | { 9 | 10 | class SelfCollisionRaysConstraint : public ContinuousCollisionConstraint 11 | { 12 | public: 13 | //----------------------------------------------------------------------------------------------------- 14 | /// @brief Default constructor. 15 | //----------------------------------------------------------------------------------------------------- 16 | SelfCollisionRaysConstraint() = default; 17 | //----------------------------------------------------------------------------------------------------- 18 | /// @brief Default copy constructor. 19 | //----------------------------------------------------------------------------------------------------- 20 | SelfCollisionRaysConstraint(const SelfCollisionRaysConstraint&) = default; 21 | //----------------------------------------------------------------------------------------------------- 22 | /// @brief Default copy assignment operator. 23 | //----------------------------------------------------------------------------------------------------- 24 | SelfCollisionRaysConstraint& operator=(const SelfCollisionRaysConstraint&) = default; 25 | //----------------------------------------------------------------------------------------------------- 26 | /// @brief Default move constructor. 27 | //----------------------------------------------------------------------------------------------------- 28 | SelfCollisionRaysConstraint(SelfCollisionRaysConstraint&&) = default; 29 | //----------------------------------------------------------------------------------------------------- 30 | /// @brief Default move assignment operator. 31 | //----------------------------------------------------------------------------------------------------- 32 | SelfCollisionRaysConstraint& operator=(SelfCollisionRaysConstraint&&) = default; 33 | //----------------------------------------------------------------------------------------------------- 34 | /// @brief Virtual default destructor. 35 | //----------------------------------------------------------------------------------------------------- 36 | virtual ~SelfCollisionRaysConstraint() override = default; 37 | //----------------------------------------------------------------------------------------------------- 38 | /// @brief This projects the constraint onto the particles. 39 | /// @param io_particles is a reference to the particles that this constraint is to be projected on to. 40 | /// @param _spatialHash is a reference to the solver's hash table which can be used to make the collision, 41 | /// test more efficient. 42 | //----------------------------------------------------------------------------------------------------- 43 | //----------------------------------------------------------------------------------------------------- 44 | /// @brief Attempts to resolve cloth on cloth collisions by calculating line, triangle intersections. 45 | /// The intersecting points will have their velocities reversed, and their positions reverted to the, 46 | /// previous ones to simulate a bounce. 47 | /// @param _meshIndex is the index of the referenced mesh, who's collisions we will resolve. 48 | //----------------------------------------------------------------------------------------------------- 49 | virtual void project( 50 | const size_t &_meshIndex, 51 | std::vector> &_meshes, 52 | const SpatialHash::SpatialHashTable &_spatialHash 53 | ) override; 54 | //----------------------------------------------------------------------------------------------------- 55 | /// @brief This is used by classes that contain constraints to implement copy constructors. 56 | /// @return A raw pointer to a newly allocated clone of the derived class. 57 | //----------------------------------------------------------------------------------------------------- 58 | virtual ContinuousCollisionConstraint* clone() const override; 59 | 60 | }; 61 | 62 | } 63 | 64 | #endif // SELFCOLLISIONCONSTRAINTRAYS_H 65 | -------------------------------------------------------------------------------- /csb/include/SelfCollisionSpheresConstraint.h: -------------------------------------------------------------------------------- 1 | #ifndef SELFCOLLISIONSPHERESCONSTRAINT_H 2 | #define SELFCOLLISIONSPHERESCONSTRAINT_H 3 | 4 | #include "ContinuousCollisionConstraint.h" 5 | 6 | namespace csb 7 | { 8 | 9 | class SelfCollisionSpheresConstraint : public ContinuousCollisionConstraint 10 | { 11 | public: 12 | //----------------------------------------------------------------------------------------------------- 13 | /// @brief Default constructor. 14 | //----------------------------------------------------------------------------------------------------- 15 | SelfCollisionSpheresConstraint() = default; 16 | //----------------------------------------------------------------------------------------------------- 17 | /// @brief Constructor. 18 | /// @param _diameter is size of the spheres that should be placed around all particles to prevent, 19 | /// intersection. 20 | //----------------------------------------------------------------------------------------------------- 21 | SelfCollisionSpheresConstraint(const float _diameter) : 22 | m_sphereDiameter(_diameter) 23 | {} 24 | //----------------------------------------------------------------------------------------------------- 25 | /// @brief Default copy constructor. 26 | //----------------------------------------------------------------------------------------------------- 27 | SelfCollisionSpheresConstraint(const SelfCollisionSpheresConstraint&) = default; 28 | //----------------------------------------------------------------------------------------------------- 29 | /// @brief Default copy assignment operator. 30 | //----------------------------------------------------------------------------------------------------- 31 | SelfCollisionSpheresConstraint& operator=(const SelfCollisionSpheresConstraint&) = default; 32 | //----------------------------------------------------------------------------------------------------- 33 | /// @brief Default move constructor. 34 | //----------------------------------------------------------------------------------------------------- 35 | SelfCollisionSpheresConstraint(SelfCollisionSpheresConstraint&&) = default; 36 | //----------------------------------------------------------------------------------------------------- 37 | /// @brief Default move assignment operator. 38 | //----------------------------------------------------------------------------------------------------- 39 | SelfCollisionSpheresConstraint& operator=(SelfCollisionSpheresConstraint&&) = default; 40 | //----------------------------------------------------------------------------------------------------- 41 | /// @brief Virtual default destructor. 42 | //----------------------------------------------------------------------------------------------------- 43 | virtual ~SelfCollisionSpheresConstraint() override = default; 44 | //----------------------------------------------------------------------------------------------------- 45 | /// @brief This projects the constraint onto the particles. 46 | /// @param io_particles is a reference to the particles that this constraint is to be projected on to. 47 | /// @param _spatialHash is a reference to the solver's hash table which can be used to make the collision, 48 | /// test more efficient. 49 | //----------------------------------------------------------------------------------------------------- 50 | 51 | //----------------------------------------------------------------------------------------------------- 52 | /// @brief Attempts to resolve cloth on cloth collisions by representing particles as connected spheres, 53 | /// and checking whether these spheres intersect. Particles that are too close are projected away from, 54 | /// eachother. 55 | /// @param _meshIndex is the index of the referenced mesh, who's collisions we will resolve. 56 | //----------------------------------------------------------------------------------------------------- 57 | virtual void project( 58 | const size_t &_meshIndex, 59 | std::vector> &_meshes, 60 | const SpatialHash::SpatialHashTable &_spatialHash 61 | ) override; 62 | //----------------------------------------------------------------------------------------------------- 63 | /// @brief This is used by classes that contain constraints to implement copy constructors. 64 | /// @return A raw pointer to a newly allocated clone of the derived class. 65 | //----------------------------------------------------------------------------------------------------- 66 | virtual ContinuousCollisionConstraint* clone() const override; 67 | //----------------------------------------------------------------------------------------------------- 68 | /// @brief Sets the sphere diameter for each particle. 69 | /// @param _diameter is the new diameter of the collision spheres. 70 | //----------------------------------------------------------------------------------------------------- 71 | void setSphereDiameter(const float _radius); 72 | //----------------------------------------------------------------------------------------------------- 73 | /// @brief Gets the sphere diameter for each particle. 74 | /// @return The size of the collision spheres. 75 | //----------------------------------------------------------------------------------------------------- 76 | float getSphereDiameter() const noexcept; 77 | 78 | private: 79 | //----------------------------------------------------------------------------------------------------- 80 | /// @brief The size of the collision spheres. 81 | //----------------------------------------------------------------------------------------------------- 82 | float m_sphereDiameter = 1.f; 83 | }; 84 | 85 | } 86 | 87 | #endif // SELFCOLLISIONSPHERESCONSTRAINT_H 88 | -------------------------------------------------------------------------------- /csb/include/SpatialHash.h: -------------------------------------------------------------------------------- 1 | #ifndef SPATIALHASH_H 2 | #define SPATIALHASH_H 3 | 4 | #include "vec3.hpp" 5 | #include "vector" 6 | #include 7 | 8 | namespace csb 9 | { 10 | 11 | namespace SpatialHash 12 | { 13 | 14 | struct SpatialHashTable 15 | { 16 | //----------------------------------------------------------------------------------------------------- 17 | /// @brief Stores mesh id and particle index pairs, using a spatial hashing function, this is used to 18 | /// query neighbours in order to speed up collision detection. 19 | //----------------------------------------------------------------------------------------------------- 20 | std::vector>> m_hashTable; 21 | //----------------------------------------------------------------------------------------------------- 22 | /// @brief Stores a list of particle id's that are within a triangle's bounding box the table is, 23 | /// indexed using triangle, id plus an offset for the referenced mesh. 24 | //----------------------------------------------------------------------------------------------------- 25 | std::vector> m_triangleBoundingBoxHash; 26 | //----------------------------------------------------------------------------------------------------- 27 | /// @brief Stores offsets per mesh, that should be used when indexing into the triangle hash table. 28 | //----------------------------------------------------------------------------------------------------- 29 | std::vector m_triHashOffset; 30 | //----------------------------------------------------------------------------------------------------- 31 | /// @brief The offset to be added to particle positions before hashing. 32 | //----------------------------------------------------------------------------------------------------- 33 | float m_cellOffset = 10000.0f; 34 | }; 35 | 36 | //----------------------------------------------------------------------------------------------------- 37 | /// @brief Calculates the cell that the given co-ordinate lies within, this is used for spatial hahsing. 38 | /// @param _coord is the 3D co-ordinate who's cell we want to calculate. 39 | /// @param _cellOffset is added to the 3D co-ordinate to avoid issues around the origin, negative, 40 | /// co-ordinates will be hashed differently to positive ones, even if the difference is small. 41 | /// @return a 3D integer co-ordinate that represents a 3D cell in our simulation space. 42 | //----------------------------------------------------------------------------------------------------- 43 | glm::ivec3 calcCell(const glm::vec3& _coord, const float _cellSize, const float _cellOffset); 44 | //----------------------------------------------------------------------------------------------------- 45 | /// @brief Calculates the hash id for a given 3D cell, used for spatial hahsing. 46 | /// @param _cell is the 3D cell that we want to hash. 47 | /// @return a hash id for the given cell, that can be used to query all particles in this cell from the, 48 | /// hash table. 49 | //----------------------------------------------------------------------------------------------------- 50 | size_t hashCell (const glm::ivec3& _cell, const size_t& _tableSize); 51 | //----------------------------------------------------------------------------------------------------- 52 | /// @brief Wraps the calcCell and hashCell functions, to hash a particle. 53 | /// @param _coord is the 3D co-ordinate of the particle we want to hash. 54 | /// @param _cellOffset is added to the 3D co-ordinate to avoid issues around the origin, negative, 55 | /// co-ordinates will be hashed differently to positive ones, even if the difference is small. 56 | /// @return a hash id for the given particle, that can be used to query all particles in this cell from, 57 | /// the hash table. 58 | //----------------------------------------------------------------------------------------------------- 59 | size_t hashParticle(const glm::vec3& _coord, const size_t &_tableSize, const float _cellSize, const float _cellOffset); 60 | 61 | } 62 | 63 | } 64 | 65 | #endif // SPATIALHASH_H 66 | -------------------------------------------------------------------------------- /csb/include/StaticCollisionConstraint.h: -------------------------------------------------------------------------------- 1 | #ifndef STATICCOLLISIONCONSTRAINT_H 2 | #define STATICCOLLISIONCONSTRAINT_H 3 | 4 | #include 5 | #include 6 | #include "Particle.h" 7 | #include "SpatialHash.h" 8 | 9 | namespace csb 10 | { 11 | 12 | class StaticCollisionConstraint 13 | { 14 | public: 15 | //----------------------------------------------------------------------------------------------------- 16 | /// @brief Default constructor. 17 | //----------------------------------------------------------------------------------------------------- 18 | StaticCollisionConstraint() = default; 19 | //----------------------------------------------------------------------------------------------------- 20 | /// @brief Default copy constructor. 21 | //----------------------------------------------------------------------------------------------------- 22 | StaticCollisionConstraint(const StaticCollisionConstraint&) = default; 23 | //----------------------------------------------------------------------------------------------------- 24 | /// @brief Default copy assignment operator. 25 | //----------------------------------------------------------------------------------------------------- 26 | StaticCollisionConstraint& operator=(const StaticCollisionConstraint&) = default; 27 | //----------------------------------------------------------------------------------------------------- 28 | /// @brief Default move constructor. 29 | //----------------------------------------------------------------------------------------------------- 30 | StaticCollisionConstraint(StaticCollisionConstraint&&) = default; 31 | //----------------------------------------------------------------------------------------------------- 32 | /// @brief Default move assignment operator. 33 | //----------------------------------------------------------------------------------------------------- 34 | StaticCollisionConstraint& operator=(StaticCollisionConstraint&&) = default; 35 | //----------------------------------------------------------------------------------------------------- 36 | /// @brief Virtual default destructor. 37 | //----------------------------------------------------------------------------------------------------- 38 | virtual ~StaticCollisionConstraint(); 39 | //----------------------------------------------------------------------------------------------------- 40 | /// @brief This projects the constraint onto the particles. 41 | /// @param io_particles is a reference to the particles that this constraint is to be projected on to. 42 | /// @param _spatialHash is a reference to the solver's hash table which can be used to make the collision, 43 | /// test more efficient. 44 | //----------------------------------------------------------------------------------------------------- 45 | virtual void project(std::vector &io_particles, const SpatialHash::SpatialHashTable &_spatialHash) = 0; 46 | //----------------------------------------------------------------------------------------------------- 47 | /// @brief This is used by classes that contain constraints to implement copy constructors. 48 | /// @return A raw pointer to a newly allocated clone of the derived class. 49 | //----------------------------------------------------------------------------------------------------- 50 | virtual StaticCollisionConstraint* clone() const = 0; 51 | //----------------------------------------------------------------------------------------------------- 52 | /// @brief This init function will be called post setting the cell and hash table sizes, inside of the, 53 | /// solver, so any calculations involving them for spatial hashing should take place here. 54 | //----------------------------------------------------------------------------------------------------- 55 | virtual void init() = 0; 56 | //----------------------------------------------------------------------------------------------------- 57 | /// @brief Sets the member that tracks the hash table size, used for spatial hashing. 58 | /// @param _newSize is the size of the hash table. 59 | //----------------------------------------------------------------------------------------------------- 60 | void setHashTableSize(const size_t &_newSize); 61 | //----------------------------------------------------------------------------------------------------- 62 | /// @brief Sets the member that tracks the spatial cell size, used for spatial hashing. 63 | /// @param _newSize is the cell size. 64 | //----------------------------------------------------------------------------------------------------- 65 | void setCellSize(const float _newSize); 66 | //----------------------------------------------------------------------------------------------------- 67 | /// @brief Sets the member that tracks the spatial cell offset, used for spatial hashing. 68 | /// @param _newOffset is the cell offset. 69 | //----------------------------------------------------------------------------------------------------- 70 | void setCellOffset(const float _newOffset); 71 | 72 | protected: 73 | //----------------------------------------------------------------------------------------------------- 74 | /// @brief Tracks hash table size, used for spatial hashing. 75 | //----------------------------------------------------------------------------------------------------- 76 | size_t m_hashTableSize = 0; 77 | //----------------------------------------------------------------------------------------------------- 78 | /// @brief Tracks the spatial cell size, used for spatial hashing. 79 | //----------------------------------------------------------------------------------------------------- 80 | float m_cellSize = 0.f; 81 | //----------------------------------------------------------------------------------------------------- 82 | /// @brief The offset to be added to particle positions before hashing. 83 | //----------------------------------------------------------------------------------------------------- 84 | float m_cellOffset = 0.0f; 85 | 86 | }; 87 | 88 | } 89 | 90 | #endif // STATICCOLLISIONCONSTRAINT_H 91 | -------------------------------------------------------------------------------- /csb/src/BendingConstraint.cpp: -------------------------------------------------------------------------------- 1 | #include "BendingConstraint.h" 2 | #define GLM_ENABLE_EXPERIMENTAL 3 | #include "glm/gtx/fast_square_root.hpp" 4 | #undef GLM_ENABLE_EXPERIMENTAL 5 | 6 | //---------------------------------------------------------------------------------------------------------------------------- 7 | csb::BendingConstraint::BendingConstraint( 8 | const size_t _p1, 9 | const size_t _p2, 10 | const size_t _p3, 11 | const float _rest, 12 | const float _stiffness, 13 | const std::vector&_particles 14 | ) : 15 | m_p({{_p1, _p2, _p3}}), 16 | m_rest(_rest), 17 | m_stiffness(_stiffness) 18 | { 19 | // Calculate the weights using the inverse mass of all particles 20 | // we weight towards the third particle as this is the centre 21 | auto W = _particles[_p1].m_invMass + _particles[_p2].m_invMass + 2.f * _particles[_p3].m_invMass; 22 | for (unsigned int i = 0; i < m_w.size(); ++i) 23 | m_w[i] = _particles[m_p[i]].m_invMass / W; 24 | } 25 | //---------------------------------------------------------------------------------------------------------------------------- 26 | void csb::BendingConstraint::project(std::vector &io_particles) 27 | { 28 | auto& p1 = io_particles[m_p[0]]; 29 | auto& p2 = io_particles[m_p[1]]; 30 | auto& p3 = io_particles[m_p[2]]; 31 | 32 | static constexpr float third = 1.0f / 3.0f; 33 | // Get the centroid of the three particles 34 | auto centre = third * (*p1.m_pos + *p2.m_pos + *p3.m_pos); 35 | // Then get the direction vector from the middle particle to the centroid 36 | glm::vec3 dirCentre = *p3.m_pos - centre; 37 | // Calculate the distance to the centroid 38 | auto distCentre = glm::fastLength(dirCentre); 39 | 40 | // Calculate the relative error 41 | float sub = m_rest / distCentre; 42 | float diff = 1.0f; 43 | if (std::isfinite(sub)) 44 | diff -= sub; 45 | // Force is based on the correction required 46 | auto force = dirCentre * diff; 47 | 48 | // Use the previously calculated weights 49 | *p1.m_pos += (m_stiffness * m_w[0] * 2.f * force); 50 | *p2.m_pos += (m_stiffness * m_w[1] * 2.f * force); 51 | *p3.m_pos += (m_stiffness * m_w[2] * -4.f * force); 52 | } 53 | //---------------------------------------------------------------------------------------------------------------------------- 54 | csb::PositionConstraint* csb::BendingConstraint::clone() const 55 | { 56 | return new BendingConstraint(*this); 57 | } 58 | //---------------------------------------------------------------------------------------------------------------------------- 59 | size_t csb::BendingConstraint::getParticleIndex(const unsigned short _index) const noexcept 60 | { 61 | return m_p[_index]; 62 | } 63 | //---------------------------------------------------------------------------------------------------------------------------- 64 | float csb::BendingConstraint::getParticleWeight(const unsigned short _index) const noexcept 65 | { 66 | return m_w[_index]; 67 | } 68 | //---------------------------------------------------------------------------------------------------------------------------- 69 | float csb::BendingConstraint::getRest() const noexcept 70 | { 71 | return m_rest; 72 | } 73 | //---------------------------------------------------------------------------------------------------------------------------- 74 | float csb::BendingConstraint::getStiffness() const noexcept 75 | { 76 | return m_stiffness; 77 | } 78 | //---------------------------------------------------------------------------------------------------------------------------- 79 | void csb::BendingConstraint::setParticleIndex(const unsigned short _index, const size_t &_p) 80 | { 81 | m_p[_index] = _p; 82 | } 83 | //---------------------------------------------------------------------------------------------------------------------------- 84 | void csb::BendingConstraint::setParticleWeight(const unsigned short _index, const float _weight) 85 | { 86 | m_w[_index] = _weight; 87 | } 88 | //---------------------------------------------------------------------------------------------------------------------------- 89 | void csb::BendingConstraint::setRest(const float _rest) 90 | { 91 | m_rest = _rest; 92 | } 93 | //---------------------------------------------------------------------------------------------------------------------------- 94 | void csb::BendingConstraint::setStiffness(const float _stiffness) 95 | { 96 | m_stiffness = _stiffness; 97 | } 98 | //---------------------------------------------------------------------------------------------------------------------------- 99 | -------------------------------------------------------------------------------- /csb/src/ContinuousCollisionConstraint.cpp: -------------------------------------------------------------------------------- 1 | #include "ContinuousCollisionConstraint.h" 2 | 3 | 4 | //---------------------------------------------------------------------------------------------------------------------------- 5 | csb::ContinuousCollisionConstraint::~ContinuousCollisionConstraint() = default; 6 | //---------------------------------------------------------------------------------------------------------------------------- 7 | std::vector& csb::ContinuousCollisionConstraint::getParticles(SimulatedMesh&_mesh) const noexcept 8 | { 9 | return _mesh.m_particles; 10 | } 11 | //---------------------------------------------------------------------------------------------------------------------------- 12 | -------------------------------------------------------------------------------- /csb/src/DistanceConstraint.cpp: -------------------------------------------------------------------------------- 1 | #include "DistanceConstraint.h" 2 | #define GLM_ENABLE_EXPERIMENTAL 3 | #include "glm/gtx/fast_square_root.hpp" 4 | #undef GLM_ENABLE_EXPERIMENTAL 5 | 6 | //---------------------------------------------------------------------------------------------------------------------------- 7 | void csb::DistanceConstraint::project(std::vector &io_particles) 8 | { 9 | auto& p1 = io_particles[m_p1]; 10 | auto& p2 = io_particles[m_p2]; 11 | // Get the current distance between the two particles 12 | auto delta = *p2.m_pos - *p1.m_pos; 13 | auto deltaLen = glm::fastLength(delta); 14 | // Calculate the required correction to return to rest length 15 | auto diff = (deltaLen - m_rest) / (deltaLen * (p1.m_invMass + p2.m_invMass)); 16 | delta *= diff; 17 | 18 | // Use the masses of the particles 19 | *p1.m_pos += (delta * p1.m_invMass); 20 | *p2.m_pos -= (delta * p2.m_invMass); 21 | } 22 | //---------------------------------------------------------------------------------------------------------------------------- 23 | csb::PositionConstraint* csb::DistanceConstraint::clone() const 24 | { 25 | return new DistanceConstraint(*this); 26 | } 27 | //---------------------------------------------------------------------------------------------------------------------------- 28 | float csb::DistanceConstraint::getRest() const noexcept 29 | { 30 | return m_rest; 31 | } 32 | //---------------------------------------------------------------------------------------------------------------------------- 33 | size_t csb::DistanceConstraint::getParticleIndex1() const noexcept 34 | { 35 | return m_p1; 36 | } 37 | //---------------------------------------------------------------------------------------------------------------------------- 38 | size_t csb::DistanceConstraint::getParticleIndex2() const noexcept 39 | { 40 | return m_p2; 41 | } 42 | //---------------------------------------------------------------------------------------------------------------------------- 43 | void csb::DistanceConstraint::setRest(const float _rest) 44 | { 45 | m_rest = _rest; 46 | } 47 | //---------------------------------------------------------------------------------------------------------------------------- 48 | void csb::DistanceConstraint::setParticleIndex1(const size_t &_p1) 49 | { 50 | m_p1 = _p1; 51 | } 52 | //---------------------------------------------------------------------------------------------------------------------------- 53 | void csb::DistanceConstraint::setParticleIndex2(const size_t &_p2) 54 | { 55 | m_p2 = _p2; 56 | } 57 | //---------------------------------------------------------------------------------------------------------------------------- 58 | -------------------------------------------------------------------------------- /csb/src/PinConstraint.cpp: -------------------------------------------------------------------------------- 1 | #include "PinConstraint.h" 2 | 3 | 4 | //---------------------------------------------------------------------------------------------------------------------------- 5 | void csb::PinConstraint::project(std::vector &io_particles) 6 | { 7 | // Set the particle position 8 | *io_particles[m_p].m_pos = m_pin; 9 | io_particles[m_p].m_prevPos = m_pin; 10 | } 11 | //---------------------------------------------------------------------------------------------------------------------------- 12 | csb::PositionConstraint* csb::PinConstraint::clone() const 13 | { 14 | return new PinConstraint(*this); 15 | } 16 | //---------------------------------------------------------------------------------------------------------------------------- 17 | size_t csb::PinConstraint::getParticleIndex() const noexcept 18 | { 19 | return m_p; 20 | } 21 | //---------------------------------------------------------------------------------------------------------------------------- 22 | glm::vec3 csb::PinConstraint::getPinPosition() const noexcept 23 | { 24 | return m_pin; 25 | } 26 | //---------------------------------------------------------------------------------------------------------------------------- 27 | void csb::PinConstraint::setParticleIndex(const size_t &_p) 28 | { 29 | m_p = _p; 30 | } 31 | //---------------------------------------------------------------------------------------------------------------------------- 32 | void csb::PinConstraint::setPinPosition(const glm::vec3 &_pin) 33 | { 34 | m_pin = _pin; 35 | } 36 | //---------------------------------------------------------------------------------------------------------------------------- 37 | -------------------------------------------------------------------------------- /csb/src/PositionConstraint.cpp: -------------------------------------------------------------------------------- 1 | #include "PositionConstraint.h" 2 | 3 | //---------------------------------------------------------------------------------------------------------------------------- 4 | csb::PositionConstraint::~PositionConstraint() = default; 5 | //---------------------------------------------------------------------------------------------------------------------------- 6 | -------------------------------------------------------------------------------- /csb/src/SelfCollisionRaysConstraint.cpp: -------------------------------------------------------------------------------- 1 | #include "SelfCollisionRaysConstraint.h" 2 | #include "SpatialHash.h" 3 | #define GLM_ENABLE_EXPERIMENTAL 4 | #include "gtx/fast_square_root.hpp" 5 | #include "gtx/norm.hpp" 6 | #include "gtx/normal.hpp" 7 | #include "gtx/intersect.hpp" 8 | #undef GLM_ENABLE_EXPERIMENTAL 9 | 10 | 11 | //---------------------------------------------------------------------------------------------------------------------------- 12 | void csb::SelfCollisionRaysConstraint::project( 13 | const size_t &_meshIndex, 14 | std::vector > &_meshes, 15 | const SpatialHash::SpatialHashTable &_spatialHash 16 | ) 17 | { 18 | const auto hashOffset = _spatialHash.m_triHashOffset[_meshIndex]; 19 | auto& mesh = *_meshes[_meshIndex]; 20 | const auto& indices = mesh.getIndices(); 21 | auto& particles = getParticles(mesh); 22 | const auto size = mesh.getNIndices() / 3; 23 | // Loop over all faces 24 | for (size_t i = 0; i < size; ++i) 25 | { 26 | const size_t index = i * 3; 27 | const auto& T0 = *particles[indices[index]].m_pos; 28 | const auto& T1 = *particles[indices[index + 1]].m_pos; 29 | const auto& T2 = *particles[indices[index + 2]].m_pos; 30 | const auto TNorm = glm::triangleNormal(T0, T1, T2); 31 | 32 | // Loop over all hashed cells for this face 33 | const auto& cells = _spatialHash.m_triangleBoundingBoxHash[i + hashOffset]; 34 | for (const auto& hashCell : cells) 35 | { 36 | // Loop over all particles in the cell 37 | const auto& particleIds = _spatialHash.m_hashTable[hashCell]; 38 | for (const auto& meshPid : particleIds) 39 | { 40 | const auto meshId = meshPid.first; 41 | const auto pid = meshPid.second; 42 | // skip the particles in this meshes triangle 43 | if (meshId == _meshIndex && ((pid == indices[index]) || (pid == indices[index + 1]) || (pid == indices[index + 2]))) 44 | continue; 45 | auto& otherParticles = getParticles(*_meshes[meshId]); 46 | const auto& particle = otherParticles[pid]; 47 | 48 | // Calculate the start and end points of the line, and get the direction vector 49 | const auto& L0 = particle.m_prevPos; 50 | const auto& L1 = *particle.m_pos; 51 | const auto dir = L1 - L0; 52 | 53 | // Use these vectors to determine whether the line is entirely on one side of the tri 54 | const auto distStart = glm::dot(T0 - L0, TNorm); 55 | const auto distEnd = glm::dot(T0 - L1, TNorm); 56 | 57 | glm::vec3 intersection; 58 | // Check not same side of triangle, and an intersection is present 59 | if (glm::intersectLineTriangle(L0, dir, T0, T1, T2, intersection) && (distStart * distEnd < 0.0f)) 60 | { 61 | // Rewind the positions and kill the velocity 62 | *particles[indices[index]].m_pos = particles[indices[index]].m_prevPos; 63 | *particles[indices[index+1]].m_pos = particles[indices[index+1]].m_prevPos; 64 | *particles[indices[index+2]].m_pos = particles[indices[index+2]].m_prevPos; 65 | *otherParticles[pid].m_pos = otherParticles[pid].m_prevPos; 66 | } 67 | } 68 | } 69 | } 70 | } 71 | //---------------------------------------------------------------------------------------------------------------------------- 72 | csb::ContinuousCollisionConstraint* csb::SelfCollisionRaysConstraint::clone() const 73 | { 74 | return new SelfCollisionRaysConstraint(*this); 75 | } 76 | //---------------------------------------------------------------------------------------------------------------------------- 77 | -------------------------------------------------------------------------------- /csb/src/SelfCollisionSpheresConstraint.cpp: -------------------------------------------------------------------------------- 1 | #include "SelfCollisionSpheresConstraint.h" 2 | #include "SpatialHash.h" 3 | #include "gtx/norm.hpp" 4 | #include "gtx/fast_square_root.hpp" 5 | 6 | 7 | //---------------------------------------------------------------------------------------------------------------------------- 8 | void csb::SelfCollisionSpheresConstraint::project( 9 | const size_t &_meshIndex, 10 | std::vector > &_meshes, 11 | const SpatialHash::SpatialHashTable &_spatialHash 12 | ) 13 | { 14 | auto& mesh = *_meshes[_meshIndex]; 15 | const auto& adjacency = mesh.getAdjacencyInfo(); 16 | auto& particles = getParticles(mesh); 17 | const auto size = mesh.getNVerts(); 18 | for (GLushort i = 0; i < size; ++i) 19 | { 20 | auto& P = particles[i]; 21 | // Build a list of ignored particles, consiting of itself and it's first ring neighbours. 22 | auto ignored = adjacency[i]; 23 | ignored.push_back(i); 24 | std::sort(ignored.begin(), ignored.end()); 25 | 26 | const auto& hashTable = _spatialHash.m_hashTable; 27 | // Get all of the particles in this cell 28 | auto considered = hashTable[SpatialHash::hashParticle(*P.m_pos, hashTable.size(), m_sphereDiameter, _spatialHash.m_cellOffset)]; 29 | std::sort(considered.begin(), considered.end()); 30 | 31 | 32 | // Scope the using declaration 33 | { 34 | // I think this is more readable, we erase the ignored particles from the list of considered ones 35 | using namespace std; 36 | considered.erase( 37 | remove_if(begin(considered), end(considered), [&ignored, &_meshIndex](const auto x) 38 | { 39 | return (x.first == _meshIndex) && binary_search(begin(ignored), end(ignored),x.second); 40 | } 41 | ), end(considered)); 42 | } 43 | 44 | // We'll accumulate all the collision responses here 45 | glm::vec3 offset(0.f); 46 | int intersectionCount = 0; 47 | 48 | for (const auto& pid : considered) 49 | { 50 | // Get the particle we are checking against 51 | auto& otherParticles = getParticles(*_meshes[pid.first]); 52 | const auto& Q = otherParticles[pid.second]; 53 | // Get the displacement vector and squared distance between the particles 54 | const auto disp = *P.m_pos - *Q.m_pos; 55 | const auto dist = glm::length2(disp); 56 | 57 | // By setting the distance to be larger than the distance between particles 58 | // we should cover the cloth surface, however we can't set them too big, 59 | // otherwise conflicts with neighbours will occur and we'll see flickering. 60 | // We check the squared distances to avoid expensive length calculations. 61 | if (dist < m_sphereDiameter * m_sphereDiameter) 62 | { 63 | // Get the correction vector (2R - D) 64 | const auto move = (m_sphereDiameter - glm::fastSqrt(dist)); 65 | offset += (glm::fastNormalize(disp) * move); 66 | ++intersectionCount; 67 | } 68 | } 69 | 70 | // If an intersection took place 71 | if (intersectionCount) 72 | { 73 | // Set a lower bound for the offset to reduce flickering 74 | offset *= glm::step(0.001f, offset); 75 | // Average the correction 76 | (*P.m_pos) += offset/static_cast(intersectionCount); 77 | // zero the velocity 78 | P.m_prevPos = *P.m_pos; 79 | } 80 | } 81 | } 82 | //---------------------------------------------------------------------------------------------------------------------------- 83 | csb::ContinuousCollisionConstraint* csb::SelfCollisionSpheresConstraint::clone() const 84 | { 85 | return new SelfCollisionSpheresConstraint(*this); 86 | } 87 | //---------------------------------------------------------------------------------------------------------------------------- 88 | void csb::SelfCollisionSpheresConstraint::setSphereDiameter(const float _radius) 89 | { 90 | m_sphereDiameter = _radius; 91 | } 92 | //---------------------------------------------------------------------------------------------------------------------------- 93 | float csb::SelfCollisionSpheresConstraint::getSphereDiameter() const noexcept 94 | { 95 | return m_sphereDiameter; 96 | } 97 | //---------------------------------------------------------------------------------------------------------------------------- 98 | -------------------------------------------------------------------------------- /csb/src/SpatialHash.cpp: -------------------------------------------------------------------------------- 1 | #include "SpatialHash.h" 2 | #include "common.hpp" 3 | 4 | 5 | //---------------------------------------------------------------------------------------------------------------------------- 6 | glm::ivec3 csb::SpatialHash::calcCell(const glm::vec3& _coord, const float _cellSize, const float _cellOffset) 7 | { 8 | const auto coord = _coord + _cellOffset; 9 | // cellsize is equal to the average edge length for max performance 10 | return glm::ivec3( 11 | static_cast(glm::floor(coord.x / _cellSize)), 12 | static_cast(glm::floor(coord.y / _cellSize)), 13 | static_cast(glm::floor(coord.z / _cellSize)) 14 | ); 15 | } 16 | //---------------------------------------------------------------------------------------------------------------------------- 17 | size_t csb::SpatialHash::hashCell(const glm::ivec3& _cell, const size_t &_tableSize) 18 | { 19 | // C++ modulo doesn't always produce a positive output 20 | static constexpr auto posMod = [](const auto _x, const auto _m) 21 | { 22 | const auto m = static_cast(_m); 23 | return static_cast(((_x % m) + m) % m); 24 | }; 25 | 26 | // From "Optimized Spatial Hashing for Collision Detection of Deformable Objects" 27 | static constexpr int primes[] = {73856093, 19349663, 83492791}; 28 | return posMod((_cell.x * primes[0]) ^ (_cell.y * primes[1]) ^ (_cell.z * primes[2]), _tableSize); 29 | } 30 | //---------------------------------------------------------------------------------------------------------------------------- 31 | size_t csb::SpatialHash::hashParticle(const glm::vec3& _coord, const size_t &_tableSize, const float _cellSize, const float _cellOffset) 32 | { 33 | // Convenience function 34 | return csb::SpatialHash::hashCell(calcCell(_coord, _cellSize, _cellOffset), _tableSize); 35 | } 36 | //---------------------------------------------------------------------------------------------------------------------------- 37 | -------------------------------------------------------------------------------- /csb/src/SphereCollisionConstraint.cpp: -------------------------------------------------------------------------------- 1 | #include "SphereCollisionConstraint.h" 2 | #include "SpatialHash.h" 3 | #include "common.hpp" 4 | #define GLM_ENABLE_EXPERIMENTAL 5 | #include "glm/gtx/fast_square_root.hpp" 6 | #undef GLM_ENABLE_EXPERIMENTAL 7 | 8 | 9 | //---------------------------------------------------------------------------------------------------------------------------- 10 | csb::SphereCollisionConstraint::SphereCollisionConstraint(const glm::vec3 &_centre, const float _radius) : 11 | m_centre(_centre), 12 | m_radius(_radius) 13 | {} 14 | //---------------------------------------------------------------------------------------------------------------------------- 15 | void csb::SphereCollisionConstraint::init() 16 | { 17 | updateBoundingBoxCells(); 18 | } 19 | //---------------------------------------------------------------------------------------------------------------------------- 20 | void csb::SphereCollisionConstraint::updateBoundingBoxCells() 21 | { 22 | // Minimum and maximum corners of the bounding box 23 | const auto bbMin = m_centre - glm::vec3(m_radius); 24 | const auto bbMax = m_centre + glm::vec3(m_radius); 25 | 26 | // Get the min and max cells in the spatial hash 27 | const auto min = SpatialHash::calcCell(bbMin, m_cellSize, m_cellOffset); 28 | const auto max = SpatialHash::calcCell(bbMax, m_cellSize, m_cellOffset); 29 | 30 | m_cells.clear(); 31 | // Reserve the number of cells 32 | m_cells.reserve(static_cast((max.x - min.x) * (max.y - min.y) * (max.z - min.z))); 33 | // hash all cells within the bounding box of this sphere 34 | for (int x = min.x; x <= max.x; ++x) 35 | for (int y = min.y; y <= max.y; ++y) 36 | for (int z = min.z; z <= max.z; ++z) 37 | { 38 | m_cells.push_back(SpatialHash::hashCell({x,y,z}, m_hashTableSize)); 39 | } 40 | } 41 | //---------------------------------------------------------------------------------------------------------------------------- 42 | const std::vector csb::SphereCollisionConstraint::cells() const noexcept 43 | { 44 | return m_cells; 45 | } 46 | //---------------------------------------------------------------------------------------------------------------------------- 47 | glm::vec3 csb::SphereCollisionConstraint::getCentre() const noexcept 48 | { 49 | return m_centre; 50 | } 51 | //---------------------------------------------------------------------------------------------------------------------------- 52 | float csb::SphereCollisionConstraint::getRadius() const noexcept 53 | { 54 | return m_radius; 55 | } 56 | //---------------------------------------------------------------------------------------------------------------------------- 57 | void csb::SphereCollisionConstraint::setCentre(const glm::vec3 &_centre) 58 | { 59 | m_centre = _centre; 60 | } 61 | //---------------------------------------------------------------------------------------------------------------------------- 62 | void csb::SphereCollisionConstraint::setRadius(const float _radius) 63 | { 64 | m_radius = _radius; 65 | } 66 | //---------------------------------------------------------------------------------------------------------------------------- 67 | void csb::SphereCollisionConstraint::project(std::vector &io_particles, const SpatialHash::SpatialHashTable &_spatialHash) 68 | { 69 | // Get the particles within the spheres bounding box 70 | std::vector intersectingParticles; 71 | for (const auto& cell : m_cells) 72 | { 73 | const auto& contained = _spatialHash.m_hashTable[cell]; 74 | intersectingParticles.reserve(intersectingParticles.size() + contained.size()); 75 | for (const auto& particleId : contained) 76 | intersectingParticles.push_back(particleId.second); 77 | } 78 | // Project all particles within the sphere, onto the nearest surface point 79 | for (const auto& p : intersectingParticles) 80 | { 81 | const auto disp = *io_particles[p].m_pos - m_centre; 82 | const auto distanceFromCentre = glm::fastLength(disp); 83 | if (distanceFromCentre <= m_radius) 84 | { 85 | *io_particles[p].m_pos = m_centre + glm::fastNormalize(disp) * m_radius; 86 | } 87 | } 88 | } 89 | //---------------------------------------------------------------------------------------------------------------------------- 90 | csb::StaticCollisionConstraint* csb::SphereCollisionConstraint::clone() const 91 | { 92 | return new SphereCollisionConstraint(*this); 93 | } 94 | //---------------------------------------------------------------------------------------------------------------------------- 95 | -------------------------------------------------------------------------------- /csb/src/StaticCollisionConstraint.cpp: -------------------------------------------------------------------------------- 1 | #include "StaticCollisionConstraint.h" 2 | 3 | 4 | //---------------------------------------------------------------------------------------------------------------------------- 5 | csb::StaticCollisionConstraint::~StaticCollisionConstraint() = default; 6 | //---------------------------------------------------------------------------------------------------------------------------- 7 | void csb::StaticCollisionConstraint::setHashTableSize(const size_t&_newSize) 8 | { 9 | m_hashTableSize = _newSize; 10 | } 11 | //---------------------------------------------------------------------------------------------------------------------------- 12 | void csb::StaticCollisionConstraint::setCellSize(const float _newSize) 13 | { 14 | m_cellSize = _newSize; 15 | } 16 | //---------------------------------------------------------------------------------------------------------------------------- 17 | void csb::StaticCollisionConstraint::setCellOffset(const float _newOffset) 18 | { 19 | m_cellOffset = _newOffset; 20 | } 21 | //---------------------------------------------------------------------------------------------------------------------------- 22 | -------------------------------------------------------------------------------- /demo/Demo.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = app 2 | TARGET = Demo 3 | 4 | UI_HEADERS_DIR = ui 5 | OBJECTS_DIR = obj 6 | MOC_DIR = moc 7 | UI_DIR = ui 8 | 9 | QT += opengl core gui 10 | CONFIG += console c++14 11 | CONFIG -= app_bundle 12 | 13 | DEPENDPATH += . ../csb/lib 14 | INCLUDEPATH += ../csb/include 15 | 16 | INCLUDEPATH += \ 17 | /usr/local/include/glm/glm \ 18 | /usr/local/include/glm \ 19 | /usr/local/include \ 20 | $$PWD/include \ 21 | $$PWD/ui \ 22 | $$PWD/shaders 23 | 24 | HEADERS += \ 25 | include/MainWindow.h \ 26 | include/Camera.h \ 27 | include/TrackballCamera.h \ 28 | include/CameraStates.h \ 29 | include/Material.h \ 30 | include/Scene.h \ 31 | include/DemoScene.h \ 32 | include/ShaderLib.h \ 33 | include/MeshVBO.h \ 34 | include/MaterialWireframe.h \ 35 | include/MaterialFractal.h \ 36 | include/MaterialCSBpbr.h 37 | 38 | SOURCES += \ 39 | src/main.cpp \ 40 | src/MainWindow.cpp \ 41 | src/Camera.cpp \ 42 | src/TrackballCamera.cpp \ 43 | src/CameraStates.cpp \ 44 | src/Material.cpp \ 45 | src/Scene.cpp \ 46 | src/DemoScene.cpp \ 47 | src/ShaderLib.cpp \ 48 | src/MeshVBO.cpp \ 49 | src/MaterialWireframe.cpp \ 50 | src/MaterialFractal.cpp \ 51 | src/MaterialCSBpbr.cpp 52 | 53 | DISTFILES += $$files(shaders/*, true) 54 | 55 | OTHER_FILES += \ 56 | $$files(shaderPrograms/*, true) \ 57 | $$files(models/*, true) 58 | 59 | FORMS += ui/mainwindow.ui 60 | 61 | QMAKE_CXXFLAGS += -O3 -std=c++14 -msse -msse2 -msse3 62 | 63 | LIBS += -L../csb/lib -lcsb 64 | 65 | linux:{ 66 | LIBS += -lGL -lGLU -lGLEW -lassimp 67 | } 68 | 69 | mac:{ 70 | LIBS+= -L/usr/local/lib -lassimp 71 | QMAKE_CXXFLAGS += -arch x86_64 72 | } 73 | 74 | unix:{ 75 | # add the lib to rpath so it can be dynamically loaded 76 | QMAKE_LFLAGS += "-Wl,-rpath,\'\$$ORIGIN/../csb/lib\'" 77 | } 78 | 79 | -------------------------------------------------------------------------------- /demo/images/gloss.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitronoid/csb/c3d67051410c67e784003db0df4f7c81a702d248/demo/images/gloss.png -------------------------------------------------------------------------------- /demo/images/sky_xneg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitronoid/csb/c3d67051410c67e784003db0df4f7c81a702d248/demo/images/sky_xneg.png -------------------------------------------------------------------------------- /demo/images/sky_xpos.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitronoid/csb/c3d67051410c67e784003db0df4f7c81a702d248/demo/images/sky_xpos.png -------------------------------------------------------------------------------- /demo/images/sky_yneg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitronoid/csb/c3d67051410c67e784003db0df4f7c81a702d248/demo/images/sky_yneg.png -------------------------------------------------------------------------------- /demo/images/sky_ypos.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitronoid/csb/c3d67051410c67e784003db0df4f7c81a702d248/demo/images/sky_ypos.png -------------------------------------------------------------------------------- /demo/images/sky_zneg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitronoid/csb/c3d67051410c67e784003db0df4f7c81a702d248/demo/images/sky_zneg.png -------------------------------------------------------------------------------- /demo/images/sky_zpos.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitronoid/csb/c3d67051410c67e784003db0df4f7c81a702d248/demo/images/sky_zpos.png -------------------------------------------------------------------------------- /demo/include/CameraStates.h: -------------------------------------------------------------------------------- 1 | #ifndef CAMERASTATES_H 2 | #define CAMERASTATES_H 3 | 4 | #include "vec2.hpp" 5 | 6 | class Camera; 7 | 8 | class CameraState 9 | { 10 | public: 11 | //----------------------------------------------------------------------------------------------------- 12 | /// @brief Default constructor. 13 | //----------------------------------------------------------------------------------------------------- 14 | CameraState() = default; 15 | //----------------------------------------------------------------------------------------------------- 16 | /// @brief Default copy constructor. 17 | //----------------------------------------------------------------------------------------------------- 18 | CameraState(const CameraState&) = default; 19 | //----------------------------------------------------------------------------------------------------- 20 | /// @brief Default copy assignment operator. 21 | //----------------------------------------------------------------------------------------------------- 22 | CameraState& operator=(const CameraState&) = default; 23 | //----------------------------------------------------------------------------------------------------- 24 | /// @brief Default move constructor. 25 | //----------------------------------------------------------------------------------------------------- 26 | CameraState(CameraState&&) = default; 27 | //----------------------------------------------------------------------------------------------------- 28 | /// @brief Default move assignment operator. 29 | //----------------------------------------------------------------------------------------------------- 30 | CameraState& operator=(CameraState&&) = default; 31 | //----------------------------------------------------------------------------------------------------- 32 | /// @brief Default virtual destructor. 33 | //----------------------------------------------------------------------------------------------------- 34 | virtual ~CameraState() = default; 35 | //----------------------------------------------------------------------------------------------------- 36 | /// @brief Used to respond to mouse movement. 37 | /// @param [io] io_camera is the camera that holds this state. 38 | /// @param [in] _mousePos is the new position of the mouse. 39 | //----------------------------------------------------------------------------------------------------- 40 | virtual void handleMouseMove(Camera*const io_camera, const glm::vec2 &_mousePos) = 0; 41 | //----------------------------------------------------------------------------------------------------- 42 | /// @brief Handles key press events. 43 | /// @param [io] io_camera is the camera that holds this state. 44 | /// @param [in] _key is the Qt key that has been pressed/released. 45 | /// @param [in] _isPress whether this key has been pressed or released. 46 | //----------------------------------------------------------------------------------------------------- 47 | virtual void handleKey(Camera*const io_camera, const int _key, const bool _isPress) = 0; 48 | 49 | protected: 50 | //----------------------------------------------------------------------------------------------------- 51 | /// @brief This is a function that resets the camera to it's default position based on a key press, 52 | /// it has been included as for my example, I wanted the same behaviour for all states. 53 | /// @param [io] io_camera is the camera that holds this state. 54 | /// @param [in] _key is the Qt key that has been pressed/released. 55 | /// @param [in] _isPress whether this key has been pressed or released. 56 | //----------------------------------------------------------------------------------------------------- 57 | void resetPosition(Camera* const io_camera, const int _key, const bool _isPress); 58 | }; 59 | 60 | //----------------------------------------------------------------------------------------------------- 61 | /// @brief Class that represents the behaviour of a zooming camera. 62 | //----------------------------------------------------------------------------------------------------- 63 | class CameraZoom : public CameraState 64 | { 65 | public: 66 | virtual void handleMouseMove(Camera*const io_camera, const glm::vec2 &_mousePos) override; 67 | virtual void handleKey(Camera*const io_camera, const int _key, const bool _isPress) override; 68 | }; 69 | 70 | //----------------------------------------------------------------------------------------------------- 71 | /// @brief Class that represents the behaviour of a rotating camera. 72 | //----------------------------------------------------------------------------------------------------- 73 | class CameraRotate : public CameraState 74 | { 75 | public: 76 | virtual void handleMouseMove(Camera*const io_camera, const glm::vec2 &_mousePos) override; 77 | virtual void handleKey(Camera*const io_camera, const int _key, const bool _isPress) override; 78 | }; 79 | 80 | //----------------------------------------------------------------------------------------------------- 81 | /// @brief Class that represents the behaviour of a passive camera. 82 | //----------------------------------------------------------------------------------------------------- 83 | class CameraPassive : public CameraState 84 | { 85 | public: 86 | virtual void handleMouseMove(Camera*const io_camera, const glm::vec2 &_mousePos) override; 87 | virtual void handleKey(Camera * const io_camera, const int _key, const bool _isPress) override; 88 | }; 89 | 90 | 91 | #endif // CAMERASTATES_H 92 | -------------------------------------------------------------------------------- /demo/include/MainWindow.h: -------------------------------------------------------------------------------- 1 | #ifndef MAINWINDOW_H 2 | #define MAINWINDOW_H 3 | 4 | #include 5 | #include "Scene.h" 6 | #include "ui_mainwindow.h" 7 | 8 | class MainWindow : public QMainWindow 9 | { 10 | Q_OBJECT 11 | public: 12 | //----------------------------------------------------------------------------------------------------- 13 | /// @brief Explcit constructor for the main window. It is explicit so QWidgets can't be implicitly cast 14 | /// to MainWindows. 15 | /// @param [io] io_parent the parent widget of this window. 16 | //----------------------------------------------------------------------------------------------------- 17 | explicit MainWindow(QWidget *io_parent = nullptr) : 18 | QMainWindow(io_parent) 19 | {} 20 | //----------------------------------------------------------------------------------------------------- 21 | /// @brief Default destructor 22 | //----------------------------------------------------------------------------------------------------- 23 | ~MainWindow() = default; 24 | //----------------------------------------------------------------------------------------------------- 25 | /// @brief Used to initialise the Window with a Scene, placing it in the central widget. 26 | /// @param [io] io_scene is the scene to be placed in the central widget. 27 | //----------------------------------------------------------------------------------------------------- 28 | void init(const std::shared_ptr &io_scene); 29 | 30 | private: 31 | //----------------------------------------------------------------------------------------------------- 32 | /// @brief Used to handle a key press, will get delegated to the scene. 33 | /// @param [io] io_event the key that was pressed. 34 | //----------------------------------------------------------------------------------------------------- 35 | void keyPressEvent(QKeyEvent * io_event); 36 | //----------------------------------------------------------------------------------------------------- 37 | /// @brief Used to handle a mouse move, will get delegated to the scene. 38 | /// @param [io] io_event contains the mouse position. 39 | //----------------------------------------------------------------------------------------------------- 40 | void mouseMoveEvent(QMouseEvent * io_event); 41 | //----------------------------------------------------------------------------------------------------- 42 | /// @brief Used to handle a mouse button press, will get delegated to the scene. 43 | /// @param [io] io_event contains the mouse button that was pressed. 44 | //----------------------------------------------------------------------------------------------------- 45 | void mousePressEvent(QMouseEvent *io_event); 46 | //----------------------------------------------------------------------------------------------------- 47 | /// @brief Used to handle a mouse button release, will get delegated to the scene. 48 | /// @param [io] io_event contains the mouse button that was released. 49 | //----------------------------------------------------------------------------------------------------- 50 | void mouseReleaseEvent(QMouseEvent *io_event); 51 | //----------------------------------------------------------------------------------------------------- 52 | /// @brief The Qt UI form. 53 | //----------------------------------------------------------------------------------------------------- 54 | Ui::MainWindow m_ui; 55 | //----------------------------------------------------------------------------------------------------- 56 | /// @brief A pointer to the scene that should be placed in the central widget. 57 | //----------------------------------------------------------------------------------------------------- 58 | std::shared_ptr m_scene = nullptr; 59 | 60 | }; 61 | 62 | #endif // MAINWINDOW_H 63 | -------------------------------------------------------------------------------- /demo/include/Material.h: -------------------------------------------------------------------------------- 1 | #ifndef MATERIAL_H 2 | #define MATERIAL_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "ShaderLib.h" 9 | #include "Camera.h" 10 | 11 | class Material 12 | { 13 | public: 14 | //----------------------------------------------------------------------------------------------------- 15 | /// @brief Constructor 16 | /// @param [io] io_camera is the camera that is viewing the material 17 | /// @param [io] io_shaderLib contains the shader program that this material affects. 18 | /// @param [io] io_matrices is a pointer to the scene matrices used for the vertex shader. 19 | //----------------------------------------------------------------------------------------------------- 20 | Material( 21 | const std::shared_ptr &io_camera, 22 | const std::shared_ptr &io_shaderLib, 23 | std::array* io_matrices 24 | ) : 25 | m_shaderLib(io_shaderLib), 26 | m_matrices(io_matrices), 27 | m_cam(io_camera) 28 | {} 29 | //----------------------------------------------------------------------------------------------------- 30 | /// @brief Default copy constructor. 31 | //----------------------------------------------------------------------------------------------------- 32 | Material(const Material&) = default; 33 | //----------------------------------------------------------------------------------------------------- 34 | /// @brief Default copy assignment operator. 35 | //----------------------------------------------------------------------------------------------------- 36 | Material& operator=(const Material&) = default; 37 | //----------------------------------------------------------------------------------------------------- 38 | /// @brief Default move constructor. 39 | //----------------------------------------------------------------------------------------------------- 40 | Material(Material&&) = default; 41 | //----------------------------------------------------------------------------------------------------- 42 | /// @brief Default move assignment operator. 43 | //----------------------------------------------------------------------------------------------------- 44 | Material& operator=(Material&&) = default; 45 | //----------------------------------------------------------------------------------------------------- 46 | /// @brief Default virtual destructor. 47 | //----------------------------------------------------------------------------------------------------- 48 | virtual ~Material(); 49 | //----------------------------------------------------------------------------------------------------- 50 | /// @brief Used to intialise a passed shader, subclasses must call this base function. 51 | //----------------------------------------------------------------------------------------------------- 52 | virtual void init() = 0; 53 | //----------------------------------------------------------------------------------------------------- 54 | /// @brief Used to set the name of the shader that this material should be applied to. 55 | //----------------------------------------------------------------------------------------------------- 56 | void setShaderName(const std::string &_name); 57 | //----------------------------------------------------------------------------------------------------- 58 | /// @brief Used to update shader values. 59 | //----------------------------------------------------------------------------------------------------- 60 | virtual void update() = 0; 61 | //----------------------------------------------------------------------------------------------------- 62 | /// @brief Used to set this as the active shader, and pass the uniform values stored in this material. 63 | //----------------------------------------------------------------------------------------------------- 64 | void apply(); 65 | //----------------------------------------------------------------------------------------------------- 66 | /// @brief The file name of the json shader file that this material works with. 67 | //----------------------------------------------------------------------------------------------------- 68 | virtual const char* shaderFileName() const = 0; 69 | 70 | virtual void handleKey(QKeyEvent* io_event, QOpenGLContext* io_context); 71 | 72 | protected: 73 | //----------------------------------------------------------------------------------------------------- 74 | /// @brief A pointer to the central Shader Library. 75 | //----------------------------------------------------------------------------------------------------- 76 | std::shared_ptr m_shaderLib = nullptr; 77 | //----------------------------------------------------------------------------------------------------- 78 | /// @brief Unique id of the shader within the shader library, that this material affects. 79 | //----------------------------------------------------------------------------------------------------- 80 | std::string m_shaderName; 81 | //----------------------------------------------------------------------------------------------------- 82 | /// @brief A pointer to matrices this material should use for the vertex shader. 83 | //----------------------------------------------------------------------------------------------------- 84 | std::array* m_matrices = nullptr; 85 | //----------------------------------------------------------------------------------------------------- 86 | /// @brief A pointer to the camera that is viewing the material. 87 | //----------------------------------------------------------------------------------------------------- 88 | std::shared_ptr m_cam = nullptr; 89 | }; 90 | 91 | #endif // MATERIAL_H 92 | -------------------------------------------------------------------------------- /demo/include/MaterialCSBpbr.h: -------------------------------------------------------------------------------- 1 | #ifndef MATERIALCSBPBR_H 2 | #define MATERIALCSBPBR_H 3 | 4 | #include "Material.h" 5 | 6 | class MaterialCSBpbr : public Material 7 | { 8 | public: 9 | MaterialCSBpbr( 10 | const std::shared_ptr &io_camera, 11 | const std::shared_ptr &io_shaderLib, 12 | std::array* io_matrices, 13 | const glm::vec3 &_albedo, 14 | const float _ao, 15 | const float _exposure, 16 | const float _roughness, 17 | const float _metallic 18 | ) : 19 | Material(io_camera, io_shaderLib, io_matrices), 20 | m_albedo(_albedo), 21 | m_ao(_ao), 22 | m_exposure(_exposure), 23 | m_roughness(_roughness), 24 | m_metallic(_metallic) 25 | {} 26 | MaterialCSBpbr(const MaterialCSBpbr&) = default; 27 | MaterialCSBpbr& operator=(const MaterialCSBpbr&) = default; 28 | MaterialCSBpbr(MaterialCSBpbr&&) = default; 29 | MaterialCSBpbr& operator=(MaterialCSBpbr&&) = default; 30 | ~MaterialCSBpbr() override = default; 31 | 32 | virtual void init() override; 33 | 34 | virtual void update() override; 35 | 36 | virtual const char* shaderFileName() const override; 37 | 38 | 39 | private: 40 | glm::vec3 m_albedo; 41 | float m_ao; 42 | float m_exposure; 43 | float m_roughness; 44 | float m_metallic; 45 | 46 | }; 47 | 48 | #endif // MATERIALCSBPBR_H 49 | -------------------------------------------------------------------------------- /demo/include/MaterialEnvMap.h: -------------------------------------------------------------------------------- 1 | #ifndef MATERIALENVMAP_H 2 | #define MATERIALENVMAP_H 3 | 4 | #include "Material.h" 5 | #include 6 | #include 7 | 8 | class Camera; 9 | 10 | class MaterialEnvMap : public Material 11 | { 12 | public: 13 | MaterialEnvMap(const std::shared_ptr &io_camera, const std::shared_ptr &io_shaderLib, std::array* io_matrices) : 14 | Material(io_camera, io_shaderLib, io_matrices) 15 | {} 16 | MaterialEnvMap(const MaterialEnvMap&) = default; 17 | MaterialEnvMap& operator=(const MaterialEnvMap&) = default; 18 | MaterialEnvMap(MaterialEnvMap&&) = default; 19 | MaterialEnvMap& operator=(MaterialEnvMap&&) = default; 20 | ~MaterialEnvMap() override = default; 21 | 22 | virtual void init() override; 23 | 24 | virtual void update() override; 25 | 26 | virtual const char* shaderFileName() const override; 27 | 28 | private: 29 | void initEnvMap(); 30 | void initGlossMap(); 31 | std::unique_ptr m_envMap; 32 | std::unique_ptr m_glossMap; 33 | 34 | }; 35 | 36 | #endif // MATERIALENVMAP_H 37 | -------------------------------------------------------------------------------- /demo/include/MaterialFractal.h: -------------------------------------------------------------------------------- 1 | #ifndef MATERIALFRACTAL_H 2 | #define MATERIALFRACTAL_H 3 | 4 | #include "Material.h" 5 | #include 6 | 7 | class Camera; 8 | 9 | class MaterialFractal : public Material 10 | { 11 | public: 12 | MaterialFractal(const std::shared_ptr &io_camera, const std::shared_ptr &io_shaderLib, std::array* io_matrices) : 13 | Material(io_camera, io_shaderLib, io_matrices) 14 | {} 15 | MaterialFractal(const MaterialFractal&) = default; 16 | MaterialFractal& operator=(const MaterialFractal&) = default; 17 | MaterialFractal(MaterialFractal&&) = default; 18 | MaterialFractal& operator=(MaterialFractal&&) = default; 19 | ~MaterialFractal() override = default; 20 | 21 | virtual void init() override; 22 | 23 | virtual void update() override; 24 | 25 | virtual const char* shaderFileName() const override; 26 | 27 | virtual void handleKey(QKeyEvent* io_event, QOpenGLContext* io_context) override; 28 | 29 | private: 30 | std::chrono::high_resolution_clock::time_point m_last; 31 | float m_time = 0.0f; 32 | bool m_updateTime = true; 33 | }; 34 | 35 | 36 | #endif // MATERIALFRACTAL_H 37 | -------------------------------------------------------------------------------- /demo/include/MaterialPBR.h: -------------------------------------------------------------------------------- 1 | #ifndef MATERIALPBR_H 2 | #define MATERIALPBR_H 3 | 4 | #include "Material.h" 5 | #include "vec3.hpp" 6 | 7 | class MaterialPBR : public Material 8 | { 9 | public: 10 | MaterialPBR( 11 | const std::shared_ptr &io_camera, 12 | const std::shared_ptr &io_shaderLib, 13 | std::array* io_matrices, 14 | const glm::vec3 &_albedo, 15 | const float _ao, 16 | const float _exposure, 17 | const float _roughness, 18 | const float _metallic 19 | ) : 20 | Material(io_camera, io_shaderLib, io_matrices), 21 | m_albedo(_albedo), 22 | m_ao(_ao), 23 | m_exposure(_exposure), 24 | m_roughness(_roughness), 25 | m_metallic(_metallic) 26 | {} 27 | MaterialPBR(const MaterialPBR&) = default; 28 | MaterialPBR& operator=(const MaterialPBR&) = default; 29 | MaterialPBR(MaterialPBR&&) = default; 30 | MaterialPBR& operator=(MaterialPBR&&) = default; 31 | ~MaterialPBR() override = default; 32 | 33 | virtual void init() override; 34 | 35 | virtual void update() override; 36 | 37 | virtual const char* shaderFileName() const override; 38 | 39 | 40 | private: 41 | glm::vec3 m_albedo; 42 | float m_ao; 43 | float m_exposure; 44 | float m_roughness; 45 | float m_metallic; 46 | 47 | }; 48 | 49 | #endif // MATERIALPBR_H 50 | -------------------------------------------------------------------------------- /demo/include/MaterialPhong.h: -------------------------------------------------------------------------------- 1 | #ifndef MATERIALPHONG_H 2 | #define MATERIALPHONG_H 3 | 4 | #include "Material.h" 5 | 6 | class Camera; 7 | 8 | class MaterialPhong : public Material 9 | { 10 | public: 11 | MaterialPhong(const std::shared_ptr &io_camera, const std::shared_ptr &io_shaderLib, std::array* io_matrices) : 12 | Material(io_camera, io_shaderLib, io_matrices) 13 | {} 14 | MaterialPhong(const MaterialPhong&) = default; 15 | MaterialPhong& operator=(const MaterialPhong&) = default; 16 | MaterialPhong(MaterialPhong&&) = default; 17 | MaterialPhong& operator=(MaterialPhong&&) = default; 18 | ~MaterialPhong() override = default; 19 | 20 | virtual void init() override; 21 | 22 | virtual void update() override; 23 | 24 | virtual const char* shaderFileName() const override; 25 | 26 | }; 27 | 28 | #endif // MATERIALPHONG_H 29 | -------------------------------------------------------------------------------- /demo/include/MaterialWireframe.h: -------------------------------------------------------------------------------- 1 | #ifndef MATERIALWIREFRAME_H 2 | #define MATERIALWIREFRAME_H 3 | 4 | #include "Material.h" 5 | 6 | class Camera; 7 | 8 | class MaterialWireframe : public Material 9 | { 10 | public: 11 | MaterialWireframe(const std::shared_ptr &io_camera, const std::shared_ptr &io_shaderLib, std::array* io_matrices) : 12 | Material(io_camera, io_shaderLib, io_matrices) 13 | {} 14 | MaterialWireframe(const MaterialWireframe&) = default; 15 | MaterialWireframe& operator=(const MaterialWireframe&) = default; 16 | MaterialWireframe(MaterialWireframe&&) = default; 17 | MaterialWireframe& operator=(MaterialWireframe&&) = default; 18 | ~MaterialWireframe() override = default; 19 | 20 | virtual void init() override; 21 | 22 | virtual void update() override; 23 | 24 | virtual const char* shaderFileName() const override; 25 | 26 | }; 27 | 28 | #endif // MATERIALWIREFRAME_H 29 | -------------------------------------------------------------------------------- /demo/include/Scene.h: -------------------------------------------------------------------------------- 1 | #ifndef SCENE_H_ 2 | #define SCENE_H_ 3 | 4 | #include "TriMesh.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "MeshVBO.h" 14 | #include "Camera.h" 15 | 16 | 17 | //------------------------------------------------------------------------------------------------------- 18 | /// @brief Enum used to access matrices in a more readable way. 19 | //------------------------------------------------------------------------------------------------------- 20 | namespace SceneMatrices 21 | { 22 | enum MATRIX { MODEL_VIEW, PROJECTION, NORMAL }; 23 | } 24 | 25 | class Scene : public QOpenGLWidget, protected QOpenGLFunctions 26 | { 27 | Q_OBJECT 28 | public: 29 | //----------------------------------------------------------------------------------------------------- 30 | /// @brief Constructor for Scene. 31 | /// @param [io] io_camera the camera used to view the scene. 32 | /// @param [io] io_parent the parent window to create the GL context in. 33 | //----------------------------------------------------------------------------------------------------- 34 | Scene(const std::shared_ptr &io_camera, QWidget *io_parent); 35 | //----------------------------------------------------------------------------------------------------- 36 | /// @brief Default copy constructor. 37 | //----------------------------------------------------------------------------------------------------- 38 | Scene(const Scene&) = default; 39 | //----------------------------------------------------------------------------------------------------- 40 | /// @brief Default copy assignment operator. 41 | //----------------------------------------------------------------------------------------------------- 42 | Scene& operator=(const Scene&) = default; 43 | //----------------------------------------------------------------------------------------------------- 44 | /// @brief Default move constructor. 45 | //----------------------------------------------------------------------------------------------------- 46 | Scene(Scene&&) = default; 47 | //----------------------------------------------------------------------------------------------------- 48 | /// @brief Default move assignment operator. 49 | //----------------------------------------------------------------------------------------------------- 50 | Scene& operator=(Scene&&) = default; 51 | //----------------------------------------------------------------------------------------------------- 52 | /// @brief Default virtual destructor. 53 | //----------------------------------------------------------------------------------------------------- 54 | virtual ~Scene() = default; 55 | //----------------------------------------------------------------------------------------------------- 56 | /// @brief Receives and acts on a key event. 57 | /// @param [io] io_event is the key event that was received. 58 | //----------------------------------------------------------------------------------------------------- 59 | virtual void keyPress(QKeyEvent* io_event); 60 | //----------------------------------------------------------------------------------------------------- 61 | /// @brief Receives and acts on a mouse event, when moved. 62 | /// @param [io] io_event is the mouse event that was received. 63 | //----------------------------------------------------------------------------------------------------- 64 | virtual void mouseMove(QMouseEvent * io_event); 65 | //----------------------------------------------------------------------------------------------------- 66 | /// @brief Receives and acts on a mouse event, when clicked. 67 | /// @param [io] io_event is the mouse event that was received. 68 | //----------------------------------------------------------------------------------------------------- 69 | virtual void mouseClick(QMouseEvent * io_event); 70 | //----------------------------------------------------------------------------------------------------- 71 | /// @brief Used to intialise the scene, subclasses must call this base function. 72 | //----------------------------------------------------------------------------------------------------- 73 | virtual void init(); 74 | 75 | protected: 76 | //----------------------------------------------------------------------------------------------------- 77 | /// @brief Used to intialise the OpenGL context and apply our settings. 78 | //----------------------------------------------------------------------------------------------------- 79 | void initializeGL(); 80 | //----------------------------------------------------------------------------------------------------- 81 | /// @brief this is called whenever the window is re-sized 82 | /// @param [in] _w the width of the resized window 83 | /// @param [in] _h the height of the resized window 84 | //----------------------------------------------------------------------------------------------------- 85 | void resizeGL(int _w , int _h); 86 | //----------------------------------------------------------------------------------------------------- 87 | /// @brief this is the main gl drawing routine which is called whenever the window needs to be re-drawn, 88 | /// you should implement your draw calls in renderScene as this function calls those. 89 | //----------------------------------------------------------------------------------------------------- 90 | void paintGL(); 91 | //----------------------------------------------------------------------------------------------------- 92 | /// @brief you should implement this function to draw your openGL scene, the start of your, 93 | /// implementation should call this base function. 94 | //----------------------------------------------------------------------------------------------------- 95 | virtual void renderScene(); 96 | //----------------------------------------------------------------------------------------------------- 97 | /// @brief Array of matrices, that stores the model view, projection, and normal matrices. 98 | //----------------------------------------------------------------------------------------------------- 99 | std::array m_matrices; 100 | //----------------------------------------------------------------------------------------------------- 101 | /// @brief a pointer to the camera that is used to view this scene. 102 | //----------------------------------------------------------------------------------------------------- 103 | std::shared_ptr m_camera; 104 | 105 | }; 106 | 107 | #endif //SCENE_H_ 108 | -------------------------------------------------------------------------------- /demo/include/ShaderLib.h: -------------------------------------------------------------------------------- 1 | #ifndef SHADERLIB_H 2 | #define SHADERLIB_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | class ShaderLib 10 | { 11 | public: 12 | //----------------------------------------------------------------------------------------------------- 13 | /// @brief Creates a shader program from a json file, by extracting the path of all required glsl 14 | /// shaders for that program, compiling, attaching and linking them. 15 | /// @param [in] _jsonFileName is the path to the json file. 16 | /// @return is the name that this shader is stored under. 17 | //----------------------------------------------------------------------------------------------------- 18 | std::string loadShaderProg(const QString &_jsonFileName); 19 | //----------------------------------------------------------------------------------------------------- 20 | /// @brief Creates a shader program and loads a vertex and fragment shader, attaching both. 21 | /// @param [in] _name is the name that this shader program should be stored under. 22 | /// @param [in] _shaderPaths contains paths to the vertex, fragment, and geometry shaders in that order, 23 | /// any paths left blank are ignored. 24 | //----------------------------------------------------------------------------------------------------- 25 | void createShader(const std::string &_name, const std::array &_shaderPaths); 26 | //----------------------------------------------------------------------------------------------------- 27 | /// @brief Binds a stored shader. 28 | /// @param [in] _name is the name of the shader program that should be bound. 29 | //----------------------------------------------------------------------------------------------------- 30 | void useShader(const std::string& _name); 31 | //----------------------------------------------------------------------------------------------------- 32 | /// @brief Accesses a stored shader program. 33 | /// @param [in] _name is the name of the shader program should be accessed. 34 | /// @return a pointer to the specified shader. 35 | //----------------------------------------------------------------------------------------------------- 36 | QOpenGLShaderProgram* getShader(const std::string& _name); 37 | //----------------------------------------------------------------------------------------------------- 38 | /// @brief Accesses the currently bound shader program. 39 | /// @return a pointer to the currently bound shader program. 40 | //----------------------------------------------------------------------------------------------------- 41 | QOpenGLShaderProgram* getCurrentShader(); 42 | 43 | private: 44 | enum SHADER_TYPES {VERTEX, FRAGMENT, GEOMETRY}; 45 | //----------------------------------------------------------------------------------------------------- 46 | /// @brief A map from shader name to shader program, so that they can be reused and easily bound. 47 | //----------------------------------------------------------------------------------------------------- 48 | std::unordered_map> m_shaderPrograms; 49 | //----------------------------------------------------------------------------------------------------- 50 | /// @brief A map from shader name to shader, so that they can be reused by shader programs. 51 | //----------------------------------------------------------------------------------------------------- 52 | std::unordered_map> m_shaderParts; 53 | //----------------------------------------------------------------------------------------------------- 54 | /// @brief A pointer to the currently bound shader program. 55 | //----------------------------------------------------------------------------------------------------- 56 | QOpenGLShaderProgram* m_currentShader; 57 | }; 58 | 59 | #endif // SHADERLIB_H 60 | -------------------------------------------------------------------------------- /demo/models/cube.obj: -------------------------------------------------------------------------------- 1 | # This file uses centimeters as units for non-parametric coordinates. 2 | 3 | v -1.0 -1.0 1.0 4 | v 1.0 -1.0 1.0 5 | v -1.0 1.0 1.0 6 | v 1.0 1.0 1.0 7 | v -1.0 1.0 -1.0 8 | v 1.0 1.0 -1.0 9 | v -1.0 -1.0 -1.0 10 | v 1.0 -1.0 -1.0 11 | f 1/1 2/2 3/3 12 | f 3/3 2/2 4/4 13 | f 3/3 4/4 5/5 14 | f 5/5 4/4 6/6 15 | f 5/5 6/6 7/7 16 | f 7/7 6/6 8/8 17 | f 7/7 8/8 1/9 18 | f 1/9 8/8 2/10 19 | f 2/2 8/11 4/4 20 | f 4/4 8/11 6/12 21 | f 7/13 1/1 5/14 22 | f 5/14 1/1 3/3 23 | -------------------------------------------------------------------------------- /demo/models/rayTest.obj: -------------------------------------------------------------------------------- 1 | # This file uses centimeters as units for non-parametric coordinates. 2 | 3 | v -0.072169 0.250000 0.125000 4 | v -0.072000 0.250000 -0.125000 5 | v 0.144338 0.250000 0.000000 6 | v 0.000000 -0.250000 0.000000 7 | v -0.288675 -0.500000 -0.500000 8 | v -0.288675 -0.500000 0.500000 9 | v 0.577350 -0.500000 0.000000 10 | f 1/1 3/3 2/2 11 | f 1/4 2/5 4/8 12 | f 2/5 3/6 4/8 13 | f 3/6 1/7 4/8 14 | f 5/9 6/10 7/11 15 | -------------------------------------------------------------------------------- /demo/models/xyplane.obj: -------------------------------------------------------------------------------- 1 | # This file uses centimeters as units for non-parametric coordinates. 2 | 3 | v -0.500000 -0.000000 0.500000 4 | v 0.500000 -0.000000 0.500000 5 | v -0.500000 0.000000 -0.500000 6 | v 0.500000 0.000000 -0.500000 7 | vt 0.000000 0.000000 8 | vt 1.000000 0.000000 9 | vt 0.000000 1.000000 10 | vt 1.000000 1.000000 11 | f 1/1 2/2 4/4 3/3 12 | -------------------------------------------------------------------------------- /demo/shaderPrograms/csbPBR.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name" : "csb_pbr_hard", 3 | "Vertex" : "shaders/csb_pbr_vertex.glsl", 4 | "Geometry" : "shaders/csb_hard_normals.glsl", 5 | "Fragment" : "shaders/csb_pbr_frag.glsl" 6 | } -------------------------------------------------------------------------------- /demo/shaderPrograms/fractal.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name" : "Fractal", 3 | "Vertex" : "shaders/FractalVertex.glsl", 4 | "Fragment" : "shaders/FractalFragment.glsl" 5 | } -------------------------------------------------------------------------------- /demo/shaderPrograms/wireframe.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name" : "Wireframe", 3 | "Vertex" : "shaders/WireframeVertex.glsl", 4 | "Fragment" : "shaders/WireframeFragment.glsl", 5 | "Geometry" : "shaders/WireframeGeometry.glsl" 6 | } -------------------------------------------------------------------------------- /demo/shaders/FractalFragment.glsl: -------------------------------------------------------------------------------- 1 | #version 420 // Keeping you on the bleeding edge! 2 | #extension GL_EXT_gpu_shader4 : enable 3 | 4 | layout (location=0) out vec4 fragColor; 5 | 6 | // The uv coordinates of this fragment 7 | in vec2 FragmentUV; 8 | 9 | // The time elapsed (by default there is no animation) 10 | uniform float t = 0.0; 11 | 12 | // The number of iterations of this fractal 13 | uniform int iter = 100; 14 | 15 | // The signature for our fractal function 16 | subroutine vec3 fractalFunctionType(vec2); 17 | 18 | // This uniform variable indicates which fractal function to use 19 | subroutine uniform fractalFunctionType fractalFunction; 20 | 21 | // Define our color palette to make a nice colour ramp 22 | uniform vec3 ColorPalette[4] = vec3[](vec3(0.5, 0.5, 0.5), vec3(0.5, 0.5, 0.5), vec3(1.0, 1.0, 1.0), vec3(0.00, 0.10, 0.20)); 23 | //uniform vec3 ColorPalette[4] = vec3[](vec3(0.5, 0.5, 0.5), vec3(0.5, 0.5, 0.5), vec3(1.0, 1.0, 1.0), vec3(0.00, 0.33, 0.67)); 24 | 25 | // Cosine based palette from http://iquilezles.org/www/articles/palettes/palettes.htm 26 | vec3 Palette(in float t) { 27 | return ColorPalette[0] + ColorPalette[1]*cos( 6.28318*(ColorPalette[2]*t+ColorPalette[3]) ); 28 | } 29 | 30 | // The inner loop of both fractal subroutines is shared (from http://nuclear.mutantstargoat.com/articles/sdr_fract/) 31 | vec3 evalFractal(in vec2 z, in vec2 c) { 32 | int i; 33 | for(i=0; i 4.0) break; 37 | z.x = x; 38 | z.y = y; 39 | } 40 | return Palette(((i == iter) ? 0.0 : float(i)) / iter); 41 | } 42 | 43 | // Calculate the Julia Fractal (from http://nuclear.mutantstargoat.com/articles/sdr_fract/) 44 | subroutine (fractalFunctionType) vec3 JuliaFunction(vec2 pos) { 45 | vec2 c; 46 | // This evaluates the seed based on the elapsed time 47 | c.x = (sin(cos(t *0.1) * 10.0) + cos(t * 2.0)*0.25 + sin(t * 3.0) * 0.1667) * 0.8; 48 | c.y = (cos(sin(t *0.1) * 10.0) + sin(t * 2.0)*0.25 + cos(t * 3.0) * 0.1667) * 0.8; 49 | return evalFractal(vec2(3.0,2.0)*pos, c); 50 | } 51 | 52 | // Calculate Mandelbrot Fractal (from http://nuclear.mutantstargoat.com/articles/sdr_fract/) 53 | subroutine (fractalFunctionType) vec3 MandelbrotFunction(vec2 pos) { 54 | float mixt = fract(t); 55 | vec2 center = vec2(mix(1.0, 1.31, mixt), 0.0); 56 | float scale = mix(0.5, 0.12, mixt); 57 | vec2 c = vec2(1.3333,1.0) * pos * scale - center; 58 | return evalFractal(c,c); 59 | } 60 | 61 | void main() { 62 | // Determine the fractal position from the fragment UV coordinates (these normally within [0,1]) 63 | vec2 fracpos = FragmentUV - vec2(0.5); 64 | 65 | // Now execute the use specified blur function on this pixel based on the depth difference 66 | fragColor = vec4(fractalFunction(fracpos), 1.0); 67 | //fragColor = vec4(Palette(FragmentUV.x), 1.0); 68 | } 69 | -------------------------------------------------------------------------------- /demo/shaders/FractalVertex.glsl: -------------------------------------------------------------------------------- 1 | #version 420 2 | #extension GL_EXT_gpu_shader4 : enable 3 | 4 | /// @brief the in vertex 5 | layout (location = 0) in vec3 inVert; 6 | /// @brief the in uv 7 | layout (location = 1) in vec2 inUV; 8 | /// @brief the normal passed in 9 | layout (location = 2) in vec3 inNormal; 10 | 11 | 12 | // We need an MVP because the plane needs to be rotated 13 | uniform mat4 MVP; 14 | uniform mat4 N; 15 | uniform mat4 M; 16 | 17 | // Pass through the UV coordinates 18 | out vec2 FragmentUV; 19 | 20 | void main() { 21 | // Pass through the vertex UV's to be interpolated through rasterizations 22 | FragmentUV = inUV; 23 | 24 | // Set the position of the current vertex 25 | gl_Position = MVP * vec4(inVert, 1.0); 26 | } 27 | 28 | -------------------------------------------------------------------------------- /demo/shaders/WireframeFragment.glsl: -------------------------------------------------------------------------------- 1 | #version 420 // Keeping you on the bleeding edge! 2 | #extension GL_EXT_gpu_shader4 : enable 3 | 4 | // This is no longer a built-in variable 5 | layout(location=0) out vec4 FragColor; 6 | 7 | in FragData 8 | { 9 | vec3 vert; 10 | vec3 norm; 11 | vec2 uv; 12 | vec3 wireframeDist; 13 | } inData; 14 | 15 | void main(void) 16 | { 17 | vec3 d = fwidth(inData.wireframeDist); 18 | 19 | vec3 a3 = smoothstep(vec3(0.0), d * 1.5, inData.wireframeDist); 20 | float edgeFactor = min(min(a3.x, a3.y), a3.z); 21 | 22 | FragColor = vec4(1.0, 1.0, 1.0, (1.0-edgeFactor)*0.95); 23 | } 24 | -------------------------------------------------------------------------------- /demo/shaders/WireframeGeometry.glsl: -------------------------------------------------------------------------------- 1 | #version 420 // Keeping you on the bleeding edge! 2 | #extension GL_EXT_gpu_shader4 : enable 3 | 4 | layout (triangles) in; 5 | layout (line_strip, max_vertices = 4) out; 6 | 7 | in VertexData 8 | { 9 | vec3 vert; 10 | vec3 norm; 11 | vec2 uv; 12 | } inData[]; 13 | 14 | 15 | out FragData 16 | { 17 | vec3 vert; 18 | vec3 norm; 19 | vec2 uv; 20 | noperspective vec3 wireframeDist; 21 | } outData; 22 | 23 | 24 | void main() 25 | { 26 | for (int i = 0; i < 4; ++i) 27 | { 28 | int index = i % 3; 29 | gl_Position = gl_in[index].gl_Position; 30 | outData.vert = inData[index].vert; 31 | outData.norm = inData[index].norm; 32 | outData.uv = inData[index].uv; 33 | outData.wireframeDist = vec3(0.0); 34 | outData.wireframeDist[index] = 1.0; 35 | EmitVertex(); 36 | } 37 | EndPrimitive(); 38 | } 39 | -------------------------------------------------------------------------------- /demo/shaders/WireframeVertex.glsl: -------------------------------------------------------------------------------- 1 | #version 420 // Keeping you on the bleeding edge! 2 | #extension GL_EXT_gpu_shader4 : enable 3 | 4 | /// @brief the vertex passed in 5 | layout (location = 0) in vec3 inVert; 6 | /// @brief the in uv 7 | layout (location = 1) in vec2 inUV; 8 | 9 | uniform mat4 MVP; 10 | uniform mat4 N; 11 | uniform mat4 M; 12 | 13 | void main() 14 | { 15 | gl_Position = MVP * vec4(inVert, 1.0); 16 | } 17 | -------------------------------------------------------------------------------- /demo/shaders/csb_hard_normals.glsl: -------------------------------------------------------------------------------- 1 | #version 420 // Keeping you on the bleeding edge! 2 | #extension GL_EXT_gpu_shader4 : enable 3 | 4 | 5 | layout(triangles) in; 6 | layout(triangle_strip, max_vertices=3) out; 7 | 8 | in VertexData 9 | { 10 | vec3 vert; 11 | vec2 uv; 12 | } inData[]; 13 | 14 | out FragData 15 | { 16 | vec3 vert; 17 | vec3 norm; 18 | vec2 uv; 19 | } outData; 20 | 21 | uniform mat4 MVP; 22 | uniform vec3 camPos; 23 | 24 | void main(void) 25 | { 26 | vec3 norm = normalize(cross(gl_in[2].gl_Position.xyz - gl_in[0].gl_Position.xyz, 27 | gl_in[1].gl_Position.xyz - gl_in[0].gl_Position.xyz)); 28 | 29 | for (int i = 0; i < 3; ++i) 30 | { 31 | gl_Position = gl_in[i].gl_Position; 32 | outData.vert = inData[i].vert; 33 | outData.uv = inData[i].uv; 34 | outData.norm = norm; 35 | 36 | EmitVertex(); 37 | } 38 | EndPrimitive(); 39 | } 40 | -------------------------------------------------------------------------------- /demo/shaders/csb_pbr_frag.glsl: -------------------------------------------------------------------------------- 1 | #version 420 // Keeping you on the bleeding edge! 2 | #extension GL_EXT_gpu_shader4 : enable 3 | // This code is based on code from here https://learnopengl.com/#!PBR/Lighting 4 | layout (location = 0) out vec4 fragColour; 5 | 6 | in FragData 7 | { 8 | vec3 vert; 9 | vec3 norm; 10 | vec2 uv; 11 | } inData; 12 | 13 | // material parameters 14 | uniform vec3 albedo; 15 | uniform float metallic; 16 | uniform float roughness; 17 | uniform float ao; 18 | // camera parameters 19 | uniform vec3 camPos; 20 | uniform float exposure; 21 | 22 | // lights 23 | const float scale = 10.0f; 24 | const float height = 4.0f; 25 | const vec3 lightPositions[4] = vec3[4]( 26 | vec3(-scale, height, -scale), 27 | vec3( scale, height, -scale), 28 | vec3(-scale, height, scale), 29 | vec3( scale, height, scale) 30 | ); 31 | 32 | const float intensity = 100.f; 33 | const vec3 lightColors[4] = vec3[4]( 34 | vec3(intensity, intensity, intensity), 35 | vec3(intensity, intensity, intensity), 36 | vec3(intensity, intensity, intensity), 37 | vec3(intensity, intensity, intensity) 38 | ); 39 | 40 | // Define pi 41 | const float PI = 3.14159265359; 42 | // ---------------------------------------------------------------------------- 43 | float DistributionGGX(vec3 N, vec3 H, float roughness) 44 | { 45 | float a = roughness*roughness; 46 | float a2 = a*a; 47 | float NdotH = max(dot(N, H), 0.0); 48 | float NdotH2 = NdotH*NdotH; 49 | 50 | float nom = a2; 51 | float denom = (NdotH2 * (a2 - 1.0) + 1.0); 52 | denom = PI * denom * denom; 53 | 54 | return nom / denom; 55 | } 56 | // ---------------------------------------------------------------------------- 57 | float GeometrySchlickGGX(float NdotV, float roughness) 58 | { 59 | float r = (roughness + 1.0); 60 | float k = (r*r) / 8.0; 61 | 62 | float nom = NdotV; 63 | float denom = NdotV * (1.0 - k) + k; 64 | 65 | return nom / denom; 66 | } 67 | // ---------------------------------------------------------------------------- 68 | float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness) 69 | { 70 | float NdotV = max(dot(N, V), 0.0); 71 | float NdotL = max(dot(N, L), 0.0); 72 | float ggx2 = GeometrySchlickGGX(NdotV, roughness); 73 | float ggx1 = GeometrySchlickGGX(NdotL, roughness); 74 | 75 | return ggx1 * ggx2; 76 | } 77 | // ---------------------------------------------------------------------------- 78 | vec3 fresnelSchlick(float cosTheta, vec3 F0) 79 | { 80 | return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0); 81 | } 82 | // ---------------------------------------------------------------------------- 83 | void main() 84 | { 85 | vec3 N = normalize(inData.norm); 86 | vec3 V = normalize(camPos - inData.vert); 87 | vec3 R = reflect(-V, N); 88 | 89 | // calculate reflectance at normal incidence; if dia-electric (like plastic) use F0 90 | // of 0.04 and if it's a metal, use their albedo color as F0 (metallic workflow) 91 | vec3 F0 = vec3(0.04); 92 | F0 = mix(F0, albedo, metallic); 93 | 94 | // reflectance equation 95 | vec3 Lo = vec3(0.0); 96 | for(int i = 0; i < 4; ++i) 97 | { 98 | vec3 trans = vec3(0.0, 0.0, -2.0); 99 | vec3 ray = lightPositions[i] - inData.vert + trans; 100 | // calculate per-light radiance 101 | vec3 L = normalize(ray); 102 | vec3 H = normalize(V + L); 103 | float dist = length(ray); 104 | float attenuation = 1.0 / (dist * dist); 105 | vec3 radiance = lightColors[i] * attenuation; 106 | 107 | // Cook-Torrance BRDF 108 | float NDF = DistributionGGX(N, H, roughness); 109 | float G = GeometrySmith(N, V, L, roughness); 110 | vec3 F = fresnelSchlick(max(dot(H, V), 0.0), F0); 111 | 112 | vec3 nominator = NDF * G * F; 113 | float denominator = 4 * max(dot(V, N), 0.0) * max(dot(L, N), 0.0) + 0.001; // 0.001 to prevent divide by zero. 114 | vec3 brdf = nominator / denominator; 115 | 116 | // kS is equal to Fresnel 117 | vec3 kS = F; 118 | // for energy conservation, the diffuse and specular light can't 119 | // be above 1.0 (unless the surface emits light); to preserve this 120 | // relationship the diffuse component (kD) should equal 1.0 - kS. 121 | vec3 kD = vec3(1.0) - kS; 122 | // multiply kD by the inverse metalness such that only non-metals 123 | // have diffuse lighting, or a linear blend if partly metal (pure metals 124 | // have no diffuse light). 125 | kD *= 1.0 - metallic; 126 | 127 | // scale light by NdotL 128 | float NdotL = max(dot(N, L), 0.0); 129 | 130 | // add to outgoing radiance Lo 131 | Lo += (kD * albedo / PI + brdf) * radiance * NdotL; // note that we already multiplied the BRDF by the Fresnel (kS) so we won't multiply by kS again 132 | } 133 | 134 | // ambient lighting (note that the next IBL tutorial will replace 135 | // this ambient lighting with environment lighting). 136 | vec3 ambient = vec3(0.03) * albedo * ao; 137 | 138 | vec3 color = ambient + Lo; 139 | 140 | // HDR tonemapping 141 | color = color / (color + vec3(1.0)); 142 | // gamma correct 143 | color = pow(color, vec3(1.0/2.2)); 144 | 145 | fragColour = vec4(color, 1.0); 146 | } 147 | -------------------------------------------------------------------------------- /demo/shaders/csb_pbr_vertex.glsl: -------------------------------------------------------------------------------- 1 | #version 420 // Keeping you on the bleeding edge! 2 | #extension GL_EXT_gpu_shader4 : enable 3 | // this demo is based on code from here https://learnopengl.com/#!PBR/Lighting 4 | 5 | /// @brief the vertex passed in 6 | layout (location = 0) in vec3 inVert; 7 | /// @brief the in uv 8 | layout (location = 1) in vec2 inUV; 9 | 10 | uniform mat4 MVP; 11 | uniform mat4 N; 12 | uniform mat4 M; 13 | 14 | void main() 15 | { 16 | gl_Position = MVP * vec4(inVert, 1.0); 17 | } 18 | -------------------------------------------------------------------------------- /demo/src/Camera.cpp: -------------------------------------------------------------------------------- 1 | #include "Camera.h" 2 | 3 | 4 | //----------------------------------------------------------------------------------------------------- 5 | void Camera::resize(const int _width, const int _height) 6 | { 7 | // Check we won't get a divide by zero 8 | m_aspectRatio = (!_height)? 1.0f : (float(_width)/float(_height)); 9 | } 10 | //----------------------------------------------------------------------------------------------------- 11 | glm::vec3 Camera::getCameraOrigin() const noexcept 12 | { 13 | return m_camOrigin; 14 | } 15 | //----------------------------------------------------------------------------------------------------- 16 | void Camera::setTarget(const float _x, const float _y, const float _z) noexcept 17 | { 18 | m_target.x = _x; 19 | m_target.y = _y; 20 | m_target.z = _z; 21 | } 22 | //----------------------------------------------------------------------------------------------------- 23 | void Camera::setOrigin(const float _x, const float _y, float _z) noexcept 24 | { 25 | m_camOrigin.x = _x; 26 | m_camOrigin.y = _y; 27 | m_camOrigin.z = _z; 28 | } 29 | //----------------------------------------------------------------------------------------------------- 30 | void Camera::update() 31 | { 32 | m_projectMatrix = glm::perspective( m_fovy, m_aspectRatio, m_nearClippingPlane, m_farClippingPlane); 33 | } 34 | //----------------------------------------------------------------------------------------------------- 35 | void Camera::setMousePos(const float _mouseX, const float _mouseY) 36 | { 37 | m_lastPos.x = _mouseX; 38 | m_lastPos.y = _mouseY; 39 | } 40 | //----------------------------------------------------------------------------------------------------- 41 | void Camera::setFov(const float _fov) 42 | { 43 | m_fovy = _fov; 44 | } 45 | //----------------------------------------------------------------------------------------------------- 46 | void Camera::resetPosition() 47 | { 48 | m_lastPos = {0.0f, 0.0f}; 49 | m_target = {0.0f, 0.0f, -2.0f}; 50 | m_camOrigin = {0.0f, 0.0f, 0.0f}; 51 | } 52 | //----------------------------------------------------------------------------------------------------- 53 | const glm::mat4& Camera::viewMatrix() 54 | { 55 | return m_viewMatrix; 56 | } 57 | //----------------------------------------------------------------------------------------------------- 58 | const glm::mat4& Camera::projMatrix() 59 | { 60 | return m_projectMatrix; 61 | } 62 | //----------------------------------------------------------------------------------------------------- 63 | 64 | -------------------------------------------------------------------------------- /demo/src/CameraStates.cpp: -------------------------------------------------------------------------------- 1 | #include "CameraStates.h" 2 | #include "Camera.h" 3 | #include 4 | #include 5 | 6 | 7 | //----------------------------------------------------------------------------------------------------- 8 | void CameraState::resetPosition(Camera* const io_camera, const int _key, const bool _isPress) 9 | { 10 | // If the F key has been pressed 11 | if (_isPress && _key == Qt::Key_F) 12 | io_camera->resetPosition(); 13 | } 14 | //----------------------------------------------------------------------------------------------------- 15 | void CameraZoom::handleMouseMove(Camera*const io_camera, const glm::vec2 &_mousePos) 16 | { 17 | // If the camera is in a zooming state, we interpret mouse movement as a zoom 18 | io_camera->mouseZoom(_mousePos); 19 | } 20 | //----------------------------------------------------------------------------------------------------- 21 | void CameraZoom::handleKey(Camera*const io_camera, const int _key, const bool _isPress) 22 | { 23 | resetPosition(io_camera, _key, _isPress); 24 | } 25 | //----------------------------------------------------------------------------------------------------- 26 | void CameraRotate::handleMouseMove(Camera*const io_camera, const glm::vec2 &_mousePos) 27 | { 28 | // If the camera is in a rotating state, we interpret mouse movement as a rotation 29 | io_camera->mouseRotate(_mousePos); 30 | } 31 | //----------------------------------------------------------------------------------------------------- 32 | void CameraRotate::handleKey(Camera*const io_camera, const int _key, const bool _isPress) 33 | { 34 | resetPosition(io_camera, _key, _isPress); 35 | } 36 | //----------------------------------------------------------------------------------------------------- 37 | void CameraPassive::handleMouseMove(Camera*const , const glm::vec2 & ) 38 | { 39 | // Passive camera's do nothing with mouse movement 40 | } 41 | //----------------------------------------------------------------------------------------------------- 42 | void CameraPassive::handleKey(Camera*const io_camera, const int _key, const bool _isPress) 43 | { 44 | resetPosition(io_camera, _key, _isPress); 45 | } 46 | //----------------------------------------------------------------------------------------------------- 47 | -------------------------------------------------------------------------------- /demo/src/MainWindow.cpp: -------------------------------------------------------------------------------- 1 | #include "MainWindow.h" 2 | 3 | 4 | void MainWindow::init(const std::shared_ptr &io_scene) 5 | { 6 | m_scene = io_scene; 7 | m_ui.setupUi(this); 8 | m_ui.s_mainWindowGridLayout->addWidget(m_scene.get(),0,0,3,5); 9 | connect(m_ui.m_rotating, SIGNAL(clicked(bool)),m_scene.get(), SLOT(rotating(bool))); 10 | connect(m_ui.m_isPaused, SIGNAL(clicked(bool)),m_scene.get(), SLOT(paused(bool))); 11 | connect(m_ui.material, SIGNAL( clicked(bool)), m_scene.get(), SLOT(nextMaterial())); 12 | } 13 | 14 | //---------------------------------------------------------------------------------------------------------------------- 15 | 16 | void MainWindow::keyPressEvent(QKeyEvent *io_event) 17 | { 18 | // this method is called every time the main window recives a key event. 19 | // we then switch on the key value and set the camera in the GLWindow 20 | switch ( io_event->key() ) 21 | { 22 | case Qt::Key_Escape : QApplication::exit(EXIT_SUCCESS); break; 23 | default : break; 24 | } 25 | m_scene->keyPress(io_event); 26 | } 27 | 28 | //---------------------------------------------------------------------------------------------------------------------- 29 | 30 | void MainWindow::mouseMoveEvent(QMouseEvent * io_event) 31 | { 32 | m_scene->mouseMove(io_event); 33 | } 34 | 35 | //---------------------------------------------------------------------------------------------------------------------- 36 | 37 | void MainWindow::mousePressEvent(QMouseEvent * io_event) 38 | { 39 | m_scene->mouseClick(io_event); 40 | } 41 | 42 | //---------------------------------------------------------------------------------------------------------------------- 43 | 44 | void MainWindow::mouseReleaseEvent(QMouseEvent * io_event) 45 | { 46 | m_scene->mouseClick(io_event); 47 | } 48 | 49 | //---------------------------------------------------------------------------------------------------------------------- 50 | -------------------------------------------------------------------------------- /demo/src/Material.cpp: -------------------------------------------------------------------------------- 1 | #include "Material.h" 2 | 3 | //----------------------------------------------------------------------------------------------------- 4 | Material::~Material() = default; 5 | //----------------------------------------------------------------------------------------------------- 6 | void Material::apply() 7 | { 8 | m_shaderLib->useShader(m_shaderName); 9 | init(); 10 | } 11 | //----------------------------------------------------------------------------------------------------- 12 | void Material::setShaderName(const std::string &_name) 13 | { 14 | m_shaderName = _name; 15 | } 16 | //----------------------------------------------------------------------------------------------------- 17 | void Material::handleKey(QKeyEvent*, QOpenGLContext*) 18 | {} 19 | -------------------------------------------------------------------------------- /demo/src/MaterialCSBpbr.cpp: -------------------------------------------------------------------------------- 1 | #include "MaterialCSBpbr.h" 2 | #include "Scene.h" 3 | #include "ShaderLib.h" 4 | 5 | void MaterialCSBpbr::init() 6 | { 7 | auto shaderPtr = m_shaderLib->getCurrentShader(); 8 | 9 | shaderPtr->setUniformValue("albedo", QVector3D{m_albedo.x, m_albedo.y, m_albedo.z}); 10 | shaderPtr->setUniformValue("ao", m_ao); 11 | shaderPtr->setUniformValue("exposure", m_exposure); 12 | shaderPtr->setUniformValue("roughness", m_roughness); 13 | shaderPtr->setUniformValue("metallic", m_metallic); 14 | 15 | // Update our matrices 16 | update(); 17 | } 18 | 19 | void MaterialCSBpbr::update() 20 | { 21 | auto shaderPtr = m_shaderLib->getShader(m_shaderName); 22 | auto eye = m_cam->getCameraEye(); 23 | shaderPtr->setUniformValue("camPos", QVector3D{eye.x, eye.y, eye.z}); 24 | 25 | // Scope the using declaration 26 | { 27 | using namespace SceneMatrices; 28 | static constexpr std::array shaderUniforms = {{"M", "MVP", "N"}}; 29 | // Send all our matrices to the GPU 30 | for (const auto matrixId : {MODEL_VIEW, PROJECTION, NORMAL}) 31 | { 32 | // Convert from glm to Qt 33 | QMatrix4x4 qmat(glm::value_ptr((*m_matrices)[matrixId])); 34 | // Need to transpose the matrix as they both use different majors 35 | shaderPtr->setUniformValue(shaderUniforms[matrixId], qmat.transposed()); 36 | } 37 | } 38 | } 39 | 40 | const char* MaterialCSBpbr::shaderFileName() const 41 | { 42 | return "shaderPrograms/csbPBR.json"; 43 | } 44 | 45 | 46 | -------------------------------------------------------------------------------- /demo/src/MaterialEnvMap.cpp: -------------------------------------------------------------------------------- 1 | #include "MaterialEnvMap.h" 2 | #include "Scene.h" 3 | #include "ShaderLib.h" 4 | 5 | void MaterialEnvMap::init() 6 | { 7 | initEnvMap(); 8 | m_envMap->bind(0); 9 | 10 | auto shaderPtr = m_shaderLib->getShader(m_shaderName); 11 | shaderPtr->setUniformValue("envMap", 0); 12 | 13 | initGlossMap(); 14 | m_glossMap->bind(1); 15 | shaderPtr->setUniformValue("glossMap", 1); 16 | 17 | update(); 18 | } 19 | 20 | void MaterialEnvMap::initGlossMap() 21 | { 22 | m_glossMap.reset(new QOpenGLTexture(QImage("images/gloss.png"))); 23 | m_glossMap->create(); 24 | m_glossMap->bind(1); 25 | using tex = QOpenGLTexture; 26 | m_glossMap->setWrapMode(tex::Repeat); 27 | m_glossMap->setMinMagFilters(tex::Linear, tex::Linear); 28 | } 29 | 30 | void MaterialEnvMap::initEnvMap() 31 | { 32 | m_envMap.reset(new QOpenGLTexture(QOpenGLTexture::TargetCubeMap)); 33 | static constexpr std::array paths = {{ 34 | "images/sky_xpos.png", 35 | "images/sky_ypos.png", 36 | "images/sky_zpos.png", 37 | "images/sky_xneg.png", 38 | "images/sky_yneg.png", 39 | "images/sky_zneg.png" 40 | }}; 41 | 42 | using tex = QOpenGLTexture; 43 | static constexpr std::array dataTypes = {{ 44 | tex::CubeMapPositiveX, 45 | tex::CubeMapPositiveY, 46 | tex::CubeMapPositiveZ, 47 | tex::CubeMapNegativeX, 48 | tex::CubeMapNegativeY, 49 | tex::CubeMapNegativeZ 50 | }}; 51 | std::array maps; 52 | for (size_t i = 0; i < maps.size(); ++i) 53 | maps[i] = QImage(paths[i]).mirrored().convertToFormat(QImage::Format_RGBA8888); 54 | 55 | m_envMap->create(); 56 | m_envMap->bind(0); 57 | m_envMap->setSize(maps[0].width(), maps[0].height(), maps[0].depth()); 58 | m_envMap->setFormat(tex::RGBAFormat); 59 | m_envMap->allocateStorage(); 60 | 61 | for (size_t i = 0; i < maps.size(); ++i) 62 | m_envMap->setData(0, 0, dataTypes[i], tex::RGBA, tex::UInt8, maps[i].constBits()); 63 | 64 | m_envMap->setMinMagFilters(tex::LinearMipMapLinear, tex::Linear); 65 | 66 | m_envMap->setWrapMode(tex::ClampToEdge); 67 | m_envMap->generateMipMaps(); 68 | m_envMap->setAutoMipMapGenerationEnabled(true); 69 | } 70 | 71 | void MaterialEnvMap::update() 72 | { 73 | auto shaderPtr = m_shaderLib->getShader(m_shaderName); 74 | auto eye = m_cam->getCameraEye(); 75 | shaderPtr->setUniformValue("camPos", QVector3D{eye.x, eye.y, eye.z}); 76 | 77 | // Scope the using declaration 78 | { 79 | using namespace SceneMatrices; 80 | static constexpr std::array shaderUniforms = {{"M", "MVP", "N"}}; 81 | // Send all our matrices to the GPU 82 | for (const auto matrixId : {MODEL_VIEW, PROJECTION, NORMAL}) 83 | { 84 | // Convert from glm to Qt 85 | QMatrix4x4 qmat(glm::value_ptr((*m_matrices)[matrixId])); 86 | // Need to transpose the matrix as they both use different majors 87 | shaderPtr->setUniformValue(shaderUniforms[matrixId], qmat.transposed()); 88 | } 89 | } 90 | } 91 | 92 | const char* MaterialEnvMap::shaderFileName() const 93 | { 94 | return "shaderPrograms/envMap.json"; 95 | } 96 | -------------------------------------------------------------------------------- /demo/src/MaterialFractal.cpp: -------------------------------------------------------------------------------- 1 | #include "MaterialFractal.h" 2 | #include "Scene.h" 3 | #include "ShaderLib.h" 4 | #include 5 | #include 6 | #include 7 | 8 | void MaterialFractal::init() 9 | { 10 | m_last = std::chrono::high_resolution_clock::now(); 11 | update(); 12 | } 13 | 14 | void MaterialFractal::update() 15 | { 16 | auto shaderPtr = m_shaderLib->getShader(m_shaderName); 17 | 18 | using namespace std::chrono; 19 | auto now = high_resolution_clock::now(); 20 | m_time += (duration_cast(now - m_last).count() * m_updateTime); 21 | m_last = now; 22 | shaderPtr->setUniformValue("t", m_time / 1000.0f); 23 | // Scope the using declaration 24 | { 25 | using namespace SceneMatrices; 26 | static constexpr std::array shaderUniforms = {{"M", "MVP", "N"}}; 27 | // Send all our matrices to the GPU 28 | for (const auto matrixId : {MODEL_VIEW, PROJECTION, NORMAL}) 29 | { 30 | // Convert from glm to Qt 31 | QMatrix4x4 qmat(glm::value_ptr((*m_matrices)[matrixId])); 32 | // Need to transpose the matrix as they both use different majors 33 | shaderPtr->setUniformValue(shaderUniforms[matrixId], qmat.transposed()); 34 | } 35 | } 36 | } 37 | 38 | const char* MaterialFractal::shaderFileName() const 39 | { 40 | return "shaderPrograms/fractal.json"; 41 | } 42 | 43 | void MaterialFractal::handleKey(QKeyEvent* io_event, QOpenGLContext* io_context) 44 | { 45 | switch(io_event->key()) 46 | { 47 | case Qt::Key_E : 48 | { 49 | static constexpr GLuint id = 1; 50 | io_context->versionFunctions()->glUniformSubroutinesuiv(GL_FRAGMENT_SHADER, 1, &id); 51 | break; 52 | } 53 | case Qt::Key_R : 54 | { 55 | static constexpr GLuint id = 0; 56 | io_context->versionFunctions()->glUniformSubroutinesuiv(GL_FRAGMENT_SHADER, 1, &id); 57 | break; 58 | } 59 | case Qt::Key_T : 60 | { 61 | m_updateTime = !m_updateTime; 62 | break; 63 | } 64 | default: break; 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /demo/src/MaterialPBR.cpp: -------------------------------------------------------------------------------- 1 | #include "MaterialPBR.h" 2 | #include "Scene.h" 3 | #include "ShaderLib.h" 4 | 5 | void MaterialPBR::init() 6 | { 7 | auto shaderPtr = m_shaderLib->getCurrentShader(); 8 | 9 | shaderPtr->setUniformValue("albedo", QVector3D{m_albedo.x, m_albedo.y, m_albedo.z}); 10 | shaderPtr->setUniformValue("ao", m_ao); 11 | shaderPtr->setUniformValue("exposure", m_exposure); 12 | shaderPtr->setUniformValue("roughness", m_roughness); 13 | shaderPtr->setUniformValue("metallic", m_metallic); 14 | 15 | // Update our matrices 16 | update(); 17 | } 18 | 19 | void MaterialPBR::update() 20 | { 21 | auto shaderPtr = m_shaderLib->getShader(m_shaderName); 22 | auto eye = m_cam->getCameraEye(); 23 | shaderPtr->setUniformValue("camPos", QVector3D{eye.x, eye.y, eye.z}); 24 | 25 | // Scope the using declaration 26 | { 27 | using namespace SceneMatrices; 28 | static constexpr std::array shaderUniforms = {{"M", "MVP", "N"}}; 29 | // Send all our matrices to the GPU 30 | for (const auto matrixId : {MODEL_VIEW, PROJECTION, NORMAL}) 31 | { 32 | // Convert from glm to Qt 33 | QMatrix4x4 qmat(glm::value_ptr((*m_matrices)[matrixId])); 34 | // Need to transpose the matrix as they both use different majors 35 | shaderPtr->setUniformValue(shaderUniforms[matrixId], qmat.transposed()); 36 | } 37 | } 38 | } 39 | 40 | const char* MaterialPBR::shaderFileName() const 41 | { 42 | return "shaderPrograms/redPBR.json"; 43 | } 44 | 45 | 46 | -------------------------------------------------------------------------------- /demo/src/MaterialPhong.cpp: -------------------------------------------------------------------------------- 1 | #include "MaterialPhong.h" 2 | #include "Scene.h" 3 | #include "ShaderLib.h" 4 | 5 | void MaterialPhong::init() 6 | { 7 | update(); 8 | } 9 | 10 | void MaterialPhong::update() 11 | { 12 | auto shaderPtr = m_shaderLib->getShader(m_shaderName); 13 | auto eye = m_cam->getCameraEye(); 14 | shaderPtr->setUniformValue("camPos", QVector3D{eye.x, eye.y, eye.z}); 15 | 16 | // Scope the using declaration 17 | { 18 | using namespace SceneMatrices; 19 | static constexpr std::array shaderUniforms = {{"M", "MVP", "N"}}; 20 | // Send all our matrices to the GPU 21 | for (const auto matrixId : {MODEL_VIEW, PROJECTION, NORMAL}) 22 | { 23 | // Convert from glm to Qt 24 | QMatrix4x4 qmat(glm::value_ptr((*m_matrices)[matrixId])); 25 | // Need to transpose the matrix as they both use different majors 26 | shaderPtr->setUniformValue(shaderUniforms[matrixId], qmat.transposed()); 27 | } 28 | } 29 | } 30 | 31 | const char* MaterialPhong::shaderFileName() const 32 | { 33 | return "shaderPrograms/phong.json"; 34 | } 35 | -------------------------------------------------------------------------------- /demo/src/MaterialWireframe.cpp: -------------------------------------------------------------------------------- 1 | #include "MaterialWireframe.h" 2 | #include "Scene.h" 3 | #include "ShaderLib.h" 4 | 5 | void MaterialWireframe::init() 6 | { 7 | update(); 8 | } 9 | 10 | void MaterialWireframe::update() 11 | { 12 | auto shaderPtr = m_shaderLib->getShader(m_shaderName); 13 | // Scope the using declaration 14 | { 15 | using namespace SceneMatrices; 16 | static constexpr std::array shaderUniforms = {{"M", "MVP", "N"}}; 17 | // Send all our matrices to the GPU 18 | for (const auto matrixId : {MODEL_VIEW, PROJECTION, NORMAL}) 19 | { 20 | // Convert from glm to Qt 21 | QMatrix4x4 qmat(glm::value_ptr((*m_matrices)[matrixId])); 22 | // Need to transpose the matrix as they both use different majors 23 | shaderPtr->setUniformValue(shaderUniforms[matrixId], qmat.transposed()); 24 | } 25 | } 26 | } 27 | 28 | const char* MaterialWireframe::shaderFileName() const 29 | { 30 | return "shaderPrograms/wireframe.json"; 31 | } 32 | -------------------------------------------------------------------------------- /demo/src/MeshVBO.cpp: -------------------------------------------------------------------------------- 1 | #include "MeshVBO.h" 2 | #include 3 | #include 4 | 5 | //----------------------------------------------------------------------------------------------------- 6 | void MeshVBO::init() 7 | { 8 | // Generate all our required buffers 9 | m_vbo.create(); 10 | m_vbo.bind(); 11 | m_ebo.create(); 12 | m_ebo.bind(); 13 | } 14 | //----------------------------------------------------------------------------------------------------- 15 | void MeshVBO::reset(const unsigned char _indicesSize, const int _nIndices, const unsigned char _dataSize, const int _nVert, const int _nUV, const int _nNorm) 16 | { 17 | { 18 | using namespace csb; 19 | // Track the amount of data being stored 20 | m_amountOfData[VERTEX] = _nVert; 21 | m_amountOfData[UV] = _nUV; 22 | m_amountOfData[NORMAL] = _nNorm; 23 | } 24 | m_totalAmountOfData = _nVert + _nNorm + _nUV; 25 | // Track the size of our stored data 26 | m_dataSize = _dataSize; 27 | // For all the buffers, we bind them then clear the data pointer 28 | m_vbo.bind(); 29 | m_vbo.setUsagePattern(QOpenGLBuffer::DynamicDraw); 30 | m_vbo.allocate(m_dataSize * m_totalAmountOfData); 31 | 32 | m_numIndices = _nIndices; 33 | m_indicesSize = _indicesSize; 34 | m_ebo.bind(); 35 | m_ebo.setUsagePattern(QOpenGLBuffer::DynamicDraw); 36 | m_ebo.allocate(_nIndices * m_indicesSize); 37 | } 38 | //----------------------------------------------------------------------------------------------------- 39 | void MeshVBO::extend(const unsigned char _indicesSize, const int _nIndices, const int _nVert, const int _nUV, const int _nNorm) 40 | { 41 | { 42 | using namespace csb; 43 | // Track the amount of data being stored 44 | m_amountOfData[VERTEX] += _nVert; 45 | m_amountOfData[UV] += _nUV; 46 | m_amountOfData[NORMAL] += _nNorm; 47 | } 48 | m_totalAmountOfData += _nVert + _nNorm + _nUV; 49 | // For all the buffers, we bind them then clear the data pointer 50 | m_vbo.bind(); 51 | m_vbo.setUsagePattern(QOpenGLBuffer::DynamicDraw); 52 | m_vbo.allocate(m_dataSize * m_totalAmountOfData); 53 | 54 | m_numIndices += _nIndices; 55 | m_indicesSize += _indicesSize; 56 | m_ebo.bind(); 57 | m_ebo.setUsagePattern(QOpenGLBuffer::DynamicDraw); 58 | m_ebo.allocate(m_numIndices * m_indicesSize); 59 | } 60 | //----------------------------------------------------------------------------------------------------- 61 | void MeshVBO::write(const void *_address, const csb::MeshAttribute _section, const int _amount, const int _offset) 62 | { 63 | assert(_amount <= m_amountOfData[_section]); 64 | // Bind the requested buffer, then set it's data pointer 65 | m_vbo.bind(); 66 | m_vbo.write(offset(_section) + _offset * m_dataSize, _address, _amount * m_dataSize); 67 | } 68 | //----------------------------------------------------------------------------------------------------- 69 | void MeshVBO::writeIndices(const void* _indices, const int _amount, const int _offset) 70 | { 71 | assert(_amount <= m_numIndices); 72 | m_ebo.bind(); 73 | m_ebo.write(_offset * m_indicesSize, _indices, _amount * m_indicesSize); 74 | } 75 | //----------------------------------------------------------------------------------------------------- 76 | unsigned char MeshVBO::dataSize() const noexcept 77 | { 78 | // Returns the size the stored of data elements 79 | return m_dataSize; 80 | } 81 | //----------------------------------------------------------------------------------------------------- 82 | int MeshVBO::dataAmount() const noexcept 83 | { 84 | // Returns the amount of data elements 85 | return m_totalAmountOfData; 86 | } 87 | //----------------------------------------------------------------------------------------------------- 88 | int MeshVBO::dataAmount(const csb::MeshAttribute _section) const noexcept 89 | { 90 | // Returns the amount of data elements 91 | return m_amountOfData[_section]; 92 | } 93 | //----------------------------------------------------------------------------------------------------- 94 | int MeshVBO::offset(const csb::MeshAttribute _section) const noexcept 95 | { 96 | int offset = 0; 97 | for (size_t i = 0; i < _section; ++i) 98 | offset += m_amountOfData[i]; 99 | return offset * m_dataSize; 100 | } 101 | //----------------------------------------------------------------------------------------------------- 102 | void MeshVBO::use() 103 | { 104 | m_vbo.bind(); 105 | m_ebo.bind(); 106 | } 107 | -------------------------------------------------------------------------------- /demo/src/Scene.cpp: -------------------------------------------------------------------------------- 1 | #include "Scene.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "MaterialPBR.h" 8 | 9 | //---------------------------------------------------------------------------------------------------------------------- 10 | Scene::Scene(const std::shared_ptr &io_camera , QWidget *io_parent) : 11 | QOpenGLWidget(io_parent), 12 | m_camera(io_camera) 13 | { 14 | // set this widget to have the initial keyboard focus 15 | // re-size the widget to that of the parent (in this case the GLFrame passed in on construction) 16 | this->resize( io_parent->size() ); 17 | } 18 | //---------------------------------------------------------------------------------------------------------------------- 19 | void Scene::initializeGL() 20 | { 21 | initializeOpenGLFunctions(); 22 | glEnable( GL_DEPTH_TEST ); 23 | glEnable( GL_MULTISAMPLE ); 24 | glEnable( GL_TEXTURE_2D ); 25 | glClearColor(0.0f, 0.0f, 0.0f, 1.0f); 26 | glViewport(0, 0, width()*devicePixelRatio(), height()*devicePixelRatio()); 27 | 28 | // This calls the derived class init func 29 | init(); 30 | } 31 | //---------------------------------------------------------------------------------------------------------------------- 32 | void Scene::resizeGL( int _w, int _h ) 33 | { 34 | m_camera->resize(_w, _h); 35 | } 36 | //---------------------------------------------------------------------------------------------------------------------- 37 | void Scene::keyPress(QKeyEvent* io_event) 38 | { 39 | m_camera->handleKey(io_event->key(), (io_event->type()==QKeyEvent::KeyPress)); 40 | } 41 | //---------------------------------------------------------------------------------------------------------------------- 42 | void Scene::mouseMove(QMouseEvent * io_event) 43 | { 44 | m_camera->handleMouseMove(glm::vec2{io_event->pos().x(), io_event->pos().y()}); 45 | update(); 46 | } 47 | //---------------------------------------------------------------------------------------------------------------------- 48 | void Scene::mouseClick(QMouseEvent * io_event) 49 | { 50 | m_camera->handleMouseClick(*io_event); 51 | update(); 52 | } 53 | //---------------------------------------------------------------------------------------------------------------------- 54 | void Scene::init() 55 | { 56 | m_camera->setMousePos(0,0); 57 | } 58 | //------------------------------------------------------------------------------------------------------------------------------ 59 | void Scene::paintGL() 60 | { 61 | // Set our viewport size based on the devide displace 62 | glViewport(0, 0, width()*devicePixelRatio(), height()*devicePixelRatio()); 63 | // Call the render scene function that may be overriden 64 | renderScene(); 65 | // Update the OpenGL widget 66 | update(); 67 | } 68 | //------------------------------------------------------------------------------------------------------------------------------ 69 | void Scene::renderScene() 70 | { 71 | // Clear the screen 72 | glClearColor(0.5f, 0.5f, 0.5f, 1.0f); 73 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 74 | // Update our camera view 75 | m_camera->update(); 76 | 77 | // Scope the using declaration 78 | { 79 | using namespace SceneMatrices; 80 | m_matrices[PROJECTION] = m_camera->projMatrix() * m_camera->viewMatrix() * m_matrices[MODEL_VIEW]; 81 | m_matrices[NORMAL] = glm::inverse(glm::transpose(m_matrices[MODEL_VIEW])); 82 | } 83 | } 84 | //------------------------------------------------------------------------------------------------------------------------------ 85 | 86 | 87 | -------------------------------------------------------------------------------- /demo/src/ShaderLib.cpp: -------------------------------------------------------------------------------- 1 | #include "ShaderLib.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | std::string ShaderLib::loadShaderProg(const QString &_jsonFileName) 8 | { 9 | auto toStr = [](const auto& val){ return val.toString(); }; 10 | // Read in raw file 11 | QFile file(_jsonFileName); 12 | file.open(QIODevice::ReadOnly | QIODevice::Text); 13 | QByteArray rawData = file.readAll(); 14 | // Parse document 15 | QJsonDocument doc(QJsonDocument::fromJson(rawData)); 16 | // Get the json object to view 17 | QJsonObject shaderParts = doc.object(); 18 | 19 | // Get a string out from the json 20 | std::string shaderName = shaderParts["Name"].toString().toStdString(); 21 | 22 | enum {VERTEX, FRAGMENT, GEOMETRY}; 23 | constexpr std::array shaderNames = {{"Vertex", "Fragment", "Geometry"}}; 24 | std::array shaderPaths; 25 | 26 | for (auto shader : {VERTEX, FRAGMENT, GEOMETRY}) 27 | { 28 | auto& name = shaderNames[shader]; 29 | shaderPaths[shader] = shaderParts.contains(name) ? toStr(shaderParts[name]) : ""; 30 | } 31 | 32 | // Load the shader if we haven't already 33 | if (!m_shaderPrograms.count(shaderName)) 34 | createShader(shaderName, shaderPaths); 35 | 36 | return shaderName; 37 | } 38 | 39 | void ShaderLib::createShader(const std::string &_name, const std::array &_shaderPaths) 40 | { 41 | QOpenGLShaderProgram *program = new QOpenGLShaderProgram(); 42 | 43 | enum {VERTEX, FRAGMENT, GEOMETRY}; 44 | using shdr = QOpenGLShader; 45 | constexpr shdr::ShaderType qShaders[] = {shdr::Vertex, shdr::Fragment, shdr::Geometry}; 46 | for (auto shader : {VERTEX, FRAGMENT, GEOMETRY}) 47 | { 48 | auto path = _shaderPaths[shader]; 49 | if (path == "") continue; 50 | auto stdPath = path.toStdString(); 51 | if (!m_shaderParts.count(stdPath)) 52 | { 53 | QOpenGLShader* shad = new QOpenGLShader(qShaders[shader]); 54 | shad->compileSourceFile(path); 55 | m_shaderParts[stdPath].reset(shad); 56 | } 57 | program->addShader(m_shaderParts[stdPath].get()); 58 | } 59 | program->link(); 60 | m_shaderPrograms[_name].reset(program); 61 | } 62 | 63 | void ShaderLib::useShader(const std::string& _name) 64 | { 65 | m_currentShader = m_shaderPrograms[_name].get(); 66 | m_currentShader->bind(); 67 | } 68 | 69 | QOpenGLShaderProgram *ShaderLib::getShader(const std::string& _name) 70 | { 71 | return m_shaderPrograms[_name].get(); 72 | } 73 | 74 | QOpenGLShaderProgram* ShaderLib::getCurrentShader() 75 | { 76 | return m_currentShader; 77 | } 78 | 79 | -------------------------------------------------------------------------------- /demo/src/TrackballCamera.cpp: -------------------------------------------------------------------------------- 1 | #include "TrackballCamera.h" 2 | #include 3 | #include 4 | #include 5 | 6 | const TrackballCamera::statePtr TrackballCamera::m_states[] = { 7 | statePtr{new CameraZoom}, 8 | statePtr{new CameraRotate}, 9 | statePtr{new CameraPassive} 10 | }; 11 | 12 | void TrackballCamera::handleMouseClick(const QMouseEvent &_action) 13 | { 14 | auto mousePos = _action.pos(); 15 | setMousePos(mousePos.x(), mousePos.y()); 16 | updateYawPitch(); 17 | // Did a branchless version for fun using bool math 18 | auto type = _action.type(); 19 | auto button = _action.buttons(); 20 | using ME = QMouseEvent; 21 | int input = (type == ME::MouseButtonPress) 22 | * ( 23 | ((button == Qt::LeftButton) * TRACKBALL_ROTATING) 24 | + ((button == Qt::RightButton) * TRACKBALL_ZOOMING) 25 | ) 26 | + (type == ME::MouseButtonRelease) * TRACKBALL_PASSIVE; 27 | m_currentState = static_cast(input); 28 | } 29 | 30 | void TrackballCamera::handleKey(const int _key, const bool _isPress) 31 | { 32 | m_states[m_currentState]->handleKey(this, _key, _isPress); 33 | } 34 | 35 | /** 36 | * @brief TrackballCamera::handleMouseMove 37 | * @param mouseX 38 | * @param mouseY 39 | * Pass the mouse move on to the relevent handler 40 | */ 41 | void TrackballCamera::handleMouseMove(const glm::vec2 &_mousePos) 42 | { 43 | m_states[m_currentState]->handleMouseMove(this, _mousePos); 44 | } 45 | 46 | /** 47 | * @brief TrackballCamera::handleMouseMove 48 | * @param mouseX current mouse coordinate X 49 | * @param mouseY current mouse coordinate Y 50 | * This is the easiest trackball available. The mouse x difference maps to yaw angle and the y maps to pitch angle. 51 | */ 52 | void TrackballCamera::mouseRotate(const glm::vec2 &_mousePos) 53 | { 54 | // Make sure the yaw is reset when we go past to -pi,pi 55 | m_yaw = m_lastYaw + (m_lastPos.x - _mousePos.x) * m_sensitivity; 56 | m_yaw = glm::mod(m_yaw, glm::pi() * 2.0f); 57 | 58 | // Make sure our pitch is clamped within the range of slightly under half pi 59 | // We use this to prevent flipping when half pi is exceeded 60 | // A small epsilon is required to prevent locking at the poles 61 | static constexpr float epsilon = 0.01f; 62 | static const float half_pi = glm::half_pi() - epsilon; 63 | m_pitch = m_lastPitch + (m_lastPos.y - _mousePos.y)*m_sensitivity; 64 | m_pitch = glm::clamp(m_pitch, -half_pi, half_pi); 65 | 66 | // Update our last yaw/pitch 67 | updateYawPitch(); 68 | // Update the last mouse position 69 | m_lastPos = _mousePos; 70 | } 71 | 72 | /** 73 | * @brief TrackballCamera::mouseZoom 74 | * @param mouseX 75 | * @param mouseY 76 | */ 77 | void TrackballCamera::mouseZoom(const glm::vec2 &_mousePos) 78 | { 79 | m_zoom += (_mousePos.y - m_lastPos.y) * 0.25f * m_sensitivity; 80 | m_lastPos = _mousePos; 81 | m_zoom = glm::clamp(m_zoom, 0.0f, 10.0f); 82 | } 83 | 84 | glm::vec3 TrackballCamera::getCameraEye() const noexcept 85 | { 86 | // Now use lookat function to set the view matrix (assume y is up) 87 | glm::mat3 r_yaw = glm::mat3_cast(glm::angleAxis(m_yaw, glm::vec3(0.0f, 1.0f, 0.0f))); 88 | glm::mat3 r_pitch = glm::mat3_cast(glm::angleAxis(m_pitch, glm::vec3(1.0f, 0.0f, 0.0f))); 89 | return m_target - (r_yaw * r_pitch * m_zoom * (m_target - m_camOrigin)); 90 | } 91 | 92 | void TrackballCamera::resetPosition() 93 | { 94 | Camera::resetPosition(); 95 | m_yaw = 0.0f; 96 | m_pitch = 0.0f; 97 | m_zoom = 2.5f; 98 | m_lastYaw = 0.0f; 99 | m_lastPitch = 0.0f; 100 | } 101 | 102 | void TrackballCamera::setZoom(const float _zoom) noexcept 103 | { 104 | m_zoom = _zoom; 105 | } 106 | 107 | void TrackballCamera::setSensitivity(const float _sensitivity) noexcept 108 | { 109 | m_sensitivity = _sensitivity; 110 | } 111 | 112 | void TrackballCamera::updateYawPitch() 113 | { 114 | m_lastYaw = m_yaw; 115 | m_lastPitch = m_pitch; 116 | } 117 | 118 | /** 119 | * @brief TrackballCamera::update 120 | * The yaw and pitch are the rotations about the y and x axes respectively, constructed using angle and axis. 121 | * Note that in glm the matrix is constructed using quaternions - I could leave them in this form for the rotation. 122 | * In this case, the eye is rotated around the target 123 | */ 124 | void TrackballCamera::update() 125 | { 126 | // Call base class to update perspective 127 | Camera::update(); 128 | m_viewMatrix = glm::lookAt(getCameraEye(), m_target, glm::vec3(0.0f,1.0f,0.0f)); 129 | } 130 | -------------------------------------------------------------------------------- /demo/src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "MainWindow.h" 3 | #include "DemoScene.h" 4 | #include "TrackballCamera.h" 5 | #include "ShaderLib.h" 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | int main(int argc, char *argv[]) 13 | { 14 | // create an OpenGL format specifier 15 | QSurfaceFormat format; 16 | // set the number of samples for multisampling 17 | // will need to enable glEnable(GL_MULTISAMPLE); once we have a context 18 | format.setSamples(4); 19 | #if defined(DARWIN) 20 | // at present mac osx Mountain Lion only supports GL3.2 21 | // the new mavericks will have GL 4.x so can change 22 | format.setMajorVersion(4); 23 | format.setMinorVersion(2); 24 | #else 25 | // with luck we have the latest GL version so set to this 26 | format.setMajorVersion(4); 27 | format.setMinorVersion(3); 28 | #endif 29 | // now we are going to set to CoreProfile OpenGL so we can't use and old Immediate mode GL 30 | format.setProfile(QSurfaceFormat::CoreProfile); 31 | // now set the depth buffer to 24 bits 32 | format.setDepthBufferSize(24); 33 | 34 | // this will set the format for all widgets 35 | QSurfaceFormat::setDefaultFormat(format); 36 | // make an instance of the QApplication 37 | QApplication app(argc, argv); 38 | // Create a new MainWindow 39 | MainWindow window; 40 | // Create a camera 41 | std::shared_ptr cam(std::make_shared()); 42 | // Create a shader library 43 | std::shared_ptr lib(std::make_shared()); 44 | // Create a scene to place inside the window 45 | std::shared_ptr scene(std::make_shared(cam, lib, &window)); 46 | // Initialise the window using our scene 47 | window.init(scene); 48 | // show it 49 | window.show(); 50 | // hand control over to Qt framework 51 | return app.exec(); 52 | } 53 | -------------------------------------------------------------------------------- /demo/ui/mainwindow.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | MainWindow 4 | 5 | 6 | 7 | 0 8 | 0 9 | 800 10 | 600 11 | 12 | 13 | 14 | CSB 15 | 16 | 17 | 18 | 19 | 20 | 21 | Qt::Horizontal 22 | 23 | 24 | 25 | 40 26 | 20 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | Qt::Horizontal 35 | 36 | 37 | 38 | 40 39 | 20 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | Qt::Horizontal 48 | 49 | 50 | 51 | 40 52 | 20 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | Qt::Horizontal 61 | 62 | 63 | 64 | 40 65 | 20 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | Material 80 | 81 | 82 | 83 | 84 | 85 | 86 | true 87 | 88 | 89 | Rotating 90 | 91 | 92 | false 93 | 94 | 95 | 96 | 97 | 98 | 99 | Qt::Vertical 100 | 101 | 102 | 103 | 20 104 | 40 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | Qt::Horizontal 113 | 114 | 115 | 116 | 40 117 | 20 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | Pause 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 0 138 | 0 139 | 800 140 | 26 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | -------------------------------------------------------------------------------- /demo/ui/ui_mainwindow.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | ** Form generated from reading UI file 'mainwindow.ui' 3 | ** 4 | ** Created by: Qt User Interface Compiler version 5.9.0 5 | ** 6 | ** WARNING! All changes made in this file will be lost when recompiling UI file! 7 | ********************************************************************************/ 8 | 9 | #ifndef UI_MAINWINDOW_H 10 | #define UI_MAINWINDOW_H 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | QT_BEGIN_NAMESPACE 27 | 28 | class Ui_MainWindow 29 | { 30 | public: 31 | QWidget *centralwidget; 32 | QGridLayout *s_mainWindowGridLayout; 33 | QSpacerItem *horizontalSpacer_5; 34 | QSpacerItem *horizontalSpacer_6; 35 | QSpacerItem *horizontalSpacer; 36 | QSpacerItem *horizontalSpacer_2; 37 | QGroupBox *s_drawGB; 38 | QGridLayout *gridLayout_2; 39 | QPushButton *material; 40 | QCheckBox *m_rotating; 41 | QSpacerItem *verticalSpacer; 42 | QSpacerItem *horizontalSpacer_3; 43 | QCheckBox *m_isPaused; 44 | QMenuBar *menubar; 45 | 46 | void setupUi(QMainWindow *MainWindow) 47 | { 48 | if (MainWindow->objectName().isEmpty()) 49 | MainWindow->setObjectName(QStringLiteral("MainWindow")); 50 | MainWindow->resize(800, 600); 51 | centralwidget = new QWidget(MainWindow); 52 | centralwidget->setObjectName(QStringLiteral("centralwidget")); 53 | s_mainWindowGridLayout = new QGridLayout(centralwidget); 54 | s_mainWindowGridLayout->setObjectName(QStringLiteral("s_mainWindowGridLayout")); 55 | horizontalSpacer_5 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); 56 | 57 | s_mainWindowGridLayout->addItem(horizontalSpacer_5, 0, 3, 1, 1); 58 | 59 | horizontalSpacer_6 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); 60 | 61 | s_mainWindowGridLayout->addItem(horizontalSpacer_6, 0, 1, 1, 1); 62 | 63 | horizontalSpacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); 64 | 65 | s_mainWindowGridLayout->addItem(horizontalSpacer, 0, 4, 1, 1); 66 | 67 | horizontalSpacer_2 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); 68 | 69 | s_mainWindowGridLayout->addItem(horizontalSpacer_2, 0, 2, 1, 1); 70 | 71 | s_drawGB = new QGroupBox(centralwidget); 72 | s_drawGB->setObjectName(QStringLiteral("s_drawGB")); 73 | gridLayout_2 = new QGridLayout(s_drawGB); 74 | gridLayout_2->setObjectName(QStringLiteral("gridLayout_2")); 75 | material = new QPushButton(s_drawGB); 76 | material->setObjectName(QStringLiteral("material")); 77 | 78 | gridLayout_2->addWidget(material, 0, 1, 1, 1); 79 | 80 | m_rotating = new QCheckBox(s_drawGB); 81 | m_rotating->setObjectName(QStringLiteral("m_rotating")); 82 | m_rotating->setEnabled(true); 83 | m_rotating->setTristate(false); 84 | 85 | gridLayout_2->addWidget(m_rotating, 2, 1, 1, 1); 86 | 87 | verticalSpacer = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding); 88 | 89 | gridLayout_2->addItem(verticalSpacer, 4, 1, 1, 1); 90 | 91 | horizontalSpacer_3 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); 92 | 93 | gridLayout_2->addItem(horizontalSpacer_3, 0, 2, 1, 1); 94 | 95 | m_isPaused = new QCheckBox(s_drawGB); 96 | m_isPaused->setObjectName(QStringLiteral("m_isPaused")); 97 | 98 | gridLayout_2->addWidget(m_isPaused, 3, 1, 1, 1); 99 | 100 | 101 | s_mainWindowGridLayout->addWidget(s_drawGB, 2, 5, 1, 1); 102 | 103 | MainWindow->setCentralWidget(centralwidget); 104 | menubar = new QMenuBar(MainWindow); 105 | menubar->setObjectName(QStringLiteral("menubar")); 106 | menubar->setGeometry(QRect(0, 0, 800, 26)); 107 | MainWindow->setMenuBar(menubar); 108 | 109 | retranslateUi(MainWindow); 110 | 111 | QMetaObject::connectSlotsByName(MainWindow); 112 | } // setupUi 113 | 114 | void retranslateUi(QMainWindow *MainWindow) 115 | { 116 | MainWindow->setWindowTitle(QApplication::translate("MainWindow", "CSB", Q_NULLPTR)); 117 | s_drawGB->setTitle(QString()); 118 | material->setText(QApplication::translate("MainWindow", "Material", Q_NULLPTR)); 119 | m_rotating->setText(QApplication::translate("MainWindow", "Rotating", Q_NULLPTR)); 120 | m_isPaused->setText(QApplication::translate("MainWindow", "Pause", Q_NULLPTR)); 121 | } // retranslateUi 122 | 123 | }; 124 | 125 | namespace Ui { 126 | class MainWindow: public Ui_MainWindow {}; 127 | } // namespace Ui 128 | 129 | QT_END_NAMESPACE 130 | 131 | #endif // UI_MAINWINDOW_H 132 | -------------------------------------------------------------------------------- /feedback/README.md: -------------------------------------------------------------------------------- 1 | # Assignment Feedback 2 | 3 | ## General Feedback 4 | 5 | In general, a lot of the class diagrams are not showing the attributes properly, you should re-visit your classes now and see if you can actually write code for them/ 6 | 7 | Follow the guidelines [here](http://jonmacey.blogspot.co.uk/2013/01/ca-1-initial-design-general-feedback.html) for more information as well as [this](http://jonmacey.blogspot.co.uk/2011/01/asd-ca1-assignment-feedback-and.html) 8 | 9 | Your main program loop can also be written very quickly to test it, use print statements and dummy classes to make sure you can effectively wire-up your different classes and let them interact, again this can be done with just print outs and no graphical elements, this will make any errors / design flaws apparent very quickly. Best way to do this is to read the [following](http://jonmacey.blogspot.co.uk/2012/02/getting-started-with-programming.html) posts 10 | 11 | ## Specific Feedback 12 | 13 | A good design report outlining what you are going to do with good detail. 14 | 15 | I would suggest using the ```ngl::Obj``` class as you initial method of loading the meshes as you have access to all the data you need (Faces, Verts, UV's and Normals) 16 | 17 | I would use RK4 over Euler (or perhaps verlet) as it will be more accurate, You will also need to timer to allow for the integration steps. 18 | 19 | Have you considered using [Position Based Dynamics](http://matthias-mueller-fischer.ch/publications/posBasedDyn.pdf) instead? There was a good masters project [here](https://nccastaff.bournemouth.ac.uk/jmacey/MastersProjects/MSc15/03Pieterjan/index.html) 20 | 21 | ## Class Diagram 22 | 23 | ![alt](UML.png) 24 | 25 | 1. Need to show the multiplicity of the relationships between the classes. 26 | 2. Point is not a good name for this class (make me think of 2D or 3D point) but this is more as it has velocity, it could also be a struct not a class. 27 | 3. points and normals are part of the scene. I think you need an inbetween object (Mesh) to store this data, or at least process you simulation data and produce one. You normals will need to be calculated each frame (and what about UV for textures). 28 | 4. This is good, but could be be extensible to add new constraint types, do these even need to be part of the system but actually just link to the main Point class. 29 | 30 | I think this would work better using the Position Based system. -------------------------------------------------------------------------------- /feedback/UML.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitronoid/csb/c3d67051410c67e784003db0df4f7c81a702d248/feedback/UML.png -------------------------------------------------------------------------------- /jack_diver_initial_design.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitronoid/csb/c3d67051410c67e784003db0df4f7c81a702d248/jack_diver_initial_design.pdf -------------------------------------------------------------------------------- /notes: -------------------------------------------------------------------------------- 1 | Cloth to cloth collision can be observed clearly without bending constraints, could weaken them. 2 | TEST 3 | -- 4 | Add static collision planes 5 | smooth shading 6 | energy test for solver 7 | clone test 8 | move constructor lists to .cpp 9 | 10 | FIXESS 11 | currently masses must be set before constraints are generated 12 | currently static collisions need to be updated (cell and table size), 13 | when adding a trimesh -------------------------------------------------------------------------------- /tests/src/BendingConstraintTests.cpp: -------------------------------------------------------------------------------- 1 | #include "gtest/gtest.h" 2 | #include "BendingConstraint.h" 3 | #include "gtx/fast_square_root.hpp" 4 | #include "UtilMacros.h" 5 | 6 | TEST(BendingConstraint, constructor) 7 | { 8 | std::vector testVertices { 9 | {0.f, 0.f, 0.f}, 10 | {1.f, 1.f, 0.f}, 11 | {0.f, 2.f, 0.f} 12 | }; 13 | std::vector testParticles { 14 | csb::Particle{testVertices[0], 1.f}, 15 | csb::Particle{testVertices[1], 1.f}, 16 | csb::Particle{testVertices[2], 1.f} 17 | }; 18 | 19 | csb::BendingConstraint b(0, 1, 2, 0.f, 0.f, testParticles); 20 | EXPECT_EQ(0, b.getParticleIndex(0)); 21 | EXPECT_EQ(1, b.getParticleIndex(1)); 22 | EXPECT_EQ(2, b.getParticleIndex(2)); 23 | EXPECT_EQ(0.25f, b.getParticleWeight(0)); 24 | EXPECT_EQ(0.25f, b.getParticleWeight(1)); 25 | EXPECT_EQ(0.25f, b.getParticleWeight(2)); 26 | EXPECT_EQ(0.f, b.getRest()); 27 | EXPECT_EQ(0.f, b.getStiffness()); 28 | 29 | csb::BendingConstraint bCopy(b); 30 | EXPECT_EQ(0, bCopy.getParticleIndex(0)); 31 | EXPECT_EQ(1, bCopy.getParticleIndex(1)); 32 | EXPECT_EQ(2, bCopy.getParticleIndex(2)); 33 | EXPECT_EQ(0.25f, bCopy.getParticleWeight(0)); 34 | EXPECT_EQ(0.25f, bCopy.getParticleWeight(1)); 35 | EXPECT_EQ(0.25f, bCopy.getParticleWeight(2)); 36 | EXPECT_EQ(0.f, bCopy.getRest()); 37 | EXPECT_EQ(0.f, bCopy.getStiffness()); 38 | 39 | 40 | csb::BendingConstraint bMove(std::move(b)); 41 | EXPECT_EQ(0, bMove.getParticleIndex(0)); 42 | EXPECT_EQ(1, bMove.getParticleIndex(1)); 43 | EXPECT_EQ(2, bMove.getParticleIndex(2)); 44 | EXPECT_EQ(0.25f, bMove.getParticleWeight(0)); 45 | EXPECT_EQ(0.25f, bMove.getParticleWeight(1)); 46 | EXPECT_EQ(0.25f, bMove.getParticleWeight(2)); 47 | EXPECT_EQ(0.f, bMove.getRest()); 48 | EXPECT_EQ(0.f, bMove.getStiffness()); 49 | } 50 | 51 | TEST(BendingConstraint, assignment) 52 | { 53 | std::vector testVertices { 54 | {0.f, 0.f, 0.f}, 55 | {1.f, 1.f, 0.f}, 56 | {0.f, 2.f, 0.f} 57 | }; 58 | std::vector testParticles { 59 | csb::Particle{testVertices[0], 1.f}, 60 | csb::Particle{testVertices[1], 1.f}, 61 | csb::Particle{testVertices[2], 1.f} 62 | }; 63 | 64 | csb::BendingConstraint b(0, 1, 2, 0.f, 0.f, testParticles); 65 | EXPECT_EQ(0, b.getParticleIndex(0)); 66 | EXPECT_EQ(1, b.getParticleIndex(1)); 67 | EXPECT_EQ(2, b.getParticleIndex(2)); 68 | EXPECT_EQ(0.25f, b.getParticleWeight(0)); 69 | EXPECT_EQ(0.25f, b.getParticleWeight(1)); 70 | EXPECT_EQ(0.25f, b.getParticleWeight(2)); 71 | EXPECT_EQ(0.f, b.getRest()); 72 | EXPECT_EQ(0.f, b.getStiffness()); 73 | 74 | csb::BendingConstraint bCopy; 75 | bCopy = b; 76 | EXPECT_EQ(0, bCopy.getParticleIndex(0)); 77 | EXPECT_EQ(1, bCopy.getParticleIndex(1)); 78 | EXPECT_EQ(2, bCopy.getParticleIndex(2)); 79 | EXPECT_EQ(0.25f, bCopy.getParticleWeight(0)); 80 | EXPECT_EQ(0.25f, bCopy.getParticleWeight(1)); 81 | EXPECT_EQ(0.25f, bCopy.getParticleWeight(2)); 82 | EXPECT_EQ(0.f, bCopy.getRest()); 83 | EXPECT_EQ(0.f, bCopy.getStiffness()); 84 | 85 | 86 | csb::BendingConstraint bMove; 87 | bMove = std::move(b); 88 | EXPECT_EQ(0, bMove.getParticleIndex(0)); 89 | EXPECT_EQ(1, bMove.getParticleIndex(1)); 90 | EXPECT_EQ(2, bMove.getParticleIndex(2)); 91 | EXPECT_EQ(0.25f, bMove.getParticleWeight(0)); 92 | EXPECT_EQ(0.25f, bMove.getParticleWeight(1)); 93 | EXPECT_EQ(0.25f, bMove.getParticleWeight(2)); 94 | EXPECT_EQ(0.f, bMove.getRest()); 95 | EXPECT_EQ(0.f, bMove.getStiffness()); 96 | } 97 | 98 | TEST(BendingConstraint, projection) 99 | { 100 | std::vector testVertices { 101 | {0.f, 0.f, 0.f}, 102 | {0.f, 2.f, 0.f}, 103 | {3.f, 1.f, 0.f} 104 | }; 105 | std::vector testParticles { 106 | csb::Particle{testVertices[0], 1.f}, 107 | csb::Particle{testVertices[1], 1.f}, 108 | csb::Particle{testVertices[2], 1.f} 109 | }; 110 | 111 | 112 | static constexpr float third = 1.0f / 3.0f; 113 | auto centre = third * (testVertices[0] + testVertices[1] + testVertices[2]); 114 | csb::BendingConstraint b(0, 1, 2, glm::fastDistance(testVertices[2], centre), 1.f, testParticles); 115 | 116 | // By doubling the "height" of the triangle, we can observe all the x components, 117 | // converge together. The end result is that they have shifted towards the new centroid, 118 | // but their relative differences are unchanged. 119 | testVertices[2].x = 6.f; 120 | b.project(testParticles); 121 | 122 | const auto expected1 = glm::vec3(1.f, 0.f, 0.f); 123 | const auto expected2 = glm::vec3(1.f, 2.f, 0.f); 124 | const auto expected3 = glm::vec3(4.f, 1.f, 0.f); 125 | EXPECT_VEC3_EQ(expected1, testVertices[0]); 126 | EXPECT_VEC3_EQ(expected2, testVertices[1]); 127 | EXPECT_VEC3_EQ(expected3, testVertices[2]); 128 | } 129 | 130 | TEST(BendingConstraint, weightedProjection) 131 | { 132 | std::vector testVertices { 133 | {0.f, 0.f, 0.f}, 134 | {0.f, 2.f, 0.f}, 135 | {3.f, 1.f, 0.f} 136 | }; 137 | std::vector testParticles { 138 | csb::Particle{testVertices[0], 1.f}, 139 | csb::Particle{testVertices[1], 1.f}, 140 | csb::Particle{testVertices[2], 0.f} 141 | }; 142 | 143 | 144 | static constexpr float third = 1.0f / 3.0f; 145 | auto centre = third * (testVertices[0] + testVertices[1] + testVertices[2]); 146 | csb::BendingConstraint b(0, 1, 2, glm::fastDistance(testVertices[2], centre), 1.f, testParticles); 147 | 148 | // When the constraint is weighted towards the tip, we can see that the same correction takes place, 149 | // however now the entire load has been shifted to the other vertices, 150 | testVertices[2].x = 6.f; 151 | b.project(testParticles); 152 | 153 | const auto expected1 = glm::vec3(2.f, 0.f, 0.f); 154 | const auto expected2 = glm::vec3(2.f, 2.f, 0.f); 155 | const auto expected3 = glm::vec3(6.f, 1.f, 0.f); 156 | EXPECT_VEC3_EQ(expected1, testVertices[0]); 157 | EXPECT_VEC3_EQ(expected2, testVertices[1]); 158 | EXPECT_VEC3_EQ(expected3, testVertices[2]); 159 | } 160 | -------------------------------------------------------------------------------- /tests/src/DistanceConstraintTests.cpp: -------------------------------------------------------------------------------- 1 | #include "gtest/gtest.h" 2 | #include "DistanceConstraint.h" 3 | #include "UtilMacros.h" 4 | 5 | 6 | TEST(DistanceConstraint, constructor) 7 | { 8 | csb::DistanceConstraint d(0, 1, 0.f); 9 | EXPECT_EQ(0, d.getParticleIndex1()); 10 | EXPECT_EQ(1, d.getParticleIndex2()); 11 | EXPECT_EQ(0.f, d.getRest()); 12 | 13 | csb::DistanceConstraint dCopy(d); 14 | EXPECT_EQ(0, dCopy.getParticleIndex1()); 15 | EXPECT_EQ(1, dCopy.getParticleIndex2()); 16 | EXPECT_EQ(0.f, dCopy.getRest()); 17 | 18 | csb::DistanceConstraint dMove(std::move(d)); 19 | EXPECT_EQ(0, dMove.getParticleIndex1()); 20 | EXPECT_EQ(1, dMove.getParticleIndex2()); 21 | EXPECT_EQ(0.f, dMove.getRest()); 22 | } 23 | 24 | TEST(DistanceConstraint, assignment) 25 | { 26 | csb::DistanceConstraint d(0, 1, 0.f); 27 | EXPECT_EQ(0, d.getParticleIndex1()); 28 | EXPECT_EQ(1, d.getParticleIndex2()); 29 | EXPECT_EQ(0.f, d.getRest()); 30 | 31 | csb::DistanceConstraint dCopy; 32 | dCopy = d; 33 | EXPECT_EQ(0, dCopy.getParticleIndex1()); 34 | EXPECT_EQ(1, dCopy.getParticleIndex2()); 35 | EXPECT_EQ(0.f, dCopy.getRest()); 36 | 37 | csb::DistanceConstraint dMove; 38 | dMove = std::move(d); 39 | EXPECT_EQ(0, dMove.getParticleIndex1());; 40 | EXPECT_EQ(1, dMove.getParticleIndex2()); 41 | EXPECT_EQ(0.f, dMove.getRest()); 42 | } 43 | 44 | TEST(DistanceConstraint, projection) 45 | { 46 | std::vector testVertices { 47 | {0.f, 0.f, 0.f}, 48 | {0.f, 2.f, 0.f} 49 | }; 50 | std::vector testParticles { 51 | csb::Particle{testVertices[0], 1.f}, 52 | csb::Particle{testVertices[1], 1.f} 53 | }; 54 | 55 | 56 | // Set the constrained distance to half the current distance 57 | csb::DistanceConstraint d(0, 1, 1.f); 58 | 59 | d.project(testParticles); 60 | 61 | const auto expected1 = glm::vec3(0.f, 0.5f, 0.f); 62 | const auto expected2 = glm::vec3(0.f, 1.5f, 0.f); 63 | EXPECT_VEC3_EQ(expected1, testVertices[0]); 64 | EXPECT_VEC3_EQ(expected2, testVertices[1]); 65 | } 66 | 67 | TEST(DistanceConstraint, weightedProjection) 68 | { 69 | std::vector testVertices { 70 | {0.f, 0.f, 0.f}, 71 | {0.f, 2.f, 0.f} 72 | }; 73 | std::vector testParticles { 74 | csb::Particle{testVertices[0], 0.f}, 75 | csb::Particle{testVertices[1], 1.f} 76 | }; 77 | 78 | 79 | // Set the constrained distance to half the current distance 80 | csb::DistanceConstraint d(0, 1, 1.f); 81 | 82 | d.project(testParticles); 83 | 84 | const auto expected1 = glm::vec3(0.f, 0.0f, 0.f); 85 | const auto expected2 = glm::vec3(0.f, 1.0f, 0.f); 86 | EXPECT_VEC3_EQ(expected1, testVertices[0]); 87 | EXPECT_VEC3_EQ(expected2, testVertices[1]); 88 | } 89 | -------------------------------------------------------------------------------- /tests/src/EdgeTests.cpp: -------------------------------------------------------------------------------- 1 | #include "gtest/gtest.h" 2 | #include "Edge.h" 3 | #include 4 | 5 | TEST(Edge, constructor) 6 | { 7 | csb::Edge e(0,1); 8 | EXPECT_EQ(0, e.p.first); 9 | EXPECT_EQ(1, e.p.second); 10 | 11 | csb::Edge eCopy(e); 12 | EXPECT_EQ(e.p.first, eCopy.p.first); 13 | EXPECT_EQ(e.p.second, eCopy.p.second); 14 | 15 | csb::Edge eMove(std::move(e)); 16 | EXPECT_EQ(eCopy.p.first, eMove.p.first); 17 | EXPECT_EQ(eCopy.p.second, eMove.p.second); 18 | } 19 | 20 | TEST(Edge, assignment) 21 | { 22 | csb::Edge e(0,1); 23 | EXPECT_EQ(0, e.p.first); 24 | EXPECT_EQ(1, e.p.second); 25 | 26 | csb::Edge eCopy; 27 | eCopy = e; 28 | EXPECT_EQ(e.p.first, eCopy.p.first); 29 | EXPECT_EQ(e.p.second, eCopy.p.second); 30 | 31 | csb::Edge eMove; 32 | eMove = std::move(e); 33 | EXPECT_EQ(eCopy.p.first, eMove.p.first); 34 | EXPECT_EQ(eCopy.p.second, eMove.p.second); 35 | } 36 | 37 | TEST(Edge, sorted) 38 | { 39 | csb::Edge e(10,0); 40 | EXPECT_TRUE(e.p.first < e.p.second); 41 | EXPECT_EQ(0, e.p.first); 42 | EXPECT_EQ(10, e.p.second); 43 | } 44 | 45 | TEST(Edge, equality) 46 | { 47 | csb::Edge a(10, 0); 48 | csb::Edge b(0, 10); 49 | csb::Edge c(0, 10); 50 | csb::Edge d(5, 10); 51 | 52 | EXPECT_TRUE(a == b); 53 | EXPECT_TRUE(a == c); 54 | EXPECT_TRUE(b == c); 55 | 56 | EXPECT_FALSE(a == d); 57 | EXPECT_FALSE(b == d); 58 | EXPECT_FALSE(c == d); 59 | } 60 | 61 | TEST(Edge, hash) 62 | { 63 | std::unordered_set set; 64 | csb::Edge e(1,2); 65 | set.insert(e); 66 | 67 | EXPECT_TRUE(*set.begin() == e); 68 | } 69 | -------------------------------------------------------------------------------- /tests/src/ParticleTests.cpp: -------------------------------------------------------------------------------- 1 | #include "gtest/gtest.h" 2 | #include "Particle.h" 3 | 4 | TEST(Particle, constructor) 5 | { 6 | glm::vec3 position(0.f); 7 | 8 | csb::Particle p(position, 0.f); 9 | EXPECT_EQ(position, *p.m_pos); 10 | EXPECT_EQ(0.f, p.m_invMass); 11 | 12 | csb::Particle pCopy(p); 13 | EXPECT_EQ(position, *pCopy.m_pos); 14 | EXPECT_EQ(0.f, pCopy.m_invMass); 15 | 16 | csb::Particle pMove(std::move(p)); 17 | EXPECT_EQ(*pCopy.m_pos, *pMove.m_pos); 18 | EXPECT_EQ(pCopy.m_invMass, pMove.m_invMass); 19 | } 20 | 21 | TEST(Particle, assignment) 22 | { 23 | glm::vec3 position(0.f); 24 | 25 | csb::Particle p(position, 0.f); 26 | EXPECT_EQ(position, *p.m_pos); 27 | EXPECT_EQ(0.f, p.m_invMass); 28 | 29 | csb::Particle pCopy; 30 | pCopy = p; 31 | EXPECT_EQ(*p.m_pos, *pCopy.m_pos); 32 | EXPECT_EQ(p.m_invMass, pCopy.m_invMass); 33 | 34 | csb::Particle pMove; 35 | pMove = std::move(p); 36 | EXPECT_EQ(*pCopy.m_pos, *pMove.m_pos); 37 | EXPECT_EQ(pCopy.m_invMass, pMove.m_invMass); 38 | } 39 | 40 | TEST(Particle, manipulation) 41 | { 42 | glm::vec3 position(0.f); 43 | glm::vec3 nextPosition(7.5f, 8.5f, 9.5f); 44 | 45 | csb::Particle p(position, 0.f); 46 | 47 | *p.m_pos = nextPosition; 48 | p.m_invMass = 1.f; 49 | EXPECT_EQ(nextPosition, *p.m_pos); 50 | EXPECT_EQ(position, *p.m_pos); 51 | EXPECT_EQ(glm::vec3(0.f), p.m_prevPos); 52 | EXPECT_EQ(1.f, p.m_invMass); 53 | } 54 | -------------------------------------------------------------------------------- /tests/src/PinConstraintTests.cpp: -------------------------------------------------------------------------------- 1 | #include "gtest/gtest.h" 2 | #include "PinConstraint.h" 3 | #include "UtilMacros.h" 4 | 5 | TEST(PinConstraint, constructor) 6 | { 7 | const auto pos = glm::vec3(0.f); 8 | csb::PinConstraint p(0, pos); 9 | EXPECT_EQ(0, p.getParticleIndex()); 10 | EXPECT_VEC3_EQ(pos, p.getPinPosition()); 11 | 12 | csb::PinConstraint pCopy(p); 13 | EXPECT_EQ(0, p.getParticleIndex()); 14 | EXPECT_VEC3_EQ(pos, p.getPinPosition()); 15 | 16 | csb::PinConstraint pMove(std::move(p)); 17 | EXPECT_EQ(0, p.getParticleIndex()); 18 | EXPECT_VEC3_EQ(pos, p.getPinPosition()); 19 | } 20 | 21 | TEST(PinConstraint, assignment) 22 | { 23 | const auto pos = glm::vec3(0.f); 24 | csb::PinConstraint p(0, pos); 25 | EXPECT_EQ(0, p.getParticleIndex()); 26 | EXPECT_VEC3_EQ(pos, p.getPinPosition()); 27 | 28 | csb::PinConstraint pCopy; 29 | pCopy = p; 30 | EXPECT_EQ(0, p.getParticleIndex()); 31 | EXPECT_VEC3_EQ(pos, p.getPinPosition()); 32 | 33 | csb::PinConstraint pMove; 34 | pMove = std::move(p); 35 | EXPECT_EQ(0, p.getParticleIndex()); 36 | EXPECT_VEC3_EQ(pos, p.getPinPosition()); 37 | } 38 | 39 | TEST(PinConstraint, projection) 40 | { 41 | 42 | auto pos = glm::vec3(0.f); 43 | const auto expectedPos = pos; 44 | std::vector testParticles {{pos, 1.f}}; 45 | // Set the constrained distance to half the current distance 46 | csb::PinConstraint p(0, pos); 47 | pos.x = 10.f; 48 | pos.y = 12.f; 49 | pos.z = 15.f; 50 | p.project(testParticles); 51 | 52 | EXPECT_VEC3_EQ(expectedPos, pos); 53 | } 54 | 55 | -------------------------------------------------------------------------------- /tests/src/SelfCollisionRaysTests.cpp: -------------------------------------------------------------------------------- 1 | #include "gtest/gtest.h" 2 | #include "SelfCollisionRaysConstraint.h" 3 | #include "DistanceConstraint.h" 4 | #include "gtx/intersect.hpp" 5 | #include "gtx/fast_square_root.hpp" 6 | #include "Solver.h" 7 | #include "UtilMacros.h" 8 | 9 | TEST(SelfCollisionRaysConstraint, constructor) 10 | { 11 | csb::SelfCollisionRaysConstraint s; 12 | 13 | csb::SelfCollisionRaysConstraint sCopy(s); 14 | 15 | csb::SelfCollisionRaysConstraint sMove(std::move(s)); 16 | } 17 | 18 | TEST(SelfCollisionRaysConstraint, assignment) 19 | { 20 | csb::SelfCollisionRaysConstraint s; 21 | 22 | csb::SelfCollisionRaysConstraint sCopy; 23 | sCopy = s; 24 | 25 | csb::SelfCollisionRaysConstraint sMove; 26 | sMove = std::move(s); 27 | } 28 | 29 | 30 | TEST(SelfCollisionRaysConstraint, resolution) 31 | { 32 | 33 | csb::Solver solver; 34 | solver.setTotalForce({0.f, -0.125f, 0.f}); 35 | solver.setDamping(0.f); 36 | 37 | 38 | std::vector> meshes {std::make_shared()}; 39 | meshes[0]->load("../demo/models/rayTest.obj"); 40 | meshes[0]->init(); 41 | meshes[0]->setParticleInverseMass(4, 0.f); 42 | meshes[0]->setParticleInverseMass(5, 0.f); 43 | meshes[0]->setParticleInverseMass(6, 0.f); 44 | solver.addSimulatedMesh(meshes[0]); 45 | solver.addContinuousCollision(new csb::SelfCollisionRaysConstraint); 46 | 47 | // Hash the vertices 48 | const auto& verts = meshes[0]->getVertices(); 49 | const auto& v = verts[3]; 50 | 51 | const glm::vec3 expectedStartPos(0.f, -0.25f, 0.f); 52 | EXPECT_VEC3_EQ(v, expectedStartPos); 53 | 54 | solver.step(1.f); 55 | 56 | const glm::vec3 expectedSecondPos(0.f, -0.375f, 0.f); 57 | EXPECT_VEC3_EQ(v, expectedSecondPos); 58 | 59 | solver.step(1.f); 60 | 61 | const glm::vec3 expectedIntersectPos(0.f, -0.625f, 0.f); 62 | EXPECT_VEC3_EQ(v, expectedIntersectPos); 63 | 64 | solver.step(1.f); 65 | 66 | // Intersection is detected from previous step, and then corrected in this one 67 | const glm::vec3 expectedEndPos(0.f, -0.5f, 0.f); 68 | EXPECT_VEC3_EQ(v, expectedEndPos); 69 | 70 | } 71 | -------------------------------------------------------------------------------- /tests/src/SelfCollisionSpheresTests.cpp: -------------------------------------------------------------------------------- 1 | #include "gtest/gtest.h" 2 | #include "SelfCollisionSpheresConstraint.h" 3 | #include "DistanceConstraint.h" 4 | #include "gtx/fast_square_root.hpp" 5 | #include "Solver.h" 6 | #include "UtilMacros.h" 7 | 8 | 9 | TEST(SelfCollisionSpheresConstraint, constructor) 10 | { 11 | csb::SelfCollisionSpheresConstraint s(1.f); 12 | EXPECT_EQ(1.f, s.getSphereDiameter()); 13 | 14 | csb::SelfCollisionSpheresConstraint sCopy(s); 15 | EXPECT_EQ(s.getSphereDiameter(), sCopy.getSphereDiameter()); 16 | 17 | csb::SelfCollisionSpheresConstraint sMove(std::move(s)); 18 | EXPECT_EQ(sCopy.getSphereDiameter(), sMove.getSphereDiameter()); 19 | } 20 | 21 | TEST(SelfCollisionSpheresConstraint, assignment) 22 | { 23 | csb::SelfCollisionSpheresConstraint s(1.f); 24 | EXPECT_EQ(1.f, s.getSphereDiameter()); 25 | 26 | csb::SelfCollisionSpheresConstraint sCopy; 27 | sCopy = s; 28 | EXPECT_EQ(s.getSphereDiameter(), sCopy.getSphereDiameter()); 29 | 30 | csb::SelfCollisionSpheresConstraint sMove; 31 | sMove = std::move(s); 32 | EXPECT_EQ(sCopy.getSphereDiameter(), sMove.getSphereDiameter()); 33 | } 34 | 35 | TEST(SelfCollisionSpheresConstraint, resolution) 36 | { 37 | 38 | csb::Solver solver; 39 | 40 | std::vector> meshes {std::make_shared()}; 41 | meshes[0]->load("../demo/models/xyplane.obj"); 42 | meshes[0]->init(); 43 | solver.addSimulatedMesh(meshes[0]); 44 | const auto sphereRadius = meshes[0]->getShortestEdgeLength() * 1.4f; 45 | solver.addContinuousCollision(new csb::SelfCollisionSpheresConstraint(sphereRadius)); 46 | 47 | // Hash the vertices 48 | csb::SpatialHash::SpatialHashTable hashTable; 49 | hashTable.m_hashTable.resize(9); 50 | const auto& verts = meshes[0]->getVertices(); 51 | 52 | const auto oldV1 = verts[1]; 53 | // Make sure we are using diagonal verts 54 | auto unConstrainedDist = glm::fastDistance(verts[1], verts[3]); 55 | EXPECT_FLOAT_EQ(1.4142135f, unConstrainedDist); 56 | 57 | // Create a distance constraint that will violate our collision constraint 58 | meshes[0]->addConstraint(new csb::DistanceConstraint(1, 3, 1.f)); 59 | meshes[0]->projectConstraints(); 60 | 61 | // Make sure our distance constraint has violated the collision constraint 62 | const auto constrainedDist = glm::fastDistance(verts[1], verts[3]); 63 | EXPECT_FLOAT_EQ(1.f, constrainedDist); 64 | 65 | // get the vel so we can acount for it 66 | const auto vel = oldV1 - verts[1]; 67 | 68 | // This should prevent non-neighbours from being closer than the sphereRadius 69 | solver.step(0.1f); 70 | 71 | // We need to account for the velocity which was added by the distance constraint 72 | const auto expectedDist = sphereRadius - glm::fastLength(vel); 73 | unConstrainedDist = glm::fastDistance(verts[1], verts[3]); 74 | EXPECT_FLOAT_EQ(expectedDist, unConstrainedDist); 75 | 76 | const auto& adjacency = meshes[0]->getAdjacencyInfo(); 77 | const glm::vec3 expectedPos(0.f, 1.f, 0.f); 78 | for (size_t i = 0; i < verts.size(); ++i) 79 | { 80 | const auto& v1 = verts[i]; 81 | const auto& neighbours = adjacency[i]; 82 | std::unordered_set neighbourSet(neighbours.begin(), neighbours.end()); 83 | for (size_t j = 0; j < verts.size(); ++j) 84 | { 85 | if (i == j || neighbourSet.count(j)) continue; 86 | const auto& v2 = verts[j]; 87 | const auto dist = glm::fastDistance(v1, v2); 88 | EXPECT_GE(dist, expectedDist); 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /tests/src/SolverTests.cpp: -------------------------------------------------------------------------------- 1 | #include "gtest/gtest.h" 2 | #include "Solver.h" 3 | #include "UtilMacros.h" 4 | 5 | 6 | TEST(Solver, constructor) 7 | { 8 | glm::vec3 position(0.f); 9 | 10 | csb::Solver s; 11 | const glm::vec3 gravity(0.f, -9.81f, 0.f); 12 | s.setTotalForce(gravity); 13 | s.setDamping(0.1f); 14 | EXPECT_FLOAT_EQ(0.1f, s.getDamping()); 15 | EXPECT_VEC3_EQ(gravity, s.getTotalForce()); 16 | 17 | csb::Solver sCopy(s); 18 | EXPECT_FLOAT_EQ(s.getDamping(), sCopy.getDamping()); 19 | EXPECT_VEC3_EQ(s.getTotalForce(), sCopy.getTotalForce()); 20 | 21 | csb::Solver sMove(std::move(s)); 22 | EXPECT_FLOAT_EQ(sCopy.getDamping(), sMove.getDamping()); 23 | EXPECT_VEC3_EQ(sCopy.getTotalForce(), sMove.getTotalForce()); 24 | } 25 | 26 | TEST(Solver, assignment) 27 | { 28 | glm::vec3 position(0.f); 29 | 30 | csb::Solver s; 31 | const glm::vec3 gravity(0.f, -9.81f, 0.f); 32 | s.setTotalForce(gravity); 33 | s.setDamping(0.1f); 34 | EXPECT_FLOAT_EQ(0.1f, s.getDamping()); 35 | EXPECT_VEC3_EQ(gravity, s.getTotalForce()); 36 | 37 | csb::Solver sCopy; 38 | sCopy = s; 39 | EXPECT_FLOAT_EQ(s.getDamping(), sCopy.getDamping()); 40 | EXPECT_VEC3_EQ(s.getTotalForce(), sCopy.getTotalForce()); 41 | 42 | csb::Solver sMove; 43 | sMove = std::move(s); 44 | EXPECT_FLOAT_EQ(sCopy.getDamping(), sMove.getDamping()); 45 | EXPECT_VEC3_EQ(sCopy.getTotalForce(), sMove.getTotalForce()); 46 | } 47 | 48 | TEST(Solver, integration) 49 | { 50 | std::shared_ptr mesh = std::make_shared(); 51 | mesh->load("../demo/models/xyplane.obj"); 52 | mesh->init(); 53 | const auto& vertices = mesh->getVertices(); 54 | 55 | csb::Solver s; 56 | const glm::vec3 gravity(0.f, -10.f, 0.f); 57 | s.setTotalForce(gravity); 58 | s.setDamping(0.0f); 59 | 60 | s.addSimulatedMesh(mesh); 61 | s.step(0.1f); 62 | 63 | // reduced to y axis, -10 * 0.1 * 0.1 = -0.1 64 | for (const auto& v : vertices) 65 | { 66 | EXPECT_FLOAT_EQ(v.y, -0.1f); 67 | } 68 | 69 | } 70 | 71 | TEST(Solver, integration2) 72 | { 73 | std::shared_ptr mesh = std::make_shared(); 74 | mesh->load("../demo/models/xyplane.obj"); 75 | mesh->init(); 76 | const auto& vertices = mesh->getVertices(); 77 | 78 | csb::Solver s; 79 | const glm::vec3 gravity(0.f, -10.f, 0.f); 80 | s.setTotalForce(gravity); 81 | s.setDamping(0.0f); 82 | 83 | s.addSimulatedMesh(mesh); 84 | s.step(0.1f); 85 | s.step(0.1f); 86 | s.step(0.1f); 87 | 88 | // reduced to y axis, -10 * 0.1 * 0.1 = -0.1 89 | for (const auto& v : vertices) 90 | { 91 | EXPECT_FLOAT_EQ(v.y, -0.6f); 92 | } 93 | 94 | } 95 | 96 | TEST(Solver, damping) 97 | { 98 | std::shared_ptr mesh = std::make_shared(); 99 | mesh->load("../demo/models/xyplane.obj"); 100 | mesh->init(); 101 | const auto& vertices = mesh->getVertices(); 102 | 103 | csb::Solver s; 104 | const glm::vec3 gravity(0.f, -10.f, 0.f); 105 | s.setTotalForce(gravity); 106 | s.setDamping(1.f); 107 | 108 | s.addSimulatedMesh(mesh); 109 | s.step(0.1f); 110 | s.step(0.1f); 111 | s.step(0.1f); 112 | 113 | // reduced to y axis, -10 * 0.1 * 0.1 = -0.1 114 | for (const auto& v : vertices) 115 | { 116 | EXPECT_FLOAT_EQ(v.y, -0.3f); 117 | } 118 | 119 | } 120 | 121 | -------------------------------------------------------------------------------- /tests/src/SpatialHashTests.cpp: -------------------------------------------------------------------------------- 1 | #include "gtest/gtest.h" 2 | #include "SpatialHash.h" 3 | #include "UtilMacros.h" 4 | 5 | TEST(SpatialHash, calculateCell) 6 | { 7 | static constexpr auto cellsize = 10.f; 8 | static constexpr auto celloffset = 10000.f; 9 | 10 | auto cell = csb::SpatialHash::calcCell(glm::vec3(0.f), cellsize, celloffset); 11 | glm::ivec3 expectedCell (1000); 12 | glm::ivec3 notExpectedCell (1); 13 | EXPECT_VEC3_EQ(expectedCell, cell); 14 | EXPECT_VEC3_NE(notExpectedCell, cell); 15 | 16 | cell = csb::SpatialHash::calcCell(glm::vec3(100.f, 12.f, 55.f), cellsize, celloffset); 17 | expectedCell = glm::ivec3(1010, 1001, 1005); 18 | notExpectedCell = glm::ivec3(100); 19 | EXPECT_VEC3_EQ(expectedCell, cell); 20 | EXPECT_VEC3_NE(notExpectedCell, cell); 21 | 22 | cell = csb::SpatialHash::calcCell(glm::vec3(345.f, -324.f, -68.f), cellsize, celloffset); 23 | expectedCell = glm::ivec3(1034, 967, 993); 24 | notExpectedCell = glm::ivec3(0); 25 | EXPECT_VEC3_EQ(expectedCell, cell); 26 | EXPECT_VEC3_NE(notExpectedCell, cell); 27 | } 28 | 29 | TEST(SpatialHash, hashCell) 30 | { 31 | static constexpr auto tableSize = 999ul; 32 | 33 | // Expected results calculated using wolfram 34 | 35 | glm::ivec3 cell(0); 36 | auto hash = csb::SpatialHash::hashCell(cell, tableSize); 37 | EXPECT_EQ(hash, 0); 38 | 39 | cell = glm::ivec3(2,3,4); 40 | hash = csb::SpatialHash::hashCell(cell, tableSize); 41 | EXPECT_EQ(hash, 674); 42 | 43 | 44 | cell = glm::ivec3(-100,2,-11); 45 | hash = csb::SpatialHash::hashCell(cell, tableSize); 46 | EXPECT_EQ(hash, 236); 47 | } 48 | 49 | TEST(SpatialHash, hashParticle) 50 | { 51 | static constexpr auto cellsize = 10.f; 52 | static constexpr auto celloffset = 0.f; 53 | static constexpr auto tableSize = 999ul; 54 | 55 | // Expected results calculated using wolfram 56 | 57 | glm::vec3 coord(0.f); 58 | auto hash = csb::SpatialHash::hashParticle(coord, tableSize, cellsize, celloffset); 59 | EXPECT_EQ(hash, 0); 60 | 61 | coord = glm::vec3(100.f, 12.f, 55.f); 62 | hash = csb::SpatialHash::hashParticle(coord, tableSize, cellsize, celloffset); 63 | EXPECT_EQ(hash, 598); 64 | 65 | 66 | coord = glm::vec3(345.f, -324.f, -68.f); 67 | hash = csb::SpatialHash::hashParticle(coord, tableSize, cellsize, celloffset); 68 | EXPECT_EQ(hash, 802); 69 | 70 | 71 | coord = glm::vec3(-126.f, -89.f, 14.f); 72 | auto cell = csb::SpatialHash::calcCell(coord, cellsize, celloffset); 73 | EXPECT_EQ(csb::SpatialHash::hashCell(cell, tableSize), csb::SpatialHash::hashParticle(coord, tableSize, cellsize, celloffset)); 74 | } 75 | -------------------------------------------------------------------------------- /tests/src/SphereCollisionConstraintTests.cpp: -------------------------------------------------------------------------------- 1 | #include "gtest/gtest.h" 2 | #include "SphereCollisionConstraint.h" 3 | #include "UtilMacros.h" 4 | 5 | 6 | TEST(SphereCollisionConstraint, constructor) 7 | { 8 | const glm::vec3 centre(0.f, 0.f, 0.f); 9 | csb::SphereCollisionConstraint s(centre, 1.f); 10 | EXPECT_VEC3_EQ(centre, s.getCentre()); 11 | EXPECT_EQ(1.f, s.getRadius()); 12 | 13 | 14 | csb::SphereCollisionConstraint sCopy(s); 15 | EXPECT_VEC3_EQ(s.getCentre(), sCopy.getCentre()); 16 | EXPECT_EQ(s.getRadius(), sCopy.getRadius()); 17 | 18 | csb::SphereCollisionConstraint sMove(std::move(s)); 19 | EXPECT_VEC3_EQ(sCopy.getCentre(), sMove.getCentre()); 20 | EXPECT_EQ(sCopy.getRadius(), sMove.getRadius()); 21 | } 22 | 23 | TEST(SphereCollisionConstraint, assignment) 24 | { 25 | const glm::vec3 centre(0.f, 0.f, 0.f); 26 | csb::SphereCollisionConstraint s(centre, 1.f); 27 | EXPECT_VEC3_EQ(centre, s.getCentre()); 28 | EXPECT_EQ(1.f, s.getRadius()); 29 | 30 | 31 | csb::SphereCollisionConstraint sCopy; 32 | sCopy = s; 33 | EXPECT_VEC3_EQ(s.getCentre(), sCopy.getCentre()); 34 | EXPECT_EQ(s.getRadius(), sCopy.getRadius()); 35 | 36 | csb::SphereCollisionConstraint sMove; 37 | sMove = std::move(s); 38 | EXPECT_VEC3_EQ(sCopy.getCentre(), sMove.getCentre()); 39 | EXPECT_EQ(sCopy.getRadius(), sMove.getRadius()); 40 | } 41 | 42 | TEST(SphereCollisionConstraint, boundingCells) 43 | { 44 | csb::SphereCollisionConstraint s({0.f, 0.f, 0.f}, 1.f); 45 | s.setCellSize(1.f); 46 | s.setHashTableSize(99); 47 | s.init(); 48 | 49 | const auto& lowCells = s.cells(); 50 | // cells from -1 to 1 in x, y and z, hence the result is 3^3 = 27 51 | EXPECT_EQ(27, lowCells.size()); 52 | 53 | s.setCentre({100.f, 5.f, -5.5f}); 54 | s.setRadius(50.5f); 55 | s.setCellSize(15.f); 56 | s.setHashTableSize(999); 57 | s.updateBoundingBoxCells(); 58 | 59 | const auto& highCells = s.cells(); 60 | // this has 8x8x8 cells so we expect 512 in total 61 | EXPECT_EQ(512, highCells.size()); 62 | } 63 | 64 | TEST(SphereCollisionConstraint, resolution) 65 | { 66 | std::vector testVertices { 67 | {0.f, 0.5f, 0.f} 68 | }; 69 | std::vector testParticles { 70 | csb::Particle{testVertices[0], 1.f} 71 | }; 72 | 73 | static constexpr auto cellSize = 1.f; 74 | static constexpr auto celloffset = 10000.f; 75 | 76 | csb::SpatialHash::SpatialHashTable hashTable; 77 | hashTable.m_hashTable.resize(9); 78 | hashTable.m_hashTable[csb::SpatialHash::hashParticle(testVertices[0], 9, cellSize, celloffset)].emplace_back(0,0); 79 | 80 | csb::SphereCollisionConstraint s({0.f, 0.f, 0.f}, 1.f); 81 | s.setCellSize(cellSize); 82 | s.setHashTableSize(hashTable.m_hashTable.size()); 83 | s.init(); 84 | 85 | // This should push the particle up until it is outside of the sphere 86 | s.project(testParticles, hashTable); 87 | 88 | const glm::vec3 expectedPos(0.f, 1.f, 0.f); 89 | EXPECT_VEC3_EQ(testVertices[0], expectedPos); 90 | } 91 | 92 | -------------------------------------------------------------------------------- /tests/src/TriMeshTests.cpp: -------------------------------------------------------------------------------- 1 | #include "gtest/gtest.h" 2 | #include "TriMesh.h" 3 | 4 | TEST(TriMesh, constructor) 5 | { 6 | csb::TriMesh mesh; 7 | 8 | EXPECT_TRUE(0 == mesh.getNVerts()); 9 | EXPECT_TRUE(0 == mesh.getNVertData() && 0 == mesh.getNAttribData(csb::VERTEX)); 10 | EXPECT_TRUE(0 == mesh.getNUVs()); 11 | EXPECT_TRUE(0 == mesh.getNUVData() && 0 == mesh.getNAttribData(csb::UV)); 12 | EXPECT_TRUE(0 == mesh.getNNorms()); 13 | EXPECT_TRUE(0 == mesh.getNNormData() && 0 == mesh.getNAttribData(csb::NORMAL)); 14 | EXPECT_TRUE(0 == mesh.getNIndices()); 15 | EXPECT_TRUE(0 == mesh.getNIndicesData()); 16 | EXPECT_TRUE(0 == mesh.getNData()); 17 | EXPECT_TRUE(0 == mesh.getNEdges()); 18 | } 19 | 20 | 21 | TEST(TriMesh, loadcube) 22 | { 23 | csb::TriMesh mesh; 24 | mesh.load("../demo/models/cube.obj"); 25 | 26 | // Data compared to stats reported by Maya. 27 | EXPECT_EQ(8, mesh.getNVerts()); 28 | EXPECT_EQ(24, mesh.getNVertData()); 29 | EXPECT_EQ(mesh.getNVertData(), mesh.getNAttribData(csb::VERTEX)); 30 | EXPECT_EQ(0, mesh.getNUVs()); 31 | EXPECT_EQ(0, mesh.getNUVData()); 32 | EXPECT_EQ(mesh.getNUVData(), mesh.getNAttribData(csb::UV)); 33 | EXPECT_EQ(0 , mesh.getNNorms()); 34 | EXPECT_EQ(0, mesh.getNNormData()); 35 | EXPECT_EQ(mesh.getNNormData(), mesh.getNAttribData(csb::NORMAL)); 36 | EXPECT_EQ(36, mesh.getNIndices()); 37 | EXPECT_EQ(36, mesh.getNIndicesData()); 38 | EXPECT_EQ(24, mesh.getNData()); 39 | EXPECT_EQ(18, mesh.getNEdges()); 40 | 41 | } 42 | 43 | TEST(TriMesh, reset) 44 | { 45 | csb::TriMesh mesh; 46 | mesh.load("../demo/models/cube.obj"); 47 | mesh.reset(); 48 | 49 | EXPECT_TRUE(0 == mesh.getNVerts()); 50 | EXPECT_TRUE(0 == mesh.getNVertData() && 0 == mesh.getNAttribData(csb::VERTEX)); 51 | EXPECT_TRUE(0 == mesh.getNUVs()); 52 | EXPECT_TRUE(0 == mesh.getNUVData() && 0 == mesh.getNAttribData(csb::UV)); 53 | EXPECT_TRUE(0 == mesh.getNNorms()); 54 | EXPECT_TRUE(0 == mesh.getNNormData() && 0 == mesh.getNAttribData(csb::NORMAL)); 55 | EXPECT_TRUE(0 == mesh.getNIndices()); 56 | EXPECT_TRUE(0 == mesh.getNIndicesData()); 57 | EXPECT_TRUE(0 == mesh.getNData()); 58 | EXPECT_TRUE(0 == mesh.getNEdges()); 59 | } 60 | 61 | TEST(TriMesh, copyAndMove) 62 | { 63 | csb::TriMesh mesh; 64 | mesh.load("../demo/models/cube.obj"); 65 | 66 | csb::TriMesh copy = mesh; 67 | 68 | // Data compared to stats reported by Maya. 69 | EXPECT_EQ(copy.getNVerts(), mesh.getNVerts()); 70 | EXPECT_EQ(copy.getNVertData(), mesh.getNVertData()); 71 | EXPECT_EQ(copy.getNVertData(), mesh.getNVertData()); 72 | EXPECT_EQ(copy.getNUVs(), mesh.getNUVs()); 73 | EXPECT_EQ(copy.getNUVData(), mesh.getNUVData()); 74 | EXPECT_EQ(copy.getNUVData(), mesh.getNUVData()); 75 | EXPECT_EQ(copy.getNNorms(), mesh.getNNorms()); 76 | EXPECT_EQ(copy.getNNormData(), mesh.getNNormData()); 77 | EXPECT_EQ(copy.getNNormData(), mesh.getNNormData()); 78 | EXPECT_EQ(copy.getNIndices(), mesh.getNIndices()); 79 | EXPECT_EQ(copy.getNIndicesData(), mesh.getNIndicesData()); 80 | EXPECT_EQ(copy.getNData(), mesh.getNData()); 81 | EXPECT_EQ(copy.getNEdges(), mesh.getNEdges()); 82 | 83 | csb::TriMesh move = std::move(mesh); 84 | 85 | // Data compared to stats reported by Maya. 86 | EXPECT_EQ(move.getNVerts(), copy.getNVerts()); 87 | EXPECT_EQ(move.getNVertData(), copy.getNVertData()); 88 | EXPECT_EQ(move.getNVertData(), copy.getNVertData()); 89 | EXPECT_EQ(move.getNUVs(), copy.getNUVs()); 90 | EXPECT_EQ(move.getNUVData(), copy.getNUVData()); 91 | EXPECT_EQ(move.getNUVData(), copy.getNUVData()); 92 | EXPECT_EQ(move.getNNorms(), copy.getNNorms()); 93 | EXPECT_EQ(move.getNNormData(), copy.getNNormData()); 94 | EXPECT_EQ(move.getNNormData(), copy.getNNormData()); 95 | EXPECT_EQ(move.getNIndices(), copy.getNIndices()); 96 | EXPECT_EQ(move.getNIndicesData(), copy.getNIndicesData()); 97 | EXPECT_EQ(move.getNData(), copy.getNData()); 98 | EXPECT_EQ(move.getNEdges(), copy.getNEdges()); 99 | 100 | copy = move; 101 | 102 | // Data compared to stats reported by Maya. 103 | EXPECT_EQ(copy.getNVerts(), move.getNVerts()); 104 | EXPECT_EQ(copy.getNVertData(), move.getNVertData()); 105 | EXPECT_EQ(copy.getNVertData(), move.getNVertData()); 106 | EXPECT_EQ(copy.getNUVs(), move.getNUVs()); 107 | EXPECT_EQ(copy.getNUVData(), move.getNUVData()); 108 | EXPECT_EQ(copy.getNUVData(), move.getNUVData()); 109 | EXPECT_EQ(copy.getNNorms(), move.getNNorms()); 110 | EXPECT_EQ(copy.getNNormData(), move.getNNormData()); 111 | EXPECT_EQ(copy.getNNormData(), move.getNNormData()); 112 | EXPECT_EQ(copy.getNIndices(), move.getNIndices()); 113 | EXPECT_EQ(copy.getNIndicesData(), move.getNIndicesData()); 114 | EXPECT_EQ(copy.getNData(), move.getNData()); 115 | EXPECT_EQ(copy.getNEdges(), move.getNEdges()); 116 | } 117 | 118 | 119 | TEST(TriMesh, loadplane) 120 | { 121 | csb::TriMesh mesh; 122 | mesh.load("../demo/models/hdPlane.obj"); 123 | 124 | // Data compared to stats reported by Maya. 125 | EXPECT_EQ(961, mesh.getNVerts()); 126 | EXPECT_EQ(2883, mesh.getNVertData()); 127 | EXPECT_EQ(mesh.getNVertData(), mesh.getNAttribData(csb::VERTEX)); 128 | EXPECT_EQ(961, mesh.getNUVs()); 129 | EXPECT_EQ(1922, mesh.getNUVData()); 130 | EXPECT_EQ(mesh.getNUVData(), mesh.getNAttribData(csb::UV)); 131 | EXPECT_EQ(961 , mesh.getNNorms()); 132 | EXPECT_EQ(2883, mesh.getNNormData()); 133 | EXPECT_EQ(mesh.getNNormData(), mesh.getNAttribData(csb::NORMAL)); 134 | EXPECT_EQ(5400, mesh.getNIndices()); 135 | EXPECT_EQ(5400, mesh.getNIndicesData()); 136 | EXPECT_EQ(7688, mesh.getNData()); 137 | EXPECT_EQ(2760, mesh.getNEdges()); 138 | } 139 | 140 | TEST(TriMesh, adjacency) 141 | { 142 | csb::TriMesh mesh; 143 | mesh.load("../demo/models/cube.obj"); 144 | 145 | const auto& adjacency = mesh.getAdjacencyInfo(); 146 | EXPECT_EQ(8, adjacency.size()); 147 | 148 | // Cube adjacency from Maya 149 | EXPECT_EQ(5, adjacency[0].size()); 150 | EXPECT_EQ(4, adjacency[1].size()); 151 | EXPECT_EQ(4, adjacency[2].size()); 152 | EXPECT_EQ(5, adjacency[3].size()); 153 | EXPECT_EQ(5, adjacency[4].size()); 154 | EXPECT_EQ(4, adjacency[5].size()); 155 | EXPECT_EQ(4, adjacency[6].size()); 156 | EXPECT_EQ(5, adjacency[7].size()); 157 | } 158 | 159 | TEST(TriMesh, access) 160 | { 161 | csb::TriMesh mesh; 162 | mesh.load("../demo/models/cube.obj"); 163 | 164 | EXPECT_EQ(mesh.getVertices().size(), mesh.getNVerts()); 165 | EXPECT_EQ(mesh.getUVs().size(), mesh.getNUVs()); 166 | EXPECT_EQ(mesh.getNormals().size(), mesh.getNNorms()); 167 | EXPECT_EQ(mesh.getIndices().size(), mesh.getNIndices()); 168 | 169 | const auto vertData = mesh.getVertexData(); 170 | const auto& verts = mesh.getVertices(); 171 | const auto numVerts = mesh.getNVerts(); 172 | for (size_t i = 0; i < numVerts; ++i) 173 | { 174 | const auto index = i * 3; 175 | EXPECT_EQ(*(vertData + index), verts[i].x); 176 | EXPECT_EQ(*(vertData + index + 1), verts[i].y); 177 | EXPECT_EQ(*(vertData + index + 2), verts[i].z); 178 | } 179 | 180 | const auto uvData = mesh.getUVsData(); 181 | const auto& uvs = mesh.getUVs(); 182 | const auto numUVs = mesh.getNUVs(); 183 | for (size_t i = 0; i < numUVs; ++i) 184 | { 185 | const auto index = i * 2; 186 | EXPECT_EQ(*(uvData + index), uvs[i].x); 187 | EXPECT_EQ(*(uvData + index + 1), uvs[i].y); 188 | } 189 | 190 | const auto normalData = mesh.getNormalsData(); 191 | const auto& norms = mesh.getNormals(); 192 | const auto numNorms = mesh.getNNorms(); 193 | for (size_t i = 0; i < numNorms; ++i) 194 | { 195 | const auto index = i * 3; 196 | EXPECT_EQ(*(normalData + index), norms[i].x); 197 | EXPECT_EQ(*(normalData + index + 1), norms[i].y); 198 | EXPECT_EQ(*(normalData + index + 2), norms[i].z); 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /tests/src/UtilMacros.h: -------------------------------------------------------------------------------- 1 | #ifndef UTILMACROS_H 2 | #define UTILMACROS_H 3 | 4 | #define EXPECT_VEC3_EQ(A, B) \ 5 | EXPECT_FLOAT_EQ((A).x, (B).x); \ 6 | EXPECT_FLOAT_EQ((A).y, (B).y); \ 7 | EXPECT_FLOAT_EQ((A).z, (B).z); 8 | 9 | #define EXPECT_VEC3_NE(A, B) \ 10 | EXPECT_NE((A).x, (B).x); \ 11 | EXPECT_NE((A).y, (B).y); \ 12 | EXPECT_NE((A).z, (B).z); 13 | 14 | #endif // UTILMACROS_H 15 | -------------------------------------------------------------------------------- /tests/src/main.cpp: -------------------------------------------------------------------------------- 1 | #include "gtest/gtest.h" 2 | 3 | int main(int argc, char **argv) 4 | { 5 | ::testing::InitGoogleTest(&argc, argv); 6 | return RUN_ALL_TESTS(); 7 | } 8 | -------------------------------------------------------------------------------- /tests/tests.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = app 2 | TARGET = CSBTests 3 | 4 | OBJECTS_DIR = obj 5 | 6 | QT += opengl core gui 7 | CONFIG += console c++14 8 | CONFIG -= app_bundle 9 | 10 | HEADERS += \ 11 | src/UtilMacros.h 12 | 13 | SOURCES += \ 14 | src/main.cpp \ 15 | src/ParticleTests.cpp \ 16 | src/TriMeshTests.cpp \ 17 | src/SimulatedMeshTests.cpp \ 18 | src/DistanceConstraintTests.cpp \ 19 | src/BendingConstraintTests.cpp \ 20 | src/PinConstraintTests.cpp \ 21 | src/EdgeTests.cpp \ 22 | src/SpatialHashTests.cpp \ 23 | src/SphereCollisionConstraintTests.cpp \ 24 | src/SolverTests.cpp \ 25 | src/SelfCollisionSpheresTests.cpp \ 26 | src/SelfCollisionRaysTests.cpp 27 | 28 | DEPENDPATH += . ../csb 29 | INCLUDEPATH = ../csb/include 30 | INCLUDEPATH += \ 31 | /usr/local/include/glm/glm \ 32 | /usr/local/include/glm \ 33 | /usr/local/include 34 | 35 | 36 | QMAKE_CXXFLAGS += -O3 -std=c++14 -msse -msse2 -msse3 37 | 38 | LIBS += -L../csb/lib -lcsb -lgtest -pthread 39 | 40 | linux:{ 41 | LIBS += -lGL -lGLU -lGLEW -lassimp 42 | } 43 | 44 | mac:{ 45 | LIBS+= -L/usr/local/lib -lassimp 46 | QMAKE_CXXFLAGS += -arch x86_64 47 | } 48 | 49 | unix:{ 50 | # add the lib to rpath so it can be dynamically loaded 51 | QMAKE_LFLAGS += "-Wl,-rpath,\'\$$ORIGIN/../csb/lib\'" 52 | } 53 | 54 | 55 | --------------------------------------------------------------------------------