1 /* ResidualVM - A 3D game interpreter
2 *
3 * ResidualVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the AUTHORS
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #include "engines/stark/gfx/openglsactor.h"
24
25 #include "engines/stark/model/model.h"
26 #include "engines/stark/model/animhandler.h"
27 #include "engines/stark/scene.h"
28 #include "engines/stark/services/services.h"
29 #include "engines/stark/gfx/opengls.h"
30 #include "engines/stark/gfx/texture.h"
31
32 #include "graphics/opengl/shader.h"
33
34 namespace Stark {
35 namespace Gfx {
36
OpenGLSActorRenderer(OpenGLSDriver * gfx)37 OpenGLSActorRenderer::OpenGLSActorRenderer(OpenGLSDriver *gfx) :
38 VisualActor(),
39 _gfx(gfx),
40 _faceVBO(0) {
41 _shader = _gfx->createActorShaderInstance();
42 }
43
~OpenGLSActorRenderer()44 OpenGLSActorRenderer::~OpenGLSActorRenderer() {
45 clearVertices();
46
47 delete _shader;
48 }
49
render(const Math::Vector3d & position,float direction,const LightEntryArray & lights)50 void OpenGLSActorRenderer::render(const Math::Vector3d &position, float direction, const LightEntryArray &lights) {
51 if (_modelIsDirty) {
52 // Update the OpenGL Buffer Objects if required
53 clearVertices();
54 uploadVertices();
55 _modelIsDirty = false;
56 }
57
58 _animHandler->animate(_time);
59
60 _gfx->set3DMode();
61
62 Math::Matrix4 model = getModelMatrix(position, direction);
63 Math::Matrix4 view = StarkScene->getViewMatrix();
64 Math::Matrix4 projection = StarkScene->getProjectionMatrix();
65
66 Math::Matrix4 modelViewMatrix = view * model;
67 modelViewMatrix.transpose(); // OpenGL expects matrices transposed when compared to ResidualVM's
68
69 Math::Matrix4 projectionMatrix = projection;
70 projectionMatrix.transpose(); // OpenGL expects matrices transposed when compared to ResidualVM's
71
72 Math::Matrix4 normalMatrix = modelViewMatrix;
73 normalMatrix.invertAffineOrthonormal();
74 //normalMatrix.transpose(); // OpenGL expects matrices transposed when compared to ResidualVM's
75 //normalMatrix.transpose(); // No need to transpose twice in a row
76
77 _shader->enableVertexAttribute("position1", _faceVBO, 3, GL_FLOAT, GL_FALSE, 14 * sizeof(float), 0);
78 _shader->enableVertexAttribute("position2", _faceVBO, 3, GL_FLOAT, GL_FALSE, 14 * sizeof(float), 12);
79 _shader->enableVertexAttribute("bone1", _faceVBO, 1, GL_FLOAT, GL_FALSE, 14 * sizeof(float), 24);
80 _shader->enableVertexAttribute("bone2", _faceVBO, 1, GL_FLOAT, GL_FALSE, 14 * sizeof(float), 28);
81 _shader->enableVertexAttribute("boneWeight", _faceVBO, 1, GL_FLOAT, GL_FALSE, 14 * sizeof(float), 32);
82 _shader->enableVertexAttribute("normal", _faceVBO, 3, GL_FLOAT, GL_FALSE, 14 * sizeof(float), 36);
83 _shader->enableVertexAttribute("texcoord", _faceVBO, 2, GL_FLOAT, GL_FALSE, 14 * sizeof(float), 48);
84 _shader->use(true);
85
86 _shader->setUniform("modelViewMatrix", modelViewMatrix);
87 _shader->setUniform("projectionMatrix", projectionMatrix);
88 _shader->setUniform("normalMatrix", normalMatrix.getRotation());
89 setBoneRotationArrayUniform("boneRotation");
90 setBonePositionArrayUniform("bonePosition");
91 setLightArrayUniform("lights", lights);
92
93 Common::Array<Face *> faces = _model->getFaces();
94 Common::Array<Material *> mats = _model->getMaterials();
95
96 for (Common::Array<Face *>::const_iterator face = faces.begin(); face != faces.end(); ++face) {
97 // For each face draw its vertices from the VBO, indexed by the EBO
98 const Material *material = mats[(*face)->materialId];
99 const Gfx::Texture *tex = resolveTexture(material);
100 if (tex) {
101 tex->bind();
102 } else {
103 glBindTexture(GL_TEXTURE_2D, 0);
104 }
105
106 _shader->setUniform("textured", tex != nullptr);
107 _shader->setUniform("color", Math::Vector3d(material->r, material->g, material->b));
108
109 GLuint ebo = _faceEBO[*face];
110 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
111 glDrawElements(GL_TRIANGLES, (*face)->vertexIndices.size(), GL_UNSIGNED_INT, 0);
112 }
113
114 _shader->unbind();
115 }
116
clearVertices()117 void OpenGLSActorRenderer::clearVertices() {
118 OpenGL::Shader::freeBuffer(_faceVBO); // Zero names are silently ignored
119 _faceVBO = 0;
120
121 for (FaceBufferMap::iterator it = _faceEBO.begin(); it != _faceEBO.end(); ++it) {
122 OpenGL::Shader::freeBuffer(it->_value);
123 }
124
125 _faceEBO.clear();
126 }
127
uploadVertices()128 void OpenGLSActorRenderer::uploadVertices() {
129 _faceVBO = createModelVBO(_model);
130
131 Common::Array<Face *> faces = _model->getFaces();
132 for (Common::Array<Face *>::const_iterator face = faces.begin(); face != faces.end(); ++face) {
133 _faceEBO[*face] = createFaceEBO(*face);
134 }
135 }
136
createModelVBO(const Model * model)137 uint32 OpenGLSActorRenderer::createModelVBO(const Model *model) {
138 const Common::Array<VertNode *> &modelVertices = model->getVertices();
139
140 float *vertices = new float[14 * modelVertices.size()];
141 float *vertPtr = vertices;
142
143 // Build a vertex array
144 for (Common::Array<VertNode *>::const_iterator tri = modelVertices.begin(); tri != modelVertices.end(); ++tri) {
145 *vertPtr++ = (*tri)->_pos1.x();
146 *vertPtr++ = (*tri)->_pos1.y();
147 *vertPtr++ = (*tri)->_pos1.z();
148
149 *vertPtr++ = (*tri)->_pos2.x();
150 *vertPtr++ = (*tri)->_pos2.y();
151 *vertPtr++ = (*tri)->_pos2.z();
152
153 *vertPtr++ = (*tri)->_bone1;
154 *vertPtr++ = (*tri)->_bone2;
155
156 *vertPtr++ = (*tri)->_boneWeight;
157
158 *vertPtr++ = (*tri)->_normal.x();
159 *vertPtr++ = (*tri)->_normal.y();
160 *vertPtr++ = (*tri)->_normal.z();
161
162 *vertPtr++ = -(*tri)->_texS;
163 *vertPtr++ = (*tri)->_texT;
164 }
165
166 uint32 vbo = OpenGL::Shader::createBuffer(GL_ARRAY_BUFFER, sizeof(float) * 14 * modelVertices.size(), vertices);
167 delete[] vertices;
168
169 return vbo;
170 }
171
createFaceEBO(const Face * face)172 uint32 OpenGLSActorRenderer::createFaceEBO(const Face *face) {
173 return OpenGL::Shader::createBuffer(GL_ELEMENT_ARRAY_BUFFER, sizeof(uint32) * face->vertexIndices.size(), &face->vertexIndices[0]);
174 }
175
setBonePositionArrayUniform(const char * uniform)176 void OpenGLSActorRenderer::setBonePositionArrayUniform(const char *uniform) {
177 const Common::Array<BoneNode *> &bones = _model->getBones();
178
179 GLint pos = _shader->getUniformLocation(uniform);
180 if (pos == -1) {
181 error("No uniform named '%s'", uniform);
182 }
183
184 float *positions = new float[3 * bones.size()];
185 float *positionsPtr = positions;
186
187 for (uint i = 0; i < bones.size(); i++) {
188 *positionsPtr++ = bones[i]->_animPos.x();
189 *positionsPtr++ = bones[i]->_animPos.y();
190 *positionsPtr++ = bones[i]->_animPos.z();
191 }
192
193 glUniform3fv(pos, bones.size(), positions);
194 delete[] positions;
195 }
196
setBoneRotationArrayUniform(const char * uniform)197 void OpenGLSActorRenderer::setBoneRotationArrayUniform(const char *uniform) {
198 const Common::Array<BoneNode *> &bones = _model->getBones();
199
200 GLint rot = _shader->getUniformLocation(uniform);
201 if (rot == -1) {
202 error("No uniform named '%s'", uniform);
203 }
204
205 float *rotations = new float[4 * bones.size()];
206 float *rotationsPtr = rotations;
207
208 for (uint i = 0; i < bones.size(); i++) {
209 *rotationsPtr++ = bones[i]->_animRot.x();
210 *rotationsPtr++ = bones[i]->_animRot.y();
211 *rotationsPtr++ = bones[i]->_animRot.z();
212 *rotationsPtr++ = bones[i]->_animRot.w();
213 }
214
215 glUniform4fv(rot, bones.size(), rotations);
216 delete[] rotations;
217 }
218
setLightArrayUniform(const char * uniform,const LightEntryArray & lights)219 void OpenGLSActorRenderer::setLightArrayUniform(const char *uniform, const LightEntryArray &lights) {
220 static const uint maxLights = 10;
221
222 assert(lights.size() >= 1);
223 assert(lights.size() <= maxLights);
224
225 const LightEntry *ambient = lights[0];
226 assert(ambient->type == LightEntry::kAmbient); // The first light must be the ambient light
227 _shader->setUniform("ambientColor", ambient->color);
228
229 Math::Matrix4 viewMatrix = StarkScene->getViewMatrix();
230 Math::Matrix3 viewMatrixRot = viewMatrix.getRotation();
231
232 for (uint i = 0; i < lights.size() - 1; i++) {
233 const LightEntry *l = lights[i + 1];
234
235 Math::Vector4d worldPosition;
236 worldPosition.x() = l->position.x();
237 worldPosition.y() = l->position.y();
238 worldPosition.z() = l->position.z();
239 worldPosition.w() = 1.0;
240
241 Math::Vector4d eyePosition = viewMatrix * worldPosition;
242
243 // The light type is stored in the w coordinate of the position to save an uniform slot
244 eyePosition.w() = l->type;
245
246 Math::Vector3d worldDirection = l->direction;
247 Math::Vector3d eyeDirection = viewMatrixRot * worldDirection;
248 eyeDirection.normalize();
249
250 _shader->setUniform(Common::String::format("lights[%d].position", i).c_str(), eyePosition);
251 _shader->setUniform(Common::String::format("lights[%d].direction", i).c_str(), eyeDirection);
252 _shader->setUniform(Common::String::format("lights[%d].color", i).c_str(), l->color);
253
254 Math::Vector4d params;
255 params.x() = l->falloffNear;
256 params.y() = l->falloffFar;
257 params.z() = l->innerConeAngle.getCosine();
258 params.w() = l->outerConeAngle.getCosine();
259
260 _shader->setUniform(Common::String::format("lights[%d].params", i).c_str(), params);
261 }
262
263 for (uint i = lights.size() - 1; i < maxLights; i++) {
264 // Make sure unused lights are disabled
265 _shader->setUniform(Common::String::format("lights[%d].position", i).c_str(), Math::Vector4d());
266 }
267 }
268
269 } // End of namespace Gfx
270 } // End of namespace Stark
271