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