protot/3rdparty/RuntimeCompiledCpp/Renderer/AURenMesh.cpp

439 lines
12 KiB
C++

//
// Copyright (c) 2010-2011 Matthew Jack and Doug Binks
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
#include "AURenMesh.h"
#ifndef _WIN32
#define NO_ASSIMP //Currently not adding assimp support to other platforms
#endif
#include "../Common/AUVec3f.inl" //for cross product used in calculateing normals
// Windows Requirements
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
#include <windows.h>
#endif //_WIN32
#ifdef __MACH__
#include <OpenGL/gl.h>
#else
// OpenGL requirements
#include <GL/gl.h>
#endif //__MACH__
#ifndef NO_ASSIMP
#include <assimp.hpp>
#include <aiScene.h>
#include <aiPostProcess.h>
#endif //NO_ASSIMP
#ifndef _WIN32
#include <string.h>
int _stricmp( const char* pS1, const char* pS2 )
{
return strcasecmp( pS1, pS2 );
}
#endif
#include <fstream>
#include <assert.h>
AURenMesh::AURenMesh() :
m_pafVertexCoordinates( NULL ),
m_pafTextureCoordinates( NULL ),
m_pafNormals( NULL ),
m_pausTriangleIndices( NULL ),
m_uiNumVertices( 0 ),
m_uiNumTriangles( 0 )
{
}
AURenMesh::~AURenMesh()
{
Clear();
}
void AURenMesh::Clear()
{
delete[] m_pafNormals;
delete[] m_pafVertexCoordinates;
delete[] m_pafTextureCoordinates;
delete[] m_pausTriangleIndices;
}
bool AURenMesh::LoadFromFile( const std::string& strFilename )
{
// Safely delete any existing data before loading new mesh
Clear();
int index = (int)strFilename.size() - 3;
std::string extension = index >= 0 ? strFilename.substr(index, 3) : "";
if (!_stricmp(extension.c_str(), "aml"))
{
return LoadFromFileAML(strFilename);
}
else
{
return LoadFromFileImport(strFilename);
}
}
bool AURenMesh::SaveToFile( const std::string& strFilename )
{
std::ofstream outFile;
outFile.open(strFilename.c_str(), std::ios::binary);
if( !outFile )
{
return false;
}
outFile << "AML Aurora File" << std::endl;
outFile << 1 << std::endl; // version
outFile << m_uiNumVertices << std::endl;
outFile << m_uiNumTriangles << std::endl;
outFile.write( reinterpret_cast<char*>( m_pafVertexCoordinates ), 3 * m_uiNumVertices * sizeof( float ) );
outFile.write( reinterpret_cast<char*>( m_pafTextureCoordinates ), 2 * m_uiNumVertices * sizeof( float ) );
outFile.write( reinterpret_cast<char*>( m_pafNormals ), 3 * m_uiNumVertices * sizeof( float ) );
outFile.write( reinterpret_cast<char*>( m_pausTriangleIndices ), 3 * m_uiNumTriangles * sizeof( unsigned short ) );
outFile.close();
return true;
}
bool AURenMesh::LoadFromFileAML( const std::string& strFilename_ )
{
std::ifstream inFile;
inFile.open(strFilename_.c_str(), std::ios::binary);
if( !inFile )
{
return false;
}
inFile.ignore(10000,'\n'); //ignore first line
int iVersion;
inFile >> iVersion; //currently ignore version number
inFile >> m_uiNumVertices;
inFile >> m_uiNumTriangles;
//now discard end of line
inFile.ignore(10000,'\n');
m_pafVertexCoordinates = new float[ 3 * m_uiNumVertices ];
m_pafNormals = new float[ 3 * m_uiNumVertices ];
m_pafTextureCoordinates = new float[ 2 * m_uiNumVertices ];
m_pausTriangleIndices = new unsigned short[ 3 * m_uiNumTriangles ];
inFile.read( reinterpret_cast<char*>( m_pafVertexCoordinates ), 3 * m_uiNumVertices * sizeof( float ) );
inFile.read( reinterpret_cast<char*>( m_pafTextureCoordinates ), 2 * m_uiNumVertices * sizeof( float ) );
inFile.read( reinterpret_cast<char*>( m_pafNormals ), 3 * m_uiNumVertices * sizeof( float ) );
inFile.read( reinterpret_cast<char*>( m_pausTriangleIndices ), 3 * m_uiNumTriangles * sizeof( unsigned short ) );
return true;
}
bool AURenMesh::LoadFromFileImport( const std::string& strFilename )
{
#ifndef NO_ASSIMP
Assimp::Importer importer;
const aiScene* pScene = importer.ReadFile( strFilename, aiProcessPreset_TargetRealtime_Fast );
if (!pScene || pScene->mNumMeshes == 0)
{
return false;
}
ProcessScene(pScene);
return true;
#else
assert( false );
return false;
#endif
}
void AURenMesh::ProcessScene( const aiScene* pScene )
{
#ifndef NO_ASSIMP
// Calculate total number of verts and tris across all meshes in scene
m_uiNumVertices = 0;
m_uiNumTriangles = 0;
for (unsigned int i=0; i<pScene->mNumMeshes; ++i)
{
aiMesh* pMesh = pScene->mMeshes[i];
m_uiNumVertices += pMesh->mNumVertices;
m_uiNumTriangles += pMesh->mNumFaces;
}
// Allocate sufficent space for all data
m_pafVertexCoordinates = new float[ 3 * m_uiNumVertices ];
m_pafNormals = new float[ 3 * m_uiNumVertices ];
m_pafTextureCoordinates = new float[ 2 * m_uiNumVertices ];
m_pausTriangleIndices = new unsigned short[ 3 * m_uiNumTriangles ];
// Iterate through all meshes and load data
int vertIndex = 0;
int normalIndex = 0;
int texIndex = 0;
int triIndex = 0;
for (unsigned int i=0; i<pScene->mNumMeshes; ++i)
{
aiMesh* pMesh = pScene->mMeshes[i];
// Load Verts
for (unsigned int j=0; j<pMesh->mNumVertices; ++j)
{
const aiVector3D& vec = pMesh->mVertices[j];
m_pafVertexCoordinates[vertIndex] = vec.x;
m_pafVertexCoordinates[vertIndex+1] = vec.y;
m_pafVertexCoordinates[vertIndex+2] = vec.z;
vertIndex += 3;
}
// Load Normals
for (unsigned int j=0; j<pMesh->mNumVertices; ++j)
{
const aiVector3D& vec = pMesh->mNormals[j];
m_pafNormals[normalIndex] = vec.x;
m_pafNormals[normalIndex+1] = vec.y;
m_pafNormals[normalIndex+2] = vec.z;
normalIndex += 3;
}
// Load Tex Coords
if (pMesh->HasTextureCoords(0))
{
for (unsigned int j=0; j<pMesh->mNumVertices; ++j)
{
const aiVector3D& vec = pMesh->mTextureCoords[0][j];
m_pafTextureCoordinates[texIndex] = vec.x;
m_pafTextureCoordinates[texIndex+1] = vec.y;
texIndex += 2;
}
}
// Load Tris
for (unsigned int j=0; j<pMesh->mNumFaces; ++j)
{
const aiFace& tri = pMesh->mFaces[j];
m_pausTriangleIndices[triIndex] = tri.mIndices[0];
m_pausTriangleIndices[triIndex+1] = tri.mIndices[1];
m_pausTriangleIndices[triIndex+2] = tri.mIndices[2];
triIndex += 3;
}
}
#endif
}
//////////////////////////////////////////////////////////////////////////////////////////
// Function: AURenMesh::AURenMesh
//
// Last Modified by: Douglas John Binks (DJB)
//
// Last Modified: 21 June 2000
//
// Purpose: Normalises the size and position of the object to be
// centered around the origin and of the correct BCube extent.
//
// Inputs: fBCubeHalfWidth_ : float giving the half width of the
// bounding cube.
//
// Outputs: None.
//
// Returns: None.
//
//////////////////////////////////////////////////////////////////////////////////////////
void AURenMesh::NormaliseToBCubeHalfWidth( float fBCubeHalfWidth_ )
{
//set up min and max variablse for each axis and use a real value from the
//array to initialise (as a `made up' value may be wrong unless we use
//floatmax for min etc.).
float fMinX = m_pafVertexCoordinates[0];
float fMaxX = m_pafVertexCoordinates[0];
float fMinY = m_pafVertexCoordinates[1];
float fMaxY = m_pafVertexCoordinates[1];
float fMinZ = m_pafVertexCoordinates[2];
float fMaxZ = m_pafVertexCoordinates[2];
//Go through array of vertices to find the real min and max
unsigned int uiCountCoords;
for( uiCountCoords = 0;
uiCountCoords < 3 * m_uiNumVertices;
uiCountCoords += 3 )
{
if( fMinX > m_pafVertexCoordinates[ uiCountCoords ] )
{
fMinX = m_pafVertexCoordinates[ uiCountCoords ];
}
else
{
if( fMaxX < m_pafVertexCoordinates[ uiCountCoords ] )
{
fMaxX = m_pafVertexCoordinates[ uiCountCoords ];
}
}
if( fMinY > m_pafVertexCoordinates[ uiCountCoords + 1 ] )
{
fMinY = m_pafVertexCoordinates[ uiCountCoords + 1 ];
}
else
{
if( fMaxY < m_pafVertexCoordinates[ uiCountCoords + 1 ] )
{
fMaxY = m_pafVertexCoordinates[ uiCountCoords + 1 ];
}
}
if( fMinZ > m_pafVertexCoordinates[ uiCountCoords + 2 ] )
{
fMinZ = m_pafVertexCoordinates[ uiCountCoords + 2 ];
}
else
{
if( fMaxZ < m_pafVertexCoordinates[ uiCountCoords + 2 ] )
{
fMaxZ = m_pafVertexCoordinates[ uiCountCoords + 2 ];
}
}
}
//calulate the current center
float fCenterX = ( fMaxX + fMinX )/2.0f;
float fCenterY = ( fMaxY + fMinY )/2.0f;
float fCenterZ = ( fMaxZ + fMinZ )/2.0f;
//calculate the largest distance^2 from the center
float fMaxDistance2 = 0.0f;
float fDistance2;
float fX2, fY2, fZ2;
for( uiCountCoords = 0;
uiCountCoords < 3 * m_uiNumVertices;
uiCountCoords += 3 )
{
fX2 = m_pafVertexCoordinates[ uiCountCoords ] - fCenterX;
fX2 *= fX2;
fY2 = m_pafVertexCoordinates[ uiCountCoords + 1 ] - fCenterY;
fY2 *= fY2;
fZ2 = m_pafVertexCoordinates[ uiCountCoords + 2 ] - fCenterZ;
fZ2 *= fZ2;
fDistance2 = fX2 + fY2 + fZ2;
if( fDistance2 > fMaxDistance2 )
{
fMaxDistance2 = fDistance2;
}
}
//calculate normalising coefficient such that Xnew = cf * Xold
float fCoefficient = fBCubeHalfWidth_ / sqrt( fMaxDistance2 );
//now do normalisation ( use seperate calcs for X,Y,Z so as to use possible
//parallel floating point units).
for( uiCountCoords = 0;
uiCountCoords < 3 * m_uiNumVertices;
uiCountCoords += 3 )
{
m_pafVertexCoordinates[ uiCountCoords ] =
fCoefficient * ( m_pafVertexCoordinates[ uiCountCoords ] - fCenterX );
m_pafVertexCoordinates[ uiCountCoords + 1 ] =
fCoefficient * ( m_pafVertexCoordinates[ uiCountCoords + 1 ] - fCenterY );
m_pafVertexCoordinates[ uiCountCoords + 2 ] =
fCoefficient * ( m_pafVertexCoordinates[ uiCountCoords + 2 ] - fCenterZ );
}
}
//////////////////////////////////////////////////////////////////////////////////////////
// Function: AURenMesh::Render
//
// Last Modified by: Douglas John Binks (DJB)
//
// Last Modified: 25 July 2000
//
// Purpose: Draws the mesh.
//
// Inputs: None.
//
// Outputs: None.
//
// Returns: None.
//
//////////////////////////////////////////////////////////////////////////////////////////
void AURenMesh::Render(const AUColor* pCol ) const
{
if( 0 == m_uiNumVertices )
{
return;
}
const GLfloat pafDiffuseColor[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
const GLfloat pafSpecularColor[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
const GLfloat fShininess = 40.0f;
if( 0 == pCol )
{
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, pafDiffuseColor);
glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, pafDiffuseColor);
}
else
{
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, pCol->m_Color.rgba );
glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, pCol->m_Color.rgba);
}
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, pafSpecularColor);
glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, fShininess);
const GLint iNumCoordinatesPerVertex = 3;
const GLsizei iStride = 0;
//set up vertex arrays
glEnableClientState( GL_VERTEX_ARRAY );
glEnableClientState( GL_NORMAL_ARRAY );
glVertexPointer( iNumCoordinatesPerVertex, GL_FLOAT, iStride,
(const GLvoid*)m_pafVertexCoordinates );
glNormalPointer( GL_FLOAT, iStride,
(const GLvoid*)m_pafNormals );
glEnableClientState( GL_TEXTURE_COORD_ARRAY );
glTexCoordPointer( 2, GL_FLOAT, iStride,
(const GLvoid*)m_pafTextureCoordinates );
//do actual drawing
glDrawElements( GL_TRIANGLES, 3 * m_uiNumTriangles, GL_UNSIGNED_SHORT, m_pausTriangleIndices );
//unset vertex arrays
glDisableClientState( GL_TEXTURE_COORD_ARRAY );
glDisableClientState( GL_VERTEX_ARRAY );
glDisableClientState( GL_NORMAL_ARRAY );
}