ResidualVM logo ResidualVM website - Forums - Contact us BuildBot - Doxygen - Wiki curved edge

openglsactor.cpp

Go to the documentation of this file.
00001 /* ResidualVM - A 3D game interpreter
00002  *
00003  * ResidualVM is the legal property of its developers, whose names
00004  * are too numerous to list here. Please refer to the AUTHORS
00005  * file distributed with this source distribution.
00006  *
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License
00009  * as published by the Free Software Foundation; either version 2
00010  * of the License, or (at your option) any later version.
00011  *
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00020  *
00021  */
00022 
00023 #include "engines/stark/gfx/openglsactor.h"
00024 
00025 #include "engines/stark/model/model.h"
00026 #include "engines/stark/model/animhandler.h"
00027 #include "engines/stark/scene.h"
00028 #include "engines/stark/services/services.h"
00029 #include "engines/stark/services/settings.h"
00030 #include "engines/stark/gfx/opengls.h"
00031 #include "engines/stark/gfx/texture.h"
00032 
00033 #include "graphics/opengl/shader.h"
00034 
00035 namespace Stark {
00036 namespace Gfx {
00037 
00038 OpenGLSActorRenderer::OpenGLSActorRenderer(OpenGLSDriver *gfx) :
00039         VisualActor(),
00040         _gfx(gfx),
00041         _faceVBO(0) {
00042     _shader = _gfx->createActorShaderInstance();
00043     _shadowShader = _gfx->createShadowShaderInstance();
00044 }
00045 
00046 OpenGLSActorRenderer::~OpenGLSActorRenderer() {
00047     clearVertices();
00048 
00049     delete _shader;
00050     delete _shadowShader;
00051 }
00052 
00053 void OpenGLSActorRenderer::render(const Math::Vector3d &position, float direction, const LightEntryArray &lights) {
00054     if (_modelIsDirty) {
00055         // Update the OpenGL Buffer Objects if required
00056         clearVertices();
00057         uploadVertices();
00058         _modelIsDirty = false;
00059     }
00060 
00061     // TODO: Move updates outside of the rendering code
00062     _animHandler->animate(_time);
00063     _model->updateBoundingBox();
00064 
00065     _gfx->set3DMode();
00066 
00067     Math::Matrix4 model = getModelMatrix(position, direction);
00068     Math::Matrix4 view = StarkScene->getViewMatrix();
00069     Math::Matrix4 projection = StarkScene->getProjectionMatrix();
00070 
00071     Math::Matrix4 modelViewMatrix = view * model;
00072     modelViewMatrix.transpose(); // OpenGL expects matrices transposed when compared to ResidualVM's
00073 
00074     Math::Matrix4 projectionMatrix = projection;
00075     projectionMatrix.transpose(); // OpenGL expects matrices transposed when compared to ResidualVM's
00076 
00077     Math::Matrix4 normalMatrix = modelViewMatrix;
00078     normalMatrix.invertAffineOrthonormal();
00079     //normalMatrix.transpose(); // OpenGL expects matrices transposed when compared to ResidualVM's
00080     //normalMatrix.transpose(); // No need to transpose twice in a row
00081 
00082     _shader->enableVertexAttribute("position1", _faceVBO, 3, GL_FLOAT, GL_FALSE, 14 * sizeof(float), 0);
00083     _shader->enableVertexAttribute("position2", _faceVBO, 3, GL_FLOAT, GL_FALSE, 14 * sizeof(float), 12);
00084     _shader->enableVertexAttribute("bone1", _faceVBO, 1, GL_FLOAT, GL_FALSE, 14 * sizeof(float), 24);
00085     _shader->enableVertexAttribute("bone2", _faceVBO, 1, GL_FLOAT, GL_FALSE, 14 * sizeof(float), 28);
00086     _shader->enableVertexAttribute("boneWeight", _faceVBO, 1, GL_FLOAT, GL_FALSE, 14 * sizeof(float), 32);
00087     _shader->enableVertexAttribute("normal", _faceVBO, 3, GL_FLOAT, GL_FALSE, 14 * sizeof(float), 36);
00088     _shader->enableVertexAttribute("texcoord", _faceVBO, 2, GL_FLOAT, GL_FALSE, 14 * sizeof(float), 48);
00089     _shader->use(true);
00090 
00091     _shader->setUniform("modelViewMatrix", modelViewMatrix);
00092     _shader->setUniform("projectionMatrix", projectionMatrix);
00093     _shader->setUniform("normalMatrix", normalMatrix.getRotation());
00094     setBoneRotationArrayUniform(_shader, "boneRotation");
00095     setBonePositionArrayUniform(_shader, "bonePosition");
00096     setLightArrayUniform(lights);
00097 
00098     Common::Array<Face *> faces = _model->getFaces();
00099     Common::Array<Material *> mats = _model->getMaterials();
00100 
00101     for (Common::Array<Face *>::const_iterator face = faces.begin(); face != faces.end(); ++face) {
00102         // For each face draw its vertices from the VBO, indexed by the EBO
00103         const Material *material = mats[(*face)->materialId];
00104         const Gfx::Texture *tex = resolveTexture(material);
00105         if (tex) {
00106             tex->bind();
00107         } else {
00108             glBindTexture(GL_TEXTURE_2D, 0);
00109         }
00110 
00111         _shader->setUniform("textured", tex != nullptr);
00112         _shader->setUniform("color", Math::Vector3d(material->r, material->g, material->b));
00113 
00114         GLuint ebo = _faceEBO[*face];
00115         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
00116         glDrawElements(GL_TRIANGLES, (*face)->vertexIndices.size(), GL_UNSIGNED_INT, 0);
00117     }
00118 
00119     _shader->unbind();
00120 
00121     if (_castsShadow && StarkSettings->getBoolSetting(Settings::kShadow)) {
00122         glEnable(GL_BLEND);
00123         glEnable(GL_STENCIL_TEST);
00124 
00125         _shadowShader->enableVertexAttribute("position1", _faceVBO, 3, GL_FLOAT, GL_FALSE, 14 * sizeof(float), 0);
00126         _shadowShader->enableVertexAttribute("position2", _faceVBO, 3, GL_FLOAT, GL_FALSE, 14 * sizeof(float), 12);
00127         _shadowShader->enableVertexAttribute("bone1", _faceVBO, 1, GL_FLOAT, GL_FALSE, 14 * sizeof(float), 24);
00128         _shadowShader->enableVertexAttribute("bone2", _faceVBO, 1, GL_FLOAT, GL_FALSE, 14 * sizeof(float), 28);
00129         _shadowShader->enableVertexAttribute("boneWeight", _faceVBO, 1, GL_FLOAT, GL_FALSE, 14 * sizeof(float), 32);
00130         _shadowShader->use(true);
00131 
00132         Math::Matrix4 mvp = projection * view * model;
00133         mvp.transpose();
00134         _shadowShader->setUniform("mvp", mvp);
00135 
00136         setBoneRotationArrayUniform(_shadowShader, "boneRotation");
00137         setBonePositionArrayUniform(_shadowShader, "bonePosition");
00138 
00139         Math::Matrix4 modelInverse = model;
00140         modelInverse.inverse();
00141         setShadowUniform(lights, position, modelInverse.getRotation());
00142 
00143         for (Common::Array<Face *>::const_iterator face = faces.begin(); face != faces.end(); ++face) {
00144             GLuint ebo = _faceEBO[*face];
00145             glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
00146             glDrawElements(GL_TRIANGLES, (*face)->vertexIndices.size(), GL_UNSIGNED_INT, 0);
00147         }
00148 
00149         glDisable(GL_BLEND);
00150         glDisable(GL_STENCIL_TEST);
00151 
00152         _shadowShader->unbind();
00153     }
00154 }
00155 
00156 void OpenGLSActorRenderer::clearVertices() {
00157     OpenGL::Shader::freeBuffer(_faceVBO); // Zero names are silently ignored
00158     _faceVBO = 0;
00159 
00160     for (FaceBufferMap::iterator it = _faceEBO.begin(); it != _faceEBO.end(); ++it) {
00161         OpenGL::Shader::freeBuffer(it->_value);
00162     }
00163 
00164     _faceEBO.clear();
00165 }
00166 
00167 void OpenGLSActorRenderer::uploadVertices() {
00168     _faceVBO = createModelVBO(_model);
00169 
00170     Common::Array<Face *> faces = _model->getFaces();
00171     for (Common::Array<Face *>::const_iterator face = faces.begin(); face != faces.end(); ++face) {
00172         _faceEBO[*face] = createFaceEBO(*face);
00173     }
00174 }
00175 
00176 GLuint OpenGLSActorRenderer::createModelVBO(const Model *model) {
00177     const Common::Array<VertNode *> &modelVertices = model->getVertices();
00178 
00179     float *vertices = new float[14 * modelVertices.size()];
00180     float *vertPtr = vertices;
00181 
00182     // Build a vertex array
00183     for (Common::Array<VertNode *>::const_iterator tri = modelVertices.begin(); tri != modelVertices.end(); ++tri) {
00184         *vertPtr++ = (*tri)->_pos1.x();
00185         *vertPtr++ = (*tri)->_pos1.y();
00186         *vertPtr++ = (*tri)->_pos1.z();
00187 
00188         *vertPtr++ = (*tri)->_pos2.x();
00189         *vertPtr++ = (*tri)->_pos2.y();
00190         *vertPtr++ = (*tri)->_pos2.z();
00191 
00192         *vertPtr++ = (*tri)->_bone1;
00193         *vertPtr++ = (*tri)->_bone2;
00194 
00195         *vertPtr++ = (*tri)->_boneWeight;
00196 
00197         *vertPtr++ = (*tri)->_normal.x();
00198         *vertPtr++ = (*tri)->_normal.y();
00199         *vertPtr++ = (*tri)->_normal.z();
00200 
00201         *vertPtr++ = -(*tri)->_texS;
00202         *vertPtr++ = (*tri)->_texT;
00203     }
00204 
00205     GLuint vbo = OpenGL::Shader::createBuffer(GL_ARRAY_BUFFER, sizeof(float) * 14 * modelVertices.size(), vertices);
00206     delete[] vertices;
00207 
00208     return vbo;
00209 }
00210 
00211 GLuint OpenGLSActorRenderer::createFaceEBO(const Face *face) {
00212     return OpenGL::Shader::createBuffer(GL_ELEMENT_ARRAY_BUFFER, sizeof(uint32) * face->vertexIndices.size(), &face->vertexIndices[0]);
00213 }
00214 
00215 void OpenGLSActorRenderer::setBonePositionArrayUniform(OpenGL::Shader *shader, const char *uniform) {
00216     const Common::Array<BoneNode *> &bones = _model->getBones();
00217 
00218     GLint pos = shader->getUniformLocation(uniform);
00219     if (pos == -1) {
00220         error("No uniform named '%s'", uniform);
00221     }
00222 
00223     float *positions = new float[3 * bones.size()];
00224     float *positionsPtr = positions;
00225 
00226     for (uint i = 0; i < bones.size(); i++) {
00227         *positionsPtr++ = bones[i]->_animPos.x();
00228         *positionsPtr++ = bones[i]->_animPos.y();
00229         *positionsPtr++ = bones[i]->_animPos.z();
00230     }
00231 
00232     glUniform3fv(pos, bones.size(), positions);
00233     delete[] positions;
00234 }
00235 
00236 void OpenGLSActorRenderer::setBoneRotationArrayUniform(OpenGL::Shader *shader, const char *uniform) {
00237     const Common::Array<BoneNode *> &bones = _model->getBones();
00238 
00239     GLint rot = shader->getUniformLocation(uniform);
00240     if (rot == -1) {
00241         error("No uniform named '%s'", uniform);
00242     }
00243 
00244     float *rotations = new float[4 * bones.size()];
00245     float *rotationsPtr = rotations;
00246 
00247     for (uint i = 0; i < bones.size(); i++) {
00248         *rotationsPtr++ =  bones[i]->_animRot.x();
00249         *rotationsPtr++ =  bones[i]->_animRot.y();
00250         *rotationsPtr++ =  bones[i]->_animRot.z();
00251         *rotationsPtr++ =  bones[i]->_animRot.w();
00252     }
00253 
00254     glUniform4fv(rot, bones.size(), rotations);
00255     delete[] rotations;
00256 }
00257 
00258 void OpenGLSActorRenderer::setLightArrayUniform(const LightEntryArray &lights) {
00259     static const uint maxLights = 10;
00260 
00261     assert(lights.size() >= 1);
00262     assert(lights.size() <= maxLights);
00263 
00264     const LightEntry *ambient = lights[0];
00265     assert(ambient->type == LightEntry::kAmbient); // The first light must be the ambient light
00266     _shader->setUniform("ambientColor", ambient->color);
00267 
00268     Math::Matrix4 viewMatrix = StarkScene->getViewMatrix();
00269     Math::Matrix3 viewMatrixRot = viewMatrix.getRotation();
00270 
00271     for (uint i = 0; i < lights.size() - 1; i++) {
00272         const LightEntry *l = lights[i + 1];
00273 
00274         Math::Vector4d worldPosition;
00275         worldPosition.x() = l->position.x();
00276         worldPosition.y() = l->position.y();
00277         worldPosition.z() = l->position.z();
00278         worldPosition.w() = 1.0;
00279 
00280         Math::Vector4d eyePosition = viewMatrix * worldPosition;
00281 
00282         // The light type is stored in the w coordinate of the position to save an uniform slot
00283         eyePosition.w() = l->type;
00284 
00285         Math::Vector3d worldDirection = l->direction;
00286         Math::Vector3d eyeDirection = viewMatrixRot * worldDirection;
00287         eyeDirection.normalize();
00288 
00289         _shader->setUniform(Common::String::format("lights[%d].position", i).c_str(), eyePosition);
00290         _shader->setUniform(Common::String::format("lights[%d].direction", i).c_str(), eyeDirection);
00291         _shader->setUniform(Common::String::format("lights[%d].color", i).c_str(), l->color);
00292 
00293         Math::Vector4d params;
00294         params.x() = l->falloffNear;
00295         params.y() = l->falloffFar;
00296         params.z() = l->innerConeAngle.getCosine();
00297         params.w() = l->outerConeAngle.getCosine();
00298 
00299         _shader->setUniform(Common::String::format("lights[%d].params", i).c_str(), params);
00300     }
00301 
00302     for (uint i = lights.size() - 1; i < maxLights; i++) {
00303         // Make sure unused lights are disabled
00304         _shader->setUniform(Common::String::format("lights[%d].position", i).c_str(), Math::Vector4d());
00305     }
00306 }
00307 
00308 void OpenGLSActorRenderer::setShadowUniform(const LightEntryArray &lights,
00309         const Math::Vector3d &actorPosition, Math::Matrix3 worldToModelRot) {
00310     Math::Vector3d sumDirection;
00311     bool hasLight = false;
00312 
00313     // Compute the contribution from each lights
00314     // The ambient light is skipped intentionally
00315     for (uint i = 1; i < lights.size(); ++i) {
00316         LightEntry *light = lights[i];
00317         bool contributes = false;
00318 
00319         Math::Vector3d lightDirection;
00320         switch (light->type) {
00321             case LightEntry::kPoint:
00322                 contributes = getPointLightContribution(light, actorPosition, lightDirection);
00323                 break;
00324             case LightEntry::kDirectional:
00325                 contributes = getDirectionalLightContribution(light, lightDirection);
00326                 break;
00327             case LightEntry::kSpot:
00328                 contributes = getSpotLightContribution(light, actorPosition, lightDirection);
00329                 break;
00330             case LightEntry::kAmbient:
00331             default:
00332                 break;
00333         }
00334 
00335         if (contributes) {
00336             sumDirection += lightDirection;
00337             hasLight = true;
00338         }
00339     }
00340 
00341     if (hasLight) {
00342         // Clip the horizontal length
00343         Math::Vector2d horizontalProjection(sumDirection.x(), sumDirection.y());
00344         float shadowLength = MIN(horizontalProjection.getMagnitude(), StarkScene->getMaxShadowLength());
00345 
00346         horizontalProjection.normalize();
00347         horizontalProjection *= shadowLength;
00348 
00349         sumDirection.x() = horizontalProjection.getX();
00350         sumDirection.y() = horizontalProjection.getY();
00351         sumDirection.z() = -1;
00352     } else {
00353         // Cast from above by default
00354         sumDirection.x() = 0;
00355         sumDirection.y() = 0;
00356         sumDirection.z() = -1;
00357     }
00358 
00359     //Transform the direction to the model space and pass to the shader
00360     sumDirection = worldToModelRot * sumDirection;
00361     _shadowShader->setUniform("lightDirection", sumDirection);
00362 }
00363 
00364 bool OpenGLSActorRenderer::getPointLightContribution(LightEntry *light,
00365         const Math::Vector3d &actorPosition, Math::Vector3d &direction, float weight) {
00366     float distance = light->position.getDistanceTo(actorPosition);
00367 
00368     if (distance > light->falloffFar) {
00369         return false;
00370     }
00371 
00372     float factor;
00373     if (distance > light->falloffNear) {
00374         if (light->falloffFar - light->falloffNear > 1) {
00375             factor = 1 - (distance - light->falloffNear) / (light->falloffFar - light->falloffNear);
00376         } else {
00377             factor = 0;
00378         }
00379     } else {
00380         factor = 1;
00381     }
00382 
00383     float brightness = (light->color.x() + light->color.y() + light->color.z()) / 3.0f;
00384 
00385     if (factor <= 0 || brightness <= 0) {
00386         return false;
00387     }
00388 
00389     direction = actorPosition - light->position;
00390     direction.normalize();
00391     direction *= factor * brightness * weight;
00392 
00393     return true;
00394 }
00395 
00396 bool OpenGLSActorRenderer::getDirectionalLightContribution(LightEntry *light, Math::Vector3d &direction) {
00397     float brightness = (light->color.x() + light->color.y() + light->color.z()) / 3.0f;
00398 
00399     if (brightness <= 0) {
00400         return false;
00401     }
00402 
00403     direction = light->direction;
00404     direction.normalize();
00405     direction *= brightness;
00406 
00407     return true;
00408 }
00409 
00410 bool OpenGLSActorRenderer::getSpotLightContribution(LightEntry *light,
00411         const Math::Vector3d &actorPosition, Math::Vector3d &direction) {
00412     Math::Vector3d lightToActor = actorPosition - light->position;
00413     lightToActor.normalize();
00414 
00415     float cosAngle = MAX(0.0f, lightToActor.dotProduct(light->direction));
00416     float cone = (cosAngle - light->innerConeAngle.getCosine()) /
00417             MAX(0.001f, light->outerConeAngle.getCosine() - light->innerConeAngle.getCosine());
00418     cone = CLIP(cone, 0.0f, 1.0f);
00419 
00420     if (cone <= 0) {
00421         return false;
00422     }
00423 
00424     return getPointLightContribution(light, actorPosition, direction, cone);
00425 }
00426 
00427 } // End of namespace Gfx
00428 } // End of namespace Stark


Generated on Sat Mar 16 2019 05:01:49 for ResidualVM by doxygen 1.7.1
curved edge   curved edge