mirror of
https://github.com/ptitSeb/Serious-Engine
synced 2024-12-25 15:14:51 +01:00
2605 lines
91 KiB
C++
2605 lines
91 KiB
C++
/* Copyright (c) 2002-2012 Croteam Ltd.
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of version 2 of the GNU General Public License as published by
|
|
the Free Software Foundation
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License along
|
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
|
|
|
|
#include "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.s = (fD+_fFogAddZ) * _fog_fMulZ;
|
|
// tex.s = (vtx.z) * _fog_fMulZ;
|
|
tex.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.t)) return TRUE;
|
|
vtx.x=vMin(1); vtx.y=vMin(2); vtx.z=vMax(3); GetFogMapInVertex(vtx,tex); if(InFog(tex.t)) return TRUE;
|
|
vtx.x=vMin(1); vtx.y=vMax(2); vtx.z=vMin(3); GetFogMapInVertex(vtx,tex); if(InFog(tex.t)) return TRUE;
|
|
vtx.x=vMin(1); vtx.y=vMax(2); vtx.z=vMax(3); GetFogMapInVertex(vtx,tex); if(InFog(tex.t)) return TRUE;
|
|
vtx.x=vMax(1); vtx.y=vMin(2); vtx.z=vMin(3); GetFogMapInVertex(vtx,tex); if(InFog(tex.t)) return TRUE;
|
|
vtx.x=vMax(1); vtx.y=vMin(2); vtx.z=vMax(3); GetFogMapInVertex(vtx,tex); if(InFog(tex.t)) return TRUE;
|
|
vtx.x=vMax(1); vtx.y=vMax(2); vtx.z=vMin(3); GetFogMapInVertex(vtx,tex); if(InFog(tex.t)) return TRUE;
|
|
vtx.x=vMax(1); vtx.y=vMax(2); vtx.z=vMax(3); GetFogMapInVertex(vtx,tex); if(InFog(tex.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].s);
|
|
_aTexMipHazey[ivtx].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;
|
|
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].s = 0; ptex[0].t = 0;
|
|
ptex[1].s = 0; ptex[1].t = 1;
|
|
ptex[2].s = 1; ptex[2].t = 1;
|
|
ptex[3].s = 1; ptex[3].t = 0;
|
|
// colors
|
|
pcol[0].abgr = ulAAAA;
|
|
pcol[1].abgr = ulAAAA;
|
|
pcol[2].abgr = ulAAAA;
|
|
pcol[3].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.s = -vtx.z *_fog_fMulZ;
|
|
tex.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].u = vVtx(1); tcBoxTex[0].v = 0;
|
|
tcBoxTex[1].u = 0; tcBoxTex[1].v = 0;
|
|
tcBoxTex[2].u = 0; tcBoxTex[2].v = vVtx(3);
|
|
tcBoxTex[3].u = vVtx(1); tcBoxTex[3].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=0;
|
|
for(;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].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].r = 255;
|
|
_aMeshColors[ivx].g = 127;
|
|
_aMeshColors[ivx].b = 0;
|
|
_aMeshColors[ivx].a += vw.mww_fWeight*255; // _aMeshColors[ivx].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=ctal-1;
|
|
for(;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(INT 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=1;
|
|
for(; 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=ctal-1;
|
|
for(;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<FLOAT>(fSlerpFactor,*pqRotCurrent,*pqRotNext);
|
|
// and currently playing animation
|
|
rb.rb_arRot.ar_qRot = Slerp<FLOAT>(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;
|
|
}
|
|
|