2020-07-21 18:26:45 +02:00
|
|
|
/* ResidualVM - A 3D game interpreter
|
|
|
|
*
|
|
|
|
* ResidualVM is the legal property of its developers, whose names
|
|
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
|
|
* file distributed with this source distribution.
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version 2
|
|
|
|
* of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This file is based on WME.
|
|
|
|
* http://dead-code.org/redir.php?target=wme
|
|
|
|
* Copyright (c) 2003-2013 Jan Nedoma and contributors
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "common/math.h"
|
|
|
|
#include "common/util.h"
|
|
|
|
#include "engines/wintermute/base/base_game.h"
|
|
|
|
#include "engines/wintermute/base/gfx/opengl/base_surface_opengl3d.h"
|
|
|
|
#include "engines/wintermute/base/gfx/opengl/material.h"
|
|
|
|
#include "engines/wintermute/base/gfx/opengl/meshx_opengl_shader.h"
|
|
|
|
#include "engines/wintermute/base/gfx/opengl/shadow_volume.h"
|
|
|
|
#include "engines/wintermute/base/gfx/x/frame_node.h"
|
|
|
|
#include "engines/wintermute/base/gfx/x/loader_x.h"
|
|
|
|
#include "engines/wintermute/base/gfx/x/modelx.h"
|
|
|
|
#include "engines/wintermute/dcgf.h"
|
|
|
|
#include "engines/wintermute/math/math_util.h"
|
|
|
|
|
|
|
|
namespace Wintermute {
|
|
|
|
|
|
|
|
// define constant to make it available to the linker
|
|
|
|
const uint32 MeshXOpenGLShader::kNullIndex;
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
2020-07-31 21:15:56 +02:00
|
|
|
MeshXOpenGLShader::MeshXOpenGLShader(BaseGame *inGame, OpenGL::Shader *shader) :
|
|
|
|
MeshX(inGame),
|
|
|
|
_numAttrs(0), _maxFaceInfluence(0),
|
|
|
|
_vertexData(nullptr), _vertexPositionData(nullptr),
|
|
|
|
_vertexCount(0), _indexData(nullptr), _indexCount(0),
|
|
|
|
_shader(shader), _skinAdjacency(nullptr), _skinnedMesh(false) {
|
|
|
|
glGenBuffers(1, &_vertexBuffer);
|
|
|
|
glGenBuffers(1, &_indexBuffer);
|
2020-07-21 18:26:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
MeshXOpenGLShader::~MeshXOpenGLShader() {
|
|
|
|
delete[] _skinAdjacency;
|
|
|
|
delete[] _vertexData;
|
|
|
|
delete[] _vertexPositionData;
|
|
|
|
delete[] _indexData;
|
|
|
|
|
|
|
|
for (uint32 i = 0; i < _materials.size(); i++) {
|
|
|
|
delete _materials[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
_materials.clear();
|
2020-07-31 21:15:56 +02:00
|
|
|
|
|
|
|
glDeleteBuffers(1, &_vertexBuffer);
|
|
|
|
glDeleteBuffers(1, &_indexBuffer);
|
2020-07-21 18:26:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
bool MeshXOpenGLShader::loadFromX(const Common::String &filename, XFileLexer &lexer) {
|
|
|
|
bool res = true;
|
|
|
|
|
|
|
|
lexer.advanceToNextToken(); // skip the name
|
|
|
|
lexer.advanceOnOpenBraces();
|
|
|
|
_vertexCount = readInt(lexer);
|
|
|
|
|
|
|
|
// vertex format for .X meshes will be position + normals + textures
|
|
|
|
_vertexData = new float[kVertexComponentCount * _vertexCount]();
|
|
|
|
_vertexPositionData = new float[3 * _vertexCount]();
|
|
|
|
|
|
|
|
parsePositionCoords(lexer);
|
|
|
|
|
|
|
|
int faceCount = readInt(lexer);
|
|
|
|
// we should be able to assume for now that
|
|
|
|
// we are only dealing with triangles
|
|
|
|
_indexData = new uint16[faceCount * 3]();
|
|
|
|
_indexCount = faceCount * 3;
|
|
|
|
|
|
|
|
parseFaces(lexer, faceCount);
|
|
|
|
|
|
|
|
while (!lexer.eof()) {
|
|
|
|
if (lexer.tokenIsIdentifier("MeshTextureCoords")) {
|
|
|
|
lexer.advanceToNextToken();
|
|
|
|
lexer.advanceOnOpenBraces();
|
|
|
|
|
|
|
|
parseTextureCoords(lexer);
|
|
|
|
} else if (lexer.tokenIsIdentifier("MeshNormals")) {
|
|
|
|
lexer.advanceToNextToken();
|
|
|
|
lexer.advanceOnOpenBraces();
|
|
|
|
|
|
|
|
parseNormalCoords(lexer);
|
|
|
|
} else if (lexer.tokenIsIdentifier("MeshMaterialList")) {
|
|
|
|
lexer.advanceToNextToken();
|
|
|
|
lexer.advanceOnOpenBraces();
|
|
|
|
|
|
|
|
parseMaterials(lexer, faceCount, filename);
|
|
|
|
} else if (lexer.tokenIsIdentifier("Material")) {
|
|
|
|
lexer.advanceToNextToken();
|
|
|
|
Material *mat = new Material(_gameRef);
|
|
|
|
mat->loadFromX(lexer, filename);
|
|
|
|
_materials.add(mat);
|
|
|
|
|
|
|
|
// one material = one index range
|
|
|
|
_numAttrs = 1;
|
|
|
|
_indexRanges.push_back(0);
|
|
|
|
_indexRanges.push_back(_indexCount);
|
|
|
|
} else if (lexer.tokenIsIdentifier("XSkinMeshHeader")) {
|
|
|
|
lexer.advanceToNextToken();
|
|
|
|
lexer.advanceOnOpenBraces();
|
|
|
|
|
|
|
|
// if any of this is zero, we should have an unskinned mesh
|
|
|
|
int maxSkinWeightsPerVertex = readInt(lexer);
|
|
|
|
int maxSkinWeightsPerFace = readInt(lexer);
|
|
|
|
int boneCount = readInt(lexer);
|
|
|
|
|
|
|
|
_skinnedMesh = boneCount > 0;
|
|
|
|
|
|
|
|
lexer.advanceToNextToken(); // skip semicolon
|
|
|
|
} else if (lexer.tokenIsIdentifier("SkinWeights")) {
|
|
|
|
// but now we certainly should have a skinned mesh
|
|
|
|
_skinnedMesh = true;
|
|
|
|
lexer.advanceToNextToken();
|
|
|
|
lexer.advanceOnOpenBraces();
|
|
|
|
|
|
|
|
parseSkinWeights(lexer);
|
|
|
|
} else if (lexer.tokenIsIdentifier()) {
|
|
|
|
while (!lexer.reachedClosedBraces()) {
|
|
|
|
lexer.advanceToNextToken();
|
|
|
|
}
|
|
|
|
|
|
|
|
lexer.advanceToNextToken(); // skip closed braces
|
|
|
|
} else if (lexer.reachedClosedBraces()) {
|
|
|
|
lexer.advanceToNextToken(); // skip closed braces
|
|
|
|
|
2020-07-31 21:15:56 +02:00
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer);
|
|
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBuffer);
|
|
|
|
|
|
|
|
glBufferData(GL_ARRAY_BUFFER, 4 * kVertexComponentCount * _vertexCount, _vertexData, GL_DYNAMIC_DRAW);
|
|
|
|
glBufferData(GL_ELEMENT_ARRAY_BUFFER, 2 * _indexCount, _indexData, GL_STATIC_DRAW);
|
|
|
|
|
2020-07-21 18:26:45 +02:00
|
|
|
generateAdjacency();
|
|
|
|
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
warning("MeshXOpenGLShader::loadFromX unknown token %i encountered", lexer.getTypeOfToken());
|
|
|
|
lexer.advanceToNextToken();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-31 21:15:56 +02:00
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer);
|
|
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBuffer);
|
|
|
|
|
|
|
|
glBufferData(GL_ARRAY_BUFFER, 4 * kVertexComponentCount * _vertexCount, _vertexData, GL_DYNAMIC_DRAW);
|
|
|
|
glBufferData(GL_ELEMENT_ARRAY_BUFFER, 2 * _indexCount, _indexData, GL_STATIC_DRAW);
|
|
|
|
|
2020-07-21 18:26:45 +02:00
|
|
|
generateAdjacency();
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
bool MeshXOpenGLShader::generateAdjacency() {
|
|
|
|
_adjacency = Common::Array<uint32>(_indexCount, kNullIndex);
|
|
|
|
|
|
|
|
for (uint32 i = 0; i < _indexCount / 3; ++i) {
|
|
|
|
for (uint32 j = i + 1; j < _indexCount / 3; ++j) {
|
|
|
|
for (int edge1 = 0; edge1 < 3; ++edge1) {
|
|
|
|
uint16 index1 = _indexData[i * 3 + edge1];
|
|
|
|
uint16 index2 = _indexData[i * 3 + (edge1 + 1) % 3];
|
|
|
|
|
|
|
|
for (int edge2 = 0; edge2 < 3; ++edge2) {
|
|
|
|
uint16 index3 = _indexData[j * 3 + edge2];
|
|
|
|
uint16 index4 = _indexData[j * 3 + (edge2 + 1) % 3];
|
|
|
|
|
|
|
|
if (_adjacency[i * 3 + edge1] == kNullIndex && _adjacency[j * 3 + edge2] == kNullIndex && adjacentEdge(index1, index2, index3, index4)) {
|
|
|
|
_adjacency[i * 3 + edge1] = j;
|
|
|
|
_adjacency[j * 3 + edge2] = i;
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool MeshXOpenGLShader::adjacentEdge(uint16 index1, uint16 index2, uint16 index3, uint16 index4) {
|
|
|
|
Math::Vector3d vertex1(_vertexPositionData + 3 * index1);
|
|
|
|
Math::Vector3d vertex2(_vertexPositionData + 3 * index2);
|
|
|
|
Math::Vector3d vertex3(_vertexPositionData + 3 * index3);
|
|
|
|
Math::Vector3d vertex4(_vertexPositionData + 3 * index4);
|
|
|
|
|
|
|
|
// wme uses a function from the D3DX library, which takes in an epsilon for floating point comparison
|
|
|
|
// wme passes in zero, so we just do a direct comparison
|
|
|
|
if (vertex1 == vertex3 && vertex2 == vertex4) {
|
|
|
|
return true;
|
|
|
|
} else if (vertex1 == vertex4 && vertex2 == vertex3) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
bool MeshXOpenGLShader::findBones(FrameNode *rootFrame) {
|
|
|
|
// normal meshes don't have bones
|
|
|
|
if (!_skinnedMesh) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
_boneMatrices.resize(skinWeightsList.size());
|
|
|
|
|
|
|
|
for (uint i = 0; i < skinWeightsList.size(); ++i) {
|
|
|
|
FrameNode *frame = rootFrame->findFrame(skinWeightsList[i]._boneName.c_str());
|
|
|
|
|
|
|
|
if (frame) {
|
|
|
|
_boneMatrices[i] = frame->getCombinedMatrix();
|
|
|
|
} else {
|
|
|
|
warning("MeshXOpenGLShader::findBones could not find bone %s", skinWeightsList[i]._boneName.c_str());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
bool MeshXOpenGLShader::update(FrameNode *parentFrame) {
|
|
|
|
if (_vertexData == nullptr) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool res = false;
|
|
|
|
|
|
|
|
// update skinned mesh
|
|
|
|
if (_skinnedMesh) {
|
|
|
|
BaseArray<Math::Matrix4> finalBoneMatrices;
|
|
|
|
finalBoneMatrices.resize(_boneMatrices.size());
|
|
|
|
|
|
|
|
for (uint i = 0; i < skinWeightsList.size(); ++i) {
|
|
|
|
finalBoneMatrices[i] = *_boneMatrices[i] * skinWeightsList[i]._offsetMatrix;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (uint32 i = 0; i < _vertexCount; ++i) {
|
|
|
|
for (int j = 0; j < 3; ++j) {
|
|
|
|
_vertexData[i * kVertexComponentCount + kPositionOffset + j] = 0.0f;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (uint boneIndex = 0; boneIndex < skinWeightsList.size(); ++boneIndex) {
|
|
|
|
for (uint i = 0; i < skinWeightsList[boneIndex]._vertexIndices.size(); ++i) {
|
|
|
|
uint32 vertexIndex = skinWeightsList[boneIndex]._vertexIndices[i];
|
|
|
|
Math::Vector3d pos;
|
|
|
|
pos.setData(_vertexPositionData + vertexIndex * 3);
|
|
|
|
finalBoneMatrices[boneIndex].transform(&pos, true);
|
|
|
|
pos *= skinWeightsList[boneIndex]._vertexWeights[i];
|
|
|
|
|
|
|
|
for (uint j = 0; j < 3; ++j) {
|
|
|
|
_vertexData[vertexIndex * kVertexComponentCount + kPositionOffset + j] += pos.getData()[j];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (uint i = 0; i < skinWeightsList.size(); ++i) {
|
|
|
|
finalBoneMatrices[i].transpose();
|
|
|
|
finalBoneMatrices[i].inverse();
|
|
|
|
}
|
|
|
|
|
|
|
|
for (uint32 i = 0; i < _vertexCount; ++i) {
|
|
|
|
for (int j = 0; j < 3; ++j) {
|
|
|
|
_vertexData[i * kVertexComponentCount + kNormalOffset + j] = 0.0f;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (uint boneIndex = 0; boneIndex < skinWeightsList.size(); ++boneIndex) {
|
|
|
|
for (uint i = 0; i < skinWeightsList[boneIndex]._vertexIndices.size(); ++i) {
|
|
|
|
uint32 vertexIndex = skinWeightsList[boneIndex]._vertexIndices[i];
|
|
|
|
Math::Vector3d pos;
|
|
|
|
pos.setData(_vertexNormalData + vertexIndex * 3);
|
|
|
|
finalBoneMatrices[boneIndex].transform(&pos, true);
|
|
|
|
pos *= skinWeightsList[boneIndex]._vertexWeights[i];
|
|
|
|
|
|
|
|
for (uint j = 0; j < 3; ++j) {
|
|
|
|
_vertexData[vertexIndex * kVertexComponentCount + kNormalOffset + j] += pos.getData()[j];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-31 21:15:56 +02:00
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer);
|
|
|
|
glBufferSubData(GL_ARRAY_BUFFER, 0, 4 * kVertexComponentCount * _vertexCount, _vertexData);
|
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
|
|
|
|
2020-07-21 18:26:45 +02:00
|
|
|
// updateNormals();
|
|
|
|
} else { // update static
|
|
|
|
warning("MeshXOpenGLShader::update update of static mesh is not implemented yet");
|
|
|
|
}
|
|
|
|
|
|
|
|
updateBoundingBox();
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
bool MeshXOpenGLShader::updateShadowVol(ShadowVolume *shadow, Math::Matrix4 &modelMat, const Math::Vector3d &light, float extrusionDepth) {
|
|
|
|
if (_vertexData == nullptr) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
Math::Vector3d invLight = light;
|
|
|
|
Math::Matrix4 matInverseModel = modelMat;
|
|
|
|
matInverseModel.inverse();
|
|
|
|
matInverseModel.transform(&invLight, false);
|
|
|
|
|
|
|
|
uint32 numEdges = 0;
|
|
|
|
|
|
|
|
Common::Array<bool> isFront(_indexCount / 3, false);
|
|
|
|
|
|
|
|
// First pass : for each face, record if it is front or back facing the light
|
|
|
|
for (uint32 i = 0; i < _indexCount / 3; i++) {
|
|
|
|
uint16 index0 = _indexData[3 * i + 0];
|
|
|
|
uint16 index1 = _indexData[3 * i + 1];
|
|
|
|
uint16 index2 = _indexData[3 * i + 2];
|
|
|
|
|
|
|
|
Math::Vector3d v0(_vertexData + index0 * kVertexComponentCount + kPositionOffset);
|
|
|
|
Math::Vector3d v1(_vertexData + index1 * kVertexComponentCount + kPositionOffset);
|
|
|
|
Math::Vector3d v2(_vertexData + index2 * kVertexComponentCount + kPositionOffset);
|
|
|
|
|
|
|
|
// Transform vertices or transform light?
|
|
|
|
Math::Vector3d vNormal = Math::Vector3d::crossProduct(v2 - v1, v1 - v0);
|
|
|
|
|
|
|
|
if (Math::Vector3d::dotProduct(vNormal, invLight) >= 0.0f) {
|
|
|
|
isFront[i] = false; // back face
|
|
|
|
} else {
|
|
|
|
isFront[i] = true; // front face
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Allocate a temporary edge list
|
|
|
|
Common::Array<uint16> edges(_indexCount * 2, 0);
|
|
|
|
|
|
|
|
// First pass : for each face, record if it is front or back facing the light
|
|
|
|
for (uint32 i = 0; i < _indexCount / 3; i++) {
|
|
|
|
if (isFront[i]) {
|
|
|
|
uint16 wFace0 = _indexData[3 * i + 0];
|
|
|
|
uint16 wFace1 = _indexData[3 * i + 1];
|
|
|
|
uint16 wFace2 = _indexData[3 * i + 2];
|
|
|
|
|
|
|
|
uint32 adjacent0 = _adjacency[3 * i + 0];
|
|
|
|
uint32 adjacent1 = _adjacency[3 * i + 1];
|
|
|
|
uint32 adjacent2 = _adjacency[3 * i + 2];
|
|
|
|
|
|
|
|
if (adjacent0 == kNullIndex || isFront[adjacent0] == false) {
|
|
|
|
// add edge v0-v1
|
|
|
|
edges[2 * numEdges + 0] = wFace0;
|
|
|
|
edges[2 * numEdges + 1] = wFace1;
|
|
|
|
numEdges++;
|
|
|
|
}
|
|
|
|
if (adjacent1 == kNullIndex || isFront[adjacent1] == false) {
|
|
|
|
// add edge v1-v2
|
|
|
|
edges[2 * numEdges + 0] = wFace1;
|
|
|
|
edges[2 * numEdges + 1] = wFace2;
|
|
|
|
numEdges++;
|
|
|
|
}
|
|
|
|
if (adjacent2 == kNullIndex || isFront[adjacent2] == false) {
|
|
|
|
// add edge v2-v0
|
|
|
|
edges[2 * numEdges + 0] = wFace2;
|
|
|
|
edges[2 * numEdges + 1] = wFace0;
|
|
|
|
numEdges++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (uint32 i = 0; i < numEdges; i++) {
|
|
|
|
Math::Vector3d v1(_vertexData + edges[2 * i + 0] * kVertexComponentCount + kPositionOffset);
|
|
|
|
Math::Vector3d v2(_vertexData + edges[2 * i + 1] * kVertexComponentCount + kPositionOffset);
|
|
|
|
Math::Vector3d v3 = v1 - invLight * extrusionDepth;
|
|
|
|
Math::Vector3d v4 = v2 - invLight * extrusionDepth;
|
|
|
|
|
|
|
|
// Add a quad (two triangles) to the vertex list
|
|
|
|
shadow->addVertex(v1);
|
|
|
|
shadow->addVertex(v2);
|
|
|
|
shadow->addVertex(v3);
|
|
|
|
shadow->addVertex(v2);
|
|
|
|
shadow->addVertex(v4);
|
|
|
|
shadow->addVertex(v3);
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
bool MeshXOpenGLShader::render(ModelX *model) {
|
|
|
|
if (_vertexData == nullptr) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool res = false;
|
|
|
|
|
2020-07-31 21:15:56 +02:00
|
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBuffer);
|
2020-07-21 18:26:45 +02:00
|
|
|
|
2020-07-31 21:15:56 +02:00
|
|
|
_shader->enableVertexAttribute("position", _vertexBuffer, 3, GL_FLOAT, false, 4 * kVertexComponentCount, 4 * kPositionOffset);
|
|
|
|
_shader->enableVertexAttribute("texcoord", _vertexBuffer, 2, GL_FLOAT, false, 4 * kVertexComponentCount, 4 * kTextureCoordOffset);
|
|
|
|
_shader->enableVertexAttribute("normal", _vertexBuffer, 3, GL_FLOAT, false, 4 * kVertexComponentCount, 4 * kNormalOffset);
|
2020-07-21 18:26:45 +02:00
|
|
|
|
2020-07-31 21:15:56 +02:00
|
|
|
_shader->use(true);
|
|
|
|
|
|
|
|
for (uint32 i = 0; i < _numAttrs; i++) {
|
2020-07-21 18:26:45 +02:00
|
|
|
glEnable(GL_TEXTURE_2D);
|
|
|
|
static_cast<BaseSurfaceOpenGL3D *>(_materials[i]->getSurface())->setTexture();
|
|
|
|
|
2020-07-31 21:15:56 +02:00
|
|
|
// wme does not seem to care about specular or emissive light values
|
|
|
|
Math::Vector4d diffuse(_materials[i]->_diffuse.data);
|
|
|
|
_shader->setUniform("diffuse", diffuse);
|
|
|
|
_shader->setUniform("ambient", diffuse);
|
|
|
|
|
|
|
|
size_t offset = 2 * _indexRanges[i];
|
|
|
|
glDrawElements(GL_TRIANGLES, _indexRanges[i + 1] - _indexRanges[i], GL_UNSIGNED_SHORT, (void *)offset);
|
2020-07-21 18:26:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
|
|
glDisable(GL_TEXTURE_2D);
|
|
|
|
|
2020-07-31 21:15:56 +02:00
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
|
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
|
|
|
|
2020-07-21 18:26:45 +02:00
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
bool MeshXOpenGLShader::pickPoly(Math::Vector3d *pickRayOrig, Math::Vector3d *pickRayDir) {
|
|
|
|
if (_vertexData == nullptr) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool res = false;
|
|
|
|
|
|
|
|
for (uint16 i = 0; i < _indexCount; i += 3) {
|
|
|
|
uint16 index1 = _indexData[i + 0];
|
|
|
|
uint16 index2 = _indexData[i + 1];
|
|
|
|
uint16 index3 = _indexData[i + 2];
|
|
|
|
|
|
|
|
Math::Vector3d v0;
|
|
|
|
v0.setData(&_vertexData[index1 * kVertexComponentCount + kPositionOffset]);
|
|
|
|
Math::Vector3d v1;
|
|
|
|
v1.setData(&_vertexData[index2 * kVertexComponentCount + kPositionOffset]);
|
|
|
|
Math::Vector3d v2;
|
|
|
|
v2.setData(&_vertexData[index3 * kVertexComponentCount + kPositionOffset]);
|
|
|
|
|
|
|
|
Math::Vector3d intersection;
|
|
|
|
if (lineIntersectsTriangle(*pickRayOrig, *pickRayDir, v0, v1, v2, intersection.x(), intersection.y(), intersection.z())) {
|
|
|
|
res = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
bool MeshXOpenGLShader::setMaterialSprite(const Common::String &matName, BaseSprite *sprite) {
|
|
|
|
for (uint32 i = 0; i < _materials.size(); i++) {
|
|
|
|
if (_materials[i]->getName() && _materials[i]->getName() == matName) {
|
|
|
|
_materials[i]->setSprite(sprite);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
bool MeshXOpenGLShader::setMaterialTheora(const Common::String &matName, VideoTheoraPlayer *theora) {
|
|
|
|
for (uint32 i = 0; i < _materials.size(); i++) {
|
|
|
|
if (_materials[i]->getName() && _materials[i]->getName() == matName) {
|
|
|
|
_materials[i]->setTheora(theora);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
bool MeshXOpenGLShader::invalidateDeviceObjects() {
|
|
|
|
// release buffers here
|
|
|
|
|
|
|
|
for (uint32 i = 0; i < _materials.size(); i++) {
|
|
|
|
_materials[i]->invalidateDeviceObjects();
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
bool MeshXOpenGLShader::restoreDeviceObjects() {
|
|
|
|
for (uint32 i = 0; i < _materials.size(); i++) {
|
|
|
|
_materials[i]->restoreDeviceObjects();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (_skinnedMesh) {
|
|
|
|
return generateAdjacency();
|
|
|
|
} else {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool MeshXOpenGLShader::parsePositionCoords(XFileLexer &lexer) {
|
|
|
|
for (uint i = 0; i < _vertexCount; ++i) {
|
|
|
|
for (int j = 0; j < 3; ++j) {
|
|
|
|
_vertexPositionData[i * 3 + j] = readFloat(lexer);
|
|
|
|
_vertexData[i * kVertexComponentCount + kPositionOffset + j] = _vertexPositionData[i * 3 + j];
|
|
|
|
}
|
|
|
|
|
|
|
|
_vertexPositionData[i * 3 + 2] *= -1.0f;
|
|
|
|
_vertexData[i * kVertexComponentCount + kPositionOffset + 2] *= -1.0f;
|
|
|
|
|
|
|
|
lexer.advanceToNextToken(); // skip semicolon
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool MeshXOpenGLShader::parseFaces(XFileLexer &lexer, int faceCount) {
|
|
|
|
for (int i = 0; i < faceCount; ++i) {
|
|
|
|
int indexCount = readInt(lexer);
|
|
|
|
|
|
|
|
// we can add something to triangulize faces later if the need arises
|
|
|
|
if (indexCount != 3) {
|
|
|
|
warning("MeshXOpenGLShader::loadFromX non triangle faces are not supported yet");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int j = 0; j < 3; ++j) {
|
|
|
|
_indexData[i * 3 + j] = readInt(lexer);
|
|
|
|
}
|
|
|
|
|
|
|
|
// swap to change winding and make it consistent with the coordinate mirroring
|
|
|
|
SWAP(_indexData[i * 3 + 0], _indexData[i * 3 + 2]);
|
|
|
|
|
|
|
|
lexer.advanceToNextToken(); // skip semicolon
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool MeshXOpenGLShader::parseTextureCoords(XFileLexer &lexer) {
|
|
|
|
// should be the same as _vertexCount
|
|
|
|
int textureCoordCount = readInt(lexer);
|
|
|
|
|
|
|
|
for (int i = 0; i < textureCoordCount; ++i) {
|
|
|
|
_vertexData[i * kVertexComponentCount + kTextureCoordOffset + 0] = readFloat(lexer);
|
|
|
|
_vertexData[i * kVertexComponentCount + kTextureCoordOffset + 1] = readFloat(lexer);
|
|
|
|
lexer.advanceToNextToken(); // skip semicolon
|
|
|
|
}
|
|
|
|
|
|
|
|
if (lexer.reachedClosedBraces()) {
|
|
|
|
lexer.advanceToNextToken();
|
|
|
|
} else {
|
|
|
|
warning("Missing } in mesh object");
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool MeshXOpenGLShader::parseNormalCoords(XFileLexer &lexer) {
|
|
|
|
// should be the same as _vertex count
|
|
|
|
uint vertexNormalCount = readInt(lexer);
|
|
|
|
assert(vertexNormalCount == _vertexCount);
|
|
|
|
|
|
|
|
_vertexNormalData = new float[3 * _vertexCount]();
|
|
|
|
|
|
|
|
for (uint i = 0; i < vertexNormalCount; ++i) {
|
|
|
|
_vertexData[i * kVertexComponentCount + kNormalOffset] = readFloat(lexer);
|
|
|
|
_vertexNormalData[i * 3 + 0] = _vertexData[i * kVertexComponentCount + kNormalOffset];
|
|
|
|
_vertexData[i * kVertexComponentCount + kNormalOffset + 1] = readFloat(lexer);
|
|
|
|
_vertexNormalData[i * 3 + 1] = _vertexData[i * kVertexComponentCount + kNormalOffset + 1];
|
|
|
|
// mirror z coordinate to change to OpenGL coordinate system
|
|
|
|
_vertexData[i * kVertexComponentCount + kNormalOffset + 2] = -readFloat(lexer);
|
|
|
|
_vertexNormalData[i * 3 + 2] = _vertexData[i * kVertexComponentCount + kNormalOffset + 2];
|
|
|
|
lexer.advanceToNextToken(); // skip semicolon
|
|
|
|
}
|
|
|
|
|
|
|
|
// we ignore face normals for now
|
|
|
|
while (!lexer.reachedClosedBraces()) {
|
|
|
|
lexer.advanceToNextToken();
|
|
|
|
}
|
|
|
|
|
|
|
|
lexer.advanceToNextToken(); // skip closed braces
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool MeshXOpenGLShader::parseMaterials(XFileLexer &lexer, int faceCount, const Common::String &filename) {
|
|
|
|
// there can be unused materials inside a .X file
|
|
|
|
// so this piece of information is probably useless
|
|
|
|
int materialCount = readInt(lexer);
|
|
|
|
// should be the same as faceCount
|
|
|
|
int faceMaterialCount = readInt(lexer);
|
|
|
|
assert(faceMaterialCount = faceCount);
|
|
|
|
|
|
|
|
// from looking at the wme3d sources and MSDN,
|
|
|
|
// I would say that faces using the same material
|
|
|
|
// are layed out as a contiguous block and the
|
|
|
|
// material index is only increasing
|
|
|
|
// in case this isn't true it might be a good
|
|
|
|
// idea to split the mesh
|
|
|
|
_indexRanges.push_back(0);
|
|
|
|
int currentMaterialIndex = readInt(lexer);
|
|
|
|
|
|
|
|
for (int i = 1; i < faceMaterialCount; ++i) {
|
|
|
|
int currentMaterialIndexTmp = readInt(lexer);
|
|
|
|
|
|
|
|
// again, this assumes that face indices are only increasing
|
|
|
|
if (currentMaterialIndex < currentMaterialIndexTmp) {
|
|
|
|
currentMaterialIndex = currentMaterialIndexTmp;
|
|
|
|
_indexRanges.push_back(3 * i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
_indexRanges.push_back(3 * faceCount);
|
|
|
|
_numAttrs = _indexRanges.size() - 1;
|
|
|
|
|
|
|
|
while (!lexer.eof()) {
|
|
|
|
if (lexer.tokenIsIdentifier("Material")) {
|
|
|
|
lexer.advanceToNextToken();
|
|
|
|
Material *mat = new Material(_gameRef);
|
|
|
|
mat->loadFromX(lexer, filename);
|
|
|
|
_materials.add(mat);
|
|
|
|
} else if (lexer.tokenIsIdentifier()) {
|
|
|
|
while (!lexer.reachedClosedBraces()) {
|
|
|
|
lexer.advanceToNextToken();
|
|
|
|
}
|
|
|
|
|
|
|
|
lexer.advanceToNextToken(); // skip closed braces
|
|
|
|
} else if (lexer.reachedClosedBraces()) {
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
warning("MeshXOpenGLShader::loadFromX unknown token %i encountered while loading materials", lexer.getTypeOfToken());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
lexer.advanceToNextToken(); // skip closed braces
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool MeshXOpenGLShader::parseSkinWeights(XFileLexer &lexer) {
|
|
|
|
skinWeightsList.resize(skinWeightsList.size() + 1);
|
|
|
|
SkinWeights &currSkinWeights = skinWeightsList.back();
|
|
|
|
|
|
|
|
currSkinWeights._boneName = readString(lexer);
|
|
|
|
|
|
|
|
int weightCount = readInt(lexer);
|
|
|
|
currSkinWeights._vertexIndices.resize(weightCount);
|
|
|
|
currSkinWeights._vertexWeights.resize(weightCount);
|
|
|
|
|
|
|
|
for (int i = 0; i < weightCount; ++i) {
|
|
|
|
currSkinWeights._vertexIndices[i] = readInt(lexer);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (weightCount == 0) {
|
|
|
|
lexer.advanceToNextToken();
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = 0; i < weightCount; ++i) {
|
|
|
|
currSkinWeights._vertexWeights[i] = readFloat(lexer);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (weightCount == 0) {
|
|
|
|
lexer.advanceToNextToken();
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int r = 0; r < 4; ++r) {
|
|
|
|
for (int c = 0; c < 4; ++c) {
|
|
|
|
currSkinWeights._offsetMatrix(c, r) = readFloat(lexer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// mirror at orign
|
|
|
|
currSkinWeights._offsetMatrix(2, 3) *= -1.0f;
|
|
|
|
|
|
|
|
// mirror base vectors
|
|
|
|
currSkinWeights._offsetMatrix(2, 0) *= -1.0f;
|
|
|
|
currSkinWeights._offsetMatrix(2, 1) *= -1.0f;
|
|
|
|
|
|
|
|
// change handedness
|
|
|
|
currSkinWeights._offsetMatrix(0, 2) *= -1.0f;
|
|
|
|
currSkinWeights._offsetMatrix(1, 2) *= -1.0f;
|
|
|
|
|
|
|
|
lexer.advanceToNextToken(); // semicolon of matrix
|
|
|
|
lexer.advanceToNextToken(); // closed braces of skin weights object
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void MeshXOpenGLShader::updateBoundingBox() {
|
|
|
|
if (_vertexData == nullptr || _vertexCount == 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
_BBoxStart.setData(&_vertexData[0 + kPositionOffset]);
|
|
|
|
_BBoxEnd.setData(&_vertexData[0 + kPositionOffset]);
|
|
|
|
|
|
|
|
for (uint16 i = 1; i < _vertexCount; ++i) {
|
|
|
|
|
|
|
|
Math::Vector3d v;
|
|
|
|
v.setData(&_vertexData[i * kVertexComponentCount + kPositionOffset]);
|
|
|
|
|
|
|
|
_BBoxStart.x() = MIN(_BBoxStart.x(), v.x());
|
|
|
|
_BBoxStart.y() = MIN(_BBoxStart.y(), v.y());
|
|
|
|
_BBoxStart.z() = MIN(_BBoxStart.z(), v.z());
|
|
|
|
|
|
|
|
_BBoxEnd.x() = MAX(_BBoxEnd.x(), v.x());
|
|
|
|
_BBoxEnd.y() = MAX(_BBoxEnd.y(), v.y());
|
|
|
|
_BBoxEnd.z() = MAX(_BBoxEnd.z(), v.z());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace Wintermute
|