Serious-Engine/Sources/Engine/Ska/RMRender.cpp
Ryan C. Gordon 24cb244d43 First attempt to hand-merge Ryan's Linux and Mac OS X port.
This was a _ton_ of changes, made 15 years ago, so there are probably some
problems to work out still.

Among others: Engine/Base/Stream.* was mostly abandoned and will need to be
re-ported.

Still, this is a pretty good start, and probably holds a world record for
lines of changes or something.  :)
2016-03-28 23:46:13 -04:00

2592 lines
91 KiB
C++

/* Copyright (c) 2002-2012 Croteam Ltd. All rights reserved. */
#include <Engine/StdH.h>
#include <Engine/Base/Console.h>
#include <Engine/Math/Projection.h>
#include <Engine/Math/Float.h>
#include <Engine/Math/Vector.h>
#include <Engine/Math/Quaternion.h>
#include <Engine/Math/Geometry.inl>
#include <Engine/Math/Clipping.inl>
#include <Engine/Ska/ModelInstance.h>
#include <Engine/Ska/Render.h>
#include <Engine/Ska/Mesh.h>
#include <Engine/Ska/Skeleton.h>
#include <Engine/Ska/AnimSet.h>
#include <Engine/Ska/StringTable.h>
#include <Engine/Templates/DynamicContainer.cpp>
#include <Engine/Graphics/DrawPort.h>
#include <Engine/Graphics/Fog_internal.h>
#include <Engine/Base/Statistics_Internal.h>
static CAnyProjection3D _aprProjection;
static CDrawPort *_pdp = NULL;
static enum FPUPrecisionType _fpuOldPrecision;
static INDEX _iRenderingType = 0; // 0=none, 1=view, 2=mask
static FLOAT3D _vLightDir; // Light direction
static FLOAT3D _vLightDirInView; // Light direction transformed in view space
static COLOR _colAmbient; // Ambient color
static COLOR _colLight; // Light color
static FLOAT _fDistanceFactor; // Distance to object from viewer
static Matrix12 _mObjectToAbs; // object to absolute
static Matrix12 _mAbsToViewer; // absolute to viewer
static Matrix12 _mObjToView; // object to viewer
static Matrix12 _mObjToViewStretch; // object to viewer, stretch by root model instance stretch factor
ULONG _ulFlags = RMF_SHOWTEXTURE;
static ULONG _ulRenFlags = 0;
static FLOAT _fCustomMlodDistance=-1; // custom distance for mesh lods
static FLOAT _fCustomSlodDistance=-1; // custom distance for skeleton lods
extern FLOAT ska_fLODMul;
extern FLOAT ska_fLODAdd;
// mask shader (for rendering models' shadows to shadowmaps)
static CShader _shMaskShader;
// temporary rendering structures
static CStaticStackArray<struct RenModel> _aRenModels;
static CStaticStackArray<struct RenBone> _aRenBones;
static CStaticStackArray<struct RenMesh> _aRenMesh;
static CStaticStackArray<struct RenMorph> _aRenMorph;
static CStaticStackArray<struct RenWeight> _aRenWeights;
static CStaticStackArray<struct MeshVertex> _aMorphedVtxs;
static CStaticStackArray<struct MeshNormal> _aMorphedNormals;
static CStaticStackArray<struct MeshVertex> _aFinalVtxs;
static CStaticStackArray<struct MeshNormal> _aFinalNormals;
static CStaticStackArray<struct GFXColor> _aMeshColors;
static CStaticStackArray<struct GFXTexCoord> _aTexMipFogy;
static CStaticStackArray<struct GFXTexCoord> _aTexMipHazey;
static MeshVertex *_pavFinalVertices = NULL; // pointer to final arrays
static MeshNormal *_panFinalNormals = NULL; // pointer to final normals
static INDEX _ctFinalVertices; // final vertices count
BOOL _bTransformBonelessModelToViewSpace = TRUE; // are boneless models transformed to view space
// Pointers for bone adjustment function
static void (*_pAdjustBonesCallback)(void *pData) = NULL;
static void *_pAdjustBonesData = NULL;
// Pointers for shader params adjustment function
static void (*_pAdjustShaderParams)(void *pData, INDEX iSurfaceID, CShader *pShader,ShaderParams &shParams) = NULL;
static void *_pAdjustShaderData = NULL;
static BOOL FindRenBone(RenModel &rm,int iBoneID,INDEX *piBoneIndex);
static void PrepareMeshForRendering(RenMesh &rmsh, INDEX iSkeletonlod);
static void CalculateRenderingData(CModelInstance &mi);
static void ClearRenArrays();
// load our 3x4 matrix from old-fashioned matrix+vector combination
inline void MatrixVectorToMatrix12(Matrix12 &m12,const FLOATmatrix3D &m, const FLOAT3D &v)
{
m12[ 0] = m(1,1); m12[ 1] = m(1,2); m12[ 2] = m(1,3); m12[ 3] = v(1);
m12[ 4] = m(2,1); m12[ 5] = m(2,2); m12[ 6] = m(2,3); m12[ 7] = v(2);
m12[ 8] = m(3,1); m12[ 9] = m(3,2); m12[10] = m(3,3); m12[11] = v(3);
}
// convert matrix12 to old matrix 3x3 and vector
inline void Matrix12ToMatrixVector(FLOATmatrix3D &c, FLOAT3D &v, const Matrix12 &m12)
{
c(1,1) = m12[ 0]; c(1,2) = m12[ 1]; c(1,3) = m12[ 2]; v(1) = m12[ 3];
c(2,1) = m12[ 4]; c(2,2) = m12[ 5]; c(2,3) = m12[ 6]; v(2) = m12[ 7];
c(3,1) = m12[ 8]; c(3,2) = m12[ 9]; c(3,3) = m12[10]; v(3) = m12[11];
}
// create matrix from vector without rotations
inline static void MakeStretchMatrix(Matrix12 &c, const FLOAT3D &v)
{
c[ 0] = v(1); c[ 1] = 0.0f; c[ 2] = 0.0f; c[ 3] = 0.0f;
c[ 4] = 0.0f; c[ 5] = v(2); c[ 6] = 0.0f; c[ 7] = 0.0f;
c[ 8] = 0.0f; c[ 9] = 0.0f; c[10] = v(3); c[11] = 0.0f;
}
// Remove rotation from matrix (make it front face)
inline static void RemoveRotationFromMatrix(Matrix12 &mat)
{
mat[ 0] = 1; mat[ 1] = 0; mat[ 2] = 0;
mat[ 4] = 0; mat[ 5] = 1; mat[ 6] = 0;
mat[ 8] = 0; mat[ 9] = 0; mat[10] = 1;
}
// set given matrix as identity matrix
inline static void MakeIdentityMatrix(Matrix12 &mat)
{
memset(&mat,0,sizeof(mat));
mat[0] = 1;
mat[5] = 1;
mat[10] = 1;
}
// transform vector with given matrix
inline static void TransformVector(FLOAT3 &v, const Matrix12 &m)
{
float x = v[0];
float y = v[1];
float z = v[2];
v[0] = m[0]*x + m[1]*y + m[ 2]*z + m[ 3];
v[1] = m[4]*x + m[5]*y + m[ 6]*z + m[ 7];
v[2] = m[8]*x + m[9]*y + m[10]*z + m[11];
}
inline void TransformVertex(GFXVertex &v, const Matrix12 &m)
{
float x = v.x;
float y = v.y;
float z = v.z;
v.x = m[0]*x + m[1]*y + m[ 2]*z + m[ 3];
v.y = m[4]*x + m[5]*y + m[ 6]*z + m[ 7];
v.z = m[8]*x + m[9]*y + m[10]*z + m[11];
}
// rotate vector with given matrix ( does not translate vector )
inline void RotateVector(FLOAT3 &v, const Matrix12 &m)
{
float x = v[0];
float y = v[1];
float z = v[2];
v[0] = m[0]*x + m[1]*y + m[ 2]*z;
v[1] = m[4]*x + m[5]*y + m[ 6]*z;
v[2] = m[8]*x + m[9]*y + m[10]*z;
}
// copy one matrix12 to another
inline void MatrixCopy(Matrix12 &c, const Matrix12 &m)
{
memcpy(&c,&m,sizeof(c));
}
// convert 3x4 matrix to QVect
inline void Matrix12ToQVect(QVect &qv,const Matrix12 &m12)
{
FLOATmatrix3D m;
m(1,1) = m12[ 0]; m(1,2) = m12[ 1]; m(1,3) = m12[ 2];
m(2,1) = m12[ 4]; m(2,2) = m12[ 5]; m(2,3) = m12[ 6];
m(3,1) = m12[ 8]; m(3,2) = m12[ 9]; m(3,3) = m12[10];
qv.qRot.FromMatrix(m);
qv.vPos(1) = m12[3];
qv.vPos(2) = m12[7];
qv.vPos(3) = m12[11];
}
// covert QVect to matrix 3x4
inline void QVectToMatrix12(Matrix12 &m12, const QVect &qv)
{
FLOATmatrix3D m;
qv.qRot.ToMatrix(m);
MatrixVectorToMatrix12(m12,m,qv.vPos);
}
// concatenate two 3x4 matrices C=(MxN)
inline void MatrixMultiply(Matrix12 &c,const Matrix12 &m, const Matrix12 &n)
{
c[0] = m[0]*n[0] + m[1]*n[4] + m[2]*n[8];
c[1] = m[0]*n[1] + m[1]*n[5] + m[2]*n[9];
c[2] = m[0]*n[2] + m[1]*n[6] + m[2]*n[10];
c[3] = m[0]*n[3] + m[1]*n[7] + m[2]*n[11] + m[3];
c[4] = m[4]*n[0] + m[5]*n[4] + m[6]*n[8];
c[5] = m[4]*n[1] + m[5]*n[5] + m[6]*n[9];
c[6] = m[4]*n[2] + m[5]*n[6] + m[6]*n[10];
c[7] = m[4]*n[3] + m[5]*n[7] + m[6]*n[11] + m[7];
c[8] = m[8]*n[0] + m[9]*n[4] + m[10]*n[8];
c[9] = m[8]*n[1] + m[9]*n[5] + m[10]*n[9];
c[10] = m[8]*n[2] + m[9]*n[6] + m[10]*n[10];
c[11] = m[8]*n[3] + m[9]*n[7] + m[10]*n[11] + m[11];
}
// multiply two matrices into first one
inline void MatrixMultiplyCP(Matrix12 &c,const Matrix12 &m, const Matrix12 &n)
{
Matrix12 mTemp;
MatrixMultiply(mTemp,m,n);
MatrixCopy(c,mTemp);
}
// make transpose matrix
inline void MatrixTranspose(Matrix12 &r, const Matrix12 &m)
{
r[ 0] = m[ 0];
r[ 5] = m[ 5];
r[10] = m[10];
r[ 3] = m[ 3];
r[ 7] = m[ 7];
r[11] = m[11];
r[1] = m[4];
r[2] = m[8];
r[4] = m[1];
r[8] = m[2];
r[6] = m[9];
r[9] = m[6];
r[ 3] = -r[0]*m[3] - r[1]*m[7] - r[ 2]*m[11];
r[ 7] = -r[4]*m[3] - r[5]*m[7] - r[ 6]*m[11];
r[11] = -r[8]*m[3] - r[9]*m[7] - r[10]*m[11];
}
// viewer absolute and object space projection
static FLOAT3D _vViewer;
static FLOAT3D _vViewerObj;
static FLOAT3D _vLightObj;
// returns haze/fog value in vertex
static FLOAT3D _vZDirView, _vHDirView;
static FLOAT _fFogAddZ, _fFogAddH;
static FLOAT _fHazeAdd;
// check vertex against fog
static void GetFogMapInVertex( GFXVertex4 &vtx, GFXTexCoord &tex)
{
const FLOAT fD = vtx.x*_vZDirView(1) + vtx.y*_vZDirView(2) + vtx.z*_vZDirView(3);
const FLOAT fH = vtx.x*_vHDirView(1) + vtx.y*_vHDirView(2) + vtx.z*_vHDirView(3);
tex.st.s = (fD+_fFogAddZ) * _fog_fMulZ;
// tex.st.s = (vtx.z) * _fog_fMulZ;
tex.st.t = (fH+_fFogAddH) * _fog_fMulH;
}
// check vertex against haze
static void GetHazeMapInVertex( GFXVertex4 &vtx, FLOAT &tx1)
{
const FLOAT fD = vtx.x*_vViewerObj(1) + vtx.y*_vViewerObj(2) + vtx.z*_vViewerObj(3);
tx1 = (fD+_fHazeAdd) * _haze_fMul;
}
// check model's bounding box against fog
static BOOL IsModelInFog( FLOAT3D &vMin, FLOAT3D &vMax)
{
GFXTexCoord tex;
GFXVertex4 vtx;
vtx.x=vMin(1); vtx.y=vMin(2); vtx.z=vMin(3); GetFogMapInVertex(vtx,tex); if(InFog(tex.st.t)) return TRUE;
vtx.x=vMin(1); vtx.y=vMin(2); vtx.z=vMax(3); GetFogMapInVertex(vtx,tex); if(InFog(tex.st.t)) return TRUE;
vtx.x=vMin(1); vtx.y=vMax(2); vtx.z=vMin(3); GetFogMapInVertex(vtx,tex); if(InFog(tex.st.t)) return TRUE;
vtx.x=vMin(1); vtx.y=vMax(2); vtx.z=vMax(3); GetFogMapInVertex(vtx,tex); if(InFog(tex.st.t)) return TRUE;
vtx.x=vMax(1); vtx.y=vMin(2); vtx.z=vMin(3); GetFogMapInVertex(vtx,tex); if(InFog(tex.st.t)) return TRUE;
vtx.x=vMax(1); vtx.y=vMin(2); vtx.z=vMax(3); GetFogMapInVertex(vtx,tex); if(InFog(tex.st.t)) return TRUE;
vtx.x=vMax(1); vtx.y=vMax(2); vtx.z=vMin(3); GetFogMapInVertex(vtx,tex); if(InFog(tex.st.t)) return TRUE;
vtx.x=vMax(1); vtx.y=vMax(2); vtx.z=vMax(3); GetFogMapInVertex(vtx,tex); if(InFog(tex.st.t)) return TRUE;
return FALSE;
}
// check model's bounding box against haze
static BOOL IsModelInHaze( FLOAT3D &vMin, FLOAT3D &vMax)
{
FLOAT fS;
GFXVertex4 vtx;
vtx.x=vMin(1); vtx.y=vMin(2); vtx.z=vMin(3); GetHazeMapInVertex(vtx,fS); if(InHaze(fS)) return TRUE;
vtx.x=vMin(1); vtx.y=vMin(2); vtx.z=vMax(3); GetHazeMapInVertex(vtx,fS); if(InHaze(fS)) return TRUE;
vtx.x=vMin(1); vtx.y=vMax(2); vtx.z=vMin(3); GetHazeMapInVertex(vtx,fS); if(InHaze(fS)) return TRUE;
vtx.x=vMin(1); vtx.y=vMax(2); vtx.z=vMax(3); GetHazeMapInVertex(vtx,fS); if(InHaze(fS)) return TRUE;
vtx.x=vMax(1); vtx.y=vMin(2); vtx.z=vMin(3); GetHazeMapInVertex(vtx,fS); if(InHaze(fS)) return TRUE;
vtx.x=vMax(1); vtx.y=vMin(2); vtx.z=vMax(3); GetHazeMapInVertex(vtx,fS); if(InHaze(fS)) return TRUE;
vtx.x=vMax(1); vtx.y=vMax(2); vtx.z=vMin(3); GetHazeMapInVertex(vtx,fS); if(InHaze(fS)) return TRUE;
vtx.x=vMax(1); vtx.y=vMax(2); vtx.z=vMax(3); GetHazeMapInVertex(vtx,fS); if(InHaze(fS)) return TRUE;
return FALSE;
}
BOOL PrepareHaze(void)
{
ULONG &ulRenFlags = RM_GetRenderFlags();
if( ulRenFlags & SRMF_HAZE) {
_fHazeAdd = _haze_hp.hp_fNear;
_fHazeAdd += -_mObjToView[11];
/*
// get viewer -z in viewer space
_vZDirView = FLOAT3D(0,0,-1);
// get fog direction in viewer space
// _vHDirView = _fog_vHDirAbs;
// RotateVector(_vHDirView.vector, _mAbsToViewer);
_vHDirView = _fog_vHDirView;
// get viewer offset
// _fFogAddZ = _vViewer % (rm.rm_vObjectPosition - _aprProjection->pr_vViewerPosition); // BUG in compiler !!!!
_fFogAddZ = -_mObjToView[11];
// get fog offset
_fFogAddH = _fog_fAddH;/*(
_vHDirView(1)*_mObjToView[3] +
_vHDirView(2)*_mObjToView[7] +
_vHDirView(3)*_mObjToView[11]) + _fog_fp.fp_fH3;
CPrintF("hdir:%g,%g,%g addz:%g addh:%g\n", _vHDirView(1), _vHDirView(2), _vHDirView(3), _fFogAddZ, _fFogAddH);
*/
return TRUE;
}
return FALSE;
}
BOOL PrepareFog(void)
{
ULONG &ulRenFlags = RM_GetRenderFlags();
if( ulRenFlags & SRMF_FOG) {
// get viewer -z in viewer space
_vZDirView = FLOAT3D(0,0,-1);
// get fog direction in viewer space
// _vHDirView = _fog_vHDirAbs;
// RotateVector(_vHDirView.vector, _mAbsToViewer);
_vHDirView = _fog_vHDirView;
// get viewer offset
// _fFogAddZ = _vViewer % (rm.rm_vObjectPosition - _aprProjection->pr_vViewerPosition); // BUG in compiler !!!!
_fFogAddZ = -_mObjToView[11];
// get fog offset
_fFogAddH = _fog_fAddH;/*(
_vHDirView(1)*_mObjToView[3] +
_vHDirView(2)*_mObjToView[7] +
_vHDirView(3)*_mObjToView[11]) + _fog_fp.fp_fH3;
CPrintF("hdir:%g,%g,%g addz:%g addh:%g\n", _vHDirView(1), _vHDirView(2), _vHDirView(3), _fFogAddZ, _fFogAddH);*/
return TRUE;
}
return FALSE;
}
// Update model for fog and haze
void RM_DoFogAndHaze(BOOL bOpaqueSurface)
{
// get current surface vertex array
GFXVertex4 *paVertices;
GFXColor *paColors;
GFXColor *paHazeColors;
INDEX ctVertices = shaGetVertexCount();
paVertices = shaGetVertexArray();
paColors = shaGetColorArray();
paHazeColors = shaGetNewColorArray();
// if this is opaque surface
if(bOpaqueSurface) {
//
if(PrepareFog()) {
_aTexMipFogy.PopAll();
_aTexMipFogy.Push(ctVertices);
// setup tex coords only
for( INDEX ivtx=0; ivtx<ctVertices; ivtx++) {
GetFogMapInVertex( paVertices[ivtx], _aTexMipFogy[ivtx]);
}
shaSetFogUVMap(&_aTexMipFogy[0]);
}
//
if(PrepareHaze()) {
_aTexMipHazey.PopAll();
_aTexMipHazey.Push(ctVertices);
const COLOR colH = AdjustColor( _haze_hp.hp_colColor, _slTexHueShift, _slTexSaturation);
GFXColor colHaze(colH);
// setup haze tex coords and color
for( INDEX ivtx=0; ivtx<ctVertices; ivtx++) {
GetHazeMapInVertex( paVertices[ivtx], _aTexMipHazey[ivtx].st.s);
_aTexMipHazey[ivtx].st.t = 0.0f;
paHazeColors[ivtx] = colHaze;
}
shaSetHazeUVMap(&_aTexMipHazey[0]);
shaSetHazeColorArray(&paHazeColors[0]);
}
// surface is translucent
} else {
//
if(PrepareFog()) {
GFXTexCoord tex;
for( INDEX ivtx=0; ivtx<ctVertices; ivtx++) {
GetFogMapInVertex( paVertices[ivtx], tex);
UBYTE ub = GetFogAlpha(tex) ^255;
paColors[ivtx].AttenuateA( ub);
}
}
//
if(PrepareHaze()) {
FLOAT tx1;
for( INDEX ivtx=0; ivtx<ctVertices; ivtx++) {
GetHazeMapInVertex( paVertices[ivtx], tx1);
FLOAT ub = GetHazeAlpha(tx1) ^255;
paHazeColors[ivtx] = paColors[ivtx];
paHazeColors[ivtx].AttenuateA( ub);
}
shaSetHazeColorArray(&paHazeColors[0]);
}
}
}
// LOD factor management
void RM_SetCurrentDistance(FLOAT fDistFactor)
{
_fCustomMlodDistance = fDistFactor;
_fCustomSlodDistance = fDistFactor;
}
FLOAT RM_GetMipFactor(void)
{
return 0;
}
// fill given array with array of transformed vertices
void RM_GetModelVertices( CModelInstance &mi, CStaticStackArray<FLOAT3D> &avVertices, FLOATmatrix3D &mRotation,
FLOAT3D &vPosition, FLOAT fNormalOffset, FLOAT fDistance)
{
// Transform all vertices in view space
BOOL bTemp = _bTransformBonelessModelToViewSpace;
_bTransformBonelessModelToViewSpace = TRUE;
// only root model instances
ASSERT(mi.mi_iParentBoneID==-1);
// remember parent bone ID
INDEX iOldParentBoneID = mi.mi_iParentBoneID;
// set parent bone ID as -1
mi.mi_iParentBoneID = -1;
// Reset abs to viewer matrix
MakeIdentityMatrix(_mAbsToViewer);
RM_SetCurrentDistance(fDistance);
CalculateRenderingData(mi);
// for each ren model
INDEX ctrmsh = _aRenModels.Count();
for(int irmsh=1;irmsh<ctrmsh;irmsh++) {
RenModel &rm = _aRenModels[irmsh];
INDEX ctmsh = rm.rm_iFirstMesh + rm.rm_ctMeshes;
// for each mesh in renmodel
for(int imsh=rm.rm_iFirstMesh;imsh<ctmsh;imsh++) {
// prepare mesh for rendering
RenMesh &rmsh = _aRenMesh[imsh];
PrepareMeshForRendering(rmsh,rm.rm_iSkeletonLODIndex);
INDEX ctvtx = _ctFinalVertices;
INDEX ctvtxGiven = avVertices.Count();
avVertices.Push(ctvtx);
// for each vertex in prepared mesh
for(INDEX ivtx=0;ivtx<ctvtx;ivtx++) {
#pragma message(">> Fix this")
FLOAT3D vVtx = FLOAT3D(_pavFinalVertices[ivtx].x,_pavFinalVertices[ivtx].y,_pavFinalVertices[ivtx].z);
FLOAT3D vNor = FLOAT3D(_panFinalNormals[ivtx].nx,_panFinalNormals[ivtx].ny,_panFinalNormals[ivtx].nz);
// add vertex to given vertex array
avVertices[ivtx+ctvtxGiven] = vVtx+(vNor*fNormalOffset);
}
}
}
// restore old bone parent ID
mi.mi_iParentBoneID = iOldParentBoneID;
ClearRenArrays();
_bTransformBonelessModelToViewSpace = bTemp;
}
FLOAT RM_TestRayCastHit( CModelInstance &mi, FLOATmatrix3D &mRotation, FLOAT3D &vPosition,const FLOAT3D &vOrigin,
const FLOAT3D &vTarget,FLOAT fOldDistance,INDEX *piBoneID)
{
FLOAT fDistance = 1E6f;
static int i=0;
i++;
BOOL bTemp = _bTransformBonelessModelToViewSpace;
_bTransformBonelessModelToViewSpace = TRUE;
// ASSERT((CProjection3D *)_aprProjection!=NULL);
RM_SetObjectPlacement(mRotation,vPosition);
// Reset abs to viewer matrix
MakeIdentityMatrix(_mAbsToViewer);
// allways use the first LOD
RM_SetCurrentDistance(0);
CalculateRenderingData(mi);
// for each ren model
INDEX ctrmsh = _aRenModels.Count();
for(int irmsh=1;irmsh<ctrmsh;irmsh++) {
RenModel &rm = _aRenModels[irmsh];
INDEX ctmsh = rm.rm_iFirstMesh + rm.rm_ctMeshes;
// for each mesh in renmodel
for(int imsh=rm.rm_iFirstMesh;imsh<ctmsh;imsh++) {
// prepare mesh for rendering
RenMesh &rmsh = _aRenMesh[imsh];
PrepareMeshForRendering(rmsh,rm.rm_iSkeletonLODIndex);
MeshLOD &mshlod = rmsh.rmsh_pMeshInst->mi_pMesh->msh_aMeshLODs[rmsh.rmsh_iMeshLODIndex];
INDEX ctsurf = mshlod.mlod_aSurfaces.Count();
for(int isurf=0;isurf<ctsurf;isurf++) {
MeshSurface &mshsurf = mshlod.mlod_aSurfaces[isurf];
INDEX cttri = mshsurf.msrf_aTriangles.Count();
for (int itri=0; itri<cttri;itri++) {
Vector<FLOAT,3> vVertex0(_pavFinalVertices[mshsurf.msrf_aTriangles[itri].iVertex[0]].x,
_pavFinalVertices[mshsurf.msrf_aTriangles[itri].iVertex[0]].y,
_pavFinalVertices[mshsurf.msrf_aTriangles[itri].iVertex[0]].z);
Vector<FLOAT,3> vVertex1(_pavFinalVertices[mshsurf.msrf_aTriangles[itri].iVertex[1]].x,
_pavFinalVertices[mshsurf.msrf_aTriangles[itri].iVertex[1]].y,
_pavFinalVertices[mshsurf.msrf_aTriangles[itri].iVertex[1]].z);
Vector<FLOAT,3> vVertex2(_pavFinalVertices[mshsurf.msrf_aTriangles[itri].iVertex[2]].x,
_pavFinalVertices[mshsurf.msrf_aTriangles[itri].iVertex[2]].y,
_pavFinalVertices[mshsurf.msrf_aTriangles[itri].iVertex[2]].z);
Plane <float,3> plTriPlane(vVertex0,vVertex1,vVertex2);
FLOAT fDistance0 = plTriPlane.PointDistance(vOrigin);
FLOAT fDistance1 = plTriPlane.PointDistance(vTarget);
// if the ray hits the polygon plane
if (fDistance0>=0 && fDistance0>=fDistance1) {
// calculate fraction of line before intersection
FLOAT fFraction = fDistance0/(fDistance0-fDistance1);
// calculate intersection coordinate
FLOAT3D vHitPoint = vOrigin+(vTarget-vOrigin)*fFraction;
// calculate intersection distance
FLOAT fHitDistance = (vHitPoint-vOrigin).Length();
// if the hit point can not be new closest candidate
if (fHitDistance>fOldDistance) {
// skip this triangle
continue;
}
// find major axes of the polygon plane
INDEX iMajorAxis1, iMajorAxis2;
GetMajorAxesForPlane(plTriPlane, iMajorAxis1, iMajorAxis2);
// create an intersector
CIntersector isIntersector(vHitPoint(iMajorAxis1), vHitPoint(iMajorAxis2));
// check intersections for all three edges of the polygon
isIntersector.AddEdge(
vVertex0(iMajorAxis1), vVertex0(iMajorAxis2),
vVertex1(iMajorAxis1), vVertex1(iMajorAxis2));
isIntersector.AddEdge(
vVertex1(iMajorAxis1), vVertex1(iMajorAxis2),
vVertex2(iMajorAxis1), vVertex2(iMajorAxis2));
isIntersector.AddEdge(
vVertex2(iMajorAxis1), vVertex2(iMajorAxis2),
vVertex0(iMajorAxis1), vVertex0(iMajorAxis2));
// if the polygon is intersected by the ray, and it is the closest intersection so far
if (isIntersector.IsIntersecting() && (fHitDistance < fDistance)) {
// remember hit coordinates
fDistance = fHitDistance;
// do we neet to find the bone hit by the ray?
if (piBoneID != NULL) {
INDEX iClosestVertex;
// find the vertex closest to the intersection
FLOAT fDist0 = (vHitPoint - vVertex0).Length();
FLOAT fDist1 = (vHitPoint - vVertex1).Length();
FLOAT fDist2 = (vHitPoint - vVertex2).Length();
if (fDist0 < fDist1) {
if (fDist0 < fDist2) {
iClosestVertex = mshsurf.msrf_aTriangles[itri].iVertex[0];
} else {
iClosestVertex = mshsurf.msrf_aTriangles[itri].iVertex[2];
}
} else {
if (fDist1 < fDist2) {
iClosestVertex = mshsurf.msrf_aTriangles[itri].iVertex[1];
} else {
iClosestVertex = mshsurf.msrf_aTriangles[itri].iVertex[2];
}
}
// now find the weightmap with the largest weight for this vertex
INDEX ctwmaps = mshlod.mlod_aWeightMaps.Count();
FLOAT fMaxVertexWeight = 0.0f;
INDEX iMaxWeightMap = -1;
for (int iwmap=0;iwmap<ctwmaps;iwmap++) {
MeshWeightMap& wtmap = mshlod.mlod_aWeightMaps[iwmap];
INDEX ctvtx = wtmap.mwm_aVertexWeight.Count();
for (int ivtx=0;ivtx<ctvtx;ivtx++) {
if ((wtmap.mwm_aVertexWeight[ivtx].mww_iVertex == iClosestVertex) && (wtmap.mwm_aVertexWeight[ivtx].mww_fWeight > fMaxVertexWeight)) {
fMaxVertexWeight = wtmap.mwm_aVertexWeight[ivtx].mww_fWeight;
iMaxWeightMap = wtmap.mwm_iID;
break;
}
}
}
*piBoneID = iMaxWeightMap;
}
}
}
}
}
}
}
ClearRenArrays();
_bTransformBonelessModelToViewSpace = bTemp;
return fDistance;
}
// add simple model shadow
void RM_AddSimpleShadow_View(CModelInstance &mi, const FLOAT fIntensity, const FLOATplane3D &plShadowPlane)
{
// _pfModelProfile.StartTimer( CModelProfile::PTI_VIEW_RENDERSIMPLESHADOW);
// _pfModelProfile.IncrementTimerAveragingCounter( CModelProfile::PTI_VIEW_RENDERSIMPLESHADOW);
// get viewer in absolute space
FLOAT3D vViewerAbs = _aprProjection->ViewerPlacementR().pl_PositionVector;
// if shadow destination plane is not visible, don't cast shadows
if( plShadowPlane.PointDistance(vViewerAbs)<0.01f) {
// _pfModelProfile.StopTimer( CModelProfile::PTI_VIEW_RENDERSIMPLESHADOW);
return;
}
// _pfModelProfile.StartTimer( CModelProfile::PTI_VIEW_SIMP_CALC);
// _pfModelProfile.IncrementTimerAveragingCounter( CModelProfile::PTI_VIEW_SIMP_CALC);
// get shadow plane in object space
FLOATmatrix3D mAbsToObj;
FLOAT3D vAbsToObj;
// Fix this
Matrix12ToMatrixVector(mAbsToObj,vAbsToObj,_mObjectToAbs);
FLOATplane3D plShadowPlaneObj = (plShadowPlane-vAbsToObj) * !mAbsToObj;
// project object handle so we can calc how it is far away from viewer
FLOAT3D vRef = plShadowPlaneObj.ProjectPoint(FLOAT3D(0,0,0));
TransformVector(vRef.vector,_mObjToViewStretch);
plShadowPlaneObj.pl_distance += ClampDn( -vRef(3)*0.001f, 0.01f); // move plane towards the viewer a bit to avoid z-fighting
FLOATaabbox3D box;
mi.GetCurrentColisionBox( box);
// find points on plane nearest to bounding box edges
FLOAT3D vMin = box.Min() * 1.25f;
FLOAT3D vMax = box.Max() * 1.25f;
if( _ulRenFlags & SRMF_SPECTATOR) { vMin*=2; vMax*=2; } // enlarge shadow for 1st person view
FLOAT3D v00 = plShadowPlaneObj.ProjectPoint(FLOAT3D(vMin(1),vMin(2),vMin(3)));
FLOAT3D v01 = plShadowPlaneObj.ProjectPoint(FLOAT3D(vMin(1),vMin(2),vMax(3)));
FLOAT3D v10 = plShadowPlaneObj.ProjectPoint(FLOAT3D(vMax(1),vMin(2),vMin(3)));
FLOAT3D v11 = plShadowPlaneObj.ProjectPoint(FLOAT3D(vMax(1),vMin(2),vMax(3)));
TransformVector(v00.vector,_mObjToViewStretch);
TransformVector(v01.vector,_mObjToViewStretch);
TransformVector(v10.vector,_mObjToViewStretch);
TransformVector(v11.vector,_mObjToViewStretch);
// calc done
// _pfModelProfile.StopTimer( CModelProfile::PTI_VIEW_SIMP_CALC);
// _pfModelProfile.StartTimer( CModelProfile::PTI_VIEW_SIMP_COPY);
// _pfModelProfile.IncrementTimerAveragingCounter( CModelProfile::PTI_VIEW_SIMP_COPY);
// prepare color
ASSERT( fIntensity>=0 && fIntensity<=1);
ULONG ulAAAA = NormFloatToByte(fIntensity);
ulAAAA |= (ulAAAA<<8) | (ulAAAA<<16); // alpha isn't needed
// add to vertex arrays
GFXVertex *pvtx = _avtxCommon.Push(4);
GFXTexCoord *ptex = _atexCommon.Push(4);
GFXColor *pcol = _acolCommon.Push(4);
// vertices
pvtx[0].x = v00(1); pvtx[0].y = v00(2); pvtx[0].z = v00(3);
pvtx[2].x = v11(1); pvtx[2].y = v11(2); pvtx[2].z = v11(3);
if( _ulRenFlags & SRMF_INVERTED) { // must re-adjust order for mirrored projection
pvtx[1].x = v10(1); pvtx[1].y = v10(2); pvtx[1].z = v10(3);
pvtx[3].x = v01(1); pvtx[3].y = v01(2); pvtx[3].z = v01(3);
} else {
pvtx[1].x = v01(1); pvtx[1].y = v01(2); pvtx[1].z = v01(3);
pvtx[3].x = v10(1); pvtx[3].y = v10(2); pvtx[3].z = v10(3);
}
// texture coords
ptex[0].st.s = 0; ptex[0].st.t = 0;
ptex[1].st.s = 0; ptex[1].st.t = 1;
ptex[2].st.s = 1; ptex[2].st.t = 1;
ptex[3].st.s = 1; ptex[3].st.t = 0;
// colors
pcol[0].ul.abgr = ulAAAA;
pcol[1].ul.abgr = ulAAAA;
pcol[2].ul.abgr = ulAAAA;
pcol[3].ul.abgr = ulAAAA;
// if this model has fog
if( _ulRenFlags & SRMF_FOG)
{ // for each vertex in shadow quad
GFXTexCoord tex;
for( INDEX i=0; i<4; i++) {
GFXVertex &vtx = pvtx[i];
// get distance along viewer axis and fog axis and map to texture and attenuate shadow color
const FLOAT fH = vtx.x*_fog_vHDirView(1) + vtx.y*_fog_vHDirView(2) + vtx.z*_fog_vHDirView(3);
tex.st.s = -vtx.z *_fog_fMulZ;
tex.st.t = (fH+_fog_fAddH) *_fog_fMulH;
pcol[i].AttenuateRGB(GetFogAlpha(tex)^255);
}
}
// if this model has haze
if( _ulRenFlags & SRMF_HAZE)
{ // for each vertex in shadow quad
for( INDEX i=0; i<4; i++) {
// get distance along viewer axis map to texture and attenuate shadow color
const FLOAT fS = (_haze_fAdd-pvtx[i].z) *_haze_fMul;
pcol[i].AttenuateRGB(GetHazeAlpha(fS)^255);
}
}
// one simple shadow added to rendering queue
// _pfModelProfile.StopTimer( CModelProfile::PTI_VIEW_SIMP_COPY);
// _pfModelProfile.StopTimer( CModelProfile::PTI_VIEW_RENDERSIMPLESHADOW);
}
// set callback function for bone adjustment
void RM_SetBoneAdjustCallback(void (*pAdjustBones)(void *pData), void *pData)
{
_pAdjustBonesCallback = pAdjustBones;
_pAdjustBonesData = pData;
}
void RM_SetShaderParamsAdjustCallback(void (*pAdjustShaderParams)(void *pData, INDEX iSurfaceID,CShader *pShader,ShaderParams &spParams),void *pData)
{
_pAdjustShaderParams = pAdjustShaderParams;
_pAdjustShaderData = pData;
}
// show gound for ska studio
void RM_RenderGround(CTextureObject &to)
{
gfxSetConstantColor(0xFFFFFFFF);
gfxEnableDepthTest();
gfxEnableDepthWrite();
gfxDisableAlphaTest();
gfxDisableBlend();
gfxCullFace(GFX_NONE);
CTextureData *ptd = (CTextureData *)to.GetData();
ptd->SetAsCurrent();
FLOAT3D vVtx = FLOAT3D(45,0,45);
GFXVertex vBoxVtxs[4];
GFXTexCoord tcBoxTex[4];
INDEX aiIndices[6];
// set ground vertices
vBoxVtxs[0].x = vVtx(1); vBoxVtxs[0].y = vVtx(2); vBoxVtxs[0].z = -vVtx(3);
vBoxVtxs[1].x = -vVtx(1); vBoxVtxs[1].y = vVtx(2); vBoxVtxs[1].z = -vVtx(3);
vBoxVtxs[2].x = -vVtx(1); vBoxVtxs[2].y = vVtx(2); vBoxVtxs[2].z = vVtx(3);
vBoxVtxs[3].x = vVtx(1); vBoxVtxs[3].y = vVtx(2); vBoxVtxs[3].z = vVtx(3);
// set ground texcoords
tcBoxTex[0].uv.u = vVtx(1); tcBoxTex[0].uv.v = 0;
tcBoxTex[1].uv.u = 0; tcBoxTex[1].uv.v = 0;
tcBoxTex[2].uv.u = 0; tcBoxTex[2].uv.v = vVtx(3);
tcBoxTex[3].uv.u = vVtx(1); tcBoxTex[3].uv.v = vVtx(3);
for(INDEX ivx=0;ivx<4;ivx++) {
TransformVertex(vBoxVtxs[ivx],_mAbsToViewer);
}
aiIndices[0] = 0; aiIndices[1] = 2; aiIndices[2] = 1;
aiIndices[3] = 0; aiIndices[4] = 3; aiIndices[5] = 2;
gfxSetVertexArray(vBoxVtxs,4);
gfxSetTexCoordArray(tcBoxTex, FALSE);
gfxDrawElements(6,aiIndices);
}
// render wirerame bounding box
static void RenderWireframeBox(FLOAT3D vMinVtx, FLOAT3D vMaxVtx, COLOR col)
{
// prepare wireframe settings
gfxDisableTexture();
// fill vertex array so it represents bounding box
FLOAT3D vBoxVtxs[8];
vBoxVtxs[0] = FLOAT3D( vMinVtx(1), vMinVtx(2), vMinVtx(3));
vBoxVtxs[1] = FLOAT3D( vMaxVtx(1), vMinVtx(2), vMinVtx(3));
vBoxVtxs[2] = FLOAT3D( vMaxVtx(1), vMinVtx(2), vMaxVtx(3));
vBoxVtxs[3] = FLOAT3D( vMinVtx(1), vMinVtx(2), vMaxVtx(3));
vBoxVtxs[4] = FLOAT3D( vMinVtx(1), vMaxVtx(2), vMinVtx(3));
vBoxVtxs[5] = FLOAT3D( vMaxVtx(1), vMaxVtx(2), vMinVtx(3));
vBoxVtxs[6] = FLOAT3D( vMaxVtx(1), vMaxVtx(2), vMaxVtx(3));
vBoxVtxs[7] = FLOAT3D( vMinVtx(1), vMaxVtx(2), vMaxVtx(3));
for(INDEX iwx=0;iwx<8;iwx++) TransformVector(vBoxVtxs[iwx].vector,_mObjToViewStretch);
// connect vertices into lines of bounding box
INDEX iBoxLines[12][2];
iBoxLines[ 0][0] = 0; iBoxLines[ 0][1] = 1; iBoxLines[ 1][0] = 1; iBoxLines[ 1][1] = 2;
iBoxLines[ 2][0] = 2; iBoxLines[ 2][1] = 3; iBoxLines[ 3][0] = 3; iBoxLines[ 3][1] = 0;
iBoxLines[ 4][0] = 0; iBoxLines[ 4][1] = 4; iBoxLines[ 5][0] = 1; iBoxLines[ 5][1] = 5;
iBoxLines[ 6][0] = 2; iBoxLines[ 6][1] = 6; iBoxLines[ 7][0] = 3; iBoxLines[ 7][1] = 7;
iBoxLines[ 8][0] = 4; iBoxLines[ 8][1] = 5; iBoxLines[ 9][0] = 5; iBoxLines[ 9][1] = 6;
iBoxLines[10][0] = 6; iBoxLines[10][1] = 7; iBoxLines[11][0] = 7; iBoxLines[11][1] = 4;
// for all vertices in bounding box
for( INDEX i=0; i<12; i++) {
// get starting and ending vertices of one line
FLOAT3D &v0 = vBoxVtxs[iBoxLines[i][0]];
FLOAT3D &v1 = vBoxVtxs[iBoxLines[i][1]];
_pdp->DrawLine3D(v0,v1,col);
}
}
// render bounding box
static void RenderBox(FLOAT3D vMinVtx, FLOAT3D vMaxVtx, COLOR col)
{
// prepare settings
gfxDisableTexture();
gfxEnableBlend();
gfxBlendFunc(GFX_SRC_ALPHA, GFX_INV_SRC_ALPHA);
gfxCullFace(GFX_NONE);
gfxDisableDepthWrite();
gfxSetConstantColor(col);
// fill vertex array so it represents bounding box
GFXVertex vBoxVtxs[8];
vBoxVtxs[0].x = vMinVtx(1); vBoxVtxs[0].y = vMaxVtx(2); vBoxVtxs[0].z = vMinVtx(3);
vBoxVtxs[1].x = vMinVtx(1); vBoxVtxs[1].y = vMaxVtx(2); vBoxVtxs[1].z = vMaxVtx(3);
vBoxVtxs[2].x = vMaxVtx(1); vBoxVtxs[2].y = vMaxVtx(2); vBoxVtxs[2].z = vMinVtx(3);
vBoxVtxs[3].x = vMaxVtx(1); vBoxVtxs[3].y = vMaxVtx(2); vBoxVtxs[3].z = vMaxVtx(3);
vBoxVtxs[4].x = vMinVtx(1); vBoxVtxs[4].y = vMinVtx(2); vBoxVtxs[4].z = vMinVtx(3);
vBoxVtxs[5].x = vMinVtx(1); vBoxVtxs[5].y = vMinVtx(2); vBoxVtxs[5].z = vMaxVtx(3);
vBoxVtxs[6].x = vMaxVtx(1); vBoxVtxs[6].y = vMinVtx(2); vBoxVtxs[6].z = vMinVtx(3);
vBoxVtxs[7].x = vMaxVtx(1); vBoxVtxs[7].y = vMinVtx(2); vBoxVtxs[7].z = vMaxVtx(3);
for(INDEX iwx=0;iwx<8;iwx++) {
TransformVertex(vBoxVtxs[iwx],_mObjToViewStretch);
}
INDEX aiIndices[36];
aiIndices[ 0] = 0; aiIndices[ 1] = 3; aiIndices[ 2] = 1;
aiIndices[ 3] = 0; aiIndices[ 4] = 2; aiIndices[ 5] = 3;
aiIndices[ 6] = 5; aiIndices[ 7] = 1; aiIndices[ 8] = 3;
aiIndices[ 9] = 7; aiIndices[10] = 5; aiIndices[11] = 3;
aiIndices[12] = 2; aiIndices[13] = 7; aiIndices[14] = 3;
aiIndices[15] = 6; aiIndices[16] = 7; aiIndices[17] = 2;
aiIndices[18] = 4; aiIndices[19] = 2; aiIndices[20] = 0;
aiIndices[21] = 4; aiIndices[22] = 6; aiIndices[23] = 2;
aiIndices[24] = 5; aiIndices[25] = 0; aiIndices[26] = 1;
aiIndices[27] = 5; aiIndices[28] = 4; aiIndices[29] = 0;
aiIndices[30] = 4; aiIndices[31] = 5; aiIndices[32] = 7;
aiIndices[33] = 6; aiIndices[34] = 4; aiIndices[35] = 7;
gfxSetVertexArray(vBoxVtxs,8);
gfxDrawElements(36,aiIndices);
gfxDisableBlend();
gfxEnableDepthTest();
RenderWireframeBox(vMinVtx,vMaxVtx,C_BLACK|CT_OPAQUE);
gfxEnableDepthWrite();
gfxDisableDepthBias();
}
// render bounding box on screen
void RM_RenderColisionBox(CModelInstance &mi,ColisionBox &cb, COLOR col)
{
//ColisionBox &cb = mi.GetColisionBox(icb);
gfxSetViewMatrix(NULL);
if(RM_GetFlags() & RMF_WIREFRAME) {
RenderWireframeBox(cb.Min(),cb.Max(),col|CT_OPAQUE);
} else {
gfxEnableBlend();
gfxBlendFunc(GFX_SRC_ALPHA, GFX_INV_SRC_ALPHA);
RenderBox(cb.Min(),cb.Max(),col|0x7F);
gfxDisableBlend();
}
}
// draw wireframe mesh on screen
static void RenderMeshWireframe(RenMesh &rmsh)
{
MeshLOD &mlod = rmsh.rmsh_pMeshInst->mi_pMesh->msh_aMeshLODs[rmsh.rmsh_iMeshLODIndex];
// count surfaces in mesh
INDEX ctsrf = mlod.mlod_aSurfaces.Count();
// for each surface
for(INDEX isrf=0; isrf<ctsrf; isrf++)
{
MeshSurface &msrf = mlod.mlod_aSurfaces[isrf];
COLOR colErrColor = 0xCDCDCDFF;
// surface has no shader, just show vertices
shaClean();
shaSetVertexArray((GFXVertex4*)&_pavFinalVertices[msrf.msrf_iFirstVertex],msrf.msrf_ctVertices);
shaSetIndices(&msrf.msrf_aTriangles[0].iVertex[0],msrf.msrf_aTriangles.Count()*3);
shaSetTexture(-1);
shaSetColorArray(&colErrColor,1);
shaSetColor(0);
shaDisableBlend();
shaRender();
shaClean();
}
}
// render model wireframe
static void RenderModelWireframe(RenModel &rm)
{
INDEX ctmsh = rm.rm_iFirstMesh + rm.rm_ctMeshes;
// for each mesh in renmodel
for(int imsh=rm.rm_iFirstMesh;imsh<ctmsh;imsh++) {
// render mesh
RenMesh &rmsh = _aRenMesh[imsh];
PrepareMeshForRendering(rmsh,rm.rm_iSkeletonLODIndex);
RenderMeshWireframe(rmsh);
}
}
// render normals
static void RenderNormals()
{
// only if rendering to view
if( _iRenderingType!=1) return;
gfxDisableTexture();
INDEX ctNormals = _aFinalNormals.Count();
for(INDEX ivx=0;ivx<ctNormals;ivx++)
{
FLOAT3D vNormal = FLOAT3D(_panFinalNormals[ivx].nx,_panFinalNormals[ivx].ny,_panFinalNormals[ivx].nz);
// vNormal.Normalize();
FLOAT3D vVtx1 = FLOAT3D(_pavFinalVertices[ivx].x,_pavFinalVertices[ivx].y,_pavFinalVertices[ivx].z);
FLOAT3D vVtx2 = vVtx1 + (vNormal/5);
_pdp->DrawLine3D(vVtx1,vVtx2,0xFFFFFFFF);
}
}
// render one renbone
static void RenderBone(RenBone &rb, COLOR col)
{
FLOAT fSize = rb.rb_psbBone->sb_fBoneLength / 20;
FLOAT3D vBoneStart = FLOAT3D(rb.rb_mBonePlacement[3],rb.rb_mBonePlacement[7],rb.rb_mBonePlacement[11]);
FLOAT3D vBoneEnd = FLOAT3D(0,0,-rb.rb_psbBone->sb_fBoneLength);
FLOAT3D vRingPt[4];
vRingPt[0] = FLOAT3D(-fSize,-fSize,-fSize*2);
vRingPt[1] = FLOAT3D( fSize,-fSize,-fSize*2);
vRingPt[2] = FLOAT3D( fSize, fSize,-fSize*2);
vRingPt[3] = FLOAT3D(-fSize, fSize,-fSize*2);
TransformVector(vBoneEnd.vector,rb.rb_mBonePlacement);
TransformVector(vRingPt[0].vector,rb.rb_mBonePlacement);
TransformVector(vRingPt[1].vector,rb.rb_mBonePlacement);
TransformVector(vRingPt[2].vector,rb.rb_mBonePlacement);
TransformVector(vRingPt[3].vector,rb.rb_mBonePlacement);
// connect start point of bone with end point
INDEX il;
for(il=0;il<4;il++) {
_pdp->DrawLine3D(vBoneStart,vRingPt[il],col);
_pdp->DrawLine3D(vBoneEnd,vRingPt[il],col);
}
// draw ring
for(il=0;il<3;il++) {
_pdp->DrawLine3D(vRingPt[il],vRingPt[il+1],col);
}
_pdp->DrawLine3D(vRingPt[0],vRingPt[3],col);
}
// render one bone in model instance
void RM_RenderBone(CModelInstance &mi,INDEX iBoneID)
{
UBYTE ubFillColor = 127;
CStaticStackArray<INDEX> aiRenModelIndices;
CStaticStackArray<INDEX> aiRenMeshIndices;
CalculateRenderingData(mi);
gfxEnableBlend();
gfxEnableDepthTest();
INDEX iBoneIndex = -1; // index of selected bone in renbone array
INDEX iWeightIndex = -1; // index of weight that have same id as bone
// find all renmeshes that uses this bone weightmap
INDEX ctrm = _aRenModels.Count();
// for each renmodel
for(INDEX irm=1;irm<ctrm;irm++) {
RenModel &rm = _aRenModels[irm];
// try to find bone in this renmodel
if(FindRenBone(rm,iBoneID,&iBoneIndex)) {
// for each renmesh in rm
INDEX ctmsh = rm.rm_iFirstMesh+rm.rm_ctMeshes;
for(INDEX imsh=rm.rm_iFirstMesh;imsh<ctmsh;imsh++) {
RenMesh &rm = _aRenMesh[imsh];
// for each weightmap in this renmesh
INDEX ctwm = rm.rmsh_iFirstWeight+rm.rmsh_ctWeights;
for(INDEX iwm=rm.rmsh_iFirstWeight;iwm<ctwm;iwm++) {
RenWeight &rw = _aRenWeights[iwm];
// if weight map id is same as bone id
if(rw.rw_pwmWeightMap->mwm_iID == iBoneID) {
INDEX &irmi = aiRenModelIndices.Push();
INDEX &irmshi = aiRenMeshIndices.Push();
// rememeber this weight map
irmi = irm;
irmshi = imsh;
iWeightIndex = iwm;
}
}
}
}
}
// if weightmap is found
if(iWeightIndex>=0) {
// show wertex weights for each mesh that uses this bones weightmap
INDEX ctmshi=aiRenMeshIndices.Count();
for(INDEX imshi=0;imshi<ctmshi;imshi++)
{
INDEX iMeshIndex = aiRenMeshIndices[imshi]; // index of mesh that uses selected bone
INDEX iModelIndex = aiRenModelIndices[imshi]; // index of model in witch is mesh
RenModel &rm = _aRenModels[iModelIndex];
RenMesh &rmsh = _aRenMesh[iMeshIndex];
MeshLOD &mlod = rmsh.rmsh_pMeshInst->mi_pMesh->msh_aMeshLODs[rmsh.rmsh_iMeshLODIndex];
// Create array of color
INDEX ctVertices = mlod.mlod_aVertices.Count();
_aMeshColors.PopAll();
_aMeshColors.Push(ctVertices);
memset(&_aMeshColors[0],ubFillColor,sizeof(_aMeshColors[0])*ctVertices);
// prepare this mesh for rendering
PrepareMeshForRendering(rmsh,rm.rm_iSkeletonLODIndex);
// all vertices by default are not visible ( have alpha set to 0 )
for(INDEX ivx=0;ivx<ctVertices;ivx++) {
_aMeshColors[ivx].ub.a = 0;
}
INDEX ctwm = rmsh.rmsh_iFirstWeight+rmsh.rmsh_ctWeights;
// for each weightmap in this mesh
for(INDEX irw=rmsh.rmsh_iFirstWeight;irw<ctwm;irw++) {
RenWeight &rw = _aRenWeights[irw];
if(rw.rw_iBoneIndex != iBoneIndex) continue;
INDEX ctvw = rw.rw_pwmWeightMap->mwm_aVertexWeight.Count();
// for each vertex in this veight
for(int ivw=0; ivw<ctvw; ivw++)
{
// modify color and alpha value of this vertex
MeshVertexWeight &vw = rw.rw_pwmWeightMap->mwm_aVertexWeight[ivw];
INDEX ivx = vw.mww_iVertex;
_aMeshColors[ivx].ub.r = 255;
_aMeshColors[ivx].ub.g = 127;
_aMeshColors[ivx].ub.b = 0;
_aMeshColors[ivx].ub.a += (UBYTE) (vw.mww_fWeight*255); // _aMeshColors[ivx].ub.a = 255;
}
}
// count surfaces in mesh
INDEX ctsrf = mlod.mlod_aSurfaces.Count();
// for each surface
for(INDEX isrf=0; isrf<ctsrf; isrf++) {
MeshSurface &msrf = mlod.mlod_aSurfaces[isrf];
shaSetVertexArray((GFXVertex4*)&_pavFinalVertices[msrf.msrf_iFirstVertex],msrf.msrf_ctVertices);
shaSetNormalArray((GFXNormal*)&_panFinalNormals[msrf.msrf_iFirstVertex]);
shaSetIndices(&msrf.msrf_aTriangles[0].iVertex[0],msrf.msrf_aTriangles.Count()*3);
shaSetTexture(-1);
shaCalculateLight();
GFXColor *paColors = shaGetColorArray();
// replace current color array with weight color array
memcpy(paColors,&_aMeshColors[msrf.msrf_iFirstVertex],sizeof(COLOR)*msrf.msrf_ctVertices);
shaEnableBlend();
shaBlendFunc( GFX_SRC_ALPHA, GFX_INV_SRC_ALPHA);
// render surface
shaRender();
shaClean();
}
}
}
// draw bone
if(iBoneIndex>=0) {
gfxSetViewMatrix(NULL);
gfxDisableDepthTest();
// show bone in yellow color
RenderBone(_aRenBones[iBoneIndex],0xFFFF00FF);
}
gfxDisableBlend();
aiRenModelIndices.Clear();
aiRenMeshIndices.Clear();
ClearRenArrays();
}
// render skeleton hierarchy
static void RenderSkeleton(void)
{
gfxSetViewMatrix(NULL);
// for each bone, except the dummy one
for(int irb=1; irb<_aRenBones.Count(); irb++)
{
RenBone &rb = _aRenBones[irb];
RenderBone(rb,0x5A5ADCFF); // render in blue color
}
}
static void RenderActiveBones(RenModel &rm)
{
CModelInstance *pmi = rm.rm_pmiModel;
if(pmi==NULL) return;
// count animlists
INDEX ctal = pmi->mi_aqAnims.aq_Lists.Count();
// find newes animlist that has fully faded in
INDEX iFirstAnimList = 0;
// loop from newer to older
INDEX ial;
for(ial=ctal-1;ial>=0;ial--) {
AnimList &alList = pmi->mi_aqAnims.aq_Lists[ial];
// calculate fade factor
FLOAT fFadeFactor = CalculateFadeFactor(alList);
if(fFadeFactor >= 1.0f) {
iFirstAnimList = ial;
break;
}
}
// for each anim list after iFirstAnimList
for(ial=iFirstAnimList;ial<ctal;ial++) {
AnimList &alList = pmi->mi_aqAnims.aq_Lists[ial];
INDEX ctpa = alList.al_PlayedAnims.Count();
// for each played anim
for(INDEX ipa=0;ipa<ctpa;ipa++) {
PlayedAnim &pa = alList.al_PlayedAnims[ipa];
INDEX iAnimSet,iAnimIndex;
pmi->FindAnimationByID(pa.pa_iAnimID,&iAnimSet,&iAnimIndex);
CAnimSet &as = pmi->mi_aAnimSet[iAnimSet];
Animation &an = as.as_Anims[iAnimIndex];
INDEX ctbe = an.an_abeBones.Count();
// for each bone envelope
for(INDEX ibe=0;ibe<ctbe;ibe++) {
BoneEnvelope &be = an.an_abeBones[ibe];
INDEX iBoneIndex = 0;
// try to find renbone for this bone envelope
if(FindRenBone(rm,be.be_iBoneID,&iBoneIndex)) {
RenBone &rb = _aRenBones[iBoneIndex];
// render bone
RenderBone(rb,0x00FF00FF);
}
}
}
}
}
static void RenderActiveBones(void)
{
gfxSetViewMatrix(NULL);
// for each renmodel
INDEX ctrm = _aRenModels.Count();
for(SLONG irm=0;irm<ctrm;irm++) {
RenModel &rm = _aRenModels[irm];
RenderActiveBones(rm);
}
}
// get render flags for model
ULONG &RM_GetRenderFlags()
{
return _ulRenFlags;
}
// set new flag
void RM_SetFlags(ULONG ulNewFlags)
{
_ulFlags = ulNewFlags;
}
// get curent flags
ULONG RM_GetFlags()
{
return _ulFlags;
}
// add flag
void RM_AddFlag(ULONG ulFlag)
{
_ulFlags |= ulFlag;
}
// remove flag
void RM_RemoveFlag(ULONG ulFlag)
{
_ulFlags &= ~ulFlag;
}
// find texture data id
static void FindTextureData(CTextureObject **ptoTextures, INDEX iTextureID, MeshInstance &mshi)
{
// for each texture instances
INDEX ctti=mshi.mi_tiTextures.Count();
for(INDEX iti=0;iti<ctti;iti++)
{
TextureInstance &ti = mshi.mi_tiTextures[iti];
if(ti.GetID() == iTextureID)
{
*ptoTextures = &ti.ti_toTexture;
return;
}
}
*ptoTextures = NULL;
}
// find frame (binary) index in compresed array of rotations, positions or opt_rotations
static INDEX FindFrame(UBYTE *pFirstMember, INDEX iFind, INDEX ctfn, UINT uiSize)
{
INDEX iHigh = ctfn-1;
INDEX iLow = 0;
INDEX iMid;
UWORD iHighFrameNum = *(UWORD*)(pFirstMember+(uiSize*iHigh));
if(iFind == iHighFrameNum) return iHigh;
while(TRUE) {
iMid = (iHigh+iLow)/2;
UWORD iMidFrameNum = *(UWORD*)(pFirstMember+(uiSize*iMid));
UWORD iMidFrameNumPlusOne = *(UWORD*)(pFirstMember+(uiSize*(iMid+1)));
if(iFind < iMidFrameNum) iHigh = iMid;
else if((iMid == iHigh) || (iMidFrameNumPlusOne > iFind)) return iMid;
else iLow = iMid;
}
}
// Find renbone in given renmodel
static BOOL FindRenBone(RenModel &rm,int iBoneID,INDEX *piBoneIndex)
{
int ctb = rm.rm_iFirstBone + rm.rm_ctBones;
// for each renbone in this ren model
for(int ib=rm.rm_iFirstBone;ib<ctb;ib++) {
// if bone id's match
if(iBoneID == _aRenBones[ib].rb_psbBone->sb_iID) {
// return index of this renbone
*piBoneIndex = ib;
return TRUE;
}
}
return FALSE;
}
// Find renbone in whole array on renbones
RenBone *RM_FindRenBone(INDEX iBoneID)
{
INDEX ctrb=_aRenBones.Count();
// for each renbone
for(INDEX irb=1;irb<ctrb;irb++) {
RenBone &rb = _aRenBones[irb];
// if bone id's match
if(rb.rb_psbBone->sb_iID == iBoneID) {
// return this renbone
return &rb;
}
}
return NULL;
}
// Return array of renbones
RenBone *RM_GetRenBoneArray(INDEX &ctrb)
{
ctrb = _aRenBones.Count();
if(ctrb>0) {
return &_aRenBones[0];
} else {
return NULL;
}
}
// find renmoph in given renmodel
static BOOL FindRenMorph(RenModel &rm,int iMorphID,INDEX *piMorphIndex)
{
// for each renmesh in given renmodel
INDEX ctmsh = rm.rm_iFirstMesh + rm.rm_ctMeshes;
for(INDEX irmsh=rm.rm_iFirstMesh;irmsh<ctmsh;irmsh++) {
// for each renmorph in this renmesh
INDEX ctmm = _aRenMesh[irmsh].rmsh_iFirstMorph + _aRenMesh[irmsh].rmsh_ctMorphs;
for(INDEX imm=_aRenMesh[irmsh].rmsh_iFirstMorph;imm<ctmm;imm++) {
// if id's match
if(iMorphID == _aRenMorph[imm].rmp_pmmmMorphMap->mmp_iID) {
// return this renmorph
*piMorphIndex = imm;
return TRUE;
}
}
}
// renmorph was not found
return FALSE;
}
// Find bone by ID (bone index must be set!)
static BOOL FindBone(int iBoneID, INDEX *piBoneIndex, CModelInstance *pmi,INDEX iSkeletonLod)
{
// if model instance does not have skeleton
if(pmi->mi_psklSkeleton == NULL) return FALSE;
// if current skeleton lod is invalid
if(iSkeletonLod < 0) return FALSE;
INDEX ctslods = pmi->mi_psklSkeleton->skl_aSkeletonLODs.Count();
// if skeleton lods count is invalid
if(ctslods<1) return FALSE;
// if skeleton lod is larger than lod count
if(iSkeletonLod >= ctslods) {
// use skeleton finest skeleton lod
#pragma message(">> Check if this is ok")
iSkeletonLod = 0;
ASSERT(FALSE);
}
SkeletonLOD &slod = pmi->mi_psklSkeleton->skl_aSkeletonLODs[iSkeletonLod];
// for each bone in skeleton lod
for(int i=0;i<slod.slod_aBones.Count();i++) {
// check if bone id's match
if(iBoneID == slod.slod_aBones[i].sb_iID) {
// bone index is allready set just return true
return TRUE;
}
*piBoneIndex += 1;
}
// for each child of given model instance
INDEX ctmich = pmi->mi_cmiChildren.Count();
for(INDEX imich =0;imich<ctmich;imich++) {
// try to find bone in child model instance
if(FindBone(iBoneID,piBoneIndex,&pmi->mi_cmiChildren[imich],iSkeletonLod))
return TRUE;
}
// bone was not found
return FALSE;
}
// decompres axis for quaternion if animations are optimized
static void DecompressAxis(FLOAT3D &vNormal, UWORD ubH, UWORD ubP)
{
ANGLE h = (ubH/65535.0f)*360.0f-180.0f;
ANGLE p = (ubP/65535.0f)*360.0f-180.0f;
FLOAT &x = vNormal(1);
FLOAT &y = vNormal(2);
FLOAT &z = vNormal(3);
x = -Sin(h)*Cos(p);
y = Sin(p);
z = -Cos(h)*Cos(p);
}
// initialize batch model rendering
void RM_BeginRenderingView(CAnyProjection3D &apr, CDrawPort *pdp)
{
// remember parameters
_iRenderingType = 1;
_pdp = pdp;
// prepare and set the projection
apr->ObjectPlacementL() = CPlacement3D(FLOAT3D(0,0,0), ANGLE3D(0,0,0));
apr->Prepare();
// in case of mirror projection, move mirror clip plane a bit father from the mirrored models,
// so we have less clipping (for instance, player feet)
if( apr->pr_bMirror) apr->pr_plMirrorView.pl_distance -= 0.06f; // -0.06 is because entire projection is offseted by +0.05
_aprProjection = apr;
_pdp->SetProjection( _aprProjection);
// remember the abs to viewer transformation
MatrixVectorToMatrix12(_mAbsToViewer,
_aprProjection->pr_ViewerRotationMatrix,
-_aprProjection->pr_vViewerPosition*_aprProjection->pr_ViewerRotationMatrix);
// make FPU precision low
_fpuOldPrecision = GetFPUPrecision();
SetFPUPrecision(FPT_24BIT);
}
// cleanup after batch model rendering
void RM_EndRenderingView( BOOL bRestoreOrtho/*=TRUE*/)
{
ASSERT( _iRenderingType==1 && _pdp!=NULL);
// assure that FPU precision was low all the model rendering time, then revert to old FPU precision
ASSERT( GetFPUPrecision()==FPT_24BIT);
SetFPUPrecision(_fpuOldPrecision);
// back to 2D projection?
if( bRestoreOrtho) _pdp->SetOrtho();
_pdp->SetOrtho();
_iRenderingType = 0;
_pdp = NULL;
}
// for mark renderer
extern CAnyProjection3D _aprProjection;
extern UBYTE *_pubMask;
extern SLONG _slMaskWidth;
extern SLONG _slMaskHeight;
// begin/end model rendering to shadow mask
void RM_BeginModelRenderingMask( CAnyProjection3D &prProjection, UBYTE *pubMask, SLONG slMaskWidth, SLONG slMaskHeight)
{
ASSERT( _iRenderingType==0);
_iRenderingType = 2;
_aprProjection = prProjection;
_pubMask = pubMask;
_slMaskWidth = slMaskWidth;
_slMaskHeight = slMaskHeight;
// prepare and set the projection
_aprProjection->ObjectPlacementL() = CPlacement3D(FLOAT3D(0,0,0), ANGLE3D(0,0,0));
_aprProjection->Prepare();
// remember the abs to viewer transformation
MatrixVectorToMatrix12(_mAbsToViewer,
_aprProjection->pr_ViewerRotationMatrix,
-_aprProjection->pr_vViewerPosition*_aprProjection->pr_ViewerRotationMatrix);
// set mask shader
extern void InternalShader_Mask(void);
extern void InternalShaderDesc_Mask(ShaderDesc &shDesc);
_shMaskShader.ShaderFunc = InternalShader_Mask;
_shMaskShader.GetShaderDesc = InternalShaderDesc_Mask;
}
void RM_EndModelRenderingMask(void)
{
ASSERT( _iRenderingType==2);
_iRenderingType = 0;
}
// setup light parameters
void RM_SetLightColor(COLOR colAmbient, COLOR colLight)
{
_colAmbient = colAmbient;
_colLight = colLight;
}
void RM_SetLightDirection(FLOAT3D &vLightDir)
{
_vLightDir = vLightDir * (-1);
}
// calculate object matrices for givem model instance
void RM_SetObjectMatrices(CModelInstance &mi)
{
ULONG ulFlags = RM_GetRenderFlags();
// adjust clipping to frustum
if( ulFlags & SRMF_INSIDE) gfxDisableClipping();
else gfxEnableClipping();
// adjust clipping to mirror-plane (if any)
extern INDEX gap_iOptimizeClipping;
if((CProjection3D *)_aprProjection != NULL) {
if( gap_iOptimizeClipping>0 && (_aprProjection->pr_bMirror || _aprProjection->pr_bWarp)) {
if( ulFlags & SRMF_INMIRROR) {
gfxDisableClipPlane();
gfxFrontFace( GFX_CCW);
} else {
gfxEnableClipPlane();
gfxFrontFace( GFX_CW);
}
}
}
MatrixMultiply(_mObjToView,_mAbsToViewer, _mObjectToAbs);
Matrix12 mStretch;
MakeStretchMatrix(mStretch, mi.mi_vStretch);
MatrixMultiply(_mObjToViewStretch,_mObjToView,mStretch);
}
// setup object position
void RM_SetObjectPlacement(const CPlacement3D &pl)
{
FLOATmatrix3D m;
MakeRotationMatrixFast( m, pl.pl_OrientationAngle);
MatrixVectorToMatrix12(_mObjectToAbs,m, pl.pl_PositionVector);
}
void RM_SetObjectPlacement(const FLOATmatrix3D &m, const FLOAT3D &v)
{
MatrixVectorToMatrix12(_mObjectToAbs,m, v);
}
// sets custom mesh lod
void RM_SetCustomMeshLodDistance(FLOAT fMeshLod)
{
_fCustomMlodDistance = fMeshLod;
}
// sets custom skeleton lod
void RM_SetCustomSkeletonLodDistance(FLOAT fSkeletonLod)
{
_fCustomSlodDistance = fSkeletonLod;
}
// Returns index of skeleton lod at given distance
INDEX GetSkeletonLOD(CSkeleton &sk, FLOAT fDistance)
{
FLOAT fMinDistance = 1000000.0f;
INDEX iSkeletonLod = -1;
// if custom lod distance is set
if(_fCustomSlodDistance!=-1) {
// set object distance as custom distance
fDistance = _fCustomSlodDistance;
}
// for each lod in skeleton
INDEX ctslods = sk.skl_aSkeletonLODs.Count();
for(INDEX islod=0;islod<ctslods;islod++) {
SkeletonLOD &slod = sk.skl_aSkeletonLODs[islod];
// adjust lod distance by custom settings
FLOAT fLodMaxDistance = slod.slod_fMaxDistance*ska_fLODMul+ska_fLODAdd;
// check if this lod max distance is smaller than distance to object
if(fDistance < fLodMaxDistance && fLodMaxDistance < fMinDistance) {
// remember this lod
fMinDistance = fLodMaxDistance;
iSkeletonLod = islod;
}
}
return iSkeletonLod;
}
// Returns index of mesh lod at given distance
INDEX GetMeshLOD(CMesh &msh, FLOAT fDistance)
{
FLOAT fMinDistance = 1000000.0f;
INDEX iMeshLod = -1;
// if custom lod distance is set
if(_fCustomMlodDistance!=-1) {
// set object distance as custom distance
fDistance = _fCustomMlodDistance;
}
// for each lod in mesh
INDEX ctmlods = msh.msh_aMeshLODs.Count();
for(INDEX imlod=0;imlod<ctmlods;imlod++) {
MeshLOD &mlod = msh.msh_aMeshLODs[imlod];
// adjust lod distance by custom settings
FLOAT fLodMaxDistance = mlod.mlod_fMaxDistance*ska_fLODMul+ska_fLODAdd;
// check if this lod max distance is smaller than distance to object
if(fDistance<fLodMaxDistance && fLodMaxDistance<fMinDistance) {
// remember this lod
fMinDistance = fLodMaxDistance;
iMeshLod = imlod;
}
}
return iMeshLod;
}
// create first dummy model that serves as parent for the entire hierarchy
void MakeRootModel(void)
{
// create the model with one bone
RenModel &rm = _aRenModels.Push();
rm.rm_pmiModel = NULL;
rm.rm_iFirstBone = 0;
rm.rm_ctBones = 1;
rm.rm_iParentBoneIndex = -1;
rm.rm_iParentModelIndex = -1;
// add the default bone
RenBone &rb = _aRenBones.Push();
rb.rb_iParentIndex = -1;
rb.rb_psbBone = NULL;
memset(&rb.rb_apPos,0,sizeof(AnimPos));
memset(&rb.rb_arRot,0,sizeof(AnimRot));
}
// build model hierarchy
static INDEX BuildHierarchy(CModelInstance *pmiModel, INDEX irmParent)
{
INDEX ctrm = _aRenModels.Count();
// add one renmodel
RenModel &rm = _aRenModels.Push();
RenModel &rmParent = _aRenModels[irmParent];
rm.rm_pmiModel = pmiModel;
rm.rm_iParentModelIndex = irmParent;
rm.rm_iNextSiblingModel = -1;
rm.rm_iFirstBone = _aRenBones.Count();
rm.rm_ctBones = 0;
// if this model is root model
if(pmiModel->mi_iParentBoneID == (-1)) {
// set is parent bone index as 0
rm.rm_iParentBoneIndex = rmParent.rm_iFirstBone;
// model instance is attached to another model's bone
} else {
INDEX iParentBoneIndex = -1;
// does parent model insntance has a skeleton
if(rmParent.rm_pmiModel->mi_psklSkeleton != NULL && rmParent.rm_iSkeletonLODIndex>=0) {
// get index of parent bone
iParentBoneIndex = rmParent.rm_pmiModel->mi_psklSkeleton->FindBoneInLOD(pmiModel->mi_iParentBoneID,rmParent.rm_iSkeletonLODIndex);
// model instance does not have skeleton
} else {
// do not draw this model
_aRenModels.Pop();
return -1;
}
// if parent bone index was not found ( not visible in current lod)
if(iParentBoneIndex == (-1)) {
// do not draw this model
_aRenModels.Pop();
return -1;
// parent bone exists and its visible
} else {
// set this model parent bone index in array of renbones
rm.rm_iParentBoneIndex = iParentBoneIndex + rmParent.rm_iFirstBone;
}
}
// if this model instance has skeleton
if(pmiModel->mi_psklSkeleton!=NULL) {
// adjust mip factor in case of dynamic stretch factor
FLOAT fDistFactor = _fDistanceFactor;
FLOAT3D &vStretch = pmiModel->mi_vStretch;
// if model is stretched
if( vStretch != FLOAT3D(1,1,1)) {
// calculate new distance factor
fDistFactor = fDistFactor / Max(vStretch(1),Max(vStretch(2),vStretch(3)));
}
// calulate its current skeleton lod
rm.rm_iSkeletonLODIndex = GetSkeletonLOD(*pmiModel->mi_psklSkeleton,fDistFactor);
// if current skeleton lod is valid and visible
if(rm.rm_iSkeletonLODIndex > -1) {
// count all bones in this skeleton
INDEX ctsb = pmiModel->mi_psklSkeleton->skl_aSkeletonLODs[rm.rm_iSkeletonLODIndex].slod_aBones.Count();
// for each bone in skeleton
for(INDEX irb=0;irb<ctsb;irb++) {
SkeletonBone *pSkeletonBone = &pmiModel->mi_psklSkeleton->skl_aSkeletonLODs[rm.rm_iSkeletonLODIndex].slod_aBones[irb];
// add one renbone
RenBone &rb = _aRenBones.Push();
rb.rb_psbBone = pSkeletonBone;
rb.rb_iRenModelIndex = ctrm;
rm.rm_ctBones++;
// add default bone position (used if no animations)
rb.rb_apPos.ap_vPos = pSkeletonBone->sb_qvRelPlacement.vPos;
rb.rb_arRot.ar_qRot = pSkeletonBone->sb_qvRelPlacement.qRot;
// if this is root bone for this model instance
if(pSkeletonBone->sb_iParentID == (-1)) {
// set its parent bone index to be parent bone of this model instance
rb.rb_iParentIndex = rm.rm_iParentBoneIndex;
// this is child bone
} else {
// get parent index in array of renbones
INDEX rb_iParentIndex = pmiModel->mi_psklSkeleton->FindBoneInLOD(pSkeletonBone->sb_iParentID,rm.rm_iSkeletonLODIndex);
rb.rb_iParentIndex = rb_iParentIndex + rm.rm_iFirstBone;
}
}
}
}
rm.rm_iFirstMesh = _aRenMesh.Count();
rm.rm_ctMeshes = 0;
INDEX ctm = pmiModel->mi_aMeshInst.Count();
// for each mesh instance in this model instance
for(INDEX im=0;im<ctm;im++) {
// adjust mip factor in case of dynamic stretch factor
FLOAT fDistFactor = _fDistanceFactor;
FLOAT3D &vStretch = pmiModel->mi_vStretch;
// if model is stretched
if( vStretch != FLOAT3D(1,1,1)) {
// calculate new distance factor
fDistFactor = fDistFactor / Max(vStretch(1),Max(vStretch(2),vStretch(3)));// Log2( Max(vStretch(1),Max(vStretch(2),vStretch(3))));
}
// calculate current mesh lod
INDEX iMeshLodIndex = GetMeshLOD(*pmiModel->mi_aMeshInst[im].mi_pMesh,fDistFactor);
// if mesh lod is visible
if(iMeshLodIndex > -1) {
// add one ren mesh
RenMesh &rmsh = _aRenMesh.Push();
rm.rm_ctMeshes++;
rmsh.rmsh_iRenModelIndex = ctrm;
rmsh.rmsh_pMeshInst = &pmiModel->mi_aMeshInst[im];
rmsh.rmsh_iFirstMorph = _aRenMorph.Count();
rmsh.rmsh_iFirstWeight = _aRenWeights.Count();
rmsh.rmsh_ctMorphs = 0;
rmsh.rmsh_ctWeights = 0;
rmsh.rmsh_bTransToViewSpace = FALSE;
// set mesh lod index for this ren mesh
rmsh.rmsh_iMeshLODIndex = iMeshLodIndex;
// for each morph map in this mesh lod
INDEX ctmm = rmsh.rmsh_pMeshInst->mi_pMesh->msh_aMeshLODs[rmsh.rmsh_iMeshLODIndex].mlod_aMorphMaps.Count();
for(INDEX imm=0;imm<ctmm;imm++) {
// add this morph map in array of renmorphs
RenMorph &rm = _aRenMorph.Push();
rmsh.rmsh_ctMorphs++;
rm.rmp_pmmmMorphMap = &rmsh.rmsh_pMeshInst->mi_pMesh->msh_aMeshLODs[rmsh.rmsh_iMeshLODIndex].mlod_aMorphMaps[imm];
rm.rmp_fFactor = 0;
}
// for each weight map in this mesh lod
INDEX ctw = rmsh.rmsh_pMeshInst->mi_pMesh->msh_aMeshLODs[rmsh.rmsh_iMeshLODIndex].mlod_aWeightMaps.Count();
for(INDEX iw=0;iw<ctw;iw++) {
// add this weight map in array of renweights
RenWeight &rw = _aRenWeights.Push();
MeshWeightMap &mwm = rmsh.rmsh_pMeshInst->mi_pMesh->msh_aMeshLODs[rmsh.rmsh_iMeshLODIndex].mlod_aWeightMaps[iw];
rw.rw_pwmWeightMap = &mwm;
rmsh.rmsh_ctWeights++;
rw.rw_iBoneIndex = rm.rm_iFirstBone;
// find bone of this weight in current skeleton lod and get its index for this renweight
if(!FindBone(mwm.mwm_iID,&rw.rw_iBoneIndex,pmiModel,rm.rm_iSkeletonLODIndex))
{
// if bone not found, set boneindex in renweight to -1
rw.rw_iBoneIndex = -1;
}
}
}
}
rm.rm_iFirstChildModel = -1;
// for each child in this model instance
INDEX ctmich = pmiModel->mi_cmiChildren.Count();
for(int imich=0;imich<ctmich;imich++) {
// build hierarchy for child model instance
INDEX irmChildIndex = BuildHierarchy(&pmiModel->mi_cmiChildren[imich],ctrm);
// if child is visible
if(irmChildIndex != (-1)) {
// set model sibling
_aRenModels[irmChildIndex].rm_iNextSiblingModel = rm.rm_iFirstChildModel;
rm.rm_iFirstChildModel = irmChildIndex;
}
}
return ctrm;
}
// calculate transformations for all bones on already built hierarchy
static void CalculateBoneTransforms()
{
// put basic transformation in first dummy bone
MatrixCopy(_aRenBones[0].rb_mTransform, _mObjToView);
MatrixCopy(_aRenBones[0].rb_mStrTransform, _aRenBones[0].rb_mTransform);
// if callback function was specified
if(_pAdjustBonesCallback!=NULL) {
// Call callback function
_pAdjustBonesCallback(_pAdjustBonesData);
}
Matrix12 mStretch;
// for each renbone after first dummy one
int irb;
for(irb=1; irb<_aRenBones.Count(); irb++) {
Matrix12 mRelPlacement;
Matrix12 mOffset;
RenBone &rb = _aRenBones[irb];
RenBone &rbParent = _aRenBones[rb.rb_iParentIndex];
// Convert QVect of placement to matrix12
QVect qv;
qv.vPos = rb.rb_apPos.ap_vPos;
qv.qRot = rb.rb_arRot.ar_qRot;
QVectToMatrix12(mRelPlacement,qv);
// if this is root bone
if(rb.rb_psbBone->sb_iParentID == (-1)) {
// stretch root bone
RenModel &rm= _aRenModels[rb.rb_iRenModelIndex];
MakeStretchMatrix(mStretch, rm.rm_pmiModel->mi_vStretch);
RenModel &rmParent = _aRenModels[rb.rb_iRenModelIndex];
QVectToMatrix12(mOffset,rmParent.rm_pmiModel->mi_qvOffset);
// add offset to root bone
MatrixMultiplyCP(mRelPlacement,mOffset,mRelPlacement);
Matrix12 mStrParentBoneTrans;
// Create stretch matrix with parent bone transformations
MatrixMultiplyCP(mStrParentBoneTrans, rbParent.rb_mStrTransform,mStretch);
// transform bone using stretch parent's transform, relative placement
MatrixMultiply(rb.rb_mStrTransform, mStrParentBoneTrans, mRelPlacement);
MatrixMultiply(rb.rb_mTransform,rbParent.rb_mTransform, mRelPlacement);
} else {
// transform bone using parent's transform and relative placement
MatrixMultiply(rb.rb_mStrTransform, rbParent.rb_mStrTransform, mRelPlacement);
MatrixMultiply(rb.rb_mTransform,rbParent.rb_mTransform,mRelPlacement);
}
// remember tranform matrix of bone placement for bone rendering
MatrixCopy(rb.rb_mBonePlacement,rb.rb_mStrTransform);
}
// for each renmodel after first dummy one
for(int irm=1; irm<_aRenModels.Count(); irm++) {
// remember transforms for bone-less models for every renmodel, except the dummy one
Matrix12 mOffset;
Matrix12 mStretch;
RenModel &rm = _aRenModels[irm];
QVectToMatrix12(mOffset,rm.rm_pmiModel->mi_qvOffset);
MakeStretchMatrix(mStretch,rm.rm_pmiModel->mi_vStretch);
MatrixMultiply(rm.rm_mTransform,_aRenBones[rm.rm_iParentBoneIndex].rb_mTransform,mOffset);
MatrixMultiply(rm.rm_mStrTransform,_aRenBones[rm.rm_iParentBoneIndex].rb_mStrTransform,mOffset);
MatrixMultiplyCP(rm.rm_mStrTransform,rm.rm_mStrTransform,mStretch);
}
Matrix12 mInvert;
// for each renbone
for(irb=1; irb<_aRenBones.Count(); irb++) {
RenBone &rb = _aRenBones[irb];
// multiply every transform with invert matrix of bone abs placement
MatrixTranspose(mInvert,rb.rb_psbBone->sb_mAbsPlacement);
// create two versions of transform matrices, stretch and normal for vertices and normals
MatrixMultiplyCP(_aRenBones[irb].rb_mStrTransform,_aRenBones[irb].rb_mStrTransform,mInvert);
MatrixMultiplyCP(_aRenBones[irb].rb_mTransform,_aRenBones[irb].rb_mTransform,mInvert);
}
}
// Match animations in anim queue for bones
static void MatchAnims(RenModel &rm)
{
const FLOAT fLerpedTick = _pTimer->GetLerpedCurrentTick();
// return if no animsets
INDEX ctas = rm.rm_pmiModel->mi_aAnimSet.Count();
if(ctas == 0) return;
// count animlists
INDEX ctal = rm.rm_pmiModel->mi_aqAnims.aq_Lists.Count();
// find newes animlist that has fully faded in
INDEX iFirstAnimList = 0;
// loop from newer to older
INDEX ial;
for(ial=ctal-1;ial>=0;ial--) {
AnimList &alList = rm.rm_pmiModel->mi_aqAnims.aq_Lists[ial];
// calculate fade factor
FLOAT fFadeFactor = CalculateFadeFactor(alList);
if(fFadeFactor >= 1.0f) {
iFirstAnimList = ial;
break;
}
}
// for each anim list after iFirstAnimList
for(ial=iFirstAnimList;ial<ctal;ial++) {
AnimList &alList = rm.rm_pmiModel->mi_aqAnims.aq_Lists[ial];
AnimList *palListNext=NULL;
if(ial+1<ctal) palListNext = &rm.rm_pmiModel->mi_aqAnims.aq_Lists[ial+1];
// calculate fade factor
FLOAT fFadeFactor = CalculateFadeFactor(alList);
INDEX ctpa = alList.al_PlayedAnims.Count();
// for each played anim in played anim list
for(int ipa=0;ipa<ctpa;ipa++) {
FLOAT fTime = fLerpedTick;
PlayedAnim &pa = alList.al_PlayedAnims[ipa];
BOOL bAnimLooping = pa.pa_ulFlags & AN_LOOPING;
INDEX iAnimSetIndex;
INDEX iAnimIndex;
// find anim by ID in all anim sets within this model
if(rm.rm_pmiModel->FindAnimationByID(pa.pa_iAnimID,&iAnimSetIndex,&iAnimIndex)) {
// if found, animate bones
Animation &an = rm.rm_pmiModel->mi_aAnimSet[iAnimSetIndex].as_Anims[iAnimIndex];
// calculate end time for this animation list
FLOAT fFadeInEndTime = alList.al_fStartTime + alList.al_fFadeTime;
// if there is a newer anmimation list
if(palListNext!=NULL) {
// freeze time of this one to never overlap with the newer list
fTime = ClampUp(fTime, palListNext->al_fStartTime);
}
// calculate time passed since the animation started
FLOAT fTimeOffset = fTime - pa.pa_fStartTime;
// if this animation list is fading in
if (fLerpedTick < fFadeInEndTime) {
// offset the time so that it is paused at the end of fadein interval
fTimeOffset += fFadeInEndTime - fLerpedTick;
}
FLOAT f = fTimeOffset / (an.an_fSecPerFrame*pa.pa_fSpeedMul);
INDEX iCurentFrame;
INDEX iAnimFrame,iNextAnimFrame;
if(bAnimLooping) {
f = fmod(f,an.an_iFrames);
iCurentFrame = INDEX(f);
iAnimFrame = iCurentFrame % an.an_iFrames;
iNextAnimFrame = (iCurentFrame+1) % an.an_iFrames;
} else {
if(f>an.an_iFrames) f = an.an_iFrames-1;
iCurentFrame = INDEX(f);
iAnimFrame = ClampUp(iCurentFrame,an.an_iFrames-1L);
iNextAnimFrame = ClampUp(iCurentFrame+1L,an.an_iFrames-1L);
}
// for each bone envelope
INDEX ctbe = an.an_abeBones.Count();
for(int ibe=0;ibe<ctbe;ibe++) {
INDEX iBoneIndex;
// find its renbone in array of renbones
if(FindRenBone(rm,an.an_abeBones[ibe].be_iBoneID, &iBoneIndex)) {
RenBone &rb = _aRenBones[iBoneIndex];
BoneEnvelope &be = an.an_abeBones[ibe];
INDEX iRotFrameIndex;
INDEX iNextRotFrameIndex;
INDEX iRotFrameNum;
INDEX iNextRotFrameNum;
FLOAT fSlerpFactor;
FLOATquat3D qRot;
FLOATquat3D qRotCurrent;
FLOATquat3D qRotNext;
FLOATquat3D *pqRotCurrent;
FLOATquat3D *pqRotNext;
// if animation is not compresed
if(!an.an_bCompresed) {
AnimRot *arFirst = &be.be_arRot[0];
INDEX ctfn = be.be_arRot.Count();
// find index of closest frame
iRotFrameIndex = FindFrame((UBYTE*)arFirst,iAnimFrame,ctfn,sizeof(AnimRot));
// get index of next frame
if(bAnimLooping) {
iNextRotFrameIndex = (iRotFrameIndex+1) % be.be_arRot.Count();
} else {
iNextRotFrameIndex = ClampUp(iRotFrameIndex+1L,be.be_arRot.Count() - 1L);
}
iRotFrameNum = be.be_arRot[iRotFrameIndex].ar_iFrameNum;
iNextRotFrameNum = be.be_arRot[iNextRotFrameIndex].ar_iFrameNum;
pqRotCurrent = &be.be_arRot[iRotFrameIndex].ar_qRot;
pqRotNext = &be.be_arRot[iNextRotFrameIndex].ar_qRot;
// animation is not compresed
} else {
AnimRotOpt *aroFirst = &be.be_arRotOpt[0];
INDEX ctfn = be.be_arRotOpt.Count();
iRotFrameIndex = FindFrame((UBYTE*)aroFirst,iAnimFrame,ctfn,sizeof(AnimRotOpt));
// get index of next frame
if(bAnimLooping) {
iNextRotFrameIndex = (iRotFrameIndex+1L) % be.be_arRotOpt.Count();
} else {
iNextRotFrameIndex = ClampUp(iRotFrameIndex+1L,be.be_arRotOpt.Count() - 1L);
}
AnimRotOpt &aroRot = be.be_arRotOpt[iRotFrameIndex];
AnimRotOpt &aroRotNext = be.be_arRotOpt[iNextRotFrameIndex];
iRotFrameNum = aroRot.aro_iFrameNum;
iNextRotFrameNum = aroRotNext.aro_iFrameNum;
FLOAT3D vAxis;
ANGLE aAngle;
// decompress angle
aAngle = aroRot.aro_aAngle / ANG_COMPRESIONMUL;
DecompressAxis(vAxis,aroRot.aro_ubH,aroRot.aro_ubP);
qRotCurrent.FromAxisAngle(vAxis,aAngle);
aAngle = aroRotNext.aro_aAngle / ANG_COMPRESIONMUL;
DecompressAxis(vAxis,aroRotNext.aro_ubH,aroRotNext.aro_ubP);
qRotNext.FromAxisAngle(vAxis,aAngle);
pqRotCurrent = &qRotCurrent;
pqRotNext = &qRotNext;
}
if(iNextRotFrameNum<=iRotFrameNum) {
// calculate slerp factor for rotations
fSlerpFactor = (f-iRotFrameNum) / (an.an_iFrames-iRotFrameNum);
} else {
// calculate slerp factor for rotations
fSlerpFactor = (f-iRotFrameNum) / (iNextRotFrameNum-iRotFrameNum);
}
// calculate rotation for bone beetwen current and next frame in animation
qRot = Slerp(fSlerpFactor,*pqRotCurrent,*pqRotNext);
// and currently playing animation
rb.rb_arRot.ar_qRot = Slerp(fFadeFactor*pa.pa_Strength,rb.rb_arRot.ar_qRot,qRot);
AnimPos *apFirst = &be.be_apPos[0];
INDEX ctfn = be.be_apPos.Count();
INDEX iPosFrameIndex = FindFrame((UBYTE*)apFirst,iAnimFrame,ctfn,sizeof(AnimPos));
INDEX iNextPosFrameIndex;
// is animation looping
if(bAnimLooping) {
iNextPosFrameIndex = (iPosFrameIndex+1) % be.be_apPos.Count();
} else {
iNextPosFrameIndex = ClampUp(iPosFrameIndex+1L,be.be_apPos.Count()-1L);
}
INDEX iPosFrameNum = be.be_apPos[iPosFrameIndex].ap_iFrameNum;
INDEX iNextPosFrameNum = be.be_apPos[iNextPosFrameIndex].ap_iFrameNum;
FLOAT fLerpFactor;
if(iNextPosFrameNum<=iPosFrameNum) fLerpFactor = (f-iPosFrameNum) / (an.an_iFrames-iPosFrameNum);
else fLerpFactor = (f-iPosFrameNum) / (iNextPosFrameNum-iPosFrameNum);
FLOAT3D vPos;
FLOAT3D vBonePosCurrent = be.be_apPos[iPosFrameIndex].ap_vPos;
FLOAT3D vBonePosNext = be.be_apPos[iNextPosFrameIndex].ap_vPos;
// if bone envelope and bone have some length
if((be.be_OffSetLen > 0) && (rb.rb_psbBone->sb_fOffSetLen > 0)) {
// size bone to fit bone envelope
vBonePosCurrent *= (rb.rb_psbBone->sb_fOffSetLen / be.be_OffSetLen);
vBonePosNext *= (rb.rb_psbBone->sb_fOffSetLen / be.be_OffSetLen);
}
// calculate position for bone beetwen current and next frame in animation
vPos = Lerp(vBonePosCurrent,vBonePosNext,fLerpFactor);
// and currently playing animation
rb.rb_apPos.ap_vPos = Lerp(rb.rb_apPos.ap_vPos,vPos,fFadeFactor * pa.pa_Strength);
}
}
// for each morphmap
for(INDEX im=0;im<an.an_ameMorphs.Count();im++) {
INDEX iMorphIndex;
// find it in renmorph
if(FindRenMorph(rm,an.an_ameMorphs[im].me_iMorphMapID,&iMorphIndex)) {
// lerp morphs
FLOAT &fCurFactor = an.an_ameMorphs[im].me_aFactors[iAnimFrame];
FLOAT &fLastFactor = an.an_ameMorphs[im].me_aFactors[iNextAnimFrame];
FLOAT fFactor = Lerp(fCurFactor,fLastFactor,f-iAnimFrame);
_aRenMorph[iMorphIndex].rmp_fFactor = Lerp(_aRenMorph[iMorphIndex].rmp_fFactor,
fFactor,
fFadeFactor * pa.pa_Strength);
}
}
}
}
}
}
// array of pointers to texure data for shader
static CStaticStackArray<class CTextureObject*> _patoTextures;
static CStaticStackArray<struct GFXTexCoord*> _paTexCoords;
// draw mesh on screen
static void RenderMesh(RenMesh &rmsh,RenModel &rm)
{
ASSERT(_pavFinalVertices!=NULL);
ASSERT(_panFinalNormals!=NULL);
MeshLOD &mlod = rmsh.rmsh_pMeshInst->mi_pMesh->msh_aMeshLODs[rmsh.rmsh_iMeshLODIndex];
// Count surfaces in mesh
INDEX ctsrf = mlod.mlod_aSurfaces.Count();
// for each surface
for(INDEX isrf=0; isrf<ctsrf; isrf++)
{
MeshSurface &msrf = mlod.mlod_aSurfaces[isrf];
CShader *pShader = msrf.msrf_pShader;
if( _iRenderingType==2) pShader = &_shMaskShader; // force mask shader for rendering to shadowmaps
// if this surface has valid shader and show texure flag is set
if( pShader!=NULL && (RM_GetFlags() & RMF_SHOWTEXTURE))
{
// Create copy of shading params
ShaderParams *pShaderParams = &msrf.msrf_ShadingParams;
ShaderParams spForAdjustment;
// if callback function was specified
if(_pAdjustShaderParams!=NULL) {
// Call callback function
spForAdjustment = msrf.msrf_ShadingParams;
_pAdjustShaderParams( _pAdjustShaderData, msrf.msrf_iSurfaceID, pShader, spForAdjustment);
pShaderParams = &spForAdjustment;
}
// clamp surface texture count to max number of textrues in mesh
INDEX cttx = pShaderParams->sp_aiTextureIDs.Count();
INDEX cttxMax = rmsh.rmsh_pMeshInst->mi_tiTextures.Count();
// cttx = ClampUp(cttx,cttxMax);
_patoTextures.PopAll();
if(cttx>0)_patoTextures.Push(cttx);
// for each texture ID
for(INDEX itx=0;itx<cttx;itx++) {
// find texture in mesh and get pointer to texture by texture ID
FindTextureData( &_patoTextures[itx], pShaderParams->sp_aiTextureIDs[itx], *rmsh.rmsh_pMeshInst);
}
// count uvmaps
INDEX ctuvm = pShaderParams->sp_aiTexCoordsIndex.Count();
// ctuvm = ClampUp(ctuvm,mlod.mlod_aUVMaps.Count());
_paTexCoords.PopAll();
if(ctuvm>0)_paTexCoords.Push(ctuvm);
// for each uvamp
for( INDEX iuvm=0; iuvm<ctuvm; iuvm++) {
// set pointer of uvmap in array of uvmaps for shader
INDEX iuvmIndex = pShaderParams->sp_aiTexCoordsIndex[iuvm];
// if mesh lod has this uv map
if(iuvmIndex<mlod.mlod_aUVMaps.Count()) {
_paTexCoords[iuvm] = (GFXTexCoord*)&mlod.mlod_aUVMaps[iuvmIndex].muv_aTexCoords[msrf.msrf_iFirstVertex];
} else {
_paTexCoords[iuvm] = NULL;
}
}
INDEX ctTextures = _patoTextures.Count();
INDEX ctTexCoords = _paTexCoords.Count();
INDEX ctColors = pShaderParams->sp_acolColors.Count();
INDEX ctFloats = pShaderParams->sp_afFloats.Count();
// begin model rendering
const BOOL bModelSetupTimer = _sfStats.CheckTimer(CStatForm::STI_MODELSETUP);
if( bModelSetupTimer) _sfStats.StopTimer(CStatForm::STI_MODELSETUP);
_sfStats.StartTimer(CStatForm::STI_MODELRENDERING);
shaBegin( _aprProjection, pShader);
shaSetVertexArray((GFXVertex4*)&_pavFinalVertices[msrf.msrf_iFirstVertex],msrf.msrf_ctVertices);
shaSetNormalArray((GFXNormal*)&_panFinalNormals[msrf.msrf_iFirstVertex]);
shaSetIndices(&msrf.msrf_aTriangles[0].iVertex[0],msrf.msrf_aTriangles.Count()*3);
shaSetFlags(msrf.msrf_ShadingParams.sp_ulFlags);
// if mesh is transformed to view space
if(rmsh.rmsh_bTransToViewSpace) {
#pragma message(">> FIX THIS !!!")
// no ObjToView matrix is needed in shader so set empty matrix
Matrix12 mIdentity;
MakeIdentityMatrix(mIdentity);
shaSetObjToViewMatrix(mIdentity);
Matrix12 mInvObjToAbs;
MatrixTranspose(mInvObjToAbs,_mAbsToViewer);
shaSetObjToAbsMatrix(mInvObjToAbs);
} else {
// give shader current ObjToView matrix
shaSetObjToViewMatrix(_mObjToView);
shaSetObjToAbsMatrix(_mObjectToAbs);
}
// Set light parametars
shaSetLightColor(_colAmbient,_colLight);
shaSetLightDirection(_vLightDirInView);
// Set model color
shaSetModelColor(rm.rm_pmiModel->mi_colModelColor);
if(ctTextures>0) shaSetTextureArray(&_patoTextures[0],ctTextures);
if(ctTexCoords>0) shaSetUVMapsArray(&_paTexCoords[0],ctTexCoords);
if(ctColors>0) shaSetColorArray(&pShaderParams->sp_acolColors[0],ctColors);
if(ctFloats>0) shaSetFloatArray(&pShaderParams->sp_afFloats[0],ctFloats);
shaEnd();
_sfStats.StopTimer(CStatForm::STI_MODELRENDERING);
if( bModelSetupTimer) _sfStats.StartTimer(CStatForm::STI_MODELSETUP);
}
// surface has no shader or textures are turned off
else {
COLOR colErrColor = 0xCDCDCDFF;
// surface has no shader, just show vertices using custom simple shader
shaSetVertexArray((GFXVertex4*)&_pavFinalVertices[msrf.msrf_iFirstVertex],msrf.msrf_ctVertices);
shaSetNormalArray((GFXNormal*)&_panFinalNormals[msrf.msrf_iFirstVertex]);
shaSetIndices(&msrf.msrf_aTriangles[0].iVertex[0],msrf.msrf_aTriangles.Count()*3);
shaSetTexture(-1);
shaSetColorArray(&colErrColor,1);
shaSetLightColor(_colAmbient,_colLight);
shaSetLightDirection(_vLightDirInView);
shaSetModelColor(rm.rm_pmiModel->mi_colModelColor);
shaDisableBlend();
shaEnableDepthTest();
shaEnableDepthWrite();
shaSetColor(0);
shaCalculateLight();
shaRender();
shaClean();
}
}
}
// Prepare ren mesh for rendering
static void PrepareMeshForRendering(RenMesh &rmsh, INDEX iSkeletonlod)
{
// set curent mesh lod
MeshLOD &mlod = rmsh.rmsh_pMeshInst->mi_pMesh->msh_aMeshLODs[rmsh.rmsh_iMeshLODIndex];
// clear vertices array
_aMorphedVtxs.PopAll();
_aMorphedNormals.PopAll();
_aFinalVtxs.PopAll();
_aFinalNormals.PopAll();
_pavFinalVertices = NULL;
_panFinalNormals = NULL;
// Reset light direction
_vLightDirInView = _vLightDir;
// Get vertices count
INDEX ctVertices = mlod.mlod_aVertices.Count();
// Allocate memory for vertices
_aMorphedVtxs.Push(ctVertices);
_aMorphedNormals.Push(ctVertices);
_aFinalVtxs.Push(ctVertices);
_aFinalNormals.Push(ctVertices);
// Remember final vertex count
_ctFinalVertices = ctVertices;
// Copy original vertices and normals to _aMorphedVtxs
memcpy(&_aMorphedVtxs[0],&mlod.mlod_aVertices[0],sizeof(mlod.mlod_aVertices[0]) * ctVertices);
memcpy(&_aMorphedNormals[0],&mlod.mlod_aNormals[0],sizeof(mlod.mlod_aNormals[0]) * ctVertices);
// Set final vertices and normals to 0
memset(&_aFinalVtxs[0],0,sizeof(_aFinalVtxs[0])*ctVertices);
memset(&_aFinalNormals[0],0,sizeof(_aFinalNormals[0])*ctVertices);
INDEX ctmm = rmsh.rmsh_iFirstMorph + rmsh.rmsh_ctMorphs;
// blend vertices and normals for each RenMorph
for(int irm=rmsh.rmsh_iFirstMorph;irm<ctmm;irm++)
{
RenMorph &rm = _aRenMorph[irm];
// blend only if factor is > 0
if(rm.rmp_fFactor > 0.0f) {
// for each vertex and normal in morphmap
for(int ivx=0;ivx<rm.rmp_pmmmMorphMap->mmp_aMorphMap.Count();ivx++) {
// blend vertices and normals
if(rm.rmp_pmmmMorphMap->mmp_bRelative) {
// blend relative (new = cur + f*(dst-src))
INDEX vtx = rm.rmp_pmmmMorphMap->mmp_aMorphMap[ivx].mwm_iVxIndex;
MeshVertex &mvSrc = mlod.mlod_aVertices[vtx];
MeshNormal &mnSrc = mlod.mlod_aNormals[vtx];
MeshVertexMorph &mvmDst = rm.rmp_pmmmMorphMap->mmp_aMorphMap[ivx];
// blend vertices
_aMorphedVtxs[vtx].x += rm.rmp_fFactor*(mvmDst.mwm_x - mvSrc.x);
_aMorphedVtxs[vtx].y += rm.rmp_fFactor*(mvmDst.mwm_y - mvSrc.y);
_aMorphedVtxs[vtx].z += rm.rmp_fFactor*(mvmDst.mwm_z - mvSrc.z);
// blend normals
_aMorphedNormals[vtx].nx += rm.rmp_fFactor*(mvmDst.mwm_nx - mnSrc.nx);
_aMorphedNormals[vtx].ny += rm.rmp_fFactor*(mvmDst.mwm_ny - mnSrc.ny);
_aMorphedNormals[vtx].nz += rm.rmp_fFactor*(mvmDst.mwm_nz - mnSrc.nz);
} else {
// blend absolute (1-f)*cur + f*dst
INDEX vtx = rm.rmp_pmmmMorphMap->mmp_aMorphMap[ivx].mwm_iVxIndex;
MeshVertex &mvSrc = mlod.mlod_aVertices[vtx];
MeshVertexMorph &mvmDst = rm.rmp_pmmmMorphMap->mmp_aMorphMap[ivx];
// blend vertices
_aMorphedVtxs[vtx].x = (1.0f-rm.rmp_fFactor) * _aMorphedVtxs[vtx].x + rm.rmp_fFactor*mvmDst.mwm_x;
_aMorphedVtxs[vtx].y = (1.0f-rm.rmp_fFactor) * _aMorphedVtxs[vtx].y + rm.rmp_fFactor*mvmDst.mwm_y;
_aMorphedVtxs[vtx].z = (1.0f-rm.rmp_fFactor) * _aMorphedVtxs[vtx].z + rm.rmp_fFactor*mvmDst.mwm_z;
// blend normals
_aMorphedNormals[vtx].nx = (1.0f-rm.rmp_fFactor) * _aMorphedNormals[vtx].nx + rm.rmp_fFactor*mvmDst.mwm_nx;
_aMorphedNormals[vtx].ny = (1.0f-rm.rmp_fFactor) * _aMorphedNormals[vtx].ny + rm.rmp_fFactor*mvmDst.mwm_ny;
_aMorphedNormals[vtx].nz = (1.0f-rm.rmp_fFactor) * _aMorphedNormals[vtx].nz + rm.rmp_fFactor*mvmDst.mwm_nz;
}
}
}
}
INDEX ctrw = rmsh.rmsh_iFirstWeight + rmsh.rmsh_ctWeights;
INDEX ctbones = 0;
CSkeleton *pskl = _aRenModels[rmsh.rmsh_iRenModelIndex].rm_pmiModel->mi_psklSkeleton;
// if skeleton for this model exists and its currently visible
if((pskl!=NULL) && (iSkeletonlod > -1)) {
// count bones in skeleton
ctbones = pskl->skl_aSkeletonLODs[iSkeletonlod].slod_aBones.Count();
}
// if there is skeleton attached to this mesh transfrom all vertices
if(ctbones > 0 && ctrw>0) {
// for each renweight
for(int irw=rmsh.rmsh_iFirstWeight; irw<ctrw; irw++) {
RenWeight &rw = _aRenWeights[irw];
Matrix12 mTransform;
Matrix12 mStrTransform;
// if no bone for this weight
if(rw.rw_iBoneIndex == (-1)) {
// transform vertex using default model transform matrix (for boneless models)
MatrixCopy(mStrTransform, _aRenModels[rmsh.rmsh_iRenModelIndex].rm_mStrTransform);
MatrixCopy(mTransform, _aRenModels[rmsh.rmsh_iRenModelIndex].rm_mTransform);
} else {
// use bone transform matrix
MatrixCopy(mStrTransform, _aRenBones[rw.rw_iBoneIndex].rb_mStrTransform);
MatrixCopy(mTransform, _aRenBones[rw.rw_iBoneIndex].rb_mTransform);
}
// if this is front face mesh remove rotation from transfrom matrix
if(mlod.mlod_ulFlags & ML_FULL_FACE_FORWARD) {
RemoveRotationFromMatrix(mStrTransform);
}
// for each vertex in this weight
INDEX ctvw = rw.rw_pwmWeightMap->mwm_aVertexWeight.Count();
for(int ivw=0; ivw<ctvw; ivw++) {
MeshVertexWeight &vw = rw.rw_pwmWeightMap->mwm_aVertexWeight[ivw];
INDEX ivx = vw.mww_iVertex;
MeshVertex mv = _aMorphedVtxs[ivx];
MeshNormal mn = _aMorphedNormals[ivx];
// transform vertex and normal with this weight transform matrix
TransformVector((FLOAT3&)mv,mStrTransform);
RotateVector((FLOAT3&)mn,mTransform); // Don't stretch normals
// Add new values to final vertices
_aFinalVtxs[ivx].x += mv.x * vw.mww_fWeight;
_aFinalVtxs[ivx].y += mv.y * vw.mww_fWeight;
_aFinalVtxs[ivx].z += mv.z * vw.mww_fWeight;
_aFinalNormals[ivx].nx += mn.nx * vw.mww_fWeight;
_aFinalNormals[ivx].ny += mn.ny * vw.mww_fWeight;
_aFinalNormals[ivx].nz += mn.nz * vw.mww_fWeight;
}
}
_pavFinalVertices = &_aFinalVtxs[0];
_panFinalNormals = &_aFinalNormals[0];
// mesh is in view space so transform light to view space
RotateVector(_vLightDirInView.vector,_mObjToView);
// set flag that mesh is in view space
rmsh.rmsh_bTransToViewSpace = TRUE;
// reset view matrix bacause model is allready transformed in view space
gfxSetViewMatrix(NULL);
// if no skeleton
} else {
// if flag is set to transform all vertices to view space
if(_bTransformBonelessModelToViewSpace) {
// transform every vertex using default model transform matrix (for boneless models)
Matrix12 mTransform;
Matrix12 mStrTransform;
MatrixCopy(mTransform, _aRenModels[rmsh.rmsh_iRenModelIndex].rm_mTransform);
MatrixCopy(mStrTransform, _aRenModels[rmsh.rmsh_iRenModelIndex].rm_mStrTransform);
// if this is front face mesh remove rotation from transfrom matrix
if(mlod.mlod_ulFlags & ML_FULL_FACE_FORWARD) {
RemoveRotationFromMatrix(mStrTransform);
}
// for each vertex
for(int ivx=0;ivx<ctVertices;ivx++) {
MeshVertex &mv = _aMorphedVtxs[ivx];
MeshNormal &mn = _aMorphedNormals[ivx];
// Transform vertex
TransformVector((FLOAT3&)mv,mStrTransform);
// Rotate normal
RotateVector((FLOAT3&)mn,mTransform);
_aFinalVtxs[ivx].x = mv.x;
_aFinalVtxs[ivx].y = mv.y;
_aFinalVtxs[ivx].z = mv.z;
_aFinalNormals[ivx].nx = mn.nx;
_aFinalNormals[ivx].ny = mn.ny;
_aFinalNormals[ivx].nz = mn.nz;
}
_pavFinalVertices = &_aFinalVtxs[0];
_panFinalNormals = &_aFinalNormals[0];
// mesh is in view space so transform light to view space
RotateVector(_vLightDirInView.vector,_mObjToView);
// set flag that mesh is in view space
rmsh.rmsh_bTransToViewSpace = TRUE;
// reset view matrix bacause model is allready transformed in view space
gfxSetViewMatrix(NULL);
// leave vertices in obj space
} else {
Matrix12 &m12 = _aRenModels[rmsh.rmsh_iRenModelIndex].rm_mStrTransform;
FLOAT gfxm[16];
#pragma message(">> Fix face forward meshes, when objects are left in object space")
// set view matrix to gfx
gfxm[ 0] = m12[ 0]; gfxm[ 1] = m12[ 4]; gfxm[ 2] = m12[ 8]; gfxm[ 3] = 0;
gfxm[ 4] = m12[ 1]; gfxm[ 5] = m12[ 5]; gfxm[ 6] = m12[ 9]; gfxm[ 7] = 0;
gfxm[ 8] = m12[ 2]; gfxm[ 9] = m12[ 6]; gfxm[10] = m12[10]; gfxm[11] = 0;
gfxm[12] = m12[ 3]; gfxm[13] = m12[ 7]; gfxm[14] = m12[11]; gfxm[15] = 1;
gfxSetViewMatrix(gfxm);
RenModel &rm = _aRenModels[rmsh.rmsh_iRenModelIndex];
RenBone &rb = _aRenBones[rm.rm_iParentBoneIndex];
RotateVector(_vLightDirInView.vector,rb.rb_mBonePlacement);
_pavFinalVertices = &mlod.mlod_aVertices[0];
_panFinalNormals = &mlod.mlod_aNormals[0];
// mark this mesh as in object space
rmsh.rmsh_bTransToViewSpace = FALSE;
}
}
}
// render one ren model
static void RenderModel_View(RenModel &rm)
{
ASSERT( _iRenderingType==1);
const BOOL bShowNormals = RM_GetFlags() & RMF_SHOWNORMALS;
// for each mesh in renmodel
INDEX ctmsh = rm.rm_iFirstMesh + rm.rm_ctMeshes;
for( int imsh=rm.rm_iFirstMesh;imsh<ctmsh;imsh++) {
RenMesh &rmsh = _aRenMesh[imsh];
// prepare mesh for rendering
PrepareMeshForRendering(rmsh,rm.rm_iSkeletonLODIndex);
// render mesh
RenderMesh(rmsh,rm);
// show normals in required
if( bShowNormals) RenderNormals();
}
}
// render one ren model to shadowmap
static void RenderModel_Mask(RenModel &rm)
{
ASSERT( _iRenderingType==2);
// flag to transform all vertices in view space
const BOOL bTemp = _bTransformBonelessModelToViewSpace;
_bTransformBonelessModelToViewSpace = TRUE;
RM_SetCurrentDistance(0);
INDEX ctmsh = rm.rm_iFirstMesh + rm.rm_ctMeshes;
// for each mesh in renmodel
for(int imsh=rm.rm_iFirstMesh;imsh<ctmsh;imsh++) {
// render mesh
RenMesh &rmsh = _aRenMesh[imsh];
PrepareMeshForRendering(rmsh,rm.rm_iSkeletonLODIndex);
RenderMesh(rmsh,rm);
}
// done
_bTransformBonelessModelToViewSpace = bTemp;
}
// Get bone abs position
BOOL RM_GetRenBoneAbs(CModelInstance &mi,INDEX iBoneID,RenBone &rb)
{
// do not transform to view space
MakeIdentityMatrix(_mAbsToViewer);
CalculateRenderingData(mi);
INDEX ctrb = _aRenBones.Count();
// for each render bone after dummy one
for(INDEX irb=1;irb<ctrb;irb++) {
RenBone &rbone = _aRenBones[irb];
// check if this is serched bone
if(rbone.rb_psbBone->sb_iID == iBoneID) {
rb = rbone;
ClearRenArrays();
return TRUE;
}
}
// Clear ren arrays
ClearRenArrays();
return FALSE;
}
// Returns true if bone exists and sets two given vectors as start and end point of specified bone
BOOL RM_GetBoneAbsPosition(CModelInstance &mi,INDEX iBoneID, FLOAT3D &vStartPoint, FLOAT3D &vEndPoint)
{
// do not transform to view space
MakeIdentityMatrix(_mAbsToViewer);
// use higher lod for bone finding
RM_SetCurrentDistance(0);
CalculateRenderingData(mi);
INDEX ctrb = _aRenBones.Count();
// for each render bone after dummy one
for(INDEX irb=1;irb<ctrb;irb++) {
RenBone &rb = _aRenBones[irb];
// check if this is serched bone
if(rb.rb_psbBone->sb_iID == iBoneID) {
vStartPoint = FLOAT3D(0,0,0);
vEndPoint = FLOAT3D(0,0,rb.rb_psbBone->sb_fBoneLength);
TransformVector(vStartPoint.vector,rb.rb_mBonePlacement);
TransformVector(vEndPoint.vector,rb.rb_mBonePlacement);
ClearRenArrays();
return TRUE;
}
}
// Clear ren arrays
ClearRenArrays();
return FALSE;
}
// Calculate complete rendering data for model instance
static void CalculateRenderingData(CModelInstance &mi)
{
RM_SetObjectMatrices(mi);
// distance to model is z param in objtoview matrix
_fDistanceFactor = -_mObjToView[11];
// create first dummy model that serves as parent for the entire hierarchy
MakeRootModel();
// build entire hierarchy with children
BuildHierarchy(&mi, 0);
INDEX ctrm = _aRenModels.Count();
// for each renmodel
for(int irm=1;irm<ctrm;irm++) {
// match model animations
MatchAnims(_aRenModels[irm]);
}
// Calculate transformations for all bones on already built hierarchy
CalculateBoneTransforms();
}
// Render one SKA model with its children
void RM_RenderSKA(CModelInstance &mi)
{
// Calculate all rendering data for this model instance
//if( _iRenderingType==2) CalculateRenderingData( mi, 0);
//else
CalculateRenderingData(mi);
// for each renmodel
INDEX ctrmsh = _aRenModels.Count();
for(int irmsh=1;irmsh<ctrmsh;irmsh++) {
RenModel &rm = _aRenModels[irmsh];
// set object matrices
RM_SetObjectMatrices(*rm.rm_pmiModel);
// render this model
if( _iRenderingType==1) RenderModel_View(rm);
else RenderModel_Mask(rm);
}
// done if cluster shadows were rendered
if( _iRenderingType==2) {
// reset arrays
ClearRenArrays();
return;
}
// no cluster shadows - see if anything else needs to be rendered
ASSERT( _iRenderingType==1);
// if render wireframe is requested
if(RM_GetFlags() & RMF_WIREFRAME) {
gfxDisableTexture();
// set polygon offset
gfxPolygonMode(GFX_LINE);
gfxEnableDepthBias();
// for each ren model
INDEX ctrmsh = _aRenModels.Count();
for(int irmsh=1;irmsh<ctrmsh;irmsh++)
{
RenModel &rm = _aRenModels[irmsh];
// render renmodel in wireframe
RenderModelWireframe(rm);
}
// restore polygon offset
gfxDisableDepthBias();
gfxPolygonMode(GFX_FILL);
}
extern INDEX ska_bShowColision;
extern INDEX ska_bShowSkeleton;
// show skeleton
if(ska_bShowSkeleton || RM_GetFlags() & RMF_SHOWSKELETON) {
gfxDisableTexture();
gfxDisableDepthTest();
// render skeleton
RenderSkeleton();
gfxEnableDepthTest();
}
#pragma message(">> Add ska_bShowActiveBones")
if(/*ska_bShowActiveBones || */ RM_GetFlags() & RMF_SHOWACTIVEBONES) {
gfxDisableTexture();
gfxDisableDepthTest();
// render only active bones
RenderActiveBones();
gfxEnableDepthTest();
}
// show root model instance colision box
if(ska_bShowColision) {
RM_SetObjectMatrices(mi);
if (mi.mi_cbAABox.Count()>0)
{
ColisionBox &cb = mi.GetCurrentColisionBox();
RM_RenderColisionBox(mi,cb,C_mlGREEN);
}
}
// reset arrays
ClearRenArrays();
}
// clear all ren arrays
static void ClearRenArrays()
{
_pAdjustBonesCallback = NULL;
_pAdjustBonesData = NULL;
_pAdjustShaderParams = NULL;
_pAdjustShaderData = NULL;
// clear all arrays
_aRenModels.PopAll();
_aRenBones.PopAll();
_aRenMesh.PopAll();
_aRenWeights.PopAll();
_aRenMorph.PopAll();
_fCustomMlodDistance = -1;
_fCustomSlodDistance = -1;
}