mirror of
https://github.com/ptitSeb/Serious-Engine
synced 2024-12-25 15:14:51 +01:00
3321 lines
119 KiB
C++
3321 lines
119 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/Statistics_internal.h>
|
|
#include <Engine/Base/Console.h>
|
|
#include <Engine/Models/ModelObject.h>
|
|
#include <Engine/Models/ModelData.h>
|
|
#include <Engine/Models/ModelProfile.h>
|
|
#include <Engine/Models/RenderModel.h>
|
|
#include <Engine/Models/Model_internal.h>
|
|
#include <Engine/Models/Normals.h>
|
|
#include <Engine/Graphics/GfxLibrary.h>
|
|
#include <Engine/Graphics/Fog_internal.h>
|
|
#include <Engine/Base/Lists.inl>
|
|
#include <Engine/World/WorldEditingProfile.h>
|
|
|
|
#include <Engine/Templates/StaticArray.cpp>
|
|
#include <Engine/Templates/StaticStackArray.cpp>
|
|
|
|
#include <Engine/Models/RenderModel_internal.h>
|
|
|
|
// asm shortcuts
|
|
#define O offset
|
|
#define Q qword ptr
|
|
#define D dword ptr
|
|
#define W word ptr
|
|
#define B byte ptr
|
|
|
|
#define ASMOPT 1
|
|
|
|
|
|
extern BOOL CVA_bModels;
|
|
extern BOOL GFX_bTruform;
|
|
extern BOOL _bMultiPlayer;
|
|
|
|
extern const UBYTE *pubClipByte;
|
|
extern const FLOAT *pfSinTable;
|
|
extern const FLOAT *pfCosTable;
|
|
|
|
static GfxAPIType _eAPI;
|
|
|
|
static BOOL _bForceTranslucency; // force translucency of opaque/transparent surfaces (for model fading)
|
|
static ULONG _ulMipLayerFlags;
|
|
static INDEX _icol=0;
|
|
|
|
|
|
// mip arrays
|
|
static CStaticStackArray<GFXVertex3> _avtxMipBase;
|
|
static CStaticStackArray<GFXTexCoord> _atexMipBase; // for reflection and specular
|
|
static CStaticStackArray<GFXNormal3> _anorMipBase;
|
|
static CStaticStackArray<GFXColor> _acolMipBase;
|
|
|
|
static CStaticStackArray<GFXTexCoord> _atexMipFogy;
|
|
static CStaticStackArray<UBYTE> _ashdMipFogy;
|
|
static CStaticStackArray<FLOAT> _atx1MipHaze;
|
|
static CStaticStackArray<UBYTE> _ashdMipHaze;
|
|
|
|
// surface arrays
|
|
static CStaticStackArray<GFXVertex4> _avtxSrfBase;
|
|
static CStaticStackArray<GFXNormal4> _anorSrfBase; // normals for Truform!
|
|
static CStaticStackArray<GFXTexCoord> _atexSrfBase;
|
|
static CStaticStackArray<GFXColor> _acolSrfBase;
|
|
|
|
// shadows arrays
|
|
static CStaticStackArray<FLOAT> _aooqMipShad;
|
|
static CStaticStackArray<GFXTexCoord4> _atx4SrfShad;
|
|
|
|
|
|
// pointers to arrays for quicker access
|
|
static GFXColor *pcolSrfBase;
|
|
static GFXColor *pcolMipBase;
|
|
static GFXVertex3 *pvtxMipBase;
|
|
static GFXNormal3 *pnorMipBase;
|
|
static GFXTexCoord *ptexMipBase;
|
|
static GFXTexCoord *ptexMipFogy;
|
|
static UBYTE *pshdMipFogy;
|
|
static FLOAT *ptx1MipHaze;
|
|
static UBYTE *pshdMipHaze;
|
|
static FLOAT *pooqMipShad;
|
|
static UWORD *puwSrfToMip;
|
|
|
|
// misc
|
|
static ULONG _ulColorMask = 0;
|
|
static INDEX _ctAllMipVx = 0;
|
|
static INDEX _ctAllSrfVx = 0;
|
|
static BOOL _bFlatFill = FALSE;
|
|
static SLONG _slLR=0, _slLG=0, _slLB=0;
|
|
static SLONG _slAR=0, _slAG=0, _slAB=0;
|
|
static const __int64 mmRounder = 0x007F007F007F007F;
|
|
static const __int64 mmF000 = 0x00FF000000000000;
|
|
|
|
// viewer absolute and object space projection
|
|
static FLOAT3D _vViewer;
|
|
static FLOAT3D _vViewerObj;
|
|
static FLOAT3D _vLightObj;
|
|
|
|
// some constants for asm float ops
|
|
static const FLOAT f2 = 2.0f;
|
|
static const FLOAT f05 = 0.5f;
|
|
|
|
|
|
// convinient routine for timing of texture setting
|
|
static __forceinline void SetCurrentTexture( CTextureData *ptd, INDEX iFrame)
|
|
{
|
|
_pfModelProfile.StartTimer( CModelProfile::PTI_VIEW_SETTEXTURE);
|
|
_pfModelProfile.IncrementTimerAveragingCounter( CModelProfile::PTI_VIEW_SETTEXTURE);
|
|
if( ptd==NULL || _bFlatFill) gfxDisableTexture();
|
|
else ptd->SetAsCurrent(iFrame);
|
|
_pfModelProfile.StopTimer( CModelProfile::PTI_VIEW_SETTEXTURE);
|
|
}
|
|
|
|
|
|
// reset model vertex buffers
|
|
static void ResetVertexArrays(void)
|
|
{
|
|
_avtxMipBase.PopAll();
|
|
_atexMipBase.PopAll();
|
|
_acolMipBase.PopAll();
|
|
_anorMipBase.PopAll();
|
|
|
|
_avtxSrfBase.PopAll();
|
|
_anorSrfBase.PopAll();
|
|
_atexSrfBase.PopAll();
|
|
_acolSrfBase.PopAll();
|
|
|
|
_atexMipFogy.PopAll();
|
|
_ashdMipFogy.PopAll();
|
|
_atx1MipHaze.PopAll();
|
|
_ashdMipHaze.PopAll();
|
|
}
|
|
|
|
|
|
// reset and free all arrays
|
|
extern void Models_ClearVertexArrays(void)
|
|
{
|
|
_avtxMipBase.Clear();
|
|
_atexMipBase.Clear();
|
|
_acolMipBase.Clear();
|
|
_anorMipBase.Clear();
|
|
|
|
_atexMipFogy.Clear();
|
|
_ashdMipFogy.Clear();
|
|
_atx1MipHaze.Clear();
|
|
_ashdMipHaze.Clear();
|
|
|
|
_avtxSrfBase.Clear();
|
|
_anorSrfBase.Clear();
|
|
_atexSrfBase.Clear();
|
|
_acolSrfBase.Clear();
|
|
|
|
_aooqMipShad.Clear();
|
|
_atx4SrfShad.Clear();
|
|
}
|
|
|
|
|
|
struct Triangle {
|
|
INDEX i0;
|
|
INDEX i1;
|
|
INDEX i2;
|
|
BOOL bDone;
|
|
Triangle(void) : bDone(FALSE) {};
|
|
void Clear(void) { bDone = FALSE; };
|
|
void Rotate(void) { const INDEX i=i0;
|
|
i0=i1; i1=i2; i2=i; }
|
|
};
|
|
|
|
static INDEX _ctSurfaces = 0;
|
|
static INDEX _ctTriangles = 0;
|
|
static INDEX _ctStrips = 0;
|
|
static INDEX _ctMaxStripLen = 0;
|
|
static INDEX _ctMinStripLen = 10000;
|
|
static FLOAT _fTriPerStrip = 0.0f;
|
|
static FLOAT _fTriPerSurface = 0.0f;
|
|
static FLOAT _fStripPerSurface = 0.0f;
|
|
static INDEX _ctStripStartFailed = 0;
|
|
|
|
|
|
static CStaticArray<Triangle> _atri;
|
|
static CStaticArray<Triangle> _atriDone;
|
|
static void GetNeighbourTriangleVertices(Triangle *ptri, INDEX &i1, INDEX &i2)
|
|
{
|
|
Triangle &triA = *ptri;
|
|
for( INDEX itri=0; itri<_atri.Count(); itri++) {
|
|
Triangle &triB = _atri[itri];
|
|
if( triB.bDone) continue;
|
|
i1=triA.i1; i2=triA.i2;
|
|
if (i2 == triB.i0 && i1 == triB.i1) return;
|
|
if (i2 == triB.i1 && i1 == triB.i2) return;
|
|
if (i2 == triB.i2 && i1 == triB.i0) return;
|
|
triA.Rotate();
|
|
i1=triA.i1; i2=triA.i2;
|
|
if (i2 == triB.i0 && i1 == triB.i1) return;
|
|
if (i2 == triB.i1 && i1 == triB.i2) return;
|
|
if (i2 == triB.i2 && i1 == triB.i0) return;
|
|
triA.Rotate();
|
|
i1=triA.i1; i2=triA.i2;
|
|
if (i2 == triB.i0 && i1 == triB.i1) return;
|
|
if (i2 == triB.i1 && i1 == triB.i2) return;
|
|
if (i2 == triB.i2 && i1 == triB.i0) return;
|
|
triA.Rotate();
|
|
}
|
|
// none found
|
|
i1 = -1;
|
|
i2 = -1;
|
|
_ctStripStartFailed++;
|
|
}
|
|
|
|
|
|
static Triangle *GetNextStripTriangle(INDEX &i1, INDEX &i2, INDEX iTriInStrip)
|
|
{
|
|
for( INDEX itri=0; itri<_atri.Count(); itri++) {
|
|
Triangle &tri = _atri[itri];
|
|
if( tri.bDone) continue;
|
|
if( iTriInStrip%2 == 0) {
|
|
if (i1==tri.i0 && i2==tri.i1) { i1=i2; i2=tri.i2; return &tri;} tri.Rotate();
|
|
if (i1==tri.i0 && i2==tri.i1) { i1=i2; i2=tri.i2; return &tri;} tri.Rotate();
|
|
if (i1==tri.i0 && i2==tri.i1) { i1=i2; i2=tri.i2; return &tri;} tri.Rotate();
|
|
} else {
|
|
if (i2==tri.i0 && i1==tri.i1) { i1=i2; i2=tri.i2; return &tri;} tri.Rotate();
|
|
if (i2==tri.i0 && i1==tri.i1) { i1=i2; i2=tri.i2; return &tri;} tri.Rotate();
|
|
if (i2==tri.i0 && i1==tri.i1) { i1=i2; i2=tri.i2; return &tri;} tri.Rotate();
|
|
}
|
|
}
|
|
i1 = -1;
|
|
i2 = -1;
|
|
return NULL;
|
|
}
|
|
|
|
static Triangle *GetFirstTriangle(void)
|
|
{
|
|
for( INDEX itri=0; itri<_atri.Count(); itri++) {
|
|
Triangle &tri1 = _atri[itri];
|
|
if( tri1.bDone) continue;
|
|
return &tri1;
|
|
}
|
|
ASSERTALWAYS( "WTF?");
|
|
return &_atri[0];
|
|
}
|
|
|
|
|
|
static void PrepareSurfaceElements( ModelMipInfo &mmi, MappingSurface &ms)
|
|
{
|
|
_ctSurfaces++;
|
|
// find total number of triangles
|
|
INDEX ctTriangles = 0;
|
|
{for( INDEX iipo=0; iipo<ms.ms_aiPolygons.Count(); iipo++) {
|
|
ModelPolygon &mp = mmi.mmpi_Polygons[ ms.ms_aiPolygons[iipo]];
|
|
ctTriangles += (mp.mp_PolygonVertices.Count()-2);
|
|
}}
|
|
ASSERT( ctTriangles>=ms.ms_aiPolygons.Count());
|
|
|
|
_ctTriangles += ctTriangles;
|
|
// allocate that much triangles
|
|
_atri.Clear();
|
|
_atri.New(ctTriangles);
|
|
_atriDone.Clear();
|
|
_atriDone.New(ctTriangles);
|
|
|
|
// put all triangles there (do tri-fans) -> should do tri-strips ? !!!!
|
|
INDEX iTriangle = 0;
|
|
{for( INDEX iipo=0; iipo<ms.ms_aiPolygons.Count(); iipo++) {
|
|
ModelPolygon &mp = mmi.mmpi_Polygons[ ms.ms_aiPolygons[iipo]];
|
|
{for( INDEX ivx=2; ivx<mp.mp_PolygonVertices.Count(); ivx++) {
|
|
Triangle &tri = _atri[iTriangle++];
|
|
tri.i0 = mp.mp_PolygonVertices[0 ].mpv_ptvTextureVertex->mtv_iSurfaceVx;
|
|
tri.i1 = mp.mp_PolygonVertices[ivx-1].mpv_ptvTextureVertex->mtv_iSurfaceVx;
|
|
tri.i2 = mp.mp_PolygonVertices[ivx-0].mpv_ptvTextureVertex->mtv_iSurfaceVx;
|
|
}}
|
|
}}
|
|
|
|
// start with first triangle
|
|
INDEX ctTrianglesDone = 0;
|
|
Triangle *ptri = &_atri[0];
|
|
INDEX i1, i2;
|
|
GetNeighbourTriangleVertices(ptri, i1, i2);
|
|
//_RPT2(_CRT_WARN, "Begin: i1=%d i2=%d\n", i1,i2);
|
|
|
|
_ctStrips++;
|
|
INDEX ctTriPerStrip = 0;
|
|
// repeat
|
|
FOREVER {
|
|
// put current triangles into done triangles
|
|
_atriDone[ctTrianglesDone++] = *ptri;
|
|
//_RPT3(_CRT_WARN, "Added: %d %d %d\n", ptri->i0, ptri->i1, ptri->i2);
|
|
ptri->bDone = TRUE;
|
|
ctTriPerStrip++;
|
|
|
|
// stop if all triangles are done
|
|
if( ctTrianglesDone>=ctTriangles) break;
|
|
|
|
// get some neighbour of current triangle
|
|
Triangle *ptriNext = NULL;
|
|
extern INDEX mdl_bCreateStrips;
|
|
if( mdl_bCreateStrips) {
|
|
ptriNext = GetNextStripTriangle( i1, i2, ctTriPerStrip);
|
|
//_RPT2(_CRT_WARN, "Next: i1=%d i2=%d\n", i1,i2);
|
|
}
|
|
// if no neighbour
|
|
if( ptriNext==NULL) {
|
|
// get first one that is not done
|
|
ptriNext = GetFirstTriangle();
|
|
GetNeighbourTriangleVertices( ptriNext, i1, i2);
|
|
//_RPT2(_CRT_WARN, "Rebegin: i1=%d i2=%d\n", i1,i2);
|
|
_ctMaxStripLen = Max( _ctMaxStripLen, ctTriPerStrip);
|
|
_ctMinStripLen = Max( _ctMinStripLen, ctTriPerStrip);
|
|
_ctStrips++;
|
|
ctTriPerStrip = 0;
|
|
}
|
|
// take that as current triangle
|
|
ptri = ptriNext;
|
|
}
|
|
_ctMaxStripLen = Max( _ctMaxStripLen, ctTriPerStrip);
|
|
_ctMinStripLen = Min( _ctMinStripLen, ctTriPerStrip);
|
|
ASSERT( ctTrianglesDone==ctTriangles);
|
|
|
|
// create elements
|
|
ms.ms_ctSrfEl = ctTriangles*3;
|
|
INDEX *paiElements = mmi.mmpi_aiElements.Push(ms.ms_ctSrfEl);
|
|
// dump all triangles
|
|
//_RPT0(_CRT_WARN, "Result:\n");
|
|
INDEX iel = 0;
|
|
{for( INDEX itri=0; itri<ctTriangles; itri++){
|
|
paiElements[iel++] = _atriDone[itri].i0;
|
|
paiElements[iel++] = _atriDone[itri].i1;
|
|
paiElements[iel++] = _atriDone[itri].i2;
|
|
//_RPT3(_CRT_WARN, "%d %d %d\n", _atriDone[itri].i0, _atriDone[itri].i1, _atriDone[itri].i2);
|
|
}}
|
|
mmi.mmpi_ctTriangles += ctTriangles;
|
|
|
|
_fTriPerStrip = FLOAT(_ctTriangles) / _ctStrips;
|
|
_fTriPerSurface = FLOAT(_ctTriangles) / _ctSurfaces;
|
|
_fStripPerSurface = FLOAT(_ctStrips) / _ctSurfaces;
|
|
// _ctStripStartFailed
|
|
_atri.Clear();
|
|
_atriDone.Clear();
|
|
}
|
|
|
|
|
|
|
|
// compare two surfaces by type of diffuse surface
|
|
static int qsort_CompareSurfaceDiffuseTypes( const void *pSrf1, const void *pSrf2)
|
|
{
|
|
MappingSurface &srf1 = *(MappingSurface*)pSrf1;
|
|
MappingSurface &srf2 = *(MappingSurface*)pSrf2;
|
|
// invisible, empty or obsolete surfaces goes to end ...
|
|
if( srf1.ms_aiPolygons.Count()==0 ||
|
|
(srf1.ms_ulRenderingFlags&SRF_INVISIBLE) ||
|
|
srf1.ms_sttTranslucencyType==STT_ALPHAGOURAUD) return +1;
|
|
if( srf2.ms_aiPolygons.Count()==0 ||
|
|
(srf2.ms_ulRenderingFlags&SRF_INVISIBLE) ||
|
|
srf2.ms_sttTranslucencyType==STT_ALPHAGOURAUD) return -1;
|
|
// same surface types - sort by specular then reflection layer
|
|
if( srf1.ms_sttTranslucencyType==srf2.ms_sttTranslucencyType) {
|
|
BOOL bSrf1Spec = srf1.ms_ulRenderingFlags & SRF_SPECULAR;
|
|
BOOL bSrf2Spec = srf2.ms_ulRenderingFlags & SRF_SPECULAR;
|
|
BOOL bSrf1Refl = srf1.ms_ulRenderingFlags & SRF_REFLECTIONS;
|
|
BOOL bSrf2Refl = srf2.ms_ulRenderingFlags & SRF_REFLECTIONS;
|
|
if( bSrf1Spec && !bSrf2Spec) return -1;
|
|
if( !bSrf1Spec && bSrf2Spec) return +1;
|
|
if( bSrf1Refl && !bSrf2Refl) return -1;
|
|
if( !bSrf1Refl && bSrf2Refl) return +1;
|
|
// identical surfaces
|
|
return 0;
|
|
}
|
|
// ... opaque surfaces goes to begining ...
|
|
if( srf1.ms_sttTranslucencyType==STT_OPAQUE) return -1;
|
|
if( srf2.ms_sttTranslucencyType==STT_OPAQUE) return +1;
|
|
// ... then transparent ...
|
|
if( srf1.ms_sttTranslucencyType==STT_TRANSPARENT) {
|
|
if( srf2.ms_sttTranslucencyType==STT_OPAQUE) return +1;
|
|
return -1;
|
|
}
|
|
if( srf2.ms_sttTranslucencyType==STT_TRANSPARENT) {
|
|
if( srf1.ms_sttTranslucencyType==STT_OPAQUE) return -1;
|
|
return +1;
|
|
}
|
|
// ... then translucent ...
|
|
if( srf1.ms_sttTranslucencyType==STT_TRANSLUCENT) {
|
|
if( srf2.ms_sttTranslucencyType==STT_OPAQUE ||
|
|
srf2.ms_sttTranslucencyType==STT_TRANSPARENT) return +1;
|
|
return -1;
|
|
}
|
|
if( srf2.ms_sttTranslucencyType==STT_TRANSLUCENT) {
|
|
if( srf1.ms_sttTranslucencyType==STT_OPAQUE ||
|
|
srf1.ms_sttTranslucencyType==STT_TRANSPARENT) return -1;
|
|
return +1;
|
|
}
|
|
// ... then additive ...
|
|
if( srf1.ms_sttTranslucencyType==STT_ADD) {
|
|
if( srf2.ms_sttTranslucencyType==STT_MULTIPLY) return -1;
|
|
return +1;
|
|
}
|
|
if( srf2.ms_sttTranslucencyType==STT_ADD) {
|
|
if( srf1.ms_sttTranslucencyType==STT_MULTIPLY) return +1;
|
|
return -1;
|
|
}
|
|
// ... then multiplicative.
|
|
if( srf1.ms_sttTranslucencyType==STT_MULTIPLY) return +1;
|
|
if( srf2.ms_sttTranslucencyType==STT_MULTIPLY) return -1;
|
|
ASSERTALWAYS( "Unrecognized surface type!");
|
|
return 0;
|
|
}
|
|
|
|
|
|
// prepare mip model's array for OpenGL
|
|
static void PrepareModelMipForRendering( CModelData &md, INDEX iMip)
|
|
{
|
|
ModelMipInfo &mmi = md.md_MipInfos[iMip];
|
|
mmi.mmpi_ulLayerFlags = MMI_OPAQUE|MMI_TRANSLUCENT; // initially entire model can be both opaque and translucent
|
|
mmi.mmpi_ctTriangles = 0;
|
|
|
|
// get number of vertices in this mip
|
|
INDEX ctMdlVx = md.md_VerticesCt;
|
|
INDEX ctMipVx = 0;
|
|
INDEX iMdlVx;
|
|
ULONG ulVxMask = 1UL<<iMip;
|
|
for( iMdlVx=0; iMdlVx<ctMdlVx; iMdlVx++) {
|
|
if( md.md_VertexMipMask[iMdlVx] & ulVxMask) ctMipVx++;
|
|
}
|
|
|
|
// create model<->mip remapping tables for vertices
|
|
mmi.mmpi_ctMipVx = ctMipVx;
|
|
mmi.mmpi_auwMipToMdl.Clear();
|
|
mmi.mmpi_auwMipToMdl.New(ctMipVx);
|
|
CStaticArray<INDEX> aiMdlToMip;
|
|
aiMdlToMip.New(ctMdlVx);
|
|
INDEX iMipVx = 0;
|
|
for( iMdlVx=0; iMdlVx<ctMdlVx; iMdlVx++) {
|
|
aiMdlToMip[iMdlVx] = 0x12345678; // set to invalid to catch eventual bugs
|
|
if((md.md_VertexMipMask[iMdlVx] & ulVxMask)) {
|
|
aiMdlToMip[iMdlVx]= iMipVx;
|
|
mmi.mmpi_auwMipToMdl[iMipVx++] = iMdlVx;
|
|
}
|
|
}
|
|
|
|
// get total number of surface vertices
|
|
CStaticArray<struct ModelTextureVertex> &amtv = mmi.mmpi_TextureVertices;
|
|
mmi.mmpi_ctSrfVx = amtv.Count();
|
|
|
|
// allocate surface vertex arrays
|
|
mmi.mmpi_auwSrfToMip.Clear();
|
|
mmi.mmpi_avmexTexCoord.Clear();
|
|
mmi.mmpi_auwSrfToMip.New(mmi.mmpi_ctSrfVx);
|
|
mmi.mmpi_avmexTexCoord.New(mmi.mmpi_ctSrfVx);
|
|
|
|
// alloc bump mapping vectors only if needed
|
|
mmi.mmpi_avBumpU.Clear();
|
|
mmi.mmpi_avBumpV.Clear();
|
|
|
|
// count surfaces
|
|
INDEX ctSurfaces = 0;
|
|
{FOREACHINSTATICARRAY( mmi.mmpi_MappingSurfaces, MappingSurface, itms) ctSurfaces++; }
|
|
// sort surfaces by diffuse type
|
|
qsort( &mmi.mmpi_MappingSurfaces[0], ctSurfaces, sizeof(MappingSurface), qsort_CompareSurfaceDiffuseTypes);
|
|
|
|
// initialize array for all surfaces' elements
|
|
mmi.mmpi_aiElements.Clear();
|
|
|
|
// for each surface
|
|
INDEX iSrfVx = 0;
|
|
INDEX iSrfEl = 0;
|
|
{FOREACHINSTATICARRAY( mmi.mmpi_MappingSurfaces, MappingSurface, itms)
|
|
{
|
|
MappingSurface &ms = *itms;
|
|
// if it is empty surface
|
|
if( ms.ms_aiPolygons.Count()==0) {
|
|
// just clear all its data
|
|
ms.ms_ctSrfVx = 0;
|
|
ms.ms_ctSrfEl = 0;
|
|
ms.ms_iSrfVx0 = MAX_SLONG; // set to invalid to catch eventual bugs
|
|
// proceed to next surface
|
|
continue;
|
|
}
|
|
|
|
// determine surface and mip model rendering type (write to z-buffer or not)
|
|
if( !(ms.ms_ulRenderingFlags&SRF_DIFFUSE)
|
|
|| ms.ms_sttTranslucencyType==STT_TRANSLUCENT
|
|
|| ms.ms_sttTranslucencyType==STT_ADD
|
|
|| ms.ms_sttTranslucencyType==STT_MULTIPLY) {
|
|
ms.ms_ulRenderingFlags &= ~SRF_OPAQUE;
|
|
mmi.mmpi_ulLayerFlags &= ~MMI_OPAQUE;
|
|
} else {
|
|
ms.ms_ulRenderingFlags |= SRF_OPAQUE;
|
|
mmi.mmpi_ulLayerFlags &= ~MMI_TRANSLUCENT;
|
|
}
|
|
// accumulate flags
|
|
mmi.mmpi_ulLayerFlags |= ms.ms_ulRenderingFlags;
|
|
|
|
// assign surface vertex numbers
|
|
ms.ms_iSrfVx0 = iSrfVx;
|
|
ms.ms_ctSrfVx = ms.ms_aiTextureVertices.Count();
|
|
// for each vertex
|
|
for( INDEX iVxInSurface=0; iVxInSurface<ms.ms_ctSrfVx; iVxInSurface++) {
|
|
// get texture vertex
|
|
ModelTextureVertex &mtv = amtv[ms.ms_aiTextureVertices[iVxInSurface]];
|
|
// remember index for elements preparing
|
|
mtv.mtv_iSurfaceVx = iSrfVx;
|
|
// remember index for rendering
|
|
mmi.mmpi_auwSrfToMip[iSrfVx] = aiMdlToMip[mtv.mtv_iTransformedVertex];
|
|
// assign data to texture array
|
|
mmi.mmpi_avmexTexCoord[iSrfVx](1) = (FLOAT)mtv.mtv_UV(1);
|
|
mmi.mmpi_avmexTexCoord[iSrfVx](2) = (FLOAT)mtv.mtv_UV(2);
|
|
iSrfVx++;
|
|
} // set vertex indices
|
|
PrepareSurfaceElements( mmi, ms);
|
|
}}
|
|
|
|
// for each patch
|
|
FOREACHINSTATICARRAY( mmi.mmpi_aPolygonsPerPatch, PolygonsPerPatch, itppp)
|
|
{
|
|
PolygonsPerPatch &ppp = *itppp;
|
|
// find total number of triangles
|
|
INDEX ctTriangles = 0;
|
|
INDEX iipo;
|
|
for( iipo=0; iipo<ppp.ppp_iPolygons.Count(); iipo++) {
|
|
ModelPolygon &mp = mmi.mmpi_Polygons[ ppp.ppp_iPolygons[iipo]];
|
|
ctTriangles += (mp.mp_PolygonVertices.Count()-2);
|
|
}
|
|
|
|
// allocate that much elements
|
|
ppp.ppp_auwElements.Clear();
|
|
ppp.ppp_auwElements.New(ctTriangles*3);
|
|
|
|
// put all triangles there (do tri-fans) -> should do tri-strips ? !!!!
|
|
INDEX iel = 0;
|
|
for( iipo=0; iipo<ppp.ppp_iPolygons.Count(); iipo++) {
|
|
ModelPolygon &mp = mmi.mmpi_Polygons[ ppp.ppp_iPolygons[iipo]];
|
|
for( INDEX ivx=2; ivx<mp.mp_PolygonVertices.Count(); ivx++) {
|
|
ppp.ppp_auwElements[iel++] = mp.mp_PolygonVertices[0 ].mpv_ptvTextureVertex->mtv_iSurfaceVx;
|
|
ppp.ppp_auwElements[iel++] = mp.mp_PolygonVertices[ivx-1].mpv_ptvTextureVertex->mtv_iSurfaceVx;
|
|
ppp.ppp_auwElements[iel++] = mp.mp_PolygonVertices[ivx-0].mpv_ptvTextureVertex->mtv_iSurfaceVx;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
extern void PrepareModelForRendering( CModelData &md)
|
|
{
|
|
// do nothing, if the model has already been initialized for rendering
|
|
if( md.md_bPreparedForRendering) return;
|
|
_pfModelProfile.StartTimer( CModelProfile::PTI_VIEW_PREPAREFORRENDERING);
|
|
_pfWorldEditingProfile.StartTimer(CWorldEditingProfile::PTI_TRISTRIPMODELS);
|
|
// prepare each mip model
|
|
for( INDEX iMip=0; iMip<md.md_MipCt; iMip++) PrepareModelMipForRendering( md, iMip);
|
|
// mark as prepared
|
|
md.md_bPreparedForRendering = TRUE;
|
|
// all done
|
|
_pfWorldEditingProfile.StopTimer(CWorldEditingProfile::PTI_TRISTRIPMODELS);
|
|
_pfModelProfile.StopTimer( CModelProfile::PTI_VIEW_PREPAREFORRENDERING);
|
|
}
|
|
|
|
|
|
|
|
// strip rendering support
|
|
COLOR _acol[] = { C_BLACK, C_WHITE,
|
|
C_dGRAY, C_GRAY, C_lGRAY, C_dRED, C_RED, C_lRED,
|
|
C_dGREEN, C_GREEN, C_lGREEN, C_dBLUE, C_BLUE, C_lBLUE,
|
|
C_dCYAN, C_CYAN, C_lCYAN, C_dMAGENTA, C_MAGENTA, C_lMAGENTA,
|
|
C_dYELLOW, C_YELLOW, C_lYELLOW, C_dORANGE, C_ORANGE, C_lORANGE,
|
|
C_dBROWN, C_BROWN, C_lBROWN, C_dPINK, C_PINK, C_lPINK };
|
|
|
|
const INDEX _ctcol = sizeof(_acol)/sizeof(_acol[0]);
|
|
|
|
static void SetCol(void)
|
|
{
|
|
glCOLOR( _acol[_icol]);
|
|
_icol = (_icol+1)%_ctcol;
|
|
}
|
|
|
|
static void DrawStrips( const INDEX ct, const INDEX *pai)
|
|
{
|
|
// set strip color
|
|
pglDisableClientState( GL_COLOR_ARRAY);
|
|
gfxDisableTexture();
|
|
SetCol();
|
|
|
|
// render elements as strips
|
|
pglBegin( GL_TRIANGLE_STRIP);
|
|
INDEX ctMaxTriPerStrip = 0;
|
|
INDEX i = 0;
|
|
INDEX iInStrip = 0;
|
|
INDEX iL0, iL1;
|
|
|
|
while( i<ct/3)
|
|
{
|
|
INDEX i0 = pai[i*3+0];
|
|
INDEX i1 = pai[i*3+1];
|
|
INDEX i2 = pai[i*3+2];
|
|
ctMaxTriPerStrip = Max( ctMaxTriPerStrip, INDEX(iInStrip));
|
|
|
|
if( iInStrip==0) {
|
|
pglEnd();
|
|
SetCol();
|
|
pglBegin( GL_TRIANGLE_STRIP);
|
|
pglArrayElement(i0);
|
|
pglArrayElement(i1);
|
|
pglArrayElement(i2);
|
|
iL0 = i1;
|
|
iL1 = i2;
|
|
i++;
|
|
iInStrip++;
|
|
} else {
|
|
if (iInStrip%2==0) {
|
|
if (iL0==i0 && iL1==i1) {
|
|
pglArrayElement(i2);
|
|
iL0=iL1; iL1=i2;
|
|
i++;
|
|
iInStrip++;
|
|
} else {
|
|
iInStrip=0;
|
|
}
|
|
} else {
|
|
if (iL0==i1 && iL1==i0) {
|
|
pglArrayElement(i2);
|
|
iL0=iL1; iL1=i2;
|
|
i++;
|
|
iInStrip++;
|
|
} else {
|
|
iInStrip=0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
pglEnd();
|
|
OGL_CHECKERROR;
|
|
}
|
|
|
|
|
|
// returns haze/fog value in vertex
|
|
static FLOAT3D _vFViewerObj, _vHDirObj;
|
|
static FLOAT _fFogAddZ, _fFogAddH;
|
|
static FLOAT _fHazeAdd;
|
|
|
|
|
|
// check vertex against fog
|
|
static void GetFogMapInVertex( GFXVertex3 &vtx, GFXTexCoord &tex)
|
|
{
|
|
#if ASMOPT == 1
|
|
__asm {
|
|
mov esi,D [vtx]
|
|
mov edi,D [tex]
|
|
fld D [esi]GFXVertex3.x
|
|
fmul D [_vFViewerObj+0]
|
|
fld D [esi]GFXVertex3.y
|
|
fmul D [_vFViewerObj+4]
|
|
fld D [esi]GFXVertex3.z
|
|
fmul D [_vFViewerObj+8]
|
|
fxch st(2)
|
|
faddp st(1),st(0)
|
|
faddp st(1),st(0) // fD
|
|
fld D [esi]GFXVertex3.x
|
|
fmul D [_vHDirObj+0]
|
|
fld D [esi]GFXVertex3.y
|
|
fmul D [_vHDirObj+4]
|
|
fld D [esi]GFXVertex3.z
|
|
fmul D [_vHDirObj+8]
|
|
fxch st(2)
|
|
faddp st(1),st(0)
|
|
faddp st(1),st(0) // fH, fD
|
|
fxch st(1)
|
|
fadd D [_fFogAddZ]
|
|
fmul D [_fog_fMulZ]
|
|
fxch st(1)
|
|
fadd D [_fFogAddH]
|
|
fmul D [_fog_fMulH]
|
|
fxch st(1)
|
|
fstp D [edi+0]
|
|
fstp D [edi+4]
|
|
}
|
|
#else
|
|
const FLOAT fD = vtx.x*_vFViewerObj(1) + vtx.y*_vFViewerObj(2) + vtx.z*_vFViewerObj(3);
|
|
const FLOAT fH = vtx.x*_vHDirObj(1) + vtx.y*_vHDirObj(2) + vtx.z*_vHDirObj(3);
|
|
tex.s = (fD+_fFogAddZ) * _fog_fMulZ;
|
|
tex.t = (fH+_fFogAddH) * _fog_fMulH;
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
// check vertex against haze
|
|
static void GetHazeMapInVertex( GFXVertex3 &vtx, FLOAT &tx1)
|
|
{
|
|
#if ASMOPT == 1
|
|
__asm {
|
|
mov esi,D [vtx]
|
|
mov edi,D [tx1]
|
|
fld D [esi]GFXVertex3.x
|
|
fmul D [_vViewerObj+0]
|
|
fld D [esi]GFXVertex3.y
|
|
fmul D [_vViewerObj+4]
|
|
fld D [esi]GFXVertex3.z
|
|
fmul D [_vViewerObj+8]
|
|
fxch st(2)
|
|
faddp st(1),st(0)
|
|
faddp st(1),st(0)
|
|
fadd D [_fHazeAdd]
|
|
fmul D [_haze_fMul]
|
|
fstp D [edi]
|
|
}
|
|
#else
|
|
const FLOAT fD = vtx.x*_vViewerObj(1) + vtx.y*_vViewerObj(2) + vtx.z*_vViewerObj(3);
|
|
tx1 = (fD+_fHazeAdd) * _haze_fMul;
|
|
#endif
|
|
}
|
|
|
|
|
|
// check model's bounding box against fog
|
|
static BOOL IsModelInFog( FLOAT3D &vMin, FLOAT3D &vMax)
|
|
{
|
|
GFXTexCoord tex;
|
|
GFXVertex3 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;
|
|
GFXVertex3 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;
|
|
}
|
|
|
|
|
|
|
|
// render all pending elements
|
|
static void FlushElements( INDEX ctElem, INDEX *pai)
|
|
{
|
|
ASSERT(ctElem>0);
|
|
// choose rendering mode
|
|
extern INDEX mdl_bShowStrips;
|
|
if( _bMultiPlayer) mdl_bShowStrips = 0; // don't allow in multiplayer mode!
|
|
if( !mdl_bShowStrips) {
|
|
_pfModelProfile.StartTimer( CModelProfile::PTI_VIEW_DRAWELEMENTS);
|
|
_pfModelProfile.IncrementTimerAveragingCounter( CModelProfile::PTI_VIEW_DRAWELEMENTS, ctElem/3);
|
|
_pGfx->gl_ctModelTriangles += ctElem/3;
|
|
gfxDrawElements( ctElem, pai);
|
|
extern INDEX mdl_bShowTriangles;
|
|
if( _bMultiPlayer) mdl_bShowTriangles = 0; // don't allow in multiplayer mode!
|
|
if( mdl_bShowTriangles) {
|
|
gfxSetConstantColor(C_YELLOW|222); // this also disables color array
|
|
gfxPolygonMode(GFX_LINE);
|
|
gfxDrawElements( ctElem, pai);
|
|
gfxPolygonMode(GFX_FILL);
|
|
gfxEnableColorArray(); // need to re-enable color array
|
|
} // done
|
|
_pfModelProfile.StopTimer( CModelProfile::PTI_VIEW_DRAWELEMENTS);
|
|
}
|
|
// show strips
|
|
else if( _eAPI==GAT_OGL) {
|
|
DrawStrips( ctElem, pai);
|
|
OGL_CHECKERROR;
|
|
}
|
|
}
|
|
|
|
|
|
// returns if any type of translucent surface was required
|
|
static void SetRenderingParameters( SurfaceTranslucencyType stt)
|
|
{
|
|
_pfModelProfile.StartTimer( CModelProfile::PTI_VIEW_ONESIDE_GLSETUP);
|
|
_pfModelProfile.IncrementTimerAveragingCounter( CModelProfile::PTI_VIEW_ONESIDE_GLSETUP);
|
|
|
|
if( stt==STT_TRANSLUCENT || (_bForceTranslucency && ((stt==STT_OPAQUE) || (stt==STT_TRANSPARENT)))) {
|
|
gfxEnableBlend();
|
|
gfxBlendFunc( GFX_SRC_ALPHA, GFX_INV_SRC_ALPHA);
|
|
gfxDisableAlphaTest();
|
|
gfxDisableDepthWrite();
|
|
} else if( stt==STT_OPAQUE) {
|
|
gfxDisableAlphaTest();
|
|
gfxDisableBlend();
|
|
gfxEnableDepthWrite();
|
|
} else if( stt==STT_TRANSPARENT) {
|
|
gfxDisableBlend();
|
|
gfxEnableAlphaTest();
|
|
gfxEnableDepthWrite();
|
|
} else if( stt==STT_ADD) {
|
|
gfxEnableBlend();
|
|
gfxBlendFunc( GFX_SRC_ALPHA, GFX_ONE);
|
|
gfxDisableAlphaTest();
|
|
gfxDisableDepthWrite();
|
|
} else if( stt==STT_MULTIPLY) {
|
|
gfxEnableBlend();
|
|
gfxBlendFunc( GFX_ZERO, GFX_INV_SRC_COLOR);
|
|
gfxDisableAlphaTest();
|
|
gfxDisableDepthWrite();
|
|
} else {
|
|
ASSERTALWAYS( "Unsupported model rendering mode.");
|
|
}
|
|
// all done
|
|
_pfModelProfile.StopTimer( CModelProfile::PTI_VIEW_ONESIDE_GLSETUP);
|
|
}
|
|
|
|
|
|
// render one side of a surface (return TRUE if any type of translucent surface has been rendered)
|
|
static void RenderOneSide( CRenderModel &rm, BOOL bBackSide, ULONG ulLayerFlags)
|
|
{
|
|
_pfModelProfile.StartTimer( CModelProfile::PTI_VIEW_ONESIDE);
|
|
_pfModelProfile.IncrementTimerAveragingCounter( CModelProfile::PTI_VIEW_ONESIDE);
|
|
_icol = 0;
|
|
|
|
// set face culling
|
|
if( bBackSide) {
|
|
if( !(_ulMipLayerFlags&SRF_DOUBLESIDED)) {
|
|
_pfModelProfile.StopTimer( CModelProfile::PTI_VIEW_ONESIDE);
|
|
return;
|
|
} else gfxCullFace(GFX_FRONT);
|
|
} else gfxCullFace(GFX_BACK);
|
|
|
|
// start with invalid rendering parameters
|
|
SurfaceTranslucencyType sttLast = STT_INVALID;
|
|
|
|
// for each surface in current mip model
|
|
INDEX iStartElem=0;
|
|
INDEX ctElements=0;
|
|
ModelMipInfo &mmi = *rm.rm_pmmiMip;
|
|
{FOREACHINSTATICARRAY( mmi.mmpi_MappingSurfaces, MappingSurface, itms)
|
|
{
|
|
const MappingSurface &ms = *itms;
|
|
const ULONG ulFlags = ms.ms_ulRenderingFlags;
|
|
// end rendering if surface is invisible or empty - these are the last surfaces in surface list
|
|
if( (ulFlags&SRF_INVISIBLE) || ms.ms_ctSrfVx==0) break;
|
|
// skip surface if ...
|
|
if( !(ulFlags&ulLayerFlags) // not in this layer,
|
|
|| (bBackSide && !(ulFlags&SRF_DOUBLESIDED)) // rendering back side and surface is not double sided,
|
|
|| !(_ulColorMask&ms.ms_ulOnColor) // not on or off.
|
|
|| (_ulColorMask&ms.ms_ulOffColor)) {
|
|
if( ctElements>0) FlushElements( ctElements, &mmi.mmpi_aiElements[iStartElem]);
|
|
iStartElem+= ctElements+ms.ms_ctSrfEl;
|
|
ctElements = 0;
|
|
continue;
|
|
}
|
|
|
|
// if should set parameters
|
|
if( ulLayerFlags&SRF_DIFFUSE) {
|
|
// get rendering parameters
|
|
SurfaceTranslucencyType stt = ms.ms_sttTranslucencyType;
|
|
// if surface uses rendering parameters different than last one
|
|
if( sttLast!=stt) {
|
|
// set up new API states
|
|
if( ctElements>0) FlushElements( ctElements, &mmi.mmpi_aiElements[iStartElem]);
|
|
SetRenderingParameters(stt);
|
|
sttLast=stt;
|
|
iStartElem+= ctElements;
|
|
ctElements = 0;
|
|
}
|
|
} // batch the surface polygons for rendering
|
|
ctElements += ms.ms_ctSrfEl;
|
|
}}
|
|
// flush leftovers
|
|
if( ctElements>0) FlushElements( ctElements, &mmi.mmpi_aiElements[iStartElem]);
|
|
// all done
|
|
_pfModelProfile.StopTimer( CModelProfile::PTI_VIEW_ONESIDE);
|
|
}
|
|
|
|
|
|
|
|
// render model thru colors
|
|
static void RenderColors( CRenderModel &rm)
|
|
{
|
|
// only if required
|
|
if( rm.rm_rtRenderType&RT_NO_POLYGON_FILL) return;
|
|
_icol = 0;
|
|
|
|
// parameters
|
|
gfxCullFace(GFX_BACK);
|
|
gfxDisableBlend();
|
|
gfxDisableAlphaTest();
|
|
gfxDisableTexture();
|
|
gfxEnableDepthWrite();
|
|
|
|
gfxSetVertexArray( &_avtxSrfBase[0], _avtxSrfBase.Count());
|
|
gfxSetColorArray( &_acolSrfBase[0]);
|
|
|
|
// for each surface in current mip model
|
|
INDEX iStartElem=0;
|
|
INDEX ctElements=0;
|
|
ModelMipInfo &mmi = *rm.rm_pmmiMip;
|
|
FOREACHINSTATICARRAY( mmi.mmpi_MappingSurfaces, MappingSurface, itms)
|
|
{
|
|
const MappingSurface &ms = *itms;
|
|
// skip if surface is invisible or empty
|
|
if( (ms.ms_ulRenderingFlags&SRF_INVISIBLE) || ms.ms_ctSrfVx==0
|
|
|| !(_ulColorMask&ms.ms_ulOnColor) || (_ulColorMask&ms.ms_ulOffColor)) {
|
|
if( ctElements>0) FlushElements( ctElements, &mmi.mmpi_aiElements[iStartElem]);
|
|
iStartElem+= ctElements+ms.ms_ctSrfEl;
|
|
ctElements = 0;
|
|
continue;
|
|
}
|
|
// set surface color
|
|
COLOR srfCol;
|
|
extern INDEX GetBit( ULONG ulSource);
|
|
if( rm.rm_rtRenderType&RT_ON_COLORS) {
|
|
srfCol = PaletteColorValues[GetBit(ms.ms_ulOnColor)]|CT_OPAQUE;
|
|
} else if( rm.rm_rtRenderType&RT_OFF_COLORS) {
|
|
srfCol = PaletteColorValues[GetBit(ms.ms_ulOffColor)]|CT_OPAQUE;
|
|
} else {
|
|
srfCol = ms.ms_colColor|CT_OPAQUE;
|
|
}
|
|
// batch the surface polygons for rendering
|
|
GFXColor glcol(srfCol);
|
|
pcolSrfBase = &_acolSrfBase[ms.ms_iSrfVx0];
|
|
for( INDEX iSrfVx=0; iSrfVx<ms.ms_ctSrfVx; iSrfVx++) pcolSrfBase[iSrfVx] = glcol;
|
|
ctElements += ms.ms_ctSrfEl;
|
|
}
|
|
// all done
|
|
if( ctElements>0) FlushElements( ctElements, &mmi.mmpi_aiElements[iStartElem]);
|
|
}
|
|
|
|
|
|
// render model as wireframe
|
|
static void RenderWireframe(CRenderModel &rm)
|
|
{
|
|
// only if required
|
|
if( !(rm.rm_rtRenderType&RT_WIRE_ON) && !(rm.rm_rtRenderType&RT_HIDDEN_LINES)) return;
|
|
_icol = 0;
|
|
|
|
// parameters
|
|
gfxPolygonMode(GFX_LINE);
|
|
gfxDisableBlend();
|
|
gfxDisableAlphaTest();
|
|
gfxDisableTexture();
|
|
gfxDisableDepthTest();
|
|
|
|
gfxSetVertexArray( &_avtxSrfBase[0], _avtxSrfBase.Count());
|
|
gfxSetColorArray( &_acolSrfBase[0]);
|
|
|
|
COLOR colWire = _mrpModelRenderPrefs.GetInkColor()|CT_OPAQUE;
|
|
ModelMipInfo &mmi = *rm.rm_pmmiMip;
|
|
|
|
// first, render hidden lines (if required)
|
|
if( rm.rm_rtRenderType&RT_HIDDEN_LINES)
|
|
{
|
|
gfxCullFace(GFX_FRONT);
|
|
INDEX iStartElem=0;
|
|
INDEX ctElements=0;
|
|
// for each surface in current mip model
|
|
FOREACHINSTATICARRAY( mmi.mmpi_MappingSurfaces, MappingSurface, itms) {
|
|
const MappingSurface &ms = *itms;
|
|
// skip if surface is invisible or empty
|
|
if( (ms.ms_ulRenderingFlags&SRF_INVISIBLE) || ms.ms_ctSrfVx==0) {
|
|
if( ctElements>0) FlushElements( ctElements, &mmi.mmpi_aiElements[iStartElem]);
|
|
iStartElem+= ctElements+ms.ms_ctSrfEl;
|
|
ctElements = 0;
|
|
continue;
|
|
}
|
|
GFXColor glcol( colWire^0x80808080);
|
|
pcolSrfBase = &_acolSrfBase[ms.ms_iSrfVx0];
|
|
for( INDEX iSrfVx=0; iSrfVx<ms.ms_ctSrfVx; iSrfVx++) pcolSrfBase[iSrfVx] = glcol;
|
|
ctElements += ms.ms_ctSrfEl;
|
|
}
|
|
// all done
|
|
if( ctElements>0) FlushElements( ctElements, &mmi.mmpi_aiElements[iStartElem]);
|
|
gfxCullFace(GFX_BACK);
|
|
}
|
|
// then, render visible lines (if required)
|
|
if( rm.rm_rtRenderType&RT_WIRE_ON)
|
|
{
|
|
gfxCullFace(GFX_BACK);
|
|
INDEX iStartElem=0;
|
|
INDEX ctElements=0;
|
|
// for each surface in current mip model
|
|
FOREACHINSTATICARRAY( mmi.mmpi_MappingSurfaces, MappingSurface, itms) {
|
|
const MappingSurface &ms = *itms;
|
|
// done if surface is invisible or empty
|
|
if( (ms.ms_ulRenderingFlags&SRF_INVISIBLE) || ms.ms_ctSrfVx==0) {
|
|
if( ctElements>0) FlushElements( ctElements, &mmi.mmpi_aiElements[iStartElem]);
|
|
iStartElem+= ctElements+ms.ms_ctSrfEl;
|
|
ctElements = 0;
|
|
continue;
|
|
}
|
|
GFXColor glcol(colWire);
|
|
pcolSrfBase = &_acolSrfBase[ms.ms_iSrfVx0];
|
|
for( INDEX iSrfVx=0; iSrfVx<ms.ms_ctSrfVx; iSrfVx++) pcolSrfBase[iSrfVx] = glcol;
|
|
ctElements += ms.ms_ctSrfEl;
|
|
}
|
|
// all done
|
|
if( ctElements>0) FlushElements( ctElements, &mmi.mmpi_aiElements[iStartElem]);
|
|
}
|
|
// all done
|
|
gfxPolygonMode(GFX_FILL);
|
|
}
|
|
|
|
|
|
|
|
|
|
// attenuate alphas in base surface array with attenuation array
|
|
static void AttenuateAlpha( const UBYTE *pshdMip, const INDEX ctVertices)
|
|
{
|
|
_pfModelProfile.StartTimer( CModelProfile::PTI_VIEW_ATTENUATE_SURF);
|
|
_pfModelProfile.IncrementTimerAveragingCounter( CModelProfile::PTI_VIEW_ATTENUATE_SURF, _ctAllSrfVx);
|
|
for( INDEX iSrfVx=0; iSrfVx<ctVertices; iSrfVx++) {
|
|
const INDEX iMipVx = puwSrfToMip[iSrfVx];
|
|
pcolSrfBase[iSrfVx].AttenuateA( pshdMip[iMipVx]);
|
|
}
|
|
_pfModelProfile.StopTimer( CModelProfile::PTI_VIEW_ATTENUATE_SURF);
|
|
}
|
|
|
|
|
|
// attenuate colors in base surface array with attenuation array
|
|
static void AttenuateColor( const UBYTE *pshdMip, const INDEX ctVertices)
|
|
{
|
|
_pfModelProfile.StartTimer( CModelProfile::PTI_VIEW_ATTENUATE_SURF);
|
|
_pfModelProfile.IncrementTimerAveragingCounter( CModelProfile::PTI_VIEW_ATTENUATE_SURF, _ctAllSrfVx);
|
|
for( INDEX iSrfVx=0; iSrfVx<ctVertices; iSrfVx++) {
|
|
const INDEX iMipVx = puwSrfToMip[iSrfVx];
|
|
pcolSrfBase[iSrfVx].AttenuateRGB( pshdMip[iMipVx]);
|
|
}
|
|
_pfModelProfile.StopTimer( CModelProfile::PTI_VIEW_ATTENUATE_SURF);
|
|
}
|
|
|
|
|
|
// unpack vertices (and eventually normals) of one frame
|
|
static void UnpackFrame( CRenderModel &rm, BOOL bKeepNormals)
|
|
{
|
|
_pfModelProfile.StartTimer( CModelProfile::PTI_VIEW_INIT_UNPACK);
|
|
_pfModelProfile.IncrementTimerAveragingCounter( CModelProfile::PTI_VIEW_INIT_UNPACK, _ctAllMipVx);
|
|
|
|
// cache lerp ratio, compression, stretch and light factors
|
|
FLOAT fStretchX = rm.rm_vStretch(1);
|
|
FLOAT fStretchY = rm.rm_vStretch(2);
|
|
FLOAT fStretchZ = rm.rm_vStretch(3);
|
|
FLOAT fOffsetX = rm.rm_vOffset(1);
|
|
FLOAT fOffsetY = rm.rm_vOffset(2);
|
|
FLOAT fOffsetZ = rm.rm_vOffset(3);
|
|
const FLOAT fLerpRatio = rm.rm_fRatio;
|
|
const FLOAT fLightObjX = rm.rm_vLightObj(1) * -255.0f; // multiplier is made here, so it doesn't need to be done per-vertex
|
|
const FLOAT fLightObjY = rm.rm_vLightObj(2) * -255.0f;
|
|
const FLOAT fLightObjZ = rm.rm_vLightObj(3) * -255.0f;
|
|
const UWORD *puwMipToMdl = (const UWORD*)&rm.rm_pmmiMip->mmpi_auwMipToMdl[0];
|
|
SWORD *pswMipCol = (SWORD*)&pcolMipBase[_ctAllMipVx>>1];
|
|
|
|
// if 16 bit compression
|
|
if( rm.rm_pmdModelData->md_Flags & MF_COMPRESSED_16BIT)
|
|
{
|
|
// if no lerping
|
|
const ModelFrameVertex16 *pFrame0 = rm.rm_pFrame16_0;
|
|
const ModelFrameVertex16 *pFrame1 = rm.rm_pFrame16_1;
|
|
if( pFrame0==pFrame1)
|
|
{
|
|
#if ASMOPT == 1
|
|
// for each vertex in mip
|
|
const SLONG fixLerpRatio = FloatToInt(fLerpRatio*256.0f); // fix 8:8
|
|
SLONG slTmp1, slTmp2, slTmp3;
|
|
__asm {
|
|
mov edi,D [pvtxMipBase]
|
|
mov ebx,D [pswMipCol]
|
|
xor ecx,ecx
|
|
vtxLoop16:
|
|
push ecx
|
|
mov esi,D [puwMipToMdl]
|
|
movzx eax,W [esi+ecx*2]
|
|
mov esi,D [pFrame0]
|
|
lea esi,[esi+eax*8]
|
|
// store vertex
|
|
movsx eax,W [esi]ModelFrameVertex16.mfv_SWPoint[0]
|
|
movsx ecx,W [esi]ModelFrameVertex16.mfv_SWPoint[2]
|
|
movsx edx,W [esi]ModelFrameVertex16.mfv_SWPoint[4]
|
|
mov D [slTmp1],eax
|
|
mov D [slTmp2],ecx
|
|
mov D [slTmp3],edx
|
|
fild D [slTmp1]
|
|
fsub D [fOffsetX]
|
|
fmul D [fStretchX]
|
|
fild D [slTmp2]
|
|
fsub D [fOffsetY]
|
|
fmul D [fStretchY]
|
|
fild D [slTmp3]
|
|
fsub D [fOffsetZ]
|
|
fmul D [fStretchZ]
|
|
fxch st(2)
|
|
fstp D [edi]GFXVertex3.x
|
|
fstp D [edi]GFXVertex3.y
|
|
fstp D [edi]GFXVertex3.z
|
|
// determine normal
|
|
movzx eax,B [esi]ModelFrameVertex16.mfv_ubNormH
|
|
movzx edx,B [esi]ModelFrameVertex16.mfv_ubNormP
|
|
mov esi,D [pfSinTable]
|
|
fld D [esi+eax*4 +0]
|
|
fmul D [esi+edx*4 +64*4]
|
|
fld D [esi+eax*4 +64*4]
|
|
fmul D [esi+edx*4 +64*4]
|
|
fxch st(1)
|
|
fstp D [slTmp1]
|
|
fstp D [slTmp3]
|
|
mov eax,D [slTmp1]
|
|
mov ecx,D [slTmp3]
|
|
xor eax,0x80000000
|
|
xor ecx,0x80000000
|
|
mov D [slTmp1],eax
|
|
mov D [slTmp3],ecx
|
|
// determine vertex shade
|
|
fld D [slTmp1]
|
|
fmul D [fLightObjX]
|
|
fld D [esi+edx*4 +0]
|
|
fmul D [fLightObjY]
|
|
fld D [slTmp3]
|
|
fmul D [fLightObjZ]
|
|
fxch st(2)
|
|
faddp st(1),st(0)
|
|
faddp st(1),st(0)
|
|
fistp D [ebx]
|
|
// store normal (if needed)
|
|
cmp D [bKeepNormals],0
|
|
je vtxNext16
|
|
mov ecx,D [esp]
|
|
imul ecx,3*4
|
|
add ecx,D [pnorMipBase]
|
|
mov eax,D [slTmp1]
|
|
mov edx,D [esi+edx*4 +0]
|
|
mov esi,D [slTmp3]
|
|
mov D [ecx]GFXNormal.nx, eax
|
|
mov D [ecx]GFXNormal.ny, edx
|
|
mov D [ecx]GFXNormal.nz, esi
|
|
// advance to next vertex
|
|
vtxNext16:
|
|
pop ecx
|
|
add edi,3*4
|
|
add ebx,1*2
|
|
inc ecx
|
|
cmp ecx,D [_ctAllMipVx]
|
|
jl vtxLoop16
|
|
}
|
|
#else
|
|
// for each vertex in mip
|
|
for( INDEX iMipVx=0; iMipVx<_ctAllMipVx; iMipVx++) {
|
|
// get destination for unpacking
|
|
const INDEX iMdlVx = puwMipToMdl[iMipVx];
|
|
const ModelFrameVertex16 &mfv0 = pFrame0[iMdlVx];
|
|
// store vertex
|
|
GFXVertex3 &vtx = pvtxMipBase[iMipVx];
|
|
vtx.x = (mfv0.mfv_SWPoint(1) -fOffsetX) *fStretchX;
|
|
vtx.y = (mfv0.mfv_SWPoint(2) -fOffsetY) *fStretchY;
|
|
vtx.z = (mfv0.mfv_SWPoint(3) -fOffsetZ) *fStretchZ;
|
|
// determine normal
|
|
const FLOAT fSinH0 = pfSinTable[mfv0.mfv_ubNormH];
|
|
const FLOAT fSinP0 = pfSinTable[mfv0.mfv_ubNormP];
|
|
const FLOAT fCosH0 = pfCosTable[mfv0.mfv_ubNormH];
|
|
const FLOAT fCosP0 = pfCosTable[mfv0.mfv_ubNormP];
|
|
const FLOAT fNX = -fSinH0*fCosP0;
|
|
const FLOAT fNY = +fSinP0;
|
|
const FLOAT fNZ = -fCosH0*fCosP0;
|
|
// store vertex shade
|
|
pswMipCol[iMipVx] = FloatToInt(fNX*fLightObjX + fNY*fLightObjY + fNZ*fLightObjZ);
|
|
// store normal (if needed)
|
|
if( bKeepNormals) {
|
|
pnorMipBase[iMipVx].nx = fNX;
|
|
pnorMipBase[iMipVx].ny = fNY;
|
|
pnorMipBase[iMipVx].nz = fNZ;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
// if lerping
|
|
else
|
|
{
|
|
#if ASMOPT == 1
|
|
// for each vertex in mip
|
|
const SLONG fixLerpRatio = FloatToInt(fLerpRatio*256.0f); // fix 8:8
|
|
SLONG slTmp1, slTmp2, slTmp3;
|
|
__asm {
|
|
mov edi,D [pvtxMipBase]
|
|
mov ebx,D [pswMipCol]
|
|
xor ecx,ecx
|
|
vtxLoop16L:
|
|
push ecx
|
|
push ebx
|
|
mov esi,D [puwMipToMdl]
|
|
movzx ebx,W [esi+ecx*2]
|
|
mov esi,D [pFrame0]
|
|
mov ecx,D [pFrame1]
|
|
// lerp vertex
|
|
movsx eax,W [esi+ebx*8]ModelFrameVertex16.mfv_SWPoint[0]
|
|
movsx edx,W [ecx+ebx*8]ModelFrameVertex16.mfv_SWPoint[0]
|
|
sub edx,eax
|
|
imul edx,D [fixLerpRatio]
|
|
sar edx,8
|
|
add eax,edx
|
|
mov D [slTmp1],eax
|
|
movsx eax,W [esi+ebx*8]ModelFrameVertex16.mfv_SWPoint[2]
|
|
movsx edx,W [ecx+ebx*8]ModelFrameVertex16.mfv_SWPoint[2]
|
|
sub edx,eax
|
|
imul edx,D [fixLerpRatio]
|
|
sar edx,8
|
|
add eax,edx
|
|
mov D [slTmp2],eax
|
|
movsx eax,W [esi+ebx*8]ModelFrameVertex16.mfv_SWPoint[4]
|
|
movsx edx,W [ecx+ebx*8]ModelFrameVertex16.mfv_SWPoint[4]
|
|
sub edx,eax
|
|
imul edx,D [fixLerpRatio]
|
|
sar edx,8
|
|
add eax,edx
|
|
mov D [slTmp3],eax
|
|
// store vertex
|
|
fild D [slTmp1]
|
|
fsub D [fOffsetX]
|
|
fmul D [fStretchX]
|
|
fild D [slTmp2]
|
|
fsub D [fOffsetY]
|
|
fmul D [fStretchY]
|
|
fild D [slTmp3]
|
|
fsub D [fOffsetZ]
|
|
fmul D [fStretchZ]
|
|
fxch st(2)
|
|
fstp D [edi]GFXVertex3.x
|
|
fstp D [edi]GFXVertex3.y
|
|
fstp D [edi]GFXVertex3.z
|
|
// load normals
|
|
movzx eax,B [esi+ebx*8]ModelFrameVertex16.mfv_ubNormH
|
|
movzx edx,B [esi+ebx*8]ModelFrameVertex16.mfv_ubNormP
|
|
mov esi,D [pfSinTable]
|
|
fld D [esi+eax*4 +0]
|
|
fmul D [esi+edx*4 +64*4]
|
|
fld D [esi+edx*4 +0]
|
|
fld D [esi+eax*4 +64*4]
|
|
fmul D [esi+edx*4 +64*4] // fCosH0*fCosP0, fSinP0, fSinH0*fCosP0
|
|
movzx eax,B [ecx+ebx*8]ModelFrameVertex16.mfv_ubNormH
|
|
movzx edx,B [ecx+ebx*8]ModelFrameVertex16.mfv_ubNormP
|
|
fld D [esi+eax*4 +0]
|
|
fmul D [esi+edx*4 +64*4]
|
|
fld D [esi+edx*4 +0]
|
|
fld D [esi+eax*4 +64*4]
|
|
fmul D [esi+edx*4 +64*4] // fCosH1*fCosP1, fSinP1, fSinH1*fCosP1, fCosH0*fCosP0, fSinP0, fSinH0*fCosP0
|
|
// lerp normals
|
|
fxch st(5) // SH0CP0, SP1, SH1CP1, CH0CP0, SP0, CH1CP1
|
|
fsub st(2),st(0)
|
|
fxch st(4) // SP0, SP1, SH1CP1-SH0CP0, CH0CP0, SH0CP0, CH1CP1
|
|
fsub st(1),st(0) // SP0, SP1-SP0, SH1CP1-SH0CP0, CH0CP0, SH0CP0, CH1CP1
|
|
fxch st(3) // CH0CP0, SP1-SP0, SH1CP1-SH0CP0, SP0, SH0CP0, CH1CP1
|
|
fsub st(5),st(0) // CH0CP0, SP1-SP0, SH1CP1-SH0CP0, SP0, SH0CP0, CH1CP1-CH0CP0
|
|
fxch st(2) // SH1CP1-SH0CP0, SP1-SP0, CH0CP0, SP0, SH0CP0, CH1CP1-CH0CP0
|
|
fmul D [fLerpRatio]
|
|
fxch st(1) // SP1-SP0, lSH1CP1, CH0CP0, SP0, SH0CP0, CH1CP1-CH0CP0
|
|
fmul D [fLerpRatio]
|
|
fxch st(5) // CH1CP1-CH0CP0, lSH1CP1, CH0CP0, SP0, SH0CP0, lSP1SP0
|
|
fmul D [fLerpRatio]
|
|
fxch st(1) // lSH1CP1, lCH1CP1, CH0CP0, SP0, SH0CP0, lSP1SP0
|
|
faddp st(4),st(0) // lCH1CP1, CH0CP0, SP0, fNX, lSP1SP0
|
|
fxch st(2) // SP0, CH0CP0, lCH1CP1, fNX, lSP1SP0
|
|
faddp st(4),st(0) // CH0CP0, lCH1CP1, fNX, fNY
|
|
faddp st(1),st(0) // -fNZ, -fNX, fNY
|
|
fxch st(2) // fNY, -fNX, -fNZ
|
|
fstp D [slTmp2]
|
|
fstp D [slTmp1]
|
|
fstp D [slTmp3]
|
|
pop ebx
|
|
mov eax,D [slTmp1]
|
|
mov ecx,D [slTmp3]
|
|
xor eax,0x80000000
|
|
xor ecx,0x80000000
|
|
mov D [slTmp1],eax
|
|
mov D [slTmp3],ecx
|
|
// determine vertex shade
|
|
fld D [slTmp1]
|
|
fmul D [fLightObjX]
|
|
fld D [slTmp2]
|
|
fmul D [fLightObjY]
|
|
fld D [slTmp3]
|
|
fmul D [fLightObjZ]
|
|
fxch st(2)
|
|
faddp st(1),st(0)
|
|
faddp st(1),st(0)
|
|
fistp D [ebx]
|
|
// store lerped normal (if needed)
|
|
cmp D [bKeepNormals],0
|
|
je vtxNext16L
|
|
mov ecx,D [esp]
|
|
imul ecx,3*4
|
|
add ecx,D [pnorMipBase]
|
|
mov eax,D [slTmp1]
|
|
mov edx,D [slTmp2]
|
|
mov esi,D [slTmp3]
|
|
mov D [ecx]GFXNormal.nx, eax
|
|
mov D [ecx]GFXNormal.ny, edx
|
|
mov D [ecx]GFXNormal.nz, esi
|
|
// advance to next vertex
|
|
vtxNext16L:
|
|
pop ecx
|
|
add edi,3*4
|
|
add ebx,1*2
|
|
inc ecx
|
|
cmp ecx,D [_ctAllMipVx]
|
|
jl vtxLoop16L
|
|
}
|
|
#else
|
|
// for each vertex in mip
|
|
for( INDEX iMipVx=0; iMipVx<_ctAllMipVx; iMipVx++) {
|
|
// get destination for unpacking
|
|
const INDEX iMdlVx = puwMipToMdl[iMipVx];
|
|
const ModelFrameVertex16 &mfv0 = pFrame0[iMdlVx];
|
|
const ModelFrameVertex16 &mfv1 = pFrame1[iMdlVx];
|
|
// store lerped vertex
|
|
GFXVertex3 &vtx = pvtxMipBase[iMipVx];
|
|
vtx.x = (Lerp( (FLOAT)mfv0.mfv_SWPoint(1), (FLOAT)mfv1.mfv_SWPoint(1), fLerpRatio) -fOffsetX) * fStretchX;
|
|
vtx.y = (Lerp( (FLOAT)mfv0.mfv_SWPoint(2), (FLOAT)mfv1.mfv_SWPoint(2), fLerpRatio) -fOffsetY) * fStretchY;
|
|
vtx.z = (Lerp( (FLOAT)mfv0.mfv_SWPoint(3), (FLOAT)mfv1.mfv_SWPoint(3), fLerpRatio) -fOffsetZ) * fStretchZ;
|
|
// determine lerped normal
|
|
const FLOAT fSinH0 = pfSinTable[mfv0.mfv_ubNormH]; const FLOAT fSinH1 = pfSinTable[mfv1.mfv_ubNormH];
|
|
const FLOAT fSinP0 = pfSinTable[mfv0.mfv_ubNormP]; const FLOAT fSinP1 = pfSinTable[mfv1.mfv_ubNormP];
|
|
const FLOAT fCosH0 = pfCosTable[mfv0.mfv_ubNormH]; const FLOAT fCosH1 = pfCosTable[mfv1.mfv_ubNormH];
|
|
const FLOAT fCosP0 = pfCosTable[mfv0.mfv_ubNormP]; const FLOAT fCosP1 = pfCosTable[mfv1.mfv_ubNormP];
|
|
const FLOAT fNX = Lerp( -fSinH0*fCosP0, -fSinH1*fCosP1, fLerpRatio);
|
|
const FLOAT fNY = Lerp( +fSinP0, +fSinP1, fLerpRatio);
|
|
const FLOAT fNZ = Lerp( -fCosH0*fCosP0, -fCosH1*fCosP1, fLerpRatio);
|
|
// store vertex shade
|
|
pswMipCol[iMipVx] = FloatToInt(fNX*fLightObjX + fNY*fLightObjY + fNZ*fLightObjZ);
|
|
// store lerped normal (if needed)
|
|
if( bKeepNormals) {
|
|
pnorMipBase[iMipVx].nx = fNX;
|
|
pnorMipBase[iMipVx].ny = fNY;
|
|
pnorMipBase[iMipVx].nz = fNZ;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
}
|
|
}
|
|
// if 8 bit compression
|
|
else
|
|
{
|
|
const ModelFrameVertex8 *pFrame0 = rm.rm_pFrame8_0;
|
|
const ModelFrameVertex8 *pFrame1 = rm.rm_pFrame8_1;
|
|
// if no lerping
|
|
if( pFrame0==pFrame1)
|
|
{
|
|
#if ASMOPT == 1
|
|
// for each vertex in mip
|
|
const SLONG fixLerpRatio = FloatToInt(fLerpRatio*256.0f); // fix 8:8
|
|
SLONG slTmp1, slTmp2, slTmp3;
|
|
__asm {
|
|
mov edi,D [pvtxMipBase]
|
|
mov ebx,D [pswMipCol]
|
|
xor ecx,ecx
|
|
vtxLoop8:
|
|
push ecx
|
|
mov esi,D [puwMipToMdl]
|
|
movzx eax,W [esi+ecx*2]
|
|
mov esi,D [pFrame0]
|
|
lea esi,[esi+eax*4]
|
|
// store vertex
|
|
movsx eax,B [esi]ModelFrameVertex8.mfv_SBPoint[0]
|
|
movsx ecx,B [esi]ModelFrameVertex8.mfv_SBPoint[1]
|
|
movsx edx,B [esi]ModelFrameVertex8.mfv_SBPoint[2]
|
|
mov D [slTmp1],eax
|
|
mov D [slTmp2],ecx
|
|
mov D [slTmp3],edx
|
|
fild D [slTmp1]
|
|
fsub D [fOffsetX]
|
|
fmul D [fStretchX]
|
|
fild D [slTmp2]
|
|
fsub D [fOffsetY]
|
|
fmul D [fStretchY]
|
|
fild D [slTmp3]
|
|
fsub D [fOffsetZ]
|
|
fmul D [fStretchZ]
|
|
fxch st(2)
|
|
fstp D [edi]GFXVertex3.x
|
|
fstp D [edi]GFXVertex3.y
|
|
fstp D [edi]GFXVertex3.z
|
|
// determine normal
|
|
movzx eax,B [esi]ModelFrameVertex8.mfv_NormIndex
|
|
lea esi,[eax*2+eax]
|
|
// determine vertex shade
|
|
fld D [avGouraudNormals+ esi*4 +0]
|
|
fmul D [fLightObjX]
|
|
fld D [avGouraudNormals+ esi*4 +4]
|
|
fmul D [fLightObjY]
|
|
fld D [avGouraudNormals+ esi*4 +8]
|
|
fmul D [fLightObjZ]
|
|
fxch st(2)
|
|
faddp st(1),st(0)
|
|
faddp st(1),st(0)
|
|
fistp D [ebx]
|
|
// store lerped normal (if needed)
|
|
cmp D [bKeepNormals],0
|
|
je vtxNext8
|
|
mov ecx,D [esp]
|
|
imul ecx,3*4
|
|
add ecx,D [pnorMipBase]
|
|
mov eax,D [avGouraudNormals+ esi*4 +0]
|
|
mov edx,D [avGouraudNormals+ esi*4 +4]
|
|
mov esi,D [avGouraudNormals+ esi*4 +8]
|
|
mov D [ecx]GFXNormal.nx, eax
|
|
mov D [ecx]GFXNormal.ny, edx
|
|
mov D [ecx]GFXNormal.nz, esi
|
|
// advance to next vertex
|
|
vtxNext8:
|
|
pop ecx
|
|
add edi,3*4
|
|
add ebx,1*2
|
|
inc ecx
|
|
cmp ecx,D [_ctAllMipVx]
|
|
jl vtxLoop8
|
|
}
|
|
#else
|
|
// for each vertex in mip
|
|
for( INDEX iMipVx=0; iMipVx<_ctAllMipVx; iMipVx++) {
|
|
// get destination for unpacking
|
|
const INDEX iMdlVx = puwMipToMdl[iMipVx];
|
|
const ModelFrameVertex8 &mfv0 = pFrame0[iMdlVx];
|
|
// store vertex
|
|
GFXVertex3 &vtx = pvtxMipBase[iMipVx];
|
|
vtx.x = (mfv0.mfv_SBPoint(1) -fOffsetX) * fStretchX;
|
|
vtx.y = (mfv0.mfv_SBPoint(2) -fOffsetY) * fStretchY;
|
|
vtx.z = (mfv0.mfv_SBPoint(3) -fOffsetZ) * fStretchZ;
|
|
// determine normal
|
|
const FLOAT3D &vNormal0 = avGouraudNormals[mfv0.mfv_NormIndex];
|
|
const FLOAT fNX = vNormal0(1);
|
|
const FLOAT fNY = vNormal0(2);
|
|
const FLOAT fNZ = vNormal0(3);
|
|
// store vertex shade
|
|
pswMipCol[iMipVx] = FloatToInt(fNX*fLightObjX + fNY*fLightObjY + fNZ*fLightObjZ);
|
|
// store lerped normal (if needed)
|
|
if( bKeepNormals) {
|
|
pnorMipBase[iMipVx].nx = fNX;
|
|
pnorMipBase[iMipVx].ny = fNY;
|
|
pnorMipBase[iMipVx].nz = fNZ;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
// if lerping
|
|
else
|
|
{
|
|
#if ASMOPT == 1
|
|
const SLONG fixLerpRatio = FloatToInt(fLerpRatio*256.0f); // fix 8:8
|
|
SLONG slTmp1, slTmp2, slTmp3;
|
|
// re-adjust stretching factors because of fixint lerping (divide by 256)
|
|
fStretchX*=0.00390625f; fOffsetX*=256.0f;
|
|
fStretchY*=0.00390625f; fOffsetY*=256.0f;
|
|
fStretchZ*=0.00390625f; fOffsetZ*=256.0f;
|
|
// for each vertex in mip
|
|
__asm {
|
|
mov edi,D [pvtxMipBase]
|
|
mov ebx,D [pswMipCol]
|
|
xor ecx,ecx
|
|
vtxLoop8L:
|
|
push ecx
|
|
push ebx
|
|
mov esi,D [puwMipToMdl]
|
|
movzx ebx,W [esi+ecx*2]
|
|
mov esi,D [pFrame0]
|
|
mov ecx,D [pFrame1]
|
|
// lerp vertex
|
|
movsx eax,B [esi+ebx*4]ModelFrameVertex8.mfv_SBPoint[0]
|
|
movsx edx,B [ecx+ebx*4]ModelFrameVertex8.mfv_SBPoint[0]
|
|
sub edx,eax
|
|
imul edx,D [fixLerpRatio]
|
|
shl eax,8
|
|
add eax,edx
|
|
mov D [slTmp1],eax
|
|
movsx eax,B [esi+ebx*4]ModelFrameVertex8.mfv_SBPoint[1]
|
|
movsx edx,B [ecx+ebx*4]ModelFrameVertex8.mfv_SBPoint[1]
|
|
sub edx,eax
|
|
imul edx,D [fixLerpRatio]
|
|
shl eax,8
|
|
add eax,edx
|
|
mov D [slTmp2],eax
|
|
movsx eax,B [esi+ebx*4]ModelFrameVertex8.mfv_SBPoint[2]
|
|
movsx edx,B [ecx+ebx*4]ModelFrameVertex8.mfv_SBPoint[2]
|
|
sub edx,eax
|
|
imul edx,D [fixLerpRatio]
|
|
shl eax,8
|
|
add eax,edx
|
|
mov D [slTmp3],eax
|
|
// store vertex
|
|
fild D [slTmp1]
|
|
fsub D [fOffsetX]
|
|
fmul D [fStretchX]
|
|
fild D [slTmp2]
|
|
fsub D [fOffsetY]
|
|
fmul D [fStretchY]
|
|
fild D [slTmp3]
|
|
fsub D [fOffsetZ]
|
|
fmul D [fStretchZ]
|
|
fxch st(2)
|
|
fstp D [edi]GFXVertex3.x
|
|
fstp D [edi]GFXVertex3.y
|
|
fstp D [edi]GFXVertex3.z
|
|
// load normals
|
|
movzx eax,B [esi+ebx*4]ModelFrameVertex8.mfv_NormIndex
|
|
movzx edx,B [ecx+ebx*4]ModelFrameVertex8.mfv_NormIndex
|
|
lea esi,[eax*2+eax]
|
|
lea ecx,[edx*2+edx]
|
|
// lerp normals
|
|
fld D [avGouraudNormals+ ecx*4 +0]
|
|
fsub D [avGouraudNormals+ esi*4 +0]
|
|
fld D [avGouraudNormals+ ecx*4 +4]
|
|
fsub D [avGouraudNormals+ esi*4 +4]
|
|
fld D [avGouraudNormals+ ecx*4 +8]
|
|
fsub D [avGouraudNormals+ esi*4 +8]
|
|
fxch st(2) // nx1-nx0, ny1-ny0, nz1-nz0
|
|
fmul D [fLerpRatio]
|
|
fxch st(1) // ny1-ny0, lnx1, nz1-nz0
|
|
fmul D [fLerpRatio]
|
|
fxch st(2) // nz1-nz0, lnx1, lny1
|
|
fmul D [fLerpRatio]
|
|
fxch st(1) // lnx1, lnz1, lny1
|
|
fadd D [avGouraudNormals+ esi*4 +0]
|
|
fxch st(2) // lny1, lnz1, fNX
|
|
fadd D [avGouraudNormals+ esi*4 +4]
|
|
fxch st(1) // lnz1, fNY, fNX
|
|
fadd D [avGouraudNormals+ esi*4 +8]
|
|
fxch st(2) // fNX, fNY, fNZ
|
|
// determine vertex shade
|
|
fld D [fLightObjX]
|
|
fmul st(0),st(1) // flnx, fNX, fNY, fNZ
|
|
pop ebx
|
|
fld D [fLightObjY]
|
|
fmul st(0),st(3) // flny, flnx, fNX, fNY, fNZ
|
|
fld D [fLightObjZ]
|
|
fmul st(0),st(5) // flnz, flny, flnx, fNX, fNY, fNXZ
|
|
fxch st(2)
|
|
faddp st(1),st(0)
|
|
faddp st(1),st(0) // FL, fNX, fNY, fNXZ
|
|
fistp D [ebx]
|
|
// store lerped normal (if needed)
|
|
cmp D [bKeepNormals],0
|
|
je vtxNext8L
|
|
mov ecx,D [esp]
|
|
imul ecx,3*4
|
|
add ecx,D [pnorMipBase]
|
|
fstp D [ecx]GFXNormal.nx
|
|
fstp D [ecx]GFXNormal.ny
|
|
fst D [ecx]GFXNormal.nz
|
|
fld st(0)
|
|
fld st(0)
|
|
// advance to next vertex
|
|
vtxNext8L:
|
|
fstp st(0)
|
|
fcompp
|
|
pop ecx
|
|
add edi,3*4
|
|
add ebx,1*2
|
|
inc ecx
|
|
cmp ecx,D [_ctAllMipVx]
|
|
jl vtxLoop8L
|
|
}
|
|
#else
|
|
// for each vertex in mip
|
|
for( INDEX iMipVx=0; iMipVx<_ctAllMipVx; iMipVx++) {
|
|
// get destination for unpacking
|
|
const INDEX iMdlVx = puwMipToMdl[iMipVx];
|
|
const ModelFrameVertex8 &mfv0 = pFrame0[iMdlVx];
|
|
const ModelFrameVertex8 &mfv1 = pFrame1[iMdlVx];
|
|
// store lerped vertex
|
|
GFXVertex3 &vtx = pvtxMipBase[iMipVx];
|
|
vtx.x = (Lerp( (FLOAT)mfv0.mfv_SBPoint(1), (FLOAT)mfv1.mfv_SBPoint(1), fLerpRatio) -fOffsetX) * fStretchX;
|
|
vtx.y = (Lerp( (FLOAT)mfv0.mfv_SBPoint(2), (FLOAT)mfv1.mfv_SBPoint(2), fLerpRatio) -fOffsetY) * fStretchY;
|
|
vtx.z = (Lerp( (FLOAT)mfv0.mfv_SBPoint(3), (FLOAT)mfv1.mfv_SBPoint(3), fLerpRatio) -fOffsetZ) * fStretchZ;
|
|
// determine lerped normal
|
|
const FLOAT3D &vNormal0 = avGouraudNormals[mfv0.mfv_NormIndex];
|
|
const FLOAT3D &vNormal1 = avGouraudNormals[mfv1.mfv_NormIndex];
|
|
const FLOAT fNX = Lerp( (FLOAT)vNormal0(1), (FLOAT)vNormal1(1), fLerpRatio);
|
|
const FLOAT fNY = Lerp( (FLOAT)vNormal0(2), (FLOAT)vNormal1(2), fLerpRatio);
|
|
const FLOAT fNZ = Lerp( (FLOAT)vNormal0(3), (FLOAT)vNormal1(3), fLerpRatio);
|
|
// store vertex shade
|
|
pswMipCol[iMipVx] = FloatToInt(fNX*fLightObjX + fNY*fLightObjY + fNZ*fLightObjZ);
|
|
// store lerped normal (if needed)
|
|
if( bKeepNormals) {
|
|
pnorMipBase[iMipVx].nx = fNX;
|
|
pnorMipBase[iMipVx].ny = fNY;
|
|
pnorMipBase[iMipVx].nz = fNZ;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
// generate colors from shades
|
|
#if ASMOPT == 1
|
|
__asm {
|
|
pxor mm0,mm0
|
|
// construct 64-bit RGBA light
|
|
mov eax,D [_slLR]
|
|
mov ebx,D [_slLG]
|
|
mov ecx,D [_slLB]
|
|
shl ebx,16
|
|
or eax,ebx
|
|
or ecx,0x01FE0000
|
|
movd mm5,eax
|
|
movd mm7,ecx
|
|
psllq mm7,32
|
|
por mm5,mm7
|
|
psllw mm5,1 // boost for multiply
|
|
// construct 64-bit RGBA ambient
|
|
mov eax,D [_slAR]
|
|
mov ebx,D [_slAG]
|
|
mov ecx,D [_slAB]
|
|
shl ebx,16
|
|
or eax,ebx
|
|
movd mm6,eax
|
|
movd mm7,ecx
|
|
psllq mm7,32
|
|
por mm6,mm7
|
|
// init
|
|
mov esi,D [pswMipCol]
|
|
mov edi,D [pcolMipBase]
|
|
mov ecx,D [_ctAllMipVx]
|
|
shr ecx,2
|
|
jz colRest
|
|
// 4-colors loop
|
|
colLoop4:
|
|
movq mm1,Q [esi]
|
|
packuswb mm1,mm0
|
|
punpcklbw mm1,mm1
|
|
psrlw mm1,1
|
|
movq mm3,mm1
|
|
punpcklwd mm1,mm1
|
|
punpckhwd mm3,mm3
|
|
movq mm2,mm1
|
|
movq mm4,mm3
|
|
punpckldq mm1,mm1
|
|
punpckhdq mm2,mm2
|
|
punpckldq mm3,mm3
|
|
punpckhdq mm4,mm4
|
|
pmulhw mm1,mm5
|
|
pmulhw mm2,mm5
|
|
pmulhw mm3,mm5
|
|
pmulhw mm4,mm5
|
|
paddsw mm1,mm6
|
|
paddsw mm2,mm6
|
|
paddsw mm3,mm6
|
|
paddsw mm4,mm6
|
|
packuswb mm1,mm2
|
|
packuswb mm3,mm4
|
|
movq Q [edi+0],mm1
|
|
movq Q [edi+8],mm3
|
|
add esi,2*4
|
|
add edi,4*4
|
|
dec ecx
|
|
jnz colLoop4
|
|
// 1-color loop
|
|
colRest:
|
|
mov ecx,D [_ctAllMipVx]
|
|
and ecx,3
|
|
jz colEnd
|
|
colLoop1:
|
|
movsx eax,W [esi]
|
|
movd mm1,eax
|
|
packuswb mm1,mm0
|
|
punpcklbw mm1,mm1
|
|
psrlw mm1,1
|
|
punpcklwd mm1,mm1
|
|
punpckldq mm1,mm1
|
|
pmulhw mm1,mm5
|
|
paddsw mm1,mm6
|
|
packuswb mm1,mm0
|
|
movd D [edi],mm1
|
|
add esi,2
|
|
add edi,4
|
|
dec ecx
|
|
jnz colLoop1
|
|
colEnd:
|
|
emms
|
|
}
|
|
#else
|
|
// generate colors from shades
|
|
for( INDEX iMipVx=0; iMipVx<_ctAllMipVx; iMipVx++) {
|
|
GFXColor &col = pcolMipBase[iMipVx];
|
|
const SLONG slShade = Clamp( (SLONG)pswMipCol[iMipVx], 0L, 255L);
|
|
col.r = pubClipByte[_slAR + ((_slLR*slShade)>>8)];
|
|
col.g = pubClipByte[_slAG + ((_slLG*slShade)>>8)];
|
|
col.b = pubClipByte[_slAB + ((_slLB*slShade)>>8)];
|
|
col.a = slShade;
|
|
}
|
|
#endif
|
|
|
|
// all done
|
|
_pfModelProfile.StopTimer( CModelProfile::PTI_VIEW_INIT_UNPACK);
|
|
}
|
|
|
|
|
|
|
|
|
|
// BEGIN MODEL RENDERING *******************************************************************************
|
|
|
|
|
|
#pragma warning(disable: 4731)
|
|
void CModelObject::RenderModel_View( CRenderModel &rm)
|
|
{
|
|
// cache API
|
|
_eAPI = _pGfx->gl_eCurrentAPI;
|
|
#ifdef SE1_D3D
|
|
ASSERT( _eAPI==GAT_OGL || _eAPI==GAT_D3D || _eAPI==GAT_NONE);
|
|
#else // SE1_D3D
|
|
ASSERT( _eAPI==GAT_OGL || _eAPI==GAT_NONE);
|
|
#endif // SE1_D3D
|
|
if( _eAPI==GAT_NONE) return; // must have API
|
|
|
|
// adjust Truform usage
|
|
extern INDEX mdl_bTruformWeapons;
|
|
extern INDEX gap_bForceTruform;
|
|
// if weapon models don't allow tessellation or no tessellation has been set at all
|
|
if( ((rm.rm_ulFlags&RMF_WEAPON) && !mdl_bTruformWeapons) || _pGfx->gl_iTessellationLevel<1) {
|
|
// just disable truform
|
|
gfxDisableTruform();
|
|
} else {
|
|
// enable truform for everything?
|
|
if( gap_bForceTruform) gfxEnableTruform();
|
|
else {
|
|
// enable truform only for truform-ready models!
|
|
const INDEX iTesselationLevel = Min( rm.rm_iTesselationLevel, _pGfx->gl_iTessellationLevel);
|
|
if( iTesselationLevel>0) {
|
|
extern INDEX ogl_bTruformLinearNormals;
|
|
gfxSetTruform( iTesselationLevel, ogl_bTruformLinearNormals);
|
|
gfxEnableTruform();
|
|
}
|
|
else gfxDisableTruform();
|
|
}
|
|
}
|
|
// setup drawing direction (in case of mirror)
|
|
if( rm.rm_ulFlags & RMF_INVERTED) gfxFrontFace(GFX_CW);
|
|
else gfxFrontFace(GFX_CCW);
|
|
|
|
// declare pointers for general usage
|
|
INDEX iSrfVx0, ctSrfVx;
|
|
GFXTexCoord *ptexSrfBase;
|
|
GFXVertex *pvtxSrfBase;
|
|
FLOAT2D *pvTexCoord;
|
|
ModelMipInfo &mmi = *rm.rm_pmmiMip;
|
|
const ModelMipInfo &mmi0 = rm.rm_pmdModelData->md_MipInfos[0];
|
|
|
|
// calculate projection of viewer in absolute space
|
|
FLOATmatrix3D &mViewer = _aprProjection->pr_ViewerRotationMatrix;
|
|
_vViewer(1) = -mViewer(3,1);
|
|
_vViewer(2) = -mViewer(3,2);
|
|
_vViewer(3) = -mViewer(3,3);
|
|
// calculate projection of viewer in object space
|
|
_vViewerObj = _vViewer * !rm.rm_mObjectRotation;
|
|
|
|
_pfModelProfile.IncrementCounter( CModelProfile::PCI_VERTICES_FIRSTMIP, mmi0.mmpi_ctMipVx);
|
|
_pfModelProfile.IncrementCounter( CModelProfile::PCI_SURFACEVERTICES_FIRSTMIP, mmi0.mmpi_ctSrfVx);
|
|
_pfModelProfile.IncrementCounter( CModelProfile::PCI_TRIANGLES_FIRSTMIP, mmi0.mmpi_ctTriangles);
|
|
_pfModelProfile.IncrementCounter( CModelProfile::PCI_VERTICES_USEDMIP, mmi.mmpi_ctMipVx);
|
|
_pfModelProfile.IncrementCounter( CModelProfile::PCI_SURFACEVERTICES_USEDMIP, mmi.mmpi_ctSrfVx);
|
|
_pfModelProfile.IncrementCounter( CModelProfile::PCI_TRIANGLES_USEDMIP, mmi.mmpi_ctTriangles);
|
|
_sfStats.IncrementCounter( CStatForm::SCI_TRIANGLES_FIRSTMIP, mmi0.mmpi_ctTriangles);
|
|
_sfStats.IncrementCounter( CStatForm::SCI_TRIANGLES_USEDMIP, mmi.mmpi_ctTriangles);
|
|
|
|
// allocate vertex arrays
|
|
_ctAllMipVx = mmi.mmpi_ctMipVx;
|
|
_ctAllSrfVx = mmi.mmpi_ctSrfVx;
|
|
ASSERT( _ctAllMipVx>0 && _ctAllSrfVx>0);
|
|
ASSERT( _avtxMipBase.Count()==0); _avtxMipBase.Push(_ctAllMipVx);
|
|
ASSERT( _atexMipBase.Count()==0); _atexMipBase.Push(_ctAllMipVx);
|
|
ASSERT( _acolMipBase.Count()==0); _acolMipBase.Push(_ctAllMipVx);
|
|
ASSERT( _anorMipBase.Count()==0); _anorMipBase.Push(_ctAllMipVx);
|
|
|
|
ASSERT( _atexMipFogy.Count()==0); _atexMipFogy.Push(_ctAllMipVx);
|
|
ASSERT( _ashdMipFogy.Count()==0); _ashdMipFogy.Push(_ctAllMipVx);
|
|
ASSERT( _atx1MipHaze.Count()==0); _atx1MipHaze.Push(_ctAllMipVx);
|
|
ASSERT( _ashdMipHaze.Count()==0); _ashdMipHaze.Push(_ctAllMipVx);
|
|
|
|
ASSERT( _avtxSrfBase.Count()==0); _avtxSrfBase.Push(_ctAllSrfVx);
|
|
ASSERT( _atexSrfBase.Count()==0); _atexSrfBase.Push(_ctAllSrfVx);
|
|
ASSERT( _acolSrfBase.Count()==0); _acolSrfBase.Push(_ctAllSrfVx);
|
|
|
|
if( GFX_bTruform) {
|
|
ASSERT( _anorSrfBase.Count()==0);
|
|
_anorSrfBase.Push(_ctAllSrfVx);
|
|
}
|
|
|
|
// determine multitexturing capability for overbrighting purposes
|
|
extern INDEX mdl_bAllowOverbright;
|
|
const BOOL bOverbright = mdl_bAllowOverbright && _pGfx->gl_ctTextureUnits>1;
|
|
|
|
// saturate light and ambient color
|
|
const COLOR colL = AdjustColor( rm.rm_colLight, _slShdHueShift, _slShdSaturation);
|
|
const COLOR colA = AdjustColor( rm.rm_colAmbient, _slShdHueShift, _slShdSaturation);
|
|
// cache light intensities (-1 in case of overbrighting compensation)
|
|
const INDEX iBright = bOverbright ? 0 : 1;
|
|
_slLR = (colL & CT_RMASK)>>(CT_RSHIFT-iBright);
|
|
_slLG = (colL & CT_GMASK)>>(CT_GSHIFT-iBright);
|
|
_slLB = (colL & CT_BMASK)>>(CT_BSHIFT-iBright);
|
|
_slAR = (colA & CT_RMASK)>>(CT_RSHIFT-iBright);
|
|
_slAG = (colA & CT_GMASK)>>(CT_GSHIFT-iBright);
|
|
_slAB = (colA & CT_BMASK)>>(CT_BSHIFT-iBright);
|
|
if( bOverbright) {
|
|
_slAR = ClampUp( _slAR, 127L);
|
|
_slAG = ClampUp( _slAG, 127L);
|
|
_slAB = ClampUp( _slAB, 127L);
|
|
}
|
|
|
|
// set forced translucency and color mask
|
|
_bForceTranslucency = ((rm.rm_colBlend&CT_AMASK)>>CT_ASHIFT) != CT_OPAQUE;
|
|
_ulColorMask = mo_ColorMask;
|
|
// adjust all surfaces' params for eventual forced-translucency case
|
|
_ulMipLayerFlags = mmi.mmpi_ulLayerFlags;
|
|
if( _bForceTranslucency) {
|
|
_ulMipLayerFlags &= ~MMI_OPAQUE;
|
|
_ulMipLayerFlags |= MMI_TRANSLUCENT;
|
|
}
|
|
|
|
// unpack one model frame vertices and eventually normals (lerped or not lerped, as required)
|
|
pvtxMipBase = &_avtxMipBase[0];
|
|
pcolMipBase = &_acolMipBase[0];
|
|
pnorMipBase = &_anorMipBase[0];
|
|
const BOOL bNeedNormals = GFX_bTruform || (_ulMipLayerFlags&(SRF_REFLECTIONS|SRF_SPECULAR));
|
|
UnpackFrame( rm, bNeedNormals);
|
|
|
|
// cache some more pointers and vars
|
|
ptexMipBase = &_atexMipBase[0];
|
|
ptexMipFogy = &_atexMipFogy[0];
|
|
pshdMipFogy = &_ashdMipFogy[0];
|
|
ptx1MipHaze = &_atx1MipHaze[0];
|
|
pshdMipHaze = &_ashdMipHaze[0];
|
|
|
|
|
|
// PREPARE FOG AND HAZE MIP --------------------------------------------------------------------------
|
|
|
|
|
|
// if this model has haze
|
|
if( rm.rm_ulFlags & RMF_HAZE)
|
|
{
|
|
_pfModelProfile.StartTimer( CModelProfile::PTI_VIEW_INIT_HAZE_MIP);
|
|
_pfModelProfile.IncrementTimerAveragingCounter( CModelProfile::PTI_VIEW_INIT_HAZE_MIP, _ctAllMipVx);
|
|
// get viewer offset
|
|
// _fHazeAdd = (_vViewer%(rm.rm_vObjectPosition-_aprProjection->pr_vViewerPosition)) - _haze_hp.hp_fNear; // might cause a BUG in compiler ????
|
|
_fHazeAdd = -_haze_hp.hp_fNear;
|
|
_fHazeAdd += _vViewer(1) * (rm.rm_vObjectPosition(1) - _aprProjection->pr_vViewerPosition(1));
|
|
_fHazeAdd += _vViewer(2) * (rm.rm_vObjectPosition(2) - _aprProjection->pr_vViewerPosition(2));
|
|
_fHazeAdd += _vViewer(3) * (rm.rm_vObjectPosition(3) - _aprProjection->pr_vViewerPosition(3));
|
|
|
|
// if it'll be cost-effective (i.e. model has enough vertices to be potentionaly trivialy rejected)
|
|
// check bounding box of model against haze
|
|
if( _ctAllMipVx>12 && !IsModelInHaze( rm.rm_vObjectMinBB, rm.rm_vObjectMaxBB)) {
|
|
// this model has no haze after all
|
|
rm.rm_ulFlags &= ~RMF_HAZE;
|
|
}
|
|
// model is in haze (at least partially)
|
|
else {
|
|
// if model is all opaque
|
|
if( _ulMipLayerFlags&MMI_OPAQUE) {
|
|
// setup haze tex coords only
|
|
for( INDEX iMipVx=0; iMipVx<_ctAllMipVx; iMipVx++) {
|
|
GetHazeMapInVertex( pvtxMipBase[iMipVx], ptx1MipHaze[iMipVx]);
|
|
}
|
|
// if model is all translucent
|
|
} else if( _ulMipLayerFlags&MMI_TRANSLUCENT) {
|
|
// setup haze attenuation values only
|
|
FLOAT tx1;
|
|
for( INDEX iMipVx=0; iMipVx<_ctAllMipVx; iMipVx++) {
|
|
GetHazeMapInVertex( pvtxMipBase[iMipVx], tx1);
|
|
pshdMipHaze[iMipVx] = GetHazeAlpha(tx1) ^255;
|
|
}
|
|
// if model is partially opaque and partially translucent
|
|
} else {
|
|
// setup haze both tex coords and attenuation values
|
|
for( INDEX iMipVx=0; iMipVx<_ctAllMipVx; iMipVx++) {
|
|
FLOAT &tx1 = ptx1MipHaze[iMipVx];
|
|
GetHazeMapInVertex( pvtxMipBase[iMipVx], tx1);
|
|
pshdMipHaze[iMipVx] = GetHazeAlpha(tx1) ^255;
|
|
}
|
|
}
|
|
} // haze mip setup done
|
|
_pfModelProfile.StopTimer( CModelProfile::PTI_VIEW_INIT_HAZE_MIP);
|
|
}
|
|
|
|
// if this model has fog
|
|
if( rm.rm_ulFlags & RMF_FOG)
|
|
{
|
|
_pfModelProfile.StartTimer( CModelProfile::PTI_VIEW_INIT_FOG_MIP);
|
|
_pfModelProfile.IncrementTimerAveragingCounter( CModelProfile::PTI_VIEW_INIT_FOG_MIP, _ctAllMipVx);
|
|
// get viewer -z in object space
|
|
_vFViewerObj = FLOAT3D(0,0,-1) * !rm.rm_mObjectToView;
|
|
// get fog direction in object space
|
|
_vHDirObj = _fog_vHDirAbs * !(!mViewer*rm.rm_mObjectToView);
|
|
// get viewer offset
|
|
// _fFogAddZ = _vViewer % (rm.rm_vObjectPosition - _aprProjection->pr_vViewerPosition); // BUG in compiler !!!!
|
|
_fFogAddZ = _vViewer(1) * (rm.rm_vObjectPosition(1) - _aprProjection->pr_vViewerPosition(1));
|
|
_fFogAddZ += _vViewer(2) * (rm.rm_vObjectPosition(2) - _aprProjection->pr_vViewerPosition(2));
|
|
_fFogAddZ += _vViewer(3) * (rm.rm_vObjectPosition(3) - _aprProjection->pr_vViewerPosition(3));
|
|
// get fog offset
|
|
_fFogAddH = (_fog_vHDirAbs % rm.rm_vObjectPosition) + _fog_fp.fp_fH3;
|
|
|
|
// if it'll be cost-effective (i.e. model has enough vertices to be potentionaly trivialy rejected)
|
|
// check bounding box of model against fog
|
|
if( _ctAllMipVx>16 && !IsModelInFog( rm.rm_vObjectMinBB, rm.rm_vObjectMaxBB)) {
|
|
// this model has no fog after all
|
|
rm.rm_ulFlags &= ~RMF_FOG;
|
|
}
|
|
// model is in fog (at least partially)
|
|
else {
|
|
// if model is all opaque
|
|
if( _ulMipLayerFlags&MMI_OPAQUE) {
|
|
// setup for tex coords only
|
|
for( INDEX iMipVx=0; iMipVx<_ctAllMipVx; iMipVx++) {
|
|
GetFogMapInVertex( pvtxMipBase[iMipVx], ptexMipFogy[iMipVx]);
|
|
}
|
|
// if model is all translucent
|
|
} else if( _ulMipLayerFlags&MMI_TRANSLUCENT) {
|
|
// setup fog attenuation values only
|
|
GFXTexCoord tex;
|
|
for( INDEX iMipVx=0; iMipVx<_ctAllMipVx; iMipVx++) {
|
|
GetFogMapInVertex( pvtxMipBase[iMipVx], tex);
|
|
pshdMipFogy[iMipVx] = GetFogAlpha(tex) ^255;
|
|
}
|
|
// if model is partially opaque and partially translucent
|
|
} else {
|
|
// setup fog both tex coords and attenuation values
|
|
for( INDEX iMipVx=0; iMipVx<_ctAllMipVx; iMipVx++) {
|
|
GFXTexCoord &tex = ptexMipFogy[iMipVx];
|
|
GetFogMapInVertex( pvtxMipBase[iMipVx], tex);
|
|
pshdMipFogy[iMipVx] = GetFogAlpha(tex) ^255;
|
|
}
|
|
}
|
|
} // fog mip setup done
|
|
_pfModelProfile.StopTimer( CModelProfile::PTI_VIEW_INIT_FOG_MIP);
|
|
}
|
|
|
|
// begin model rendering
|
|
const BOOL bModelSetupTimer = _sfStats.CheckTimer(CStatForm::STI_MODELSETUP);
|
|
if( bModelSetupTimer) _sfStats.StopTimer(CStatForm::STI_MODELSETUP);
|
|
_sfStats.StartTimer(CStatForm::STI_MODELRENDERING);
|
|
|
|
|
|
// PREPARE SURFACE VERTICES ------------------------------------------------------------------------
|
|
|
|
|
|
_pfModelProfile.StartTimer( CModelProfile::PTI_VIEW_INIT_VERTICES);
|
|
_pfModelProfile.IncrementTimerAveragingCounter( CModelProfile::PTI_VIEW_INIT_VERTICES, _ctAllSrfVx);
|
|
|
|
// for each surface in current mip model
|
|
BOOL bEmpty = TRUE;
|
|
{FOREACHINSTATICARRAY( mmi.mmpi_MappingSurfaces, MappingSurface, itms)
|
|
{
|
|
const MappingSurface &ms = *itms;
|
|
iSrfVx0 = ms.ms_iSrfVx0;
|
|
ctSrfVx = ms.ms_ctSrfVx;
|
|
// skip to next in case of invisible or empty surface
|
|
if( (ms.ms_ulRenderingFlags&SRF_INVISIBLE) || ctSrfVx==0) break;
|
|
bEmpty = FALSE;
|
|
puwSrfToMip = &mmi.mmpi_auwSrfToMip[iSrfVx0];
|
|
pvtxSrfBase = &_avtxSrfBase[iSrfVx0];
|
|
INDEX iSrfVx;
|
|
|
|
#if ASMOPT == 1
|
|
__asm {
|
|
push ebx
|
|
mov ebx,D [puwSrfToMip]
|
|
mov esi,D [pvtxMipBase]
|
|
mov edi,D [pvtxSrfBase]
|
|
mov ecx,D [ctSrfVx]
|
|
srfVtxLoop:
|
|
movzx eax,W [ebx]
|
|
lea eax,[eax*2+eax] // *3
|
|
mov edx,D [esi+eax*4+0]
|
|
movq mm1,Q [esi+eax*4+4]
|
|
mov D [edi+0],edx
|
|
movq Q [edi+4],mm1
|
|
add ebx,2
|
|
add edi,4*4
|
|
dec ecx
|
|
jnz srfVtxLoop
|
|
emms
|
|
pop ebx
|
|
}
|
|
#else
|
|
// setup vetrex array
|
|
for( iSrfVx=0; iSrfVx<ctSrfVx; iSrfVx++) {
|
|
const INDEX iMipVx = puwSrfToMip[iSrfVx];
|
|
pvtxSrfBase[iSrfVx].x = pvtxMipBase[iMipVx].x;
|
|
pvtxSrfBase[iSrfVx].y = pvtxMipBase[iMipVx].y;
|
|
pvtxSrfBase[iSrfVx].z = pvtxMipBase[iMipVx].z;
|
|
}
|
|
#endif
|
|
// setup normal array for truform (if enabled)
|
|
if( GFX_bTruform) {
|
|
GFXNormal *pnorSrfBase = &_anorSrfBase[iSrfVx0];
|
|
for( iSrfVx=0; iSrfVx<ctSrfVx; iSrfVx++) {
|
|
const INDEX iMipVx = puwSrfToMip[iSrfVx];
|
|
pnorSrfBase[iSrfVx].nx = pnorMipBase[iMipVx].nx;
|
|
pnorSrfBase[iSrfVx].ny = pnorMipBase[iMipVx].ny;
|
|
pnorSrfBase[iSrfVx].nz = pnorMipBase[iMipVx].nz;
|
|
}
|
|
}
|
|
}}
|
|
// prepare (and lock) vertex array
|
|
gfxEnableDepthTest();
|
|
gfxSetVertexArray( &_avtxSrfBase[0], _ctAllSrfVx);
|
|
if(GFX_bTruform) gfxSetNormalArray( &_anorSrfBase[0]);
|
|
if(CVA_bModels) gfxLockArrays();
|
|
// cache light in object space (for reflection and/or specular mapping)
|
|
_vLightObj = rm.rm_vLightObj;
|
|
// texture mapping correction factors (mex -> norm float)
|
|
FLOAT fTexCorrU, fTexCorrV;
|
|
gfxSetTextureWrapping( GFX_REPEAT, GFX_REPEAT);
|
|
// color and fill mode setup
|
|
_bFlatFill = (rm.rm_rtRenderType&RT_WHITE_TEXTURE) || mo_toTexture.GetData()==NULL;
|
|
const BOOL bTexMode = rm.rm_rtRenderType & (RT_TEXTURE|RT_WHITE_TEXTURE);
|
|
const BOOL bAllLayers = bTexMode && !_bFlatFill; // disallow rendering of every layer except diffuse
|
|
|
|
// model surface vertices prepared
|
|
_pfModelProfile.StopTimer( CModelProfile::PTI_VIEW_INIT_VERTICES);
|
|
|
|
|
|
// RENDER DIFFUSE LAYER -------------------------------------------------------------------
|
|
|
|
|
|
_pfModelProfile.StartTimer( CModelProfile::PTI_VIEW_INIT_DIFF_SURF);
|
|
_pfModelProfile.IncrementTimerAveragingCounter( CModelProfile::PTI_VIEW_INIT_DIFF_SURF, _ctAllSrfVx);
|
|
|
|
// get diffuse texture corrections
|
|
CTextureData *ptdDiff = (CTextureData*)mo_toTexture.GetData();
|
|
if( ptdDiff!=NULL) {
|
|
fTexCorrU = 1.0f / ptdDiff->GetWidth();
|
|
fTexCorrV = 1.0f / ptdDiff->GetHeight();
|
|
} else {
|
|
fTexCorrU = 1.0f;
|
|
fTexCorrV = 1.0f;
|
|
}
|
|
|
|
// get model diffuse color
|
|
GFXColor colMdlDiff;
|
|
const COLOR colD = AdjustColor( rm.rm_pmdModelData->md_colDiffuse, _slTexHueShift, _slTexSaturation);
|
|
const COLOR colB = AdjustColor( rm.rm_colBlend, _slTexHueShift, _slTexSaturation);
|
|
colMdlDiff.MultiplyRGBA( colD, colB);
|
|
|
|
// for each surface in current mip model
|
|
{FOREACHINSTATICARRAY( mmi.mmpi_MappingSurfaces, MappingSurface, itms)
|
|
{
|
|
const MappingSurface &ms = *itms;
|
|
iSrfVx0 = ms.ms_iSrfVx0;
|
|
ctSrfVx = ms.ms_ctSrfVx;
|
|
if( (ms.ms_ulRenderingFlags&SRF_INVISIBLE) || ctSrfVx==0) break; // done if found invisible or empty surface
|
|
// cache surface pointers
|
|
puwSrfToMip = &mmi.mmpi_auwSrfToMip[iSrfVx0];
|
|
pvTexCoord = &mmi.mmpi_avmexTexCoord[iSrfVx0];
|
|
ptexSrfBase = &_atexSrfBase[iSrfVx0];
|
|
pcolSrfBase = &_acolSrfBase[iSrfVx0];
|
|
|
|
// get surface diffuse color and combine with model color
|
|
GFXColor colSrfDiff;
|
|
const COLOR colD = AdjustColor( ms.ms_colDiffuse, _slTexHueShift, _slTexSaturation);
|
|
colSrfDiff.MultiplyRGBA( colD, colMdlDiff);
|
|
|
|
#if ASMOPT == 1
|
|
// setup texcoord array
|
|
__asm {
|
|
push ebx
|
|
mov esi,D [pvTexCoord]
|
|
mov edi,D [ptexSrfBase]
|
|
mov ecx,D [ctSrfVx]
|
|
shr ecx,1
|
|
jz vtxRest
|
|
vtxLoop:
|
|
fld D [esi+0]
|
|
fmul D [fTexCorrU]
|
|
fld D [esi+8]
|
|
fmul D [fTexCorrU]
|
|
fld D [esi+4]
|
|
fmul D [fTexCorrV]
|
|
fld D [esi+12]
|
|
fmul D [fTexCorrV]
|
|
fxch st(3) // u1, v1, u2, v2
|
|
fstp D [edi+0]
|
|
fstp D [edi+4]
|
|
fstp D [edi+8]
|
|
fstp D [edi+12]
|
|
add esi,2*2*4
|
|
add edi,2*2*4
|
|
dec ecx
|
|
jnz vtxLoop
|
|
vtxRest:
|
|
test D [ctSrfVx],1
|
|
jz vtxEnd
|
|
fld D [esi+0]
|
|
fmul D [fTexCorrU]
|
|
fld D [esi+4]
|
|
fmul D [fTexCorrV]
|
|
fxch st(1)
|
|
fstp D [edi+0]
|
|
fstp D [edi+4]
|
|
vtxEnd:
|
|
pop ebx
|
|
}
|
|
#else
|
|
// setup texcoord array
|
|
for( INDEX iSrfVx=0; iSrfVx<ctSrfVx; iSrfVx++) {
|
|
ptexSrfBase[iSrfVx].s = pvTexCoord[iSrfVx](1) *fTexCorrU;
|
|
ptexSrfBase[iSrfVx].t = pvTexCoord[iSrfVx](2) *fTexCorrV;
|
|
}
|
|
#endif
|
|
|
|
// setup color array
|
|
if( ms.ms_sstShadingType==SST_FULLBRIGHT) {
|
|
// eventually adjust reflection color for overbrighting
|
|
GFXColor colSrfDiffAdj = colSrfDiff;
|
|
if( bOverbright) {
|
|
colSrfDiffAdj.r >>=1;
|
|
colSrfDiffAdj.g >>=1;
|
|
colSrfDiffAdj.b >>=1;
|
|
} // just copy diffuse color
|
|
for( INDEX iSrfVx=0; iSrfVx<ctSrfVx; iSrfVx++) pcolSrfBase[iSrfVx] = colSrfDiffAdj;
|
|
}
|
|
else {
|
|
#if ASMOPT == 1
|
|
// setup color array
|
|
const COLOR colS = colSrfDiff.abgr;
|
|
__asm {
|
|
push ebx
|
|
mov ebx,D [puwSrfToMip]
|
|
mov esi,D [pcolMipBase]
|
|
mov edi,D [pcolSrfBase]
|
|
pxor mm0,mm0
|
|
movd mm4,D [colS]
|
|
punpcklbw mm4,mm0
|
|
psllw mm4,7
|
|
paddw mm4,Q [mmRounder]
|
|
xor ecx,ecx
|
|
diffColLoop:
|
|
movzx eax,W [ebx+ecx*2]
|
|
movd mm1,D [esi+eax*4]
|
|
punpcklbw mm1,mm0
|
|
por mm1,Q [mmF000]
|
|
psllw mm1,1
|
|
pmulhw mm1,mm4
|
|
packuswb mm1,mm1
|
|
movd D [edi+ecx*4],mm1
|
|
inc ecx
|
|
cmp ecx,D [ctSrfVx]
|
|
jl diffColLoop
|
|
emms
|
|
pop ebx
|
|
}
|
|
#else
|
|
// setup diffuse color array
|
|
for( INDEX iSrfVx=0; iSrfVx<ctSrfVx; iSrfVx++) {
|
|
const INDEX iMipVx = puwSrfToMip[iSrfVx];
|
|
pcolSrfBase[iSrfVx].MultiplyRGBCopyA1( colSrfDiff, pcolMipBase[iMipVx]);
|
|
}
|
|
#endif
|
|
}
|
|
// eventually attenuate color in case of fog or haze
|
|
if( (ms.ms_ulRenderingFlags&SRF_OPAQUE) && !_bForceTranslucency) continue;
|
|
// eventually do some haze and/or fog attenuation of alpha channel in surface
|
|
if( rm.rm_ulFlags & RMF_HAZE) {
|
|
if( ms.ms_sttTranslucencyType==STT_MULTIPLY) AttenuateColor( pshdMipHaze, ctSrfVx);
|
|
else AttenuateAlpha( pshdMipHaze, ctSrfVx);
|
|
}
|
|
if( rm.rm_ulFlags & RMF_FOG) {
|
|
if( ms.ms_sttTranslucencyType==STT_MULTIPLY) AttenuateColor( pshdMipFogy, ctSrfVx);
|
|
else AttenuateAlpha( pshdMipFogy, ctSrfVx);
|
|
}
|
|
}}
|
|
// done with diffuse surfaces setup
|
|
_pfModelProfile.StopTimer( CModelProfile::PTI_VIEW_INIT_DIFF_SURF);
|
|
|
|
// if no texture mode is active
|
|
if( !bTexMode && _eAPI==GAT_OGL) {
|
|
gfxUnlockArrays();
|
|
// just render colors
|
|
RenderColors(rm);
|
|
// and eventually wireframe
|
|
RenderWireframe(rm);
|
|
// done
|
|
gfxDepthFunc( GFX_LESS_EQUAL);
|
|
gfxCullFace(GFX_BACK);
|
|
// reset to defaults
|
|
ResetVertexArrays();
|
|
// done
|
|
_sfStats.StopTimer(CStatForm::STI_MODELRENDERING);
|
|
if( bModelSetupTimer) _sfStats.StartTimer(CStatForm::STI_MODELSETUP);
|
|
_pfModelProfile.StopTimer( CModelProfile::PTI_VIEW_RENDERMODEL);
|
|
return;
|
|
}
|
|
|
|
// proceed with rendering
|
|
_pfModelProfile.StartTimer( CModelProfile::PTI_VIEW_RENDER_DIFFUSE);
|
|
_pfModelProfile.IncrementTimerAveragingCounter( CModelProfile::PTI_VIEW_RENDER_DIFFUSE);
|
|
|
|
// must render diffuse if there is no texture (white mode)
|
|
if( (_ulMipLayerFlags&SRF_DIFFUSE) || ptdDiff==NULL)
|
|
{
|
|
// prepare overbrighting if supported
|
|
if( bOverbright) gfxSetTextureModulation(2);
|
|
// set texture/color arrays
|
|
INDEX iFrame=0;
|
|
if( ptdDiff!=NULL) iFrame = mo_toTexture.GetFrame();;
|
|
SetCurrentTexture( ptdDiff, iFrame);
|
|
gfxSetTexCoordArray( &_atexSrfBase[0], FALSE);
|
|
gfxSetColorArray( &_acolSrfBase[0]);
|
|
// do rendering
|
|
RenderOneSide( rm, TRUE, SRF_DIFFUSE);
|
|
RenderOneSide( rm, FALSE, SRF_DIFFUSE);
|
|
// revert to normal brightness if overbrighting was on
|
|
if( bOverbright) gfxSetTextureModulation(1);
|
|
}
|
|
|
|
// adjust z-buffer and blending functions
|
|
if( _ulMipLayerFlags&MMI_OPAQUE) gfxDepthFunc( GFX_EQUAL);
|
|
else gfxDepthFunc( GFX_LESS_EQUAL);
|
|
gfxDisableDepthWrite();
|
|
gfxDisableAlphaTest(); // disable alpha testing if enabled after some surface
|
|
gfxEnableBlend();
|
|
|
|
// done with diffuse
|
|
_pfModelProfile.StopTimer( CModelProfile::PTI_VIEW_RENDER_DIFFUSE);
|
|
|
|
|
|
// RENDER PATCHES -------------------------------------------------------------------
|
|
|
|
|
|
// if patches are on and are enabled for this mip model
|
|
// TODO !!!!
|
|
// if( (mo_PatchMask!=0) && (mmi.mmpi_ulFlags&MM_PATCHES_VISIBLE)) RenderPatches_View(rm);
|
|
|
|
|
|
|
|
// RENDER DETAIL LAYER -------------------------------------------------------------------
|
|
|
|
|
|
// if this model has detail mapping
|
|
extern INDEX mdl_bRenderDetail;
|
|
CTextureData *ptdBump = (CTextureData*)mo_toBump.GetData();
|
|
const ULONG ulTransAlpha = (rm.rm_colBlend&CT_AMASK)>>CT_ASHIFT;
|
|
if( (_ulMipLayerFlags&(SRF_DETAIL|SRF_BUMP)) && mdl_bRenderDetail && ptdBump!=NULL && ulTransAlpha>192 && bAllLayers)
|
|
{
|
|
_pfModelProfile.StartTimer( CModelProfile::PTI_VIEW_INIT_BUMP_SURF);
|
|
_pfModelProfile.IncrementTimerAveragingCounter( CModelProfile::PTI_VIEW_INIT_BUMP_SURF, _ctAllSrfVx);
|
|
|
|
// get model detail color
|
|
GFXColor colMdlBump;
|
|
const COLOR colB = AdjustColor( rm.rm_pmdModelData->md_colBump, _slTexHueShift, _slTexSaturation);
|
|
colMdlBump.abgr = ByteSwap(colB);
|
|
// get detail texture corrections
|
|
fTexCorrU = 1.0f / ptdBump->GetWidth();
|
|
fTexCorrV = 1.0f / ptdBump->GetHeight();
|
|
// adjust detail stretch if needed get model's stretch
|
|
if( !(rm.rm_pmdModelData->md_Flags&MF_STRETCH_DETAIL)) {
|
|
const FLOAT fStretch = mo_Stretch.Length() * 0.57735f; // /Sqrt(3);
|
|
fTexCorrU *= fStretch;
|
|
fTexCorrV *= fStretch;
|
|
}
|
|
|
|
// for each bump surface in current mip model
|
|
FOREACHINSTATICARRAY( mmi.mmpi_MappingSurfaces, MappingSurface, itms)
|
|
{
|
|
const MappingSurface &ms = *itms;
|
|
iSrfVx0 = ms.ms_iSrfVx0;
|
|
ctSrfVx = ms.ms_ctSrfVx;
|
|
if( (ms.ms_ulRenderingFlags&SRF_INVISIBLE) || ctSrfVx==0) break; // done if found invisible or empty surface
|
|
if( !(ms.ms_ulRenderingFlags&SRF_DETAIL)) continue; // skip non-detail surface
|
|
// cache surface pointers
|
|
pvTexCoord = &mmi.mmpi_avmexTexCoord[iSrfVx0];
|
|
puwSrfToMip = &mmi.mmpi_auwSrfToMip[iSrfVx0];
|
|
ptexSrfBase = &_atexSrfBase[iSrfVx0];
|
|
pcolSrfBase = &_acolSrfBase[iSrfVx0];
|
|
// get surface detail color and combine with model color
|
|
GFXColor colSrfBump;
|
|
const COLOR colB = AdjustColor( ms.ms_colBump, _slTexHueShift, _slTexSaturation);
|
|
colSrfBump.MultiplyRGB( colB, colMdlBump);
|
|
|
|
// for each vertex in the surface
|
|
for( INDEX iSrfVx=0; iSrfVx<ctSrfVx; iSrfVx++) {
|
|
// set detail texcoord and color
|
|
INDEX iMipVx = mmi.mmpi_auwSrfToMip[iSrfVx];
|
|
ptexSrfBase[iSrfVx].s = pvTexCoord[iSrfVx](1) * fTexCorrU;
|
|
ptexSrfBase[iSrfVx].t = pvTexCoord[iSrfVx](2) * fTexCorrV;
|
|
pcolSrfBase[iSrfVx] = colSrfBump;
|
|
}
|
|
}
|
|
// detail prep done
|
|
_pfModelProfile.StopTimer( CModelProfile::PTI_VIEW_INIT_BUMP_SURF);
|
|
|
|
// begin rendering
|
|
_pfModelProfile.StartTimer( CModelProfile::PTI_VIEW_RENDER_BUMP);
|
|
_pfModelProfile.IncrementTimerAveragingCounter( CModelProfile::PTI_VIEW_RENDER_BUMP);
|
|
|
|
// setup texture/color arrays and rendering mode
|
|
SetCurrentTexture( ptdBump, mo_toBump.GetFrame());
|
|
gfxSetTexCoordArray( &_atexSrfBase[0], FALSE);
|
|
gfxSetColorArray( &_acolSrfBase[0]);
|
|
gfxDisableAlphaTest();
|
|
gfxBlendFunc( GFX_DST_COLOR, GFX_SRC_COLOR);
|
|
// do rendering
|
|
RenderOneSide( rm, TRUE, SRF_DETAIL);
|
|
RenderOneSide( rm, FALSE, SRF_DETAIL);
|
|
|
|
// detail rendering done
|
|
_pfModelProfile.StopTimer( CModelProfile::PTI_VIEW_RENDER_BUMP);
|
|
}
|
|
|
|
|
|
// RENDER REFLECTION LAYER -------------------------------------------------------------------
|
|
|
|
|
|
// if this model has reflection mapping
|
|
extern INDEX mdl_bRenderReflection;
|
|
CTextureData *ptdReflection = (CTextureData*)mo_toReflection.GetData();
|
|
if( (_ulMipLayerFlags&SRF_REFLECTIONS) && mdl_bRenderReflection && ptdReflection!=NULL && bAllLayers)
|
|
{
|
|
_pfModelProfile.StartTimer( CModelProfile::PTI_VIEW_INIT_REFL_MIP);
|
|
_pfModelProfile.IncrementTimerAveragingCounter( CModelProfile::PTI_VIEW_INIT_REFL_MIP, _ctAllMipVx);
|
|
// cache rotation
|
|
const FLOATmatrix3D &m = rm.rm_mObjectRotation;
|
|
|
|
#if ASMOPT == 1
|
|
__asm {
|
|
push ebx
|
|
mov ebx,D [m]
|
|
mov esi,D [pnorMipBase]
|
|
mov edi,D [ptexMipBase]
|
|
mov ecx,D [_ctAllMipVx]
|
|
reflMipLoop:
|
|
// get normal in absolute space
|
|
fld D [esi]GFXNormal.nx
|
|
fmul D [ebx+ 0*3+0]
|
|
fld D [esi]GFXNormal.ny
|
|
fmul D [ebx+ 0*3+4]
|
|
fld D [esi]GFXNormal.nz
|
|
fmul D [ebx+ 0*3+8]
|
|
fxch st(2)
|
|
faddp st(1),st(0)
|
|
faddp st(1),st(0) // fNx
|
|
fld D [esi]GFXNormal.nx
|
|
fmul D [ebx+ 4*3+0]
|
|
fld D [esi]GFXNormal.ny
|
|
fmul D [ebx+ 4*3+4]
|
|
fld D [esi]GFXNormal.nz
|
|
fmul D [ebx+ 4*3+8]
|
|
fxch st(2)
|
|
faddp st(1),st(0)
|
|
faddp st(1),st(0) // fNy, fNx
|
|
fld D [esi]GFXNormal.nx
|
|
fmul D [ebx+ 8*3+0]
|
|
fld D [esi]GFXNormal.ny
|
|
fmul D [ebx+ 8*3+4]
|
|
fld D [esi]GFXNormal.nz
|
|
fmul D [ebx+ 8*3+8]
|
|
fxch st(2)
|
|
faddp st(1),st(0)
|
|
faddp st(1),st(0) // fNz, fNy, fNx
|
|
// reflect viewer around normal
|
|
fld D [_vViewer+0]
|
|
fmul st(0), st(3)
|
|
fld D [_vViewer+4]
|
|
fmul st(0), st(3)
|
|
fld D [_vViewer+8]
|
|
fmul st(0), st(3) // vNz, vNy, vNx, fNz, fNy, fNx
|
|
fxch st(2)
|
|
faddp st(1),st(0)
|
|
faddp st(1),st(0) // fNV, fNz, fNy, fNx
|
|
fmul st(3),st(0)
|
|
fmul st(2),st(0)
|
|
fmulp st(1),st(0) // fNz*fNV, fNy*fNV, fNx*fNV
|
|
fxch st(2)
|
|
fadd st(0),st(0) // 2*fNx*fNV, fNy*fNV, fNz*fNV
|
|
fxch st(1)
|
|
fadd st(0),st(0) // 2*fNy*fNV, 2*fNx*fNV, fNz*fNV
|
|
fxch st(2)
|
|
fadd st(0),st(0) // 2*fNz*fNV, 2*fNx*fNV, 2*fNy*fNV
|
|
fxch st(1) // 2*fNx*fNV, 2*fNz*fNV, 2*fNy*fNV
|
|
fsubr D [_vViewer+0]
|
|
fxch st(2) // 2*fNy*fNV, 2*fNz*fNV, fRVx
|
|
fsubr D [_vViewer+4]
|
|
fxch st(1) // 2*fNz*fNV, fRVy, fRVx
|
|
fsubr D [_vViewer+8] // fRVz, fRVy, fRVx
|
|
// calc 1oFM
|
|
fxch st(1) // fRVy, fRVz, fRVx
|
|
fadd st(0),st(0)
|
|
fadd D [f2]
|
|
fsqrt
|
|
fdivr D [f05]
|
|
// map reflected vector to texture
|
|
fmul st(2),st(0) // f1oFM, fRVz, s
|
|
fmulp st(1),st(0)
|
|
fxch st(1) // s, t
|
|
fadd D [f05]
|
|
fxch st(1)
|
|
fadd D [f05]
|
|
fxch st(1)
|
|
fstp D [edi+0]
|
|
fstp D [edi+4]
|
|
add esi,3*4
|
|
add edi,2*4
|
|
dec ecx
|
|
jnz reflMipLoop
|
|
pop ebx
|
|
}
|
|
#else
|
|
// for each mip vertex
|
|
for( INDEX iMipVx=0; iMipVx<_ctAllMipVx; iMipVx++)
|
|
{ // get normal in absolute space
|
|
const GFXNormal &nor = pnorMipBase[iMipVx];
|
|
const FLOAT fNx = nor.nx*m(1,1) + nor.ny*m(1,2) + nor.nz*m(1,3);
|
|
const FLOAT fNy = nor.nx*m(2,1) + nor.ny*m(2,2) + nor.nz*m(2,3);
|
|
const FLOAT fNz = nor.nx*m(3,1) + nor.ny*m(3,2) + nor.nz*m(3,3);
|
|
// reflect viewer around normal
|
|
const FLOAT fNV = fNx*_vViewer(1) + fNy*_vViewer(2) + fNz*_vViewer(3);
|
|
const FLOAT fRVx = _vViewer(1) - 2*fNx*fNV;
|
|
const FLOAT fRVy = _vViewer(2) - 2*fNy*fNV;
|
|
const FLOAT fRVz = _vViewer(3) - 2*fNz*fNV;
|
|
// map reflected vector to texture
|
|
// NOTE: using X and Z axes, so that singularity gets on -Y axis (where it will least probably be seen)
|
|
const FLOAT f1oFM = 0.5f / sqrt(2+2*fRVy);
|
|
ptexMipBase[iMipVx].s = fRVx*f1oFM +0.5f;
|
|
ptexMipBase[iMipVx].t = fRVz*f1oFM +0.5f;
|
|
}
|
|
#endif
|
|
_pfModelProfile.StopTimer( CModelProfile::PTI_VIEW_INIT_REFL_MIP);
|
|
|
|
// setup surface vertices
|
|
_pfModelProfile.StartTimer( CModelProfile::PTI_VIEW_INIT_REFL_SURF);
|
|
_pfModelProfile.IncrementTimerAveragingCounter( CModelProfile::PTI_VIEW_INIT_REFL_SURF, _ctAllSrfVx);
|
|
|
|
// get model reflection color
|
|
GFXColor colMdlRefl;
|
|
const COLOR colR = AdjustColor( rm.rm_pmdModelData->md_colReflections, _slTexHueShift, _slTexSaturation);
|
|
colMdlRefl.abgr = ByteSwap(colR);
|
|
colMdlRefl.AttenuateA( (rm.rm_colBlend&CT_AMASK)>>CT_ASHIFT);
|
|
|
|
// for each reflective surface in current mip model
|
|
FOREACHINSTATICARRAY( mmi.mmpi_MappingSurfaces, MappingSurface, itms)
|
|
{
|
|
const MappingSurface &ms = *itms;
|
|
iSrfVx0 = ms.ms_iSrfVx0;
|
|
ctSrfVx = ms.ms_ctSrfVx;
|
|
if( (ms.ms_ulRenderingFlags&SRF_INVISIBLE) || ctSrfVx==0) break; // done if found invisible or empty surface
|
|
if( !(ms.ms_ulRenderingFlags&SRF_REFLECTIONS)) continue; // skip non-reflection surface
|
|
// cache surface pointers
|
|
puwSrfToMip = &mmi.mmpi_auwSrfToMip[iSrfVx0];
|
|
ptexSrfBase = &_atexSrfBase[iSrfVx0];
|
|
pcolSrfBase = &_acolSrfBase[iSrfVx0];
|
|
// get surface reflection color and combine with model color
|
|
GFXColor colSrfRefl;
|
|
const COLOR colR = AdjustColor( ms.ms_colReflections, _slTexHueShift, _slTexSaturation);
|
|
colSrfRefl.MultiplyRGBA( colR, colMdlRefl);
|
|
|
|
// set reflection texture coords
|
|
for( INDEX iSrfVx=0; iSrfVx<ctSrfVx; iSrfVx++) {
|
|
const INDEX iMipVx = puwSrfToMip[iSrfVx];
|
|
ptexSrfBase[iSrfVx] = ptexMipBase[iMipVx];
|
|
}
|
|
// for full-bright surfaces
|
|
if( ms.ms_sstShadingType==SST_FULLBRIGHT) {
|
|
// just copy reflection color
|
|
for( INDEX iSrfVx=0; iSrfVx<ctSrfVx; iSrfVx++) pcolSrfBase[iSrfVx] = colSrfRefl;
|
|
}
|
|
else { // for smooth surface
|
|
for( INDEX iSrfVx=0; iSrfVx<ctSrfVx; iSrfVx++) {
|
|
// set reflection color smooth
|
|
const INDEX iMipVx = puwSrfToMip[iSrfVx];
|
|
pcolSrfBase[iSrfVx].MultiplyRGBCopyA1( colSrfRefl, pcolMipBase[iMipVx]);
|
|
}
|
|
}
|
|
// eventually attenuate color in case of fog or haze
|
|
if( (ms.ms_ulRenderingFlags&SRF_OPAQUE) && !_bForceTranslucency) continue;
|
|
// eventually do some haze and/or fog attenuation of alpha channel in surface
|
|
if( rm.rm_ulFlags & RMF_HAZE) {
|
|
if( ms.ms_sttTranslucencyType==STT_MULTIPLY) AttenuateColor( pshdMipHaze, ctSrfVx);
|
|
else AttenuateAlpha( pshdMipHaze, ctSrfVx);
|
|
}
|
|
if( rm.rm_ulFlags & RMF_FOG) {
|
|
if( ms.ms_sttTranslucencyType==STT_MULTIPLY) AttenuateColor( pshdMipFogy, ctSrfVx);
|
|
else AttenuateAlpha( pshdMipFogy, ctSrfVx);
|
|
}
|
|
}
|
|
// reflection prep done
|
|
_pfModelProfile.StopTimer( CModelProfile::PTI_VIEW_INIT_REFL_SURF);
|
|
|
|
// begin rendering
|
|
_pfModelProfile.StartTimer( CModelProfile::PTI_VIEW_RENDER_REFLECTIONS);
|
|
_pfModelProfile.IncrementTimerAveragingCounter( CModelProfile::PTI_VIEW_RENDER_REFLECTIONS);
|
|
|
|
// setup texture/color arrays and rendering mode
|
|
SetCurrentTexture( ptdReflection, mo_toReflection.GetFrame());
|
|
gfxSetTexCoordArray( &_atexSrfBase[0], FALSE);
|
|
gfxSetColorArray( &_acolSrfBase[0]);
|
|
gfxBlendFunc( GFX_SRC_ALPHA, GFX_INV_SRC_ALPHA);
|
|
// do rendering
|
|
RenderOneSide( rm, TRUE, SRF_REFLECTIONS);
|
|
RenderOneSide( rm, FALSE, SRF_REFLECTIONS);
|
|
|
|
// reflection rendering done
|
|
_pfModelProfile.StopTimer( CModelProfile::PTI_VIEW_RENDER_REFLECTIONS);
|
|
}
|
|
|
|
|
|
// RENDER SPECULAR LAYER -------------------------------------------------------------------
|
|
|
|
|
|
// if this model has specular mapping
|
|
extern INDEX mdl_bRenderSpecular;
|
|
CTextureData *ptdSpecular = (CTextureData*)mo_toSpecular.GetData();
|
|
if( (_ulMipLayerFlags&SRF_SPECULAR) && mdl_bRenderSpecular && ptdSpecular!=NULL && bAllLayers)
|
|
{
|
|
_pfModelProfile.StartTimer( CModelProfile::PTI_VIEW_INIT_SPEC_MIP);
|
|
_pfModelProfile.IncrementTimerAveragingCounter( CModelProfile::PTI_VIEW_INIT_SPEC_MIP, _ctAllMipVx);
|
|
// cache object view rotation
|
|
const FLOATmatrix3D &m = rm.rm_mObjectToView;
|
|
|
|
#if ASMOPT == 1
|
|
__asm {
|
|
push ebx
|
|
mov ebx,D [m]
|
|
mov esi,D [pnorMipBase]
|
|
mov edi,D [ptexMipBase]
|
|
mov ecx,D [_ctAllMipVx]
|
|
specMipLoop:
|
|
// reflect light vector around vertex normal in object space
|
|
fld D [esi]GFXNormal.nx
|
|
fmul D [_vLightObj+0]
|
|
fld D [esi]GFXNormal.ny
|
|
fmul D [_vLightObj+4]
|
|
fld D [esi]GFXNormal.nz
|
|
fmul D [_vLightObj+8]
|
|
fxch st(2)
|
|
faddp st(1),st(0)
|
|
faddp st(1),st(0) // fNL
|
|
fld D [esi]GFXNormal.nx
|
|
fmul st(0),st(1) // fnl*nx, fnl
|
|
fld D [esi]GFXNormal.ny
|
|
fmul st(0),st(2) // fnl*ny, fnl*nx, fnl
|
|
fld D [esi]GFXNormal.nz
|
|
fmulp st(3),st(0) // fnl*ny, fnl*nx, fnl*nz
|
|
fxch st(1) // fnl*nx, fnl*ny, fnl*nz
|
|
fadd st(0),st(0)
|
|
fxch st(1) // fnl*ny, 2*fnl*nx, fnl*nz
|
|
fadd st(0),st(0)
|
|
fxch st(2)
|
|
fadd st(0),st(0)
|
|
fxch st(1) // 2*fnl*nx, 2*fnl*nz, 2*fnl*ny
|
|
fsubr D [_vLightObj+0]
|
|
fxch st(2) // 2*fnl*ny, 2*fnl*nz, fRx
|
|
fsubr D [_vLightObj+4]
|
|
fxch st(1) // 2*fnl*nz, fRy, fRx
|
|
fsubr D [_vLightObj+8]
|
|
fxch st(2) // fRx, fRy, fRz
|
|
// transform the reflected vector to viewer space
|
|
fld D [ebx+ 8*3+0]
|
|
fmul st(0),st(1)
|
|
fld D [ebx+ 8*3+4]
|
|
fmul st(0),st(3)
|
|
fld D [ebx+ 8*3+8]
|
|
fmul st(0),st(5)
|
|
fxch st(2)
|
|
faddp st(1),st(0)
|
|
faddp st(1),st(0) // fRVz, fRx, fRy, fRz
|
|
fld D [ebx+ 4*3+0]
|
|
fmul st(0),st(2)
|
|
fld D [ebx+ 4*3+4]
|
|
fmul st(0),st(4)
|
|
fld D [ebx+ 4*3+8]
|
|
fmul st(0),st(6)
|
|
fxch st(2)
|
|
faddp st(1),st(0)
|
|
faddp st(1),st(0) // fRVy, fRVz, fRx, fRy, fRz
|
|
fxch st(2) // fRx, fRVz, fRVy, fRy, fRz
|
|
fmul D [ebx+ 0*3+0] // fRxx, fRVz, fRVy, fRy, fRz
|
|
fxch st(3)
|
|
fmul D [ebx+ 0*3+4] // fRxy, fRVz, fRVy, fRxx, fRz
|
|
fxch st(4)
|
|
fmul D [ebx+ 0*3+8] // fRxz, fRVz, fRVy, fRxx, fRxy
|
|
fxch st(3) // fRxx, fRVz, fRVy, fRxz, fRxy
|
|
faddp st(4),st(0) // fRVz, fRVy, fRxz, fRxy+fRxx
|
|
fxch st(2) // fRxz, fRVy, fRVz, fRxy+fRxx
|
|
faddp st(3),st(0) // fRVy, fRVz, fRVx
|
|
// calc 1oFM
|
|
fxch st(1) // fRVz, fRVy, fRVx
|
|
fadd st(0),st(0)
|
|
fadd D [f2]
|
|
fsqrt
|
|
fdivr D [f05]
|
|
// map reflected vector to texture
|
|
fmul st(2),st(0) // f1oFM, fRVy, s
|
|
fmulp st(1),st(0)
|
|
fxch st(1) // s, t
|
|
fadd D [f05]
|
|
fxch st(1)
|
|
fadd D [f05]
|
|
fxch st(1)
|
|
fstp D [edi+0]
|
|
fstp D [edi+4]
|
|
add esi,3*4
|
|
add edi,2*4
|
|
dec ecx
|
|
jnz specMipLoop
|
|
pop ebx
|
|
}
|
|
#else
|
|
// for each mip vertex
|
|
for( INDEX iMipVx=0; iMipVx<_ctAllMipVx; iMipVx++)
|
|
{ // reflect light vector around vertex normal in object space
|
|
GFXNormal &nor = pnorMipBase[iMipVx];
|
|
const FLOAT fNL = nor.nx*_vLightObj(1) + nor.ny*_vLightObj(2) + nor.nz*_vLightObj(3);
|
|
const FLOAT fRx = _vLightObj(1) - 2*nor.nx*fNL;
|
|
const FLOAT fRy = _vLightObj(2) - 2*nor.ny*fNL;
|
|
const FLOAT fRz = _vLightObj(3) - 2*nor.nz*fNL;
|
|
// transform the reflected vector to viewer space
|
|
const FLOAT fRVx = fRx*m(1,1) + fRy*m(1,2) + fRz*m(1,3);
|
|
const FLOAT fRVy = fRx*m(2,1) + fRy*m(2,2) + fRz*m(2,3);
|
|
const FLOAT fRVz = fRx*m(3,1) + fRy*m(3,2) + fRz*m(3,3);
|
|
// map reflected vector to texture
|
|
const FLOAT f1oFM = 0.5f / sqrt(2+2*fRVz); // was 2*sqrt(2+2*fRVz)
|
|
ptexMipBase[iMipVx].s = fRVx*f1oFM +0.5f;
|
|
ptexMipBase[iMipVx].t = fRVy*f1oFM +0.5f;
|
|
}
|
|
#endif
|
|
_pfModelProfile.StopTimer( CModelProfile::PTI_VIEW_INIT_SPEC_MIP);
|
|
|
|
// setup surface vertices
|
|
_pfModelProfile.StartTimer( CModelProfile::PTI_VIEW_INIT_SPEC_SURF);
|
|
_pfModelProfile.IncrementTimerAveragingCounter( CModelProfile::PTI_VIEW_INIT_SPEC_SURF, _ctAllSrfVx);
|
|
|
|
// get model specular color and multiply with light color
|
|
GFXColor colMdlSpec;
|
|
const COLOR colS = AdjustColor( rm.rm_pmdModelData->md_colSpecular, _slTexHueShift, _slTexSaturation);
|
|
colMdlSpec.abgr = ByteSwap(colS);
|
|
colMdlSpec.AttenuateRGB( (rm.rm_colBlend&CT_AMASK)>>CT_ASHIFT);
|
|
colMdlSpec.r = ClampUp( (colMdlSpec.r *_slLR)>>8, 255L);
|
|
colMdlSpec.g = ClampUp( (colMdlSpec.g *_slLG)>>8, 255L);
|
|
colMdlSpec.b = ClampUp( (colMdlSpec.b *_slLB)>>8, 255L);
|
|
|
|
// for each specular surface in current mip model
|
|
FOREACHINSTATICARRAY( mmi.mmpi_MappingSurfaces, MappingSurface, itms)
|
|
{
|
|
const MappingSurface &ms = *itms;
|
|
iSrfVx0 = ms.ms_iSrfVx0;
|
|
ctSrfVx = ms.ms_ctSrfVx;
|
|
if( (ms.ms_ulRenderingFlags&SRF_INVISIBLE) || ctSrfVx==0) break; // done if found invisible or empty surface
|
|
if( !(ms.ms_ulRenderingFlags&SRF_SPECULAR)) continue; // skip non-specular surface
|
|
// cache surface pointers
|
|
puwSrfToMip = &mmi.mmpi_auwSrfToMip[iSrfVx0];
|
|
ptexSrfBase = &_atexSrfBase[iSrfVx0];
|
|
pcolSrfBase = &_acolSrfBase[iSrfVx0];
|
|
// get surface specular color and combine with model color
|
|
GFXColor colSrfSpec;
|
|
const COLOR colS = AdjustColor( ms.ms_colSpecular, _slTexHueShift, _slTexSaturation);
|
|
colSrfSpec.MultiplyRGB( colS, colMdlSpec);
|
|
|
|
// for each vertex in the surface
|
|
for( INDEX iSrfVx=0; iSrfVx<ctSrfVx; iSrfVx++) {
|
|
const INDEX iMipVx = puwSrfToMip[iSrfVx];
|
|
// set specular texture and color
|
|
ptexSrfBase[iSrfVx] = ptexMipBase[iMipVx];
|
|
const SLONG slShade = pcolMipBase[iMipVx].a;
|
|
pcolSrfBase[iSrfVx].abgr = (((colSrfSpec.r)*slShade)>>8)
|
|
| (((colSrfSpec.g)*slShade)&0x0000FF00)
|
|
| ((((colSrfSpec.b)*slShade)<<8)&0x00FF0000);
|
|
}
|
|
// eventually attenuate color in case of fog or haze
|
|
if( (ms.ms_ulRenderingFlags&SRF_OPAQUE) && !_bForceTranslucency) continue;
|
|
// eventually do some haze and/or fog attenuation of alpha channel in surface
|
|
if( rm.rm_ulFlags & RMF_HAZE) {
|
|
if( ms.ms_sttTranslucencyType==STT_MULTIPLY) AttenuateColor( pshdMipHaze, ctSrfVx);
|
|
else AttenuateAlpha( pshdMipHaze, ctSrfVx);
|
|
}
|
|
if( rm.rm_ulFlags & RMF_FOG) {
|
|
if( ms.ms_sttTranslucencyType==STT_MULTIPLY) AttenuateColor( pshdMipFogy, ctSrfVx);
|
|
else AttenuateAlpha( pshdMipFogy, ctSrfVx);
|
|
}
|
|
}
|
|
// specular prep done
|
|
_pfModelProfile.StopTimer( CModelProfile::PTI_VIEW_INIT_SPEC_SURF);
|
|
|
|
// begin rendering
|
|
_pfModelProfile.StartTimer( CModelProfile::PTI_VIEW_RENDER_SPECULAR);
|
|
_pfModelProfile.IncrementTimerAveragingCounter( CModelProfile::PTI_VIEW_RENDER_SPECULAR);
|
|
|
|
// setup texture/color arrays and rendering mode
|
|
SetCurrentTexture( ptdSpecular, mo_toSpecular.GetFrame());
|
|
gfxSetTexCoordArray( &_atexSrfBase[0], FALSE);
|
|
gfxSetColorArray( &_acolSrfBase[0]);
|
|
gfxBlendFunc( GFX_INV_SRC_ALPHA, GFX_ONE);
|
|
// do rendering
|
|
RenderOneSide( rm, TRUE , SRF_SPECULAR);
|
|
RenderOneSide( rm, FALSE, SRF_SPECULAR);
|
|
|
|
// specular rendering done
|
|
_pfModelProfile.StopTimer( CModelProfile::PTI_VIEW_RENDER_SPECULAR);
|
|
}
|
|
|
|
|
|
|
|
// RENDER HAZE LAYER -------------------------------------------------------------------
|
|
|
|
|
|
// if this model has haze and some opaque surfaces
|
|
if( (rm.rm_ulFlags&RMF_HAZE) && !(_ulMipLayerFlags&MMI_TRANSLUCENT))
|
|
{
|
|
_pfModelProfile.StartTimer( CModelProfile::PTI_VIEW_INIT_HAZE_SURF);
|
|
_pfModelProfile.IncrementTimerAveragingCounter( CModelProfile::PTI_VIEW_INIT_HAZE_SURF, _ctAllSrfVx);
|
|
// unpack haze color
|
|
const COLOR colH = AdjustColor( _haze_hp.hp_colColor, _slTexHueShift, _slTexSaturation);
|
|
GFXColor colHaze(colH);
|
|
|
|
// for each surface in current mip model
|
|
FOREACHINSTATICARRAY( mmi.mmpi_MappingSurfaces, MappingSurface, itms)
|
|
{
|
|
MappingSurface &ms = *itms;
|
|
iSrfVx0 = ms.ms_iSrfVx0;
|
|
ctSrfVx = ms.ms_ctSrfVx;
|
|
if( (ms.ms_ulRenderingFlags&SRF_INVISIBLE) || ctSrfVx==0) break; // done if found invisible or empty surface
|
|
if( !(ms.ms_ulRenderingFlags&SRF_OPAQUE)) continue; // skip not-solid or empty surfaces
|
|
// cache surface pointers
|
|
puwSrfToMip = &mmi.mmpi_auwSrfToMip[iSrfVx0];
|
|
ptexSrfBase = &_atexSrfBase[iSrfVx0];
|
|
pcolSrfBase = &_acolSrfBase[iSrfVx0];
|
|
|
|
// prepare haze vertices
|
|
for( INDEX iSrfVx=0; iSrfVx<ctSrfVx; iSrfVx++) {
|
|
const INDEX iMipVx = puwSrfToMip[iSrfVx];
|
|
ptexSrfBase[iSrfVx].s = ptx1MipHaze[iMipVx];
|
|
ptexSrfBase[iSrfVx].t = 0.0f;
|
|
pcolSrfBase[iSrfVx] = colHaze;
|
|
}
|
|
// mark that this surface has haze
|
|
ms.ms_ulRenderingFlags |= SRF_HAZE;
|
|
}
|
|
// haze prep done
|
|
_pfModelProfile.StopTimer( CModelProfile::PTI_VIEW_INIT_HAZE_SURF);
|
|
|
|
// begin rendering
|
|
_pfModelProfile.StartTimer( CModelProfile::PTI_VIEW_RENDER_HAZE);
|
|
_pfModelProfile.IncrementTimerAveragingCounter( CModelProfile::PTI_VIEW_RENDER_HAZE);
|
|
|
|
// setup texture/color arrays and rendering mode
|
|
gfxSetTextureWrapping( GFX_CLAMP, GFX_CLAMP);
|
|
gfxSetTexture( _haze_ulTexture, _haze_tpLocal);
|
|
gfxSetTexCoordArray( &_atexSrfBase[0], FALSE);
|
|
gfxSetColorArray( &_acolSrfBase[0]);
|
|
gfxBlendFunc( GFX_SRC_ALPHA, GFX_INV_SRC_ALPHA);
|
|
// do rendering
|
|
RenderOneSide( rm, TRUE, SRF_HAZE);
|
|
RenderOneSide( rm, FALSE, SRF_HAZE);
|
|
|
|
// haze rendering done
|
|
_pfModelProfile.StopTimer( CModelProfile::PTI_VIEW_RENDER_HAZE);
|
|
}
|
|
|
|
|
|
|
|
// RENDER FOG LAYER -------------------------------------------------------------------
|
|
|
|
|
|
// if this model has fog and some opaque surfaces
|
|
if( (rm.rm_ulFlags&RMF_FOG) && !(_ulMipLayerFlags&MMI_TRANSLUCENT))
|
|
{
|
|
_pfModelProfile.StartTimer( CModelProfile::PTI_VIEW_INIT_FOG_SURF);
|
|
_pfModelProfile.IncrementTimerAveragingCounter( CModelProfile::PTI_VIEW_INIT_FOG_SURF, _ctAllSrfVx);
|
|
// unpack fog color
|
|
const COLOR colF = AdjustColor( _fog_fp.fp_colColor, _slTexHueShift, _slTexSaturation);
|
|
GFXColor colFog(colF);
|
|
|
|
// for each surface in current mip model
|
|
FOREACHINSTATICARRAY( mmi.mmpi_MappingSurfaces, MappingSurface, itms)
|
|
{
|
|
MappingSurface &ms = *itms;
|
|
iSrfVx0 = ms.ms_iSrfVx0;
|
|
ctSrfVx = ms.ms_ctSrfVx;
|
|
if( (ms.ms_ulRenderingFlags&SRF_INVISIBLE) || ctSrfVx==0) break; // done if found invisible or empty surface
|
|
if( !(ms.ms_ulRenderingFlags&SRF_OPAQUE)) continue; // skip not-solid or empty surfaces
|
|
// cache surface pointers
|
|
puwSrfToMip = &mmi.mmpi_auwSrfToMip[iSrfVx0];
|
|
ptexSrfBase = &_atexSrfBase[iSrfVx0];
|
|
pcolSrfBase = &_acolSrfBase[iSrfVx0];
|
|
|
|
// prepare fog vertices
|
|
for( INDEX iSrfVx=0; iSrfVx<ctSrfVx; iSrfVx++) {
|
|
const INDEX iMipVx = puwSrfToMip[iSrfVx];
|
|
ptexSrfBase[iSrfVx] = ptexMipFogy[iMipVx];
|
|
pcolSrfBase[iSrfVx] = colFog;
|
|
}
|
|
// mark that this surface has fog
|
|
ms.ms_ulRenderingFlags |= SRF_FOG;
|
|
}
|
|
// fog prep done
|
|
_pfModelProfile.StopTimer( CModelProfile::PTI_VIEW_INIT_FOG_SURF);
|
|
|
|
// begin rendering
|
|
_pfModelProfile.StartTimer( CModelProfile::PTI_VIEW_RENDER_FOG);
|
|
_pfModelProfile.IncrementTimerAveragingCounter( CModelProfile::PTI_VIEW_RENDER_FOG);
|
|
|
|
// setup texture/color arrays and rendering mode
|
|
gfxSetTextureWrapping( GFX_CLAMP, GFX_CLAMP);
|
|
gfxSetTexture( _fog_ulTexture, _fog_tpLocal);
|
|
gfxSetTexCoordArray( &_atexSrfBase[0], FALSE);
|
|
gfxSetColorArray( &_acolSrfBase[0]);
|
|
gfxBlendFunc( GFX_SRC_ALPHA, GFX_INV_SRC_ALPHA);
|
|
gfxDisableAlphaTest();
|
|
// do rendering
|
|
RenderOneSide( rm, TRUE, SRF_FOG);
|
|
RenderOneSide( rm, FALSE, SRF_FOG);
|
|
|
|
// fog rendering done
|
|
_pfModelProfile.StopTimer( CModelProfile::PTI_VIEW_RENDER_FOG);
|
|
}
|
|
|
|
// almost done
|
|
gfxDepthFunc( GFX_LESS_EQUAL);
|
|
gfxUnlockArrays();
|
|
|
|
// eventually render wireframe
|
|
RenderWireframe(rm);
|
|
|
|
// reset model vertex buffers and rendering face
|
|
ResetVertexArrays();
|
|
|
|
// model rendered (restore cull mode)
|
|
gfxCullFace(GFX_BACK);
|
|
_sfStats.StopTimer(CStatForm::STI_MODELRENDERING);
|
|
if( bModelSetupTimer) _sfStats.StartTimer(CStatForm::STI_MODELSETUP);
|
|
_pfModelProfile.StopTimer( CModelProfile::PTI_VIEW_RENDERMODEL);
|
|
}
|
|
#pragma warning(default: 4731)
|
|
|
|
|
|
|
|
// *******************************************************************************
|
|
|
|
|
|
|
|
// render patches on model
|
|
void CModelObject::RenderPatches_View( CRenderModel &rm)
|
|
{
|
|
_pfModelProfile.StartTimer( CModelProfile::PTI_VIEW_RENDERPATCHES);
|
|
_pfModelProfile.IncrementTimerAveragingCounter( CModelProfile::PTI_VIEW_RENDERPATCHES);
|
|
|
|
// setup API rendering functions
|
|
gfxSetTextureWrapping( GFX_CLAMP, GFX_CLAMP);
|
|
gfxDepthFunc( GFX_LESS_EQUAL);
|
|
gfxBlendFunc( GFX_SRC_ALPHA, GFX_INV_SRC_ALPHA);
|
|
gfxDisableAlphaTest();
|
|
|
|
pglMatrixMode( GL_TEXTURE);
|
|
gfxSetColorArray( &_acolSrfBase[0]);
|
|
CModelData *pmd = rm.rm_pmdModelData;
|
|
ModelMipInfo *pmmi = &rm.rm_pmdModelData->md_MipInfos[rm.rm_iMipLevel];
|
|
|
|
// get main texture size
|
|
FLOAT fmexMainSizeU=1, fmexMainSizeV=1;
|
|
FLOAT f1oMainSizeV =1, f1oMainSizeU =1;
|
|
CTextureData *ptd = (CTextureData*)mo_toTexture.GetData();
|
|
if( ptd!=NULL) {
|
|
fmexMainSizeU = GetWidth();
|
|
fmexMainSizeV = GetHeight();
|
|
f1oMainSizeU = 1.0f / fmexMainSizeU;
|
|
f1oMainSizeV = 1.0f / fmexMainSizeV;
|
|
}
|
|
|
|
// for each possible patch
|
|
INDEX iExistingPatch=0;
|
|
for( INDEX iMaskBit=0; iMaskBit<MAX_TEXTUREPATCHES; iMaskBit++)
|
|
{
|
|
CTextureObject &toPatch = pmd->md_mpPatches[ iMaskBit].mp_toTexture;
|
|
CTextureData *ptdPatch = (CTextureData*)toPatch.GetData();
|
|
if( ptdPatch==NULL) continue;
|
|
if( mo_PatchMask & ((1UL)<<iMaskBit)) {
|
|
PolygonsPerPatch &ppp = pmmi->mmpi_aPolygonsPerPatch[iExistingPatch];
|
|
if( ppp.ppp_auwElements.Count()==0) continue;
|
|
// calculate correction factor (relative to greater texture dimension)
|
|
const MEX2D mexPatchOffset = pmd->md_mpPatches[iMaskBit].mp_mexPosition;
|
|
const FLOAT fSizeDivider = 1.0f / pmd->md_mpPatches[iMaskBit].mp_fStretch;
|
|
const FLOAT fmexSizeU = ptdPatch->GetWidth();
|
|
const FLOAT fmexSizeV = ptdPatch->GetHeight();
|
|
// set texture for API
|
|
SetCurrentTexture( ptdPatch, toPatch.GetFrame());
|
|
pglLoadIdentity();
|
|
pglScalef( fmexMainSizeU/fmexSizeU*fSizeDivider, fmexMainSizeV/fmexSizeV*fSizeDivider, 0);
|
|
pglTranslatef( -mexPatchOffset(1)*f1oMainSizeU, -mexPatchOffset(2)*f1oMainSizeV, 0);
|
|
gfxSetTexCoordArray( &_atexSrfBase[0], FALSE);
|
|
//AddElements( &ppp.ppp_auwElements[0], ppp.ppp_auwElements.Count());
|
|
//FlushElements();
|
|
}
|
|
iExistingPatch++;
|
|
}
|
|
// all done
|
|
pglLoadIdentity();
|
|
pglMatrixMode( GL_MODELVIEW);
|
|
OGL_CHECKERROR;
|
|
|
|
// patches rendered
|
|
_pfModelProfile.StopTimer( CModelProfile::PTI_VIEW_RENDERPATCHES);
|
|
}
|
|
|
|
|
|
|
|
// *******************************************************************************
|
|
|
|
|
|
|
|
// render comlex model shadow
|
|
void CModelObject::RenderShadow_View( CRenderModel &rm, const CPlacement3D &plLight,
|
|
const FLOAT fFallOff, const FLOAT fHotSpot, const FLOAT fIntensity,
|
|
const FLOATplane3D &plShadowPlane)
|
|
{
|
|
// no shadow, if projection is not perspective or no textures
|
|
if( !_aprProjection.IsPerspective() || !(rm.rm_rtRenderType & RT_TEXTURE)) return;
|
|
|
|
_pfModelProfile.StartTimer( CModelProfile::PTI_VIEW_RENDERSHADOW);
|
|
_pfModelProfile.IncrementTimerAveragingCounter( CModelProfile::PTI_VIEW_RENDERSHADOW);
|
|
|
|
// get viewer in absolute space
|
|
FLOAT3D vViewerAbs = _aprProjection->ViewerPlacementR().pl_PositionVector;
|
|
if( plShadowPlane.PointDistance(vViewerAbs)<0.01f) {
|
|
// shadow destination plane is not visible - don't cast shadows
|
|
_pfModelProfile.StopTimer( CModelProfile::PTI_VIEW_RENDERSHADOW);
|
|
return;
|
|
}
|
|
|
|
// get light position in object space
|
|
FLOATmatrix3D mAbsToObj = !rm.rm_mObjectRotation;
|
|
const FLOAT3D vAbsToObj = -rm.rm_vObjectPosition;
|
|
_vLightObj = (plLight.pl_PositionVector+vAbsToObj)*mAbsToObj;
|
|
// get shadow plane in object space
|
|
FLOATplane3D plShadowPlaneObj = (plShadowPlane+vAbsToObj)*mAbsToObj;
|
|
|
|
// project object handle so we can calc how it is far away from viewer
|
|
const FLOAT3D vRef = plShadowPlaneObj.ProjectPoint(FLOAT3D(0,0,0)) *rm.rm_mObjectToView +rm.rm_vObjectToView;
|
|
plShadowPlaneObj.pl_distance += ClampDn( -vRef(3)*0.001f, 0.01f); // move plane towards the viewer a bit to avoid z-fighting
|
|
|
|
// get distance from shadow plane to light
|
|
const FLOAT fDl = plShadowPlaneObj.PointDistance(_vLightObj);
|
|
if( fDl<0.1f) {
|
|
// too small - do nothing
|
|
_pfModelProfile.StopTimer( CModelProfile::PTI_VIEW_RENDERSHADOW);
|
|
return;
|
|
}
|
|
|
|
ModelMipInfo &mmi = *rm.rm_pmmiMip;
|
|
ModelMipInfo &mmi0 = rm.rm_pmdModelData->md_MipInfos[0];
|
|
_pfModelProfile.IncrementCounter( CModelProfile::PCI_SHADOWVERTICES_FIRSTMIP, mmi0.mmpi_ctMipVx);
|
|
_pfModelProfile.IncrementCounter( CModelProfile::PCI_SHADOWVERTICES_USEDMIP, mmi.mmpi_ctMipVx);
|
|
_pfModelProfile.IncrementCounter( CModelProfile::PCI_SHADOWSURFACEVERTICES_FIRSTMIP, mmi0.mmpi_ctSrfVx);
|
|
_pfModelProfile.IncrementCounter( CModelProfile::PCI_SHADOWSURFACEVERTICES_USEDMIP, mmi.mmpi_ctSrfVx);
|
|
_pfModelProfile.IncrementCounter( CModelProfile::PCI_SHADOWTRIANGLES_FIRSTMIP, mmi0.mmpi_ctTriangles);
|
|
_pfModelProfile.IncrementCounter( CModelProfile::PCI_SHADOWTRIANGLES_USEDMIP, mmi.mmpi_ctTriangles);
|
|
|
|
_sfStats.IncrementCounter( CStatForm::SCI_SHADOWTRIANGLES_FIRSTMIP, mmi0.mmpi_ctTriangles);
|
|
_sfStats.IncrementCounter( CStatForm::SCI_SHADOWTRIANGLES_USEDMIP, mmi.mmpi_ctTriangles);
|
|
|
|
// allocate vertex arrays
|
|
_ctAllMipVx = mmi.mmpi_ctMipVx;
|
|
_ctAllSrfVx = mmi.mmpi_ctSrfVx;
|
|
ASSERT( _ctAllMipVx>0 && _ctAllSrfVx>0);
|
|
ASSERT( _avtxMipBase.Count()==0); _avtxMipBase.Push(_ctAllMipVx);
|
|
ASSERT( _acolMipBase.Count()==0); _acolMipBase.Push(_ctAllMipVx);
|
|
ASSERT( _aooqMipShad.Count()==0); _aooqMipShad.Push(_ctAllMipVx);
|
|
ASSERT( _avtxSrfBase.Count()==0); _avtxSrfBase.Push(_ctAllSrfVx);
|
|
ASSERT( _acolSrfBase.Count()==0); _acolSrfBase.Push(_ctAllSrfVx);
|
|
ASSERT( _atx4SrfShad.Count()==0); _atx4SrfShad.Push(_ctAllSrfVx);
|
|
|
|
// unpack one model frame vertices and eventually normals (lerped or not lerped, as required)
|
|
pvtxMipBase = &_avtxMipBase[0];
|
|
pooqMipShad = &_aooqMipShad[0];
|
|
pcolMipBase = &_acolMipBase[0];
|
|
UnpackFrame( rm, FALSE);
|
|
UBYTE *pubsMipBase = (UBYTE*)pcolMipBase;
|
|
|
|
_pfModelProfile.StartTimer( CModelProfile::PTI_VIEW_SHAD_INIT_MIP);
|
|
_pfModelProfile.IncrementTimerAveragingCounter( CModelProfile::PTI_VIEW_SHAD_INIT_MIP, _ctAllMipVx);
|
|
|
|
// calculate light interpolants attenuated by ambient factor
|
|
const INDEX iLightMax = NormFloatToByte(fIntensity);
|
|
const FLOAT fLightStep = 255.0f* fIntensity/(fFallOff-fHotSpot);
|
|
|
|
// for each mip vertex
|
|
{for( INDEX iMipVx=0; iMipVx<_ctAllMipVx; iMipVx++)
|
|
{
|
|
// get object coordinates
|
|
GFXVertex3 &vtx = pvtxMipBase[iMipVx];
|
|
// get distance of the vertex from shadow plane
|
|
FLOAT fDt = plShadowPlaneObj(1) *vtx.x
|
|
+ plShadowPlaneObj(2) *vtx.y
|
|
+ plShadowPlaneObj(3) *vtx.z
|
|
- plShadowPlaneObj.Distance();
|
|
// if vertex below shadow plane
|
|
if( fDt<0) {
|
|
// THIS MIGHT BE WRONG - it'll make shadow vertex to be the same as model vertex !!!!
|
|
// fake as beeing just on plane
|
|
fDt=0.0f;
|
|
FLOAT fP = fDl/(fDl-fDt); // =1
|
|
FLOAT fL = fDt/(fDl-fDt); // =0
|
|
// calculate shadow vertex
|
|
vtx.x = vtx.x*fP - _vLightObj(1)*fL;
|
|
vtx.y = vtx.y*fP - _vLightObj(2)*fL;
|
|
vtx.z = vtx.z*fP - _vLightObj(3)*fL;
|
|
pooqMipShad[iMipVx] = 1.0f;
|
|
// make it transparent
|
|
pubsMipBase[iMipVx] = 0;
|
|
}
|
|
// if vertex above light
|
|
else if( (fDl-fDt)<0.01f) {
|
|
// fake as beeing just a bit below light
|
|
fDt = fDl-0.1f;
|
|
const FLOAT fDiv = 1.0f / (fDl-fDt);
|
|
const FLOAT fP = fDl *fDiv;
|
|
const FLOAT fL = fDt *fDiv;
|
|
// calculate shadow vertex
|
|
vtx.x = vtx.x*fP - _vLightObj(1)*fL;
|
|
vtx.y = vtx.y*fP - _vLightObj(2)*fL;
|
|
vtx.z = vtx.z*fP - _vLightObj(3)*fL;
|
|
pooqMipShad[iMipVx] = 1.0f;
|
|
// make it transparent
|
|
pubsMipBase[iMipVx] = 0;
|
|
}
|
|
// if vertex between shadow plane and light
|
|
else {
|
|
const FLOAT fDiv = 1.0f / (fDl-fDt);
|
|
const FLOAT fP = fDl *fDiv;
|
|
const FLOAT fL = fDt *fDiv;
|
|
// calculate shadow vertex
|
|
vtx.x = vtx.x*fP - _vLightObj(1)*fL;
|
|
vtx.y = vtx.y*fP - _vLightObj(2)*fL;
|
|
vtx.z = vtx.z*fP - _vLightObj(3)*fL;
|
|
pooqMipShad[iMipVx] = fP;
|
|
// get distance between light and shadow vertex
|
|
const FLOAT fDlsx = vtx.x - _vLightObj(1);
|
|
const FLOAT fDlsy = vtx.y - _vLightObj(2);
|
|
const FLOAT fDlsz = vtx.z - _vLightObj(3);
|
|
const FLOAT fDls = sqrt( fDlsx*fDlsx + fDlsy*fDlsy + fDlsz*fDlsz);
|
|
// calculate shadow value for this vertex
|
|
if( fDls<fHotSpot) pubsMipBase[iMipVx] = iLightMax;
|
|
else if( fDls>fFallOff) pubsMipBase[iMipVx] = 0;
|
|
else pubsMipBase[iMipVx] = FloatToInt( (fFallOff-fDls)*fLightStep);
|
|
}
|
|
}}
|
|
_pfModelProfile.StopTimer( CModelProfile::PTI_VIEW_SHAD_INIT_MIP);
|
|
|
|
// get diffuse texture corrections
|
|
FLOAT fTexCorrU, fTexCorrV;
|
|
CTextureData *ptd = (CTextureData*)mo_toTexture.GetData();
|
|
if( ptd!=NULL) {
|
|
fTexCorrU = 1.0f / ptd->GetWidth();
|
|
fTexCorrV = 1.0f / ptd->GetHeight();
|
|
} else {
|
|
fTexCorrU = 1.0f;
|
|
fTexCorrV = 1.0f;
|
|
}
|
|
|
|
_pfModelProfile.StartTimer( CModelProfile::PTI_VIEW_SHAD_INIT_SURF);
|
|
_pfModelProfile.IncrementTimerAveragingCounter( CModelProfile::PTI_VIEW_SHAD_INIT_SURF, _ctAllSrfVx);
|
|
// for each surface in current mip model
|
|
FOREACHINSTATICARRAY( mmi.mmpi_MappingSurfaces, MappingSurface, itms)
|
|
{
|
|
const MappingSurface &ms = *itms;
|
|
const INDEX iSrfVx0 = ms.ms_iSrfVx0;
|
|
const INDEX ctSrfVx = ms.ms_ctSrfVx;
|
|
if( (ms.ms_ulRenderingFlags&SRF_INVISIBLE) || ctSrfVx==0) break; // done if found invisible or empty surface
|
|
// cache surface pointers
|
|
puwSrfToMip = &mmi.mmpi_auwSrfToMip[iSrfVx0];
|
|
const FLOAT2D *pvTexCoord = (const FLOAT2D*)&mmi.mmpi_avmexTexCoord[iSrfVx0];
|
|
GFXVertex *pvtxSrfBase = &_avtxSrfBase[iSrfVx0];
|
|
GFXTexCoord4 *ptx4SrfShad = &_atx4SrfShad[iSrfVx0];
|
|
GFXColor *pcolSrfBase = &_acolSrfBase[iSrfVx0];
|
|
// for each vertex in the surface
|
|
for( INDEX iSrfVx=0; iSrfVx<ctSrfVx; iSrfVx++) {
|
|
const INDEX iMipVx = puwSrfToMip[iSrfVx];
|
|
// get 3d coords projected on plane and color (alpha only)
|
|
pvtxSrfBase[iSrfVx].x = pvtxMipBase[iMipVx].x;
|
|
pvtxSrfBase[iSrfVx].y = pvtxMipBase[iMipVx].y;
|
|
pvtxSrfBase[iSrfVx].z = pvtxMipBase[iMipVx].z;
|
|
pcolSrfBase[iSrfVx].a = pubsMipBase[iMipVx];
|
|
// get texture adjusted for perspective correction (aka projected mapping)
|
|
const FLOAT fooq = pooqMipShad[iMipVx];
|
|
ptx4SrfShad[iSrfVx].s = pvTexCoord[iSrfVx](1) *fTexCorrU *fooq;
|
|
ptx4SrfShad[iSrfVx].t = pvTexCoord[iSrfVx](2) *fTexCorrV *fooq;
|
|
ptx4SrfShad[iSrfVx].q = fooq;
|
|
}
|
|
}
|
|
_pfModelProfile.StopTimer( CModelProfile::PTI_VIEW_SHAD_INIT_SURF);
|
|
_pfModelProfile.StartTimer( CModelProfile::PTI_VIEW_SHAD_GLSETUP);
|
|
_pfModelProfile.IncrementTimerAveragingCounter( CModelProfile::PTI_VIEW_SHAD_GLSETUP, 1);
|
|
|
|
// begin rendering
|
|
const BOOL bModelSetupTimer = _sfStats.CheckTimer(CStatForm::STI_MODELSETUP);
|
|
if( bModelSetupTimer) _sfStats.StopTimer(CStatForm::STI_MODELSETUP);
|
|
_sfStats.StartTimer(CStatForm::STI_MODELRENDERING);
|
|
|
|
// prepare vertex arrays for API
|
|
gfxSetVertexArray( &_avtxSrfBase[0], _avtxSrfBase.Count());
|
|
if(CVA_bModels) gfxLockArrays();
|
|
gfxSetTexCoordArray( (GFXTexCoord*)&_atx4SrfShad[0], TRUE); // projective mapping!
|
|
gfxSetColorArray( &_acolSrfBase[0]);
|
|
|
|
// set projection and texture for API
|
|
gfxSetTextureWrapping( GFX_REPEAT, GFX_REPEAT);
|
|
INDEX iFrame=0;
|
|
if( ptd!=NULL) iFrame = mo_toTexture.GetFrame();
|
|
SetCurrentTexture( ptd, iFrame);
|
|
|
|
gfxEnableDepthTest();
|
|
gfxDepthFunc( GFX_LESS_EQUAL);
|
|
gfxDisableDepthWrite();
|
|
gfxEnableBlend();
|
|
gfxBlendFunc( GFX_ZERO, GFX_INV_SRC_ALPHA);
|
|
gfxDisableAlphaTest();
|
|
gfxDisableTruform();
|
|
|
|
gfxEnableClipping();
|
|
gfxCullFace(GFX_BACK);
|
|
|
|
_pfModelProfile.StopTimer( CModelProfile::PTI_VIEW_SHAD_GLSETUP);
|
|
|
|
_pfModelProfile.StartTimer( CModelProfile::PTI_VIEW_SHAD_RENDER);
|
|
// for each surface in current mip model
|
|
INDEX iStartElem=0;
|
|
INDEX ctElements=0;
|
|
{FOREACHINSTATICARRAY( mmi.mmpi_MappingSurfaces, MappingSurface, itms)
|
|
{
|
|
// done if surface is invisible or empty
|
|
const MappingSurface &ms = *itms;
|
|
if( (ms.ms_ulRenderingFlags&SRF_INVISIBLE) || ms.ms_ctSrfVx==0) break;
|
|
// batch elements up to non-diffuse surface
|
|
if( ms.ms_ulRenderingFlags&SRF_DIFFUSE) {
|
|
ctElements += ms.ms_ctSrfEl;
|
|
continue;
|
|
}
|
|
// flush batched elements
|
|
if( ctElements>0) FlushElements( ctElements, &mmi.mmpi_aiElements[iStartElem]);
|
|
_pfModelProfile.IncrementTimerAveragingCounter( CModelProfile::PTI_VIEW_SHAD_RENDER, ctElements/3);
|
|
iStartElem+= ctElements+ms.ms_ctSrfEl;
|
|
ctElements = 0;
|
|
}}
|
|
// flush leftovers
|
|
if( ctElements>0) FlushElements( ctElements, &mmi.mmpi_aiElements[iStartElem]);
|
|
|
|
// done
|
|
gfxUnlockArrays();
|
|
|
|
// reset vertex arrays
|
|
_avtxMipBase.PopAll();
|
|
_acolMipBase.PopAll();
|
|
_aooqMipShad.PopAll();
|
|
_avtxSrfBase.PopAll();
|
|
_acolSrfBase.PopAll();
|
|
_atx4SrfShad.PopAll();
|
|
|
|
// shadow rendered
|
|
_sfStats.StopTimer(CStatForm::STI_MODELRENDERING);
|
|
if( bModelSetupTimer) _sfStats.StartTimer(CStatForm::STI_MODELSETUP);
|
|
_pfModelProfile.StopTimer( CModelProfile::PTI_VIEW_SHAD_RENDER);
|
|
_pfModelProfile.StopTimer( CModelProfile::PTI_VIEW_RENDERSHADOW);
|
|
}
|
|
|
|
|
|
// *******************************************************************************
|
|
|
|
|
|
// render simple model shadow
|
|
void CModelObject::AddSimpleShadow_View( CRenderModel &rm, 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
|
|
const FLOATmatrix3D mAbsToObj = !rm.rm_mObjectRotation;
|
|
const FLOAT3D vAbsToObj = -rm.rm_vObjectPosition;
|
|
FLOATplane3D plShadowPlaneObj = (plShadowPlane+vAbsToObj) * mAbsToObj;
|
|
|
|
// project object handle so we can calc how it is far away from viewer
|
|
const FLOAT3D vRef = plShadowPlaneObj.ProjectPoint(FLOAT3D(0,0,0)) *rm.rm_mObjectToView +rm.rm_vObjectToView;
|
|
plShadowPlaneObj.pl_distance += ClampDn( -vRef(3)*0.001f, 0.01f); // move plane towards the viewer a bit to avoid z-fighting
|
|
|
|
// find points on plane nearest to bounding box edges
|
|
FLOAT3D vMin = rm.rm_vObjectMinBB * 1.25f;
|
|
FLOAT3D vMax = rm.rm_vObjectMaxBB * 1.25f;
|
|
if( rm.rm_ulFlags & RMF_SPECTATOR) { vMin*=2; vMax*=2; } // enlarge shadow for 1st person view
|
|
const FLOAT3D v00 = plShadowPlaneObj.ProjectPoint(FLOAT3D(vMin(1),vMin(2),vMin(3))) *rm.rm_mObjectToView +rm.rm_vObjectToView;
|
|
const FLOAT3D v01 = plShadowPlaneObj.ProjectPoint(FLOAT3D(vMin(1),vMin(2),vMax(3))) *rm.rm_mObjectToView +rm.rm_vObjectToView;
|
|
const FLOAT3D v10 = plShadowPlaneObj.ProjectPoint(FLOAT3D(vMax(1),vMin(2),vMin(3))) *rm.rm_mObjectToView +rm.rm_vObjectToView;
|
|
const FLOAT3D v11 = plShadowPlaneObj.ProjectPoint(FLOAT3D(vMax(1),vMin(2),vMax(3))) *rm.rm_mObjectToView +rm.rm_vObjectToView;
|
|
// 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( rm.rm_ulFlags & RMF_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( rm.rm_ulFlags & RMF_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( rm.rm_ulFlags & RMF_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);
|
|
}
|
|
|
|
|
|
|
|
// render several simple model shadows
|
|
void RenderBatchedSimpleShadows_View(void)
|
|
{
|
|
const INDEX ctVertices = _avtxCommon.Count();
|
|
if( ctVertices<=0) return;
|
|
// safety checks
|
|
ASSERT( _atexCommon.Count()==ctVertices);
|
|
ASSERT( _acolCommon.Count()==ctVertices);
|
|
_pfModelProfile.StartTimer( CModelProfile::PTI_VIEW_SIMP_BATCHED);
|
|
_pfModelProfile.IncrementTimerAveragingCounter( CModelProfile::PTI_VIEW_SIMP_BATCHED);
|
|
|
|
// begin rendering
|
|
const BOOL bModelSetupTimer = _sfStats.CheckTimer(CStatForm::STI_MODELSETUP);
|
|
if( bModelSetupTimer) _sfStats.StopTimer(CStatForm::STI_MODELSETUP);
|
|
_sfStats.StartTimer(CStatForm::STI_MODELRENDERING);
|
|
|
|
// setup texture and projection
|
|
_bFlatFill = FALSE;
|
|
gfxSetViewMatrix(NULL);
|
|
gfxCullFace(GFX_BACK);
|
|
gfxSetTextureWrapping( GFX_REPEAT, GFX_REPEAT);
|
|
CTextureData *ptd = (CTextureData*)_toSimpleModelShadow.GetData();
|
|
SetCurrentTexture( ptd, _toSimpleModelShadow.GetFrame());
|
|
|
|
// setup rendering mode
|
|
gfxEnableDepthTest();
|
|
gfxDepthFunc( GFX_LESS_EQUAL);
|
|
gfxDisableDepthWrite();
|
|
gfxEnableBlend();
|
|
gfxBlendFunc( GFX_ZERO, GFX_INV_SRC_COLOR);
|
|
gfxDisableAlphaTest();
|
|
gfxEnableClipping();
|
|
gfxDisableTruform();
|
|
|
|
// draw!
|
|
_pGfx->gl_ctModelTriangles += ctVertices/2;
|
|
gfxFlushQuads();
|
|
|
|
// all simple shadows rendered
|
|
gfxResetArrays();
|
|
_sfStats.StopTimer(CStatForm::STI_MODELRENDERING);
|
|
if( bModelSetupTimer) _sfStats.StartTimer(CStatForm::STI_MODELSETUP);
|
|
_pfModelProfile.StopTimer( CModelProfile::PTI_VIEW_SIMP_BATCHED);
|
|
}
|
|
|
|
|
|
|
|
|
|
|