1 /********************************************************************************
2 * ReactPhysics3D physics library, http://www.reactphysics3d.com                 *
3 * Copyright (c) 2010-2016 Daniel Chappuis                                       *
4 *********************************************************************************
5 *                                                                               *
6 * This software is provided 'as-is', without any express or implied warranty.   *
7 * In no event will the authors be held liable for any damages arising from the  *
8 * use of this software.                                                         *
9 *                                                                               *
10 * Permission is granted to anyone to use this software for any purpose,         *
11 * including commercial applications, and to alter it and redistribute it        *
12 * freely, subject to the following restrictions:                                *
13 *                                                                               *
14 * 1. The origin of this software must not be misrepresented; you must not claim *
15 *    that you wrote the original software. If you use this software in a        *
16 *    product, an acknowledgment in the product documentation would be           *
17 *    appreciated but is not required.                                           *
18 *                                                                               *
19 * 2. Altered source versions must be plainly marked as such, and must not be    *
20 *    misrepresented as being the original software.                             *
21 *                                                                               *
22 * 3. This notice may not be removed or altered from any source distribution.    *
23 *                                                                               *
24 ********************************************************************************/
25 
26 // Libraries
27 #include "HeightField.h"
28 #include "PerlinNoise.h"
29 
30 // Constructor
HeightField(bool createRigidBody,reactphysics3d::PhysicsCommon & physicsCommon,rp3d::PhysicsWorld * physicsWorld)31 HeightField::HeightField(bool createRigidBody, reactphysics3d::PhysicsCommon& physicsCommon, rp3d::PhysicsWorld* physicsWorld)
32            : PhysicsObject(physicsCommon), mVBOVertices(GL_ARRAY_BUFFER),
33              mVBONormals(GL_ARRAY_BUFFER), mVBOTextureCoords(GL_ARRAY_BUFFER),
34              mVBOIndices(GL_ELEMENT_ARRAY_BUFFER) {
35 
36     // Compute the scaling matrix
37     //mScalingMatrix = openglframework::Matrix4::identity();
38     mScalingMatrix = openglframework::Matrix4(2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 1);
39 
40     // Generate the height field
41     generateHeightField();
42 
43     // Generate the graphics mesh
44     generateGraphicsMesh();
45 
46     // Create the collision shape for the rigid body (convex mesh shape) and
47     // do not forget to delete it at the end
48     mHeightFieldShape = mPhysicsCommon.createHeightFieldShape(NB_POINTS_WIDTH, NB_POINTS_LENGTH, mMinHeight, mMaxHeight,
49                                                    mHeightData, rp3d::HeightFieldShape::HeightDataType::HEIGHT_FLOAT_TYPE);
50     mHeightFieldShape->setScale(rp3d::Vector3(2, 2, 2));
51 
52     mPreviousTransform = rp3d::Transform::identity();
53 
54     // Create a body
55     if (createRigidBody) {
56         rp3d::RigidBody* body = physicsWorld->createRigidBody(mPreviousTransform);
57         mCollider = body->addCollider(mHeightFieldShape, rp3d::Transform::identity());
58         body->updateMassPropertiesFromColliders();
59         mBody = body;
60     }
61     else {
62         mBody = physicsWorld->createCollisionBody(mPreviousTransform);
63         mCollider = mBody->addCollider(mHeightFieldShape, rp3d::Transform::identity());
64     }
65 
66 
67     // Create the VBOs and VAO
68     createVBOAndVAO();
69 
70     mTransformMatrix = mTransformMatrix * mScalingMatrix;
71 }
72 
73 // Destructor
~HeightField()74 HeightField::~HeightField() {
75 
76     // Destroy the mesh
77     destroy();
78 
79     // Destroy the VBOs and VAO
80     mVBOIndices.destroy();
81     mVBOVertices.destroy();
82     mVBONormals.destroy();
83     mVBOTextureCoords.destroy();
84     mVAO.destroy();
85 
86     mPhysicsCommon.destroyHeightFieldShape(mHeightFieldShape);
87 }
88 
89 // Render the sphere at the correct position and with the correct orientation
render(openglframework::Shader & shader,const openglframework::Matrix4 & worldToCameraMatrix)90 void HeightField::render(openglframework::Shader& shader,
91                     const openglframework::Matrix4& worldToCameraMatrix) {
92 
93     // Bind the shader
94     shader.bind();
95 
96     // Set the model to camera matrix
97     shader.setMatrix4x4Uniform("localToWorldMatrix", mTransformMatrix);
98     shader.setMatrix4x4Uniform("worldToCameraMatrix", worldToCameraMatrix);
99 
100     // Set the normal matrix (inverse transpose of the 3x3 upper-left sub matrix of the
101     // model-view matrix)
102     const openglframework::Matrix4 localToCameraMatrix = worldToCameraMatrix * mTransformMatrix;
103     const openglframework::Matrix3 normalMatrix =
104                        localToCameraMatrix.getUpperLeft3x3Matrix().getInverse().getTranspose();
105     shader.setMatrix3x3Uniform("normalMatrix", normalMatrix, false);
106 
107     // Set the vertex color
108     rp3d::RigidBody* rigidBody = dynamic_cast<rp3d::RigidBody*>(mBody);
109     openglframework::Color currentColor = rigidBody != nullptr && rigidBody->isSleeping() ? mSleepingColor : mColor;
110     openglframework::Vector4 color(currentColor.r, currentColor.g, currentColor.b, currentColor.a);
111     shader.setVector4Uniform("globalVertexColor", color, false);
112 
113     // Bind the VAO
114     mVAO.bind();
115 
116     mVBOVertices.bind();
117 
118     // Get the location of shader attribute variables
119     GLint vertexPositionLoc = shader.getAttribLocation("vertexPosition");
120     GLint vertexNormalLoc = shader.getAttribLocation("vertexNormal", false);
121 
122     glEnableVertexAttribArray(vertexPositionLoc);
123     glVertexAttribPointer(vertexPositionLoc, 3, GL_FLOAT, GL_FALSE, 0, (char*)NULL);
124 
125     mVBONormals.bind();
126 
127     if (vertexNormalLoc != -1) glVertexAttribPointer(vertexNormalLoc, 3, GL_FLOAT, GL_FALSE, 0, (char*)NULL);
128     if (vertexNormalLoc != -1) glEnableVertexAttribArray(vertexNormalLoc);
129 
130     // For each part of the mesh
131     for (unsigned int i=0; i<getNbParts(); i++) {
132         glDrawElements(GL_TRIANGLES, getNbFaces(i) * 3, GL_UNSIGNED_INT, (char*)NULL);
133     }
134 
135     glDisableVertexAttribArray(vertexPositionLoc);
136     if (vertexNormalLoc != -1) glDisableVertexAttribArray(vertexNormalLoc);
137 
138     mVBONormals.unbind();
139     mVBOVertices.unbind();
140 
141     // Unbind the VAO
142     mVAO.unbind();
143 
144     // Unbind the shader
145     shader.unbind();
146 }
147 
148 // Compute the heights of the height field
generateHeightField()149 void HeightField::generateHeightField() {
150 
151     double persistence = 9;
152     double frequency = 0.28;
153     double amplitude = 12;
154     int octaves = 1;
155     int randomseed = 23;
156     PerlinNoise perlinNoise(persistence, frequency, amplitude, octaves, randomseed);
157 
158     mMinHeight = 0;
159     mMaxHeight = 0;
160 
161     float width = (NB_POINTS_WIDTH - 1);
162     float length = (NB_POINTS_LENGTH - 1);
163 
164     for (int i=0; i<NB_POINTS_WIDTH; i++) {
165         for (int j=0; j<NB_POINTS_LENGTH; j++) {
166 
167             int arrayIndex = j * NB_POINTS_WIDTH + i;
168 
169             mHeightData[arrayIndex] = (float)(perlinNoise.GetHeight(-width * 0.5 + i, -length * 0.5 + j));
170 
171             if (i==0 && j==0) {
172                 mMinHeight = mHeightData[arrayIndex] ;
173                 mMaxHeight = mHeightData[arrayIndex] ;
174             }
175 
176             if (mHeightData[arrayIndex] > mMaxHeight) mMaxHeight = mHeightData[arrayIndex] ;
177             if (mHeightData[arrayIndex] < mMinHeight) mMinHeight = mHeightData[arrayIndex] ;
178         }
179     }
180 }
181 
182 // Generate the graphics mesh to render the height field
generateGraphicsMesh()183 void HeightField::generateGraphicsMesh() {
184 
185     std::vector<unsigned int> indices;
186     int vertexId = 0;
187 
188     for (int i=0; i<NB_POINTS_WIDTH; i++) {
189         for (int j=0; j<NB_POINTS_LENGTH; j++) {
190 
191             float originHeight = -(mMaxHeight - mMinHeight) * 0.5f - mMinHeight;
192             float height = originHeight + mHeightData[j * NB_POINTS_WIDTH + i];
193             openglframework::Vector3 vertex(-(NB_POINTS_WIDTH - 1) * 0.5f + i, height, -(NB_POINTS_LENGTH - 1) * 0.5f + j);
194 
195             mVertices.push_back(vertex);
196 
197             // Triangle indices
198             if ((i < NB_POINTS_WIDTH - 1) && (j < NB_POINTS_LENGTH - 1)) {
199 
200                 unsigned int v1 = vertexId;
201                 unsigned int v2 = vertexId + 1;
202                 unsigned int v3 = vertexId + NB_POINTS_LENGTH;
203                 unsigned int v4 = vertexId + NB_POINTS_LENGTH + 1;
204 
205                 // First triangle
206                 indices.push_back(v1);
207                 indices.push_back(v2);
208                 indices.push_back(v3);
209 
210                 // Second triangle
211                 indices.push_back(v2);
212                 indices.push_back(v4);
213                 indices.push_back(v3);
214             }
215 
216             vertexId++;
217         }
218     }
219 
220     mIndices.push_back(indices);
221 
222     calculateNormals();
223 }
224 
225 // Create the Vertex Buffer Objects used to render with OpenGL.
226 /// We create two VBOs (one for vertices and one for indices)
createVBOAndVAO()227 void HeightField::createVBOAndVAO() {
228 
229     // Create the VBO for the vertices data
230     mVBOVertices.create();
231     mVBOVertices.bind();
232     size_t sizeVertices = mVertices.size() * sizeof(openglframework::Vector3);
233     mVBOVertices.copyDataIntoVBO(sizeVertices, getVerticesPointer(), GL_STATIC_DRAW);
234     mVBOVertices.unbind();
235 
236     // Create the VBO for the normals data
237     mVBONormals.create();
238     mVBONormals.bind();
239     size_t sizeNormals = mNormals.size() * sizeof(openglframework::Vector3);
240     mVBONormals.copyDataIntoVBO(sizeNormals, getNormalsPointer(), GL_STATIC_DRAW);
241     mVBONormals.unbind();
242 
243     if (hasTexture()) {
244         // Create the VBO for the texture co data
245         mVBOTextureCoords.create();
246         mVBOTextureCoords.bind();
247         size_t sizeTextureCoords = mUVs.size() * sizeof(openglframework::Vector2);
248         mVBOTextureCoords.copyDataIntoVBO(sizeTextureCoords, getUVTextureCoordinatesPointer(), GL_STATIC_DRAW);
249         mVBOTextureCoords.unbind();
250     }
251 
252     // Create th VBO for the indices data
253     mVBOIndices.create();
254     mVBOIndices.bind();
255     size_t sizeIndices = mIndices[0].size() * sizeof(unsigned int);
256     mVBOIndices.copyDataIntoVBO(sizeIndices, getIndicesPointer(), GL_STATIC_DRAW);
257     mVBOIndices.unbind();
258 
259     // Create the VAO for both VBOs
260     mVAO.create();
261     mVAO.bind();
262 
263     // Bind the VBO of vertices
264     mVBOVertices.bind();
265 
266     // Bind the VBO of normals
267     mVBONormals.bind();
268 
269     if (hasTexture()) {
270         // Bind the VBO of texture coords
271         mVBOTextureCoords.bind();
272     }
273 
274     // Bind the VBO of indices
275     mVBOIndices.bind();
276 
277     // Unbind the VAO
278     mVAO.unbind();
279 }
280