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
00122             && StarkScene->shouldRenderShadows()
00123             && StarkSettings->getBoolSetting(Settings::kShadow)) {
00124         glEnable(GL_BLEND);
00125         glEnable(GL_STENCIL_TEST);
00126 
00127         _shadowShader->enableVertexAttribute("position1", _faceVBO, 3, GL_FLOAT, GL_FALSE, 14 * sizeof(float), 0);
00128         _shadowShader->enableVertexAttribute("position2", _faceVBO, 3, GL_FLOAT, GL_FALSE, 14 * sizeof(float), 12);
00129         _shadowShader->enableVertexAttribute("bone1", _faceVBO, 1, GL_FLOAT, GL_FALSE, 14 * sizeof(float), 24);
00130         _shadowShader->enableVertexAttribute("bone2", _faceVBO, 1, GL_FLOAT, GL_FALSE, 14 * sizeof(float), 28);
00131         _shadowShader->enableVertexAttribute("boneWeight", _faceVBO, 1, GL_FLOAT, GL_FALSE, 14 * sizeof(float), 32);
00132         _shadowShader->use(true);
00133 
00134         Math::Matrix4 mvp = projection * view * model;
00135         mvp.transpose();
00136         _shadowShader->setUniform("mvp", mvp);
00137 
00138         setBoneRotationArrayUniform(_shadowShader, "boneRotation");
00139         setBonePositionArrayUniform(_shadowShader, "bonePosition");
00140 
00141         Math::Matrix4 modelInverse = model;
00142         modelInverse.inverse();
00143         setShadowUniform(lights, position, modelInverse.getRotation());
00144 
00145         for (Common::Array<Face *>::const_iterator face = faces.begin(); face != faces.end(); ++face) {
00146             GLuint ebo = _faceEBO[*face];
00147             glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
00148             glDrawElements(GL_TRIANGLES, (*face)->vertexIndices.size(), GL_UNSIGNED_INT, 0);
00149         }
00150 
00151         glDisable(GL_BLEND);
00152         glDisable(GL_STENCIL_TEST);
00153 
00154         _shadowShader->unbind();
00155     }
00156 }
00157 
00158 void OpenGLSActorRenderer::clearVertices() {
00159     OpenGL::Shader::freeBuffer(_faceVBO); // Zero names are silently ignored
00160     _faceVBO = 0;
00161 
00162     for (FaceBufferMap::iterator it = _faceEBO.begin(); it != _faceEBO.end(); ++it) {
00163         OpenGL::Shader::freeBuffer(it->_value);
00164     }
00165 
00166     _faceEBO.clear();
00167 }
00168 
00169 void OpenGLSActorRenderer::uploadVertices() {
00170     _faceVBO = createModelVBO(_model);
00171 
00172     Common::Array<Face *> faces = _model->getFaces();
00173     for (Common::Array<Face *>::const_iterator face = faces.begin(); face != faces.end(); ++face) {
00174         _faceEBO[*face] = createFaceEBO(*face);
00175     }
00176 }
00177 
00178 GLuint OpenGLSActorRenderer::createModelVBO(const Model *model) {
00179     const Common::Array<VertNode *> &modelVertices = model->getVertices();
00180 
00181     float *vertices = new float[14 * modelVertices.size()];
00182     float *vertPtr = vertices;
00183 
00184     // Build a vertex array
00185     for (Common::Array<VertNode *>::const_iterator tri = modelVertices.begin(); tri != modelVertices.end(); ++tri) {
00186         *vertPtr++ = (*tri)->_pos1.x();
00187         *vertPtr++ = (*tri)->_pos1.y();
00188         *vertPtr++ = (*tri)->_pos1.z();
00189 
00190         *vertPtr++ = (*tri)->_pos2.x();
00191         *vertPtr++ = (*tri)->_pos2.y();
00192         *vertPtr++ = (*tri)->_pos2.z();
00193 
00194         *vertPtr++ = (*tri)->_bone1;
00195         *vertPtr++ = (*tri)->_bone2;
00196 
00197         *vertPtr++ = (*tri)->_boneWeight;
00198 
00199         *vertPtr++ = (*tri)->_normal.x();
00200         *vertPtr++ = (*tri)->_normal.y();
00201         *vertPtr++ = (*tri)->_normal.z();
00202 
00203         *vertPtr++ = -(*tri)->_texS;
00204         *vertPtr++ = (*tri)->_texT;
00205     }
00206 
00207     GLuint vbo = OpenGL::Shader::createBuffer(GL_ARRAY_BUFFER, sizeof(float) * 14 * modelVertices.size(), vertices);
00208     delete[] vertices;
00209 
00210     return vbo;
00211 }
00212 
00213 GLuint OpenGLSActorRenderer::createFaceEBO(const Face *face) {
00214     return OpenGL::Shader::createBuffer(GL_ELEMENT_ARRAY_BUFFER, sizeof(uint32) * face->vertexIndices.size(), &face->vertexIndices[0]);
00215 }
00216 
00217 void OpenGLSActorRenderer::setBonePositionArrayUniform(OpenGL::Shader *shader, const char *uniform) {
00218     const Common::Array<BoneNode *> &bones = _model->getBones();
00219 
00220     GLint pos = shader->getUniformLocation(uniform);
00221     if (pos == -1) {
00222         error("No uniform named '%s'", uniform);
00223     }
00224 
00225     float *positions = new float[3 * bones.size()];
00226     float *positionsPtr = positions;
00227 
00228     for (uint i = 0; i < bones.size(); i++) {
00229         *positionsPtr++ = bones[i]->_animPos.x();
00230         *positionsPtr++ = bones[i]->_animPos.y();
00231         *positionsPtr++ = bones[i]->_animPos.z();
00232     }
00233 
00234     glUniform3fv(pos, bones.size(), positions);
00235     delete[] positions;
00236 }
00237 
00238 void OpenGLSActorRenderer::setBoneRotationArrayUniform(OpenGL::Shader *shader, const char *uniform) {
00239     const Common::Array<BoneNode *> &bones = _model->getBones();
00240 
00241     GLint rot = shader->getUniformLocation(uniform);
00242     if (rot == -1) {
00243         error("No uniform named '%s'", uniform);
00244     }
00245 
00246     float *rotations = new float[4 * bones.size()];
00247     float *rotationsPtr = rotations;
00248 
00249     for (uint i = 0; i < bones.size(); i++) {
00250         *rotationsPtr++ =  bones[i]->_animRot.x();
00251         *rotationsPtr++ =  bones[i]->_animRot.y();
00252         *rotationsPtr++ =  bones[i]->_animRot.z();
00253         *rotationsPtr++ =  bones[i]->_animRot.w();
00254     }
00255 
00256     glUniform4fv(rot, bones.size(), rotations);
00257     delete[] rotations;
00258 }
00259 
00260 void OpenGLSActorRenderer::setLightArrayUniform(const LightEntryArray &lights) {
00261     static const uint maxLights = 10;
00262 
00263     assert(lights.size() >= 1);
00264     assert(lights.size() <= maxLights);
00265 
00266     const LightEntry *ambient = lights[0];
00267     assert(ambient->type == LightEntry::kAmbient); // The first light must be the ambient light
00268     _shader->setUniform("ambientColor", ambient->color);
00269 
00270     Math::Matrix4 viewMatrix = StarkScene->getViewMatrix();
00271     Math::Matrix3 viewMatrixRot = viewMatrix.getRotation();
00272 
00273     for (uint i = 0; i < lights.size() - 1; i++) {
00274         const LightEntry *l = lights[i + 1];
00275 
00276         Math::Vector4d worldPosition;
00277         worldPosition.x() = l->position.x();
00278         worldPosition.y() = l->position.y();
00279         worldPosition.z() = l->position.z();
00280         worldPosition.w() = 1.0;
00281 
00282         Math::Vector4d eyePosition = viewMatrix * worldPosition;
00283 
00284         // The light type is stored in the w coordinate of the position to save an uniform slot
00285         eyePosition.w() = l->type;
00286 
00287         Math::Vector3d worldDirection = l->direction;
00288         Math::Vector3d eyeDirection = viewMatrixRot * worldDirection;
00289         eyeDirection.normalize();
00290 
00291         _shader->setUniform(Common::String::format("lights[%d].position", i).c_str(), eyePosition);
00292         _shader->setUniform(Common::String::format("lights[%d].direction", i).c_str(), eyeDirection);
00293         _shader->setUniform(Common::String::format("lights[%d].color", i).c_str(), l->color);
00294 
00295         Math::Vector4d params;
00296         params.x() = l->falloffNear;
00297         params.y() = l->falloffFar;
00298         params.z() = l->innerConeAngle.getCosine();
00299         params.w() = l->outerConeAngle.getCosine();
00300 
00301         _shader->setUniform(Common::String::format("lights[%d].params", i).c_str(), params);
00302     }
00303 
00304     for (uint i = lights.size() - 1; i < maxLights; i++) {
00305         // Make sure unused lights are disabled
00306         _shader->setUniform(Common::String::format("lights[%d].position", i).c_str(), Math::Vector4d());
00307     }
00308 }
00309 
00310 void OpenGLSActorRenderer::setShadowUniform(const LightEntryArray &lights,
00311         const Math::Vector3d &actorPosition, Math::Matrix3 worldToModelRot) {
00312     Math::Vector3d sumDirection;
00313     bool hasLight = false;
00314 
00315     // Compute the contribution from each lights
00316     // The ambient light is skipped intentionally
00317     for (uint i = 1; i < lights.size(); ++i) {
00318         LightEntry *light = lights[i];
00319         bool contributes = false;
00320 
00321         Math::Vector3d lightDirection;
00322         switch (light->type) {
00323             case LightEntry::kPoint:
00324                 contributes = getPointLightContribution(light, actorPosition, lightDirection);
00325                 break;
00326             case LightEntry::kDirectional:
00327                 contributes = getDirectionalLightContribution(light, lightDirection);
00328                 break;
00329             case LightEntry::kSpot:
00330                 contributes = getSpotLightContribution(light, actorPosition, lightDirection);
00331                 break;
00332             case LightEntry::kAmbient:
00333             default:
00334                 break;
00335         }
00336 
00337         if (contributes) {
00338             sumDirection += lightDirection;
00339             hasLight = true;
00340         }
00341     }
00342 
00343     if (hasLight) {
00344         // Clip the horizontal length
00345         Math::Vector2d horizontalProjection(sumDirection.x(), sumDirection.y());
00346         float shadowLength = MIN(horizontalProjection.getMagnitude(), StarkScene->getMaxShadowLength());
00347 
00348         horizontalProjection.normalize();
00349         horizontalProjection *= shadowLength;
00350 
00351         sumDirection.x() = horizontalProjection.getX();
00352         sumDirection.y() = horizontalProjection.getY();
00353         sumDirection.z() = -1;
00354     } else {
00355         // Cast from above by default
00356         sumDirection.x() = 0;
00357         sumDirection.y() = 0;
00358         sumDirection.z() = -1;
00359     }
00360 
00361     //Transform the direction to the model space and pass to the shader
00362     sumDirection = worldToModelRot * sumDirection;
00363     _shadowShader->setUniform("lightDirection", sumDirection);
00364 }
00365 
00366 bool OpenGLSActorRenderer::getPointLightContribution(LightEntry *light,
00367         const Math::Vector3d &actorPosition, Math::Vector3d &direction, float weight) {
00368     float distance = light->position.getDistanceTo(actorPosition);
00369 
00370     if (distance > light->falloffFar) {
00371         return false;
00372     }
00373 
00374     float factor;
00375     if (distance > light->falloffNear) {
00376         if (light->falloffFar - light->falloffNear > 1) {
00377             factor = 1 - (distance - light->falloffNear) / (light->falloffFar - light->falloffNear);
00378         } else {
00379             factor = 0;
00380         }
00381     } else {
00382         factor = 1;
00383     }
00384 
00385     float brightness = (light->color.x() + light->color.y() + light->color.z()) / 3.0f;
00386 
00387     if (factor <= 0 || brightness <= 0) {
00388         return false;
00389     }
00390 
00391     direction = actorPosition - light->position;
00392     direction.normalize();
00393     direction *= factor * brightness * weight;
00394 
00395     return true;
00396 }
00397 
00398 bool OpenGLSActorRenderer::getDirectionalLightContribution(LightEntry *light, Math::Vector3d &direction) {
00399     float brightness = (light->color.x() + light->color.y() + light->color.z()) / 3.0f;
00400 
00401     if (brightness <= 0) {
00402         return false;
00403     }
00404 
00405     direction = light->direction;
00406     direction.normalize();
00407     direction *= brightness;
00408 
00409     return true;
00410 }
00411 
00412 bool OpenGLSActorRenderer::getSpotLightContribution(LightEntry *light,
00413         const Math::Vector3d &actorPosition, Math::Vector3d &direction) {
00414     Math::Vector3d lightToActor = actorPosition - light->position;
00415     lightToActor.normalize();
00416 
00417     float cosAngle = MAX(0.0f, lightToActor.dotProduct(light->direction));
00418     float cone = (cosAngle - light->innerConeAngle.getCosine()) /
00419             MAX(0.001f, light->outerConeAngle.getCosine() - light->innerConeAngle.getCosine());
00420     cone = CLIP(cone, 0.0f, 1.0f);
00421 
00422     if (cone <= 0) {
00423         return false;
00424     }
00425 
00426     return getPointLightContribution(light, actorPosition, direction, cone);
00427 }
00428 
00429 } // End of namespace Gfx
00430 } // End of namespace Stark


Generated on Sat Nov 9 2019 05:00:30 for ResidualVM by doxygen 1.7.1
curved edge   curved edge