This commit is contained in:
romkazvo
2023-08-07 19:29:24 +08:00
commit 34d6c5d489
4832 changed files with 1389451 additions and 0 deletions

View File

@@ -0,0 +1,8 @@
// ---------------------------------------------------------------------------------------------
// Crytek CryENGINE source code
// History:
// - Created by Michael Glueck
// ---------------------------------------------------------------------------------------------
#include "stdafx.h"
#include "GLMInfo.h"

View File

@@ -0,0 +1,33 @@
// ---------------------------------------------------------------------------------------------
// Crytek CryENGINE source code
// History:
// - Created by Michael Glueck
// ---------------------------------------------------------------------------------------------
#pragma once
class GLMInfo
{
protected:
//info for one particular patch
typedef struct GLMInfoPatch
{
unsigned int uiOffsetX;
unsigned int uiOffsetY;
unsigned int uiWidth;
unsigned int uiHeight;
}GLMInfoPatch;
//info struct for one GLM
typedef struct GLMInfoMesh
{
CString GLMName;
unsigned int uiLMIndex;
std::vector<GLMInfoPatch> vPatchInfos;
}GLMInfoMesh;
};

View File

@@ -0,0 +1,852 @@
// ---------------------------------------------------------------------------------------------
// Crytek CryENGINE source code
// History:
// - Created by Marco Corbetta
// - Changed by Tim Schroeder
// - Partial rewrite for editor integration
// ---------------------------------------------------------------------------------------------
#pragma once
#ifndef __INDOOR_LIGHT_PATCHES_H__
#define __INDOOR_LIGHT_PATCHES_H__
//#define REDUCE_DOT3LIGHTMAPS_TO_CLASSIC // for debugging
//#define DISPLAY_MORE_INFO
//typedef float real;
typedef double real;//typedef controlling the accuracy
#define HDR_EXP_BASE (1.04)
#define HDR_EXP_OFFSET (64.0)
#define HDR_LOG(a, b) ( log(b) / log(a) )
#include <float.h>
#include "LMCompCommon.h"
#include "LMCompStructures.h"
#include "IEntityRenderstate.h"
#include "LMLightCollection.h"
#include "..\Objects\BrushObject.h"
#include "I3dEngine.h"
#include <direct.h>
static const float scfMaxGridSize = 2.f;
#define MIN_LIGHTMAP_SIZE 4
//flags for CRadPoly
#define NOLIGHTMAP_FLAG 1
#define MERGE_FLAG 2
#define SHARED_FLAG 4
#define MERGE_SOURCE_FLAG 8
#define DECAL_FLAG 64
#define DO_NOT_COMPRESS_FLAG 128 //flag for patches
#define WRONG_NORMALS_FLAG 16
#define NOT_NORMALIZED_NORMALS_FLAG 32
#define ONLY_SUNLIGHT 256 //flag for mesh
#define DOUBLE_SIDED_MAT 512 //flag for mesh
#define REBUILD_USED 1024 //flag for mesh
#define CAST_LIGHTMAP 2048 //flag for mesh
#define RECEIVES_SUNLIGHT 4096 //flag for mesh
#define HASH_CHANGED 8192 //flag for mesh
#define RECEIVES_LIGHTMAP (8192<<1) //flag for mesh
//forces the patches to align on 4 pixel boundaries
#define MAKE_BLOCK_ALIGN
struct SUV
{
float u,v; //texture coordinates
};
inline const AABB MakeSafeAABB(const Vec3& rMin, const Vec3& rMax)
{
static const float scfMargin = 0.1f;//margin to add
Vec3 vMin(rMin); Vec3 vMax(rMax);
if(vMin.x == vMax.x) vMax.x += scfMargin;
if(vMin.y == vMax.y) vMax.y += scfMargin;
if(vMin.z == vMax.z) vMax.z += scfMargin;
return AABB(vMin, vMax);
}
//used for warning gathering
typedef enum
{
EWARNING_EXPORT_FAILED = 0, //!< export of lightmaps has failed
EWARNING_LIGHT_EXPORT_FAILED, //!< export of lightsources has failed
EWARNING_DOUBLE_SIDED, //!< double sided material
EWARNING_TOO_MANY_OCCL_LIGHTS, //!< more than 4 active occlusion map light sources at the same time on glm
EWARNING_NO_FIT, //!< glm does not fit into a single lightmap
EWARNING_HUGE_PATCH, //!< has patch(es) which are larger than halve a lightmap wide or high
EWARNING_WRONG_NORMALS, //!< glm has wrong normals
EWARNING_DENORM_NORMALS, //!< glm has denormalized normals
EWARNING_LIGHT_RADIUS, //!< light has a too little radius
EWARNING_LIGHT_INTENSITY, //!< light has a too little intensity
EWARNING_LIGHT_FRUSTUM //!< spotlight has invalid frustum
}EWARNING_TYPE;
static const unsigned int scuiWarningTextAllocation = 300;//number of chars allocated on stack for warning string
static inline const bool IsNormalized(const float cfSqLen)
{
static const float scfThreshold = 0.1f;
if(fabs(cfSqLen - 1.f) < scfThreshold)
return true;
return false;
}
//supports flexible subsampling patterns, but for simplicity just use 9x or nothing for now
class CAASettings
{
protected:
unsigned int m_uiScale;
float m_fInvScale;
public:
bool m_bEnabled;
CAASettings():m_bEnabled(false),m_uiScale(1),m_fInvScale(1.0f){}
void SetScale(const unsigned int cuiScale)
{
assert(cuiScale != 0);
m_uiScale = cuiScale;
m_fInvScale = 1.0f / (float)cuiScale;
}
const float GetInvScale()const{return m_fInvScale;}
const unsigned int GetScale()const{return m_uiScale;}
const float RetrieveRealSamplePos(const real cfOrig)
{
switch(m_uiScale)
{
case 1:
return cfOrig;
break;
case 2:
{
const real cfNumber = (real)((int)(cfOrig * 0.5f));
return (cfNumber - (real)1.0f/(real)3.0 + (real)2.0/(real)3.0 * (cfOrig - cfNumber * (real)2.0));//map onto -1/3, 1/3, 2/3, 4/3,...
}
break;
case 3:
return ((real)1.0f/(real)3.0 * cfOrig - (real)1.0f/(real)3.0);//map onto -1/3, 0/3, 1/3, 2/3, 3/3, 4/3,...
break;
default:
return m_fInvScale * cfOrig;
}
}
//returns the middle index, or tells whether this is the one or not
const bool IsMiddle(const unsigned int cuiX, const unsigned int cuiY)
{
switch(m_uiScale)
{
case 1:
return true;
case 2:
return (cuiX == 1 && cuiY == 1);
case 3:
return (cuiX == 1 && cuiY == 1);
}
return true;
}
};
class CLightPatch;
class CRadPoly;
class CRadVertex;
class CRadMesh;
struct IStatObj;
class CLightScene;
typedef std::vector<CRadPoly *> radpolylist;
typedef std::vector<CRadPoly *>::iterator radpolyit;
typedef std::vector<CRadVertex> radvertexlist;
typedef std::vector<CRadVertex>::iterator radvertexit;
typedef std::list<CLightPatch *> lightpatchlist;
typedef std::list<CLightPatch *>::iterator lightpatchit;
typedef std::list<CRadMesh *> radmeshlist;
typedef std::list<CRadMesh *>::iterator radmeshit;
typedef std::vector<std::pair<CRadPoly*,unsigned int> >::iterator SharedIter;
typedef std::vector<CRadVertex *> radpvertexlist;
typedef std::vector<CRadVertex *>::iterator radpvertexit;
#define PLANE_MX 3
#define PLANE_MY 4
#define PLANE_MZ 5
const float cfNormalizeThreshold = 0.00001f;
//need this shitty class because it is a bit too late to use a smart pointer and let it reference
//use a map and the address as key
class CRasterCubeManager
{
public:
CRasterCubeManager(){};
~CRasterCubeManager();
void AddReference(const CRadMesh* cpRadMesh);
CRasterCubeImpl* CreateRasterCube();
const bool RemoveReference(CRadMesh* cpRadMesh);
void Clear();
protected:
std::map<CRasterCubeImpl*, std::set<const CRadMesh*> > m_mRasterCubeMap; //keeps track of each rastercube allocation
};
// contains all information which a certain dot3 dominant light dir texel was stored with
// also controls subsampling
typedef struct SComplexOSDot3Texel
{
Vec3d vDot3Light; //!< world space light vector
CRadPoly *pSourcePatch; //!< pointer to triangle where tangent space comes from, is always on this patch
float fAlpha; //!< barycentric alpha value
float fBeta; //!< barycentric beta value
unsigned int uiLightMask; //!< lightmask
unsigned int bNotHit; //!< true if this was a snapped texel
SComplexOSDot3Texel():vDot3Light(),pSourcePatch(NULL),fAlpha(0.f),fBeta(0.f),uiLightMask(0),bNotHit(false){}
const Vec3d TransformIntoTS(Vec3d& rSource)const;
}SComplexOSDot3Texel;
//calc the area of a triangle using texture coords
//////////////////////////////////////////////////////////////////////
inline float CalcTriangleArea( const Vec3d &A, const Vec3d &B, const Vec3d &C )
{
return( GetLength( (C-A).Cross(B-A) )*0.5f );
}
__inline int CalcPlaneType2(const Vec3d& n)
{
if (((1.0f-n.x)<0.001f) && ((1.0f-n.x)>=0))
return PLANE_X;
else
if (((1.0f-n.y)<0.001f) && ((1.0f-n.y)>=0))
return PLANE_Y;
else
if (((1.0f-n.z)<0.001f) && ((1.0f-n.x)>=0))
return PLANE_Z;
else
{
float ax = fabs(n[0]);
float ay = fabs(n[1]);
float az = fabs(n[2]);
if (ax>=ay && ax>=az)
return PLANE_MX;
else
if (ay>=ax && ay>=az)
return PLANE_MY;
else
return PLANE_MZ;
}
}
const float DistToLine(const Vec3d& rX0, const Vec3d& rX1, const Vec3d& rPoint);
const Vec3d ProjectOntoEdge(const Vec3d& rX0, const Vec3d& rX1, const Vec3d& rPoint, const float cfDistLV);
inline const unsigned int RoundFromFloat(const float cfTex)
{
unsigned int uiNumber = (unsigned int)cfTex;
if(cfTex - (float)uiNumber >= 0.5f)
uiNumber++;
return uiNumber;
}
const Vec3d RotateIntoPlane(const Vec3d& vPlaneNormal0, const Vec3d& vPlaneNormal1, const Vec3d& rInPosition, const Vec3d& rSource0);
//! \brief used local during generation of light clustering arrangement
struct GLMCluster
{
Vec3d vMin;
Vec3d vMax;
std::set<IEntityRender *> vGLMsAffectingCluster;
};
class CRadVertex
{
public:
Vec3d m_vPos; //!< in world space
Vec3d m_vNormal; //!< vertex normal for smooth lighting calulation
float m_fpX,m_fpY; //!< used as projection coordinates and final texture coordinates as well
Vec3d m_vTNormal; //!<
Vec3d m_vBinormal; //!<
Vec3d m_vTangent; //!<
CRadVertex() : m_vPos(0.f,0.f,0.f), m_vNormal(0.f,0.f,0.f), m_fpX(0.f), m_fpY(0.f), m_vTNormal(0.f,0.f,0.f), m_vBinormal(0.f,0.f,0.f), m_vTangent(0.f,0.f,0.f)
{}
};
class CRadPoly
{
private:
void FreeLightmaps( void );
//! used by GetNearestPolyAt()
const float PointInTriangle(const Vec3d &point, const int ciIndex0, const int ciIndex1);
public:
static const unsigned int scuiOneVertexShareFlag = 0x7F;
//calculates the tangent space from the face information and applies it
void CalculateTangentSpace(SUV uv[3]);
//passes pointer to all contained polys to give them access for averaging
void SynchronizeLightmaps();
static void ApplyBaryCoordsToVertex(CRadVertex& rDest, const CRadVertex& rSource0, const CRadVertex& rSource1, const CRadVertex& rSource2, float cfAlpha, float cfBeta, const bool cbCOmputeTS = true);
const bool CheckPointInTriangle(Vec3d &inPosition, const Vec3d &rVert0, const Vec3d &rVert1, const Vec3d &rVert2, float &rOutfAlpha, float &rOutfBeta, float &rfArea3d);
//! construtor
CRadPoly();
//! construtor
CRadPoly(CRadPoly *pSource);
//! destructor
~CRadPoly();
//! /return true=the given polys have at least one vertex in common
bool Connected(CRadPoly *pOther);
//! copy the data to the give patch, the poly lightmap data is removed from memory, too
//! /param inpDestLightPatch destination
//! /param nMapS
//! /param nMapT
//! /param inpSrcPoly pointer to the poly, must not be 0
//! /param sw source pitch = width in bytes
//! /param sh number of lines
//! /param dw destination pitch
void CopyData( CLightPatch *inpDestLightPatch, int dw );
bool IsTextureUniform(const int nTreshold);
//! compress patch to 1x1 if constant value
void Compress( const unsigned int cuiMinBlockSize );
//! gather all subsamples for on etexel and sets the new value
//! /param cuiX x-coord for texel to subsample
//! /param cuiY y-coord for texel to subsample
//! /param cuiSubSamples subsample count excluding center texel
//! /param cpaIndicator pointer to indicator array indicating which subsample has received values
//! /param cpfColours pointer to colour subsample array
//! /param pDot3 pointer to dot3 subsample values
//! /param ruiMaxComponent current max of colour patch components
#ifdef APPLY_COLOUR_FIX
void GatherSubSamples(
const unsigned int cuiX, const unsigned int cuiY, const unsigned int cuiSubSamples,
const unsigned int *cpaIndicator, const float *cpfColours, const SComplexOSDot3Texel *pDot3,
unsigned int& ruiMaxComponent, const SOcclusionMapTexel *cpfOccl, const GLMOcclLightInfo& rOcclInfo);
#else
void GatherSubSamples(
const unsigned int cuiX, const unsigned int cuiY, const unsigned int cuiSubSamples,
const unsigned int *cpaIndicator, const float *cpfColours, const SComplexOSDot3Texel *pDot3, const SOcclusionMapTexel *cpfOccl, const GLMOcclLightInfo& rOcclInfo);
#endif
//!
//! /return true=polygon has 3 vertices - calculation is ok, false=its not a triangle (degenerated - or wrong vertex count) calculation failed
//! alters inPosition by projecting it into the triangls area
bool ApplyBarycentricCoordinates( Vec3d &inPosition, CRadVertex &outVertex, SComplexOSDot3Texel& rDot3Texel, const bool cbImmedReturn = false, const bool cbSnap = true);
//!
//! /param inPosition
//! /return could be 0
CRadPoly *GetNearestPolyAt( const Vec3d &inPosition );
//lighpatch
void AddWarningBorders( void );
#ifndef DISPLAY_MORE_INFO
const unsigned int CalcExtent(CLightScene *pScene, const CString& rGLMName, const CString& rCGFName, bool bOriginal, const float fGridSize, const UINT iMinBlockSize, const UINT iMaxBlockSize, unsigned int& rHugePatchFoundNumber);
#else
const unsigned int CalcExtent(CLightScene *pScene, const CString& rGLMName, const CString& rCGFName, bool bOriginal, const float fGridSize, const UINT iMinBlockSize, const UINT iMaxBlockSize);
#endif
//!
void GenerateImage( void );
//!
//! m_nW and m_nH is used
void AllocateDot3Lightmap(const bool cbGenerateHDRMaps, const bool cbGenerateOcclMaps = false);
bool InterpolateVertexAt( const float infX, const float infY, CRadVertex &outVertex, SComplexOSDot3Texel& rDot3Texel, const bool cbSubSample, const bool cbSnap=true) ;
/**
* snaps a vertex which lies outside a triangle onto the nearest edge to get some valid barycentric coordinates
* it does not extrapolate
* @param inPosition vertex outside the triangle
* @param outfAlpha alpha value for barycentric coordinate
* @param outfBeta beta value for barycentric coordinate
* @param cfTriangleArea area of triangle for computation of the new barycentric cooridnates
*/
const bool SnapVertex(Vec3d &inPosition, float &outfAlpha, float &outfBeta, const float cfTriangleArea);
/**
* tries to map coordinates into triangles sharing at least one vertex of this triangle, performs the smoothing
* @param outVertex output vertex interpolated from shared triangles
* @param inPosition vertex outside the triangle
* @param cfArea3d traingle area for snapping call
* @param rDot3Texel information for dot3 texel to set up
* @return true if one shared triangle has been found, false otherwise
*/
const bool SmoothVertex(CRadVertex &outVertex, const Vec3d &inPosition, const float cfArea3d, SComplexOSDot3Texel& rDot3Texel);
/**
* applies tangent space vectors from this patch to a vertex having a pos outside this triangle
* @param outVertex output vertex interpolated from shared triangles
* @param outfAlpha alpha value for barycentric coordinate
* @param outfBeta beta value for barycentric coordinate
*/
void ApplyTangentSpaceToVertex(CRadVertex &outVertex, float cfAlpha, float cfBeta);
void SetHDRLightmapTexel( const float infX, const float infY, const float lr, const float lg, const float lb);
void SetOcclLightmapTexel(const float infX, const float infY, const SOcclusionMapTexel& rTexel);
//!
//! /param infX [0..m_nW[
//! /param infY [0..m_nH[
//! /param r 0.. , is automatic clamped to 0..255, usual lightmap color
//! /param g 0.. , is automatic clamped to 0..255, usual lightmap color
//! /param b 0.. , is automatic clamped to 0..255, usual lightmap color
void SetSimpleLightmapTexel( const float infX, const float infY, const int lr, const int lg, const int lb, unsigned char iDP3Fac);
//! stores the world space dot3 light vector and calls SetSimpleLightmapTexel
#ifdef APPLY_COLOUR_FIX
const unsigned int SetDot3LightmapTexel(const CRadVertex& rVertex,
const float fColorRLamb, const float fColorGLamb, const float fColorBLamb,
Vec3d &inLightDir, const float cfLM_DP3LerpFactor,
SComplexOSDot3Texel& rDot3Texel, const SOcclusionMapTexel& rTexel, bool bHDR);
#else
void SetDot3LightmapTexel(const CRadVertex& rVertex,
const float fColorRLamb, const float fColorGLamb, const float fColorBLamb,
Vec3d &inLightDir, const float cfLM_DP3LerpFactor,
SComplexOSDot3Texel& rDot3Texel, const SOcclusionMapTexel& rTexel, bool bHDR);
#endif
//! stores the tangent space light vector as colour
#ifdef APPLY_COLOUR_FIX
void SetDot3TSLightmapTexel(const unsigned int cuiX, const unsigned int cuiY, const unsigned int cuiColourFixAlpha, const float cfColourScale);
#else
void SetDot3TSLightmapTexel(const unsigned int cuiX, const unsigned int cuiY);
#endif
//unsigned int consists of 4 chars as follows: [0]=1st shared vertex of this [1]=2nd shared vertex of this [2]=1st shared vertex of shared triangle [3]=2nd shared vertex of shared triangle
std::vector<std::pair<CRadPoly*,unsigned int> > m_SharingPolygons; //!< vertex sharing polygons
radpolylist m_lstMerged; //!< only patches merge
radvertexlist m_lstOriginals; //!<
Plane m_Plane; //!<
CRadPoly * m_pSource; //!<
CRadPoly * m_pMergeSource; //!< the source for this radpoly where it is contained in m_lstMerged
float m_fX1,m_fY1,m_fX2,m_fY2; //!<
int16 m_nX1,m_nY1,m_nX2,m_nY2; //!<
uint16 m_nW,m_nH; //!< really occupied width and height in the lightmap (always >=0)
uint8 m_nAx1,m_nAx2; //!<
uint8 m_nAxis; //!<
uint8 m_dwFlags; //!<
Vec3 *m_pHDRLightmapData; //! RGBE8 Lightmap
unsigned char *m_pLightmapData; //! Lightmap / or colormap when using dot3
unsigned char *m_pDominantDirData; //!< used for Dot3Lightmaps (normalized tangent space direction to the dominant lightsource, dot3 factor in alpha channel)
SComplexOSDot3Texel *m_pWSDominantDirData; //!< used for Dot3Lightmaps, stores the world space vector and where the tangent space comes from
SOcclusionMapTexel *m_pOcclMapData; //!< used for occlusionmap data
uint16 m_nOffsetW, m_nOffsetH; //!< offset into big lightmap
friend class CLightScene;
};
//////////////////////////////////////////////////////////////////////
class CRadMesh
{
public:
//! destructor
~CRadMesh();
//! call after putting in the light sources to get the right HashValue
void CreateEmpty( IEntityRender *pIEtyRend, const Matrix44& matEtyMtx );
const bool FillInValues( IEntityRender *pIEtyRend, const CBrushObject *pBrushObject, const Matrix44& matEtyMtx );
void PrepareLightmaps(CLightScene *pScene, bool bMergePolys,const float fGridSize, const Vec3d& vMinBB, const Vec3d& vMaxBB);
bool SaveLightmaps(CLightScene *pScene,bool bDebug=false);
//! /return hashing value to detect changes in the data
DWORD GetHashValue( void ) const {return(m_dwHashValue);};
void UpdateHash(const DWORD newHash){CalcNCombineHash(newHash,m_dwHashValue);}
// --------------------------------------------------
IEntityRender * m_pESource; //!<
radpolylist m_lstOldPolys; //!< this class is the owner of these objects (delete is called)
Vec3d m_vCenter; //!<
float m_fRadius,m_fRadius2; //!<
std::vector<LMCompLight *> m_LocalLights; //!< the lights that may affect this RadMesh (this class is not the owner = delete will not be called)
std::vector<LMCompLight *> m_LocalOcclLights; //!< the occlusion map lights types that may affect this RadMesh (this class is not the owner = delete will not be called)
CString m_sGLMName; //!< name of glm corresponding to this mesh
CRasterCubeImpl *m_pClusterRC; //!< Raster cube of the cluster in which this mesh is in
float m_fLMAccuracy; //!< individual GLM accuracy ranging from 0.. with 1 = default global world<->texel ratio
bool m_bReceiveLightmap; //!< indicates whether an object will receive lightmaps or not
unsigned int m_uiFlags; //!< flags, currently used during normal check and triangle initialisation
float m_fMaxVariance; //!< variance in normal length (purely for information)
IVariable *pLightmapQualityVar; //!< lightmap quality variable to adjust it if necessary
unsigned int m_uiTexCoordsRequired; //!< required texture coordinates by engine, should always match number of indices
GLMOcclLightInfo m_OcclInfo; //!< required information what light corresponds to which colour channel
//! constructor
CRadMesh() :
m_uiCoarseTexelCount(0), pLightmapQualityVar(NULL), m_fMaxVariance(0.f), m_fLMAccuracy(1.f), m_pESource(NULL),
m_dwHashValue(0), m_pClusterRC(NULL), m_bReceiveLightmap(true), m_uiFlags(0), m_uiTexCoordsRequired(0){}
private:
unsigned int m_uiCoarseTexelCount; //!< texel count required (simple summation over all patches after CalcExtent)
DWORD m_dwHashValue; //!< for selective recomputation
DWORD CalcLocalLightHashValue( void ) const;
};
/////////////////////////////////////////////////////////////////////
class CLightPatch
{
public:
CLightPatch(UINT iPatchResolution) : m_pLightmapImage(0),m_pHDRLightmapImage(0),m_pDominantDirImage(0),m_pOcclMapImage(NULL),m_nTextureNum(0)
{
m_nPatchSpace.resize(iPatchResolution);
}
~CLightPatch()
{
if(m_pLightmapImage) { delete [] m_pLightmapImage;m_pLightmapImage=NULL; }
if(m_pHDRLightmapImage) { delete [] m_pHDRLightmapImage;m_pHDRLightmapImage=NULL; }
if(m_pDominantDirImage) { delete [] m_pDominantDirImage;m_pDominantDirImage=NULL; }
if(m_pOcclMapImage) { delete [] m_pOcclMapImage;m_pOcclMapImage=NULL; }
}
//! /param nSizeX 1..
//! /param nSizey 1..
void CreateDot3Lightmap( int nSizeX, int nSizeY,const bool cbGenerateHDRMaps, const bool cbGenerateOcclMaps = false );
void Reset();
//!
const bool GetSpace(int w,int h, uint16 &x, uint16 &y);
std::vector<int> m_nPatchSpace; //!<
int m_nTextureNum; //!<
int m_nWidth,m_nHeight; //!<
std::string m_szFilename; //!<
unsigned char *m_pHDRLightmapImage; //!< HDR Lightmap texture (in Dot3Lightmaps this is used for the non dominant light sources)
unsigned char *m_pLightmapImage; //!< Lightmap texture (in Dot3Lightmaps this is used for the non dominant light sources)
unsigned char *m_pDominantDirImage; //!< only used for Dot3Lightmaps (normalized worldspace direction to the dominant lightsource)
SOcclusionMapTexel *m_pOcclMapImage; //!< used for occlusionmap data
};
//////////////////////////////////////////////////////////////////////
class CLightScene
{
public:
CLightScene();
~CLightScene();
bool CreateFromEntity(
const IndoorBaseInterface &pInterface,
LMGenParam sParam,
std::vector<std::pair<IEntityRender*, CBrushObject*> >& vNodes,
CLMLightCollection& cLights,
struct ICompilerProgress *pIProgress,
const ELMMode Mode,
volatile SSharedLMEditorData* pSharedData,
const std::set<const CBrushObject*>& vSelectionIndices,
bool &rErrorsOccured);
radmeshlist m_lstRadMeshes; //!< this class is the owner of these objects (delete is called)
radpolylist m_lstScenePolys; //!<
unsigned int m_uiCurrentPatchNumber; //!< to number the current lightmap
CLightPatch * m_pCurrentPatch; //!<
LMGenParam m_sParam;
void CreateNewLightmap( void );
// The assign queue system
struct LMAssignQueueItem
{
IEntityRender * pI_GLM; //!
std::vector<struct TexCoord2Comp> vSortedTexCoords; //!
DWORD m_dwHashValue; //!< hashing value to detect changes in the data
std::vector<std::pair<EntityId, EntityId> > vOcclIDs; //!< occlusion indices corresponding to the 0..4 colour channels
};
std::list<LMAssignQueueItem> m_lstAssignQueue; //!
std::vector<CString>& GetLogInfo()
{
return m_LogInfo;
}
std::multimap<unsigned int,std::string>& GetWarningInfo()
{
return m_WarningInfo;
}
const float ComputeHalvedLightmapQuality(const float fOldValue);
protected:
void DoLightCalculation(
unsigned int &ruiMaxColourComp,
const std::vector<LMCompLight*>& vLights,
const std::vector<LMCompLight*>& vOcclLights,
SComplexOSDot3Texel& dot3Texel,
CRadMesh *pMesh,
CRadPoly *pSource,
const CRadVertex& Vert,
const bool cbFirstPass,
const unsigned int cuiSubSampleIndex = 0);
void CheckLight(LMCompLight& rLight, const int iCurLightIdx);
void GenerateTexCoords(const CRadMesh& rRadMesh, LMAssignQueueItem& rNewGLMQueueItm);
void SelectObjectLMReceiverAndShadowCasterForChanges(
std::vector<std::pair<IEntityRender*, CBrushObject*> >& vNodes,
const std::vector<AABB>& vAABBs,
const Matrix33& rMatSunBasis,
const std::vector<unsigned int>& rvNodeRadMeshIndices,
const ELMMode Mode);
void SelectObjectsFromChangedLMShadowCaster(
std::vector<std::pair<IEntityRender*, CBrushObject*> >&vNodes,
const std::vector<AABB>& vAABBs,
const Matrix33& rMatSunBasis,
const std::vector<unsigned int>& rvNodeRadMeshIndices);
void ComputeSmoothingInformation(const unsigned int uiSmoothAngle = 45, const bool cbTSGeneratedByLM = false); //computes the sharing information and stores it into the respective per polygon vectors
void MergeTangentSpace(CRadVertex& rVertex1, CRadVertex& rVertex2); //merges two tangent spaces according to normal
const unsigned int CheckNormals(float& rfMaxVariance, const CRadMesh* const pMesh);//returns true if some normals are not normalized, rfMaxVariance is the max variance encountered
bool Create(const IndoorBaseInterface &pInterface, const Vec3d& vMinBB, const Vec3d& vMaxBB, volatile SSharedLMEditorData *pSharedData, const ELMMode Mode, const unsigned int cuiMeshesToProcessCount);
void FlushAssignQueue();
#ifdef APPLY_COLOUR_FIX
void PerformAdaptiveSubSampling(
CRadMesh *pMesh,
CRadPoly *pSource,
const std::vector<LMCompLight*>& vLights,
const std::vector<LMCompLight*>& vOcclLights,
unsigned int uiMaxComponent);
#else
void PerformAdaptiveSubSampling(
CRadMesh *pMesh,
CRadPoly *pSource,
const std::vector<LMCompLight*>& vLights,
const std::vector<LMCompLight*>& vOcclLights);
#endif
void SetSubSampleDot3LightmapTexel(
const unsigned int cuiSubSampleIndex,
const float fColorRLamb, const float fColorGLamb, const float fColorBLamb,
Vec3d &inLightDir, const float cfLM_DP3LerpFactor,
SComplexOSDot3Texel& rDot3Texel,
const SOcclusionMapTexel& rOcclTexel, bool bHDR);
void GatherSubSampleTexel(const CRadPoly *pSource, const int ciX, const int ciY, std::set<std::pair<unsigned int, unsigned int> >& rvSubTexels);
const bool FlushAndSave(volatile SSharedLMEditorData *pSharedData, const ELMMode Mode);
void Reset()
{
//i need this definitely to be deallocated, thats why clear and resize call (implementations differ to much which really deallocates)
m_lstRadMeshes.clear();
m_lstScenePolys.clear();
m_lstAssignQueue.clear();
m_lstRadMeshes.resize(0);
m_lstScenePolys.resize(0);
m_lstAssignQueue.resize(0);
m_lstClusters.clear();
m_lstClusters.resize(0);
};
void WriteLogInfo();
void Init()
{
Reset();
MEMORYSTATUS sStats;
GlobalMemoryStatus(&sStats);
m_uiStartMemoryConsumption = ((sStats.dwTotalVirtual - sStats.dwAvailVirtual) / 1024/1024);
}
const unsigned int GetUsedMemory()
{
MEMORYSTATUS sStats;
GlobalMemoryStatus(&sStats);
const unsigned int cuiCurrentTotal((sStats.dwTotalVirtual - sStats.dwAvailVirtual) / 1024/1024);
if(cuiCurrentTotal <= m_uiStartMemoryConsumption)
return 0;
return (cuiCurrentTotal - m_uiStartMemoryConsumption);
};
void DumpSysMemStats()
{
_TRACE(m_LogInfo, true, "System memory usage: %iMB\r\n", GetUsedMemory());
};
void DisplayMemStats(volatile SSharedLMEditorData *pSharedData)
{
if(pSharedData != NULL)
{
::SendMessage( pSharedData->hwnd, pSharedData->uiMemUsageMessage, min(GetUsedMemory(),1000), 0 );//update progress bar
::SendMessage( pSharedData->hwnd, pSharedData->uiMemUsageStatic, min(GetUsedMemory(),1000), 0 );//update progress bar static element
MSG msg;
while( FALSE != ::PeekMessage( &msg, 0, 0, 0, PM_REMOVE ) )
{
::TranslateMessage( &msg );
::DispatchMessage( &msg );
}
}
else
DumpSysMemStats();
}
static bool CastsLightmap(const CBrushObject *pBrushObject)
{
bool bCastLightmap = true;
if(pBrushObject)
{
CVarBlock *pVarBlock = pBrushObject->GetVarBlock();
if(pVarBlock)
{
IVariable *pVar = pVarBlock->FindVariable("CastLightmap");
if(pVar && (pVar->GetType() == IVariable::BOOL))
{
pVar->Get(bCastLightmap);
}
}
}
return bCastLightmap;
}
void InitSubSampling(const unsigned int cuiSampleCount)
{
m_puiIndicator = new unsigned int[cuiSampleCount]; assert(m_puiIndicator);
m_pSubSamples = new SComplexOSDot3Texel[cuiSampleCount]; assert(m_pSubSamples);
m_pfColours = new float [cuiSampleCount * 4/*4 colour components*/]; assert(m_pfColours);
if(m_sParam.m_bGenOcclMaps)
{
m_pOcclSamples = new SOcclusionMapTexel[cuiSampleCount]; assert(m_pOcclSamples);
}
m_uiSampleCount = cuiSampleCount;
}
private:
std::multimap<unsigned int,std::string> m_WarningInfo; //!< will contain warning summary for logfile
std::vector<CString> m_LogInfo; //!< will contain some logging info
struct ILMSerializationManager *m_pISerializationManager;
std::list<GLMCluster> m_lstClusters;
CRasterCubeManager m_RasterCubeManager;
unsigned int *m_puiIndicator; //!< index of each pixel whether a value has been set or not
SComplexOSDot3Texel *m_pSubSamples; //!< subsamples for one texel
SOcclusionMapTexel *m_pOcclSamples; //!< subsamples for occl map texel
float *m_pfColours; //!< colours
unsigned int m_uiSampleCount; //!< subsample count for one single texel
unsigned int m_uiStartMemoryConsumption;
IndoorBaseInterface m_IndInterface;
bool ComputeRasterCube(CRasterCubeImpl *pRasterCube, const std::set<IEntityRender *>& vGeom, const Matrix33 *pDirLightTransf = NULL);
void Shadow(LMCompLight& cLight, UINT& iNumHit, CRadMesh *pMesh, CRadPoly *pSource, const CRadVertex& Vert, const Vec3d& vSamplePos);
#define ALREADY_TESTED_ARRARY_SIZE 1024
class CEveryObjectOnlyOnce :public CPossibleIntersectionSink<RasterCubeUserT>
{
public:
//! /param invFrom
//! /param invDir
//! /param infRayLength
//! /param inpIgnore for bias free rayshooting from object surface, may be 0
void SetupRay( const Vec3d& vRayDir, const Vec3d& vRayOrigin, float fRayLen, CRadPoly* pRayDest)
{
m_vRayDir = vRayDir;
m_vRayOrigin = vRayOrigin;
m_fRayLen = fRayLen;
m_pRayDest = pRayDest;
m_dwAlreadyTested=0;
}
// --------------------------------------------
protected:
Vec3d m_vRayOrigin; //!< position in world space
Vec3d m_vRayDir; //!< direction in world space
DWORD m_dwAlreadyTested; //!< 0..ALREADY_TESTED_ARRARY_SIZE
float m_fRayLen; //!<
const RasterCubeUserT * m_arrAlreadyTested[ALREADY_TESTED_ARRARY_SIZE]; //!< [0..ALREADY_TESTED_ARRARY_SIZE-1], this is faster than a set<> at least for typical amounts
CRadPoly* m_pRayDest; //!< ray destination polygon
//! /return true=object is already tested, false otherwise
bool InsertAlreadyTested( RasterCubeUserT *inObject )
{
const RasterCubeUserT * *ptr=m_arrAlreadyTested;
for(DWORD i=0;i<m_dwAlreadyTested;i++)
if(*ptr++==inObject)return(true);
if(m_dwAlreadyTested<ALREADY_TESTED_ARRARY_SIZE-1) m_arrAlreadyTested[m_dwAlreadyTested++]=inObject;
else assert(0); // ALREADY_TESTED_ARRARY_SIZE is not big enougth
return(false);
}
};//CEveryObjectOnlyOnce
// ---------------------------------------------------------------------------------------------
// Intersection sink
// ---------------------------------------------------------------------------------------------
class CAnyHit : public CEveryObjectOnlyOnce
{
public:
void SetupRay(const Vec3d& vRayDir, const Vec3d& vRayOrigin, float fRayLen, const float infGridSize, CRadPoly* pRayDest = NULL)
{
CEveryObjectOnlyOnce::SetupRay(vRayDir,vRayOrigin,fRayLen, pRayDest);
m_pHitResult = 0;
m_fClosest = FLT_MAX;
m_fGridSize=infGridSize;
};
bool IsIntersecting() const { return(m_pHitResult!=0); }
// TODO
float m_fD;
Vec3d m_vPNormal;
float m_fGridSize;
const bool ReturnElement(RasterCubeUserT &inObject, float &inoutfRayMaxDist);
RasterCubeUserT *m_pHitResult;
protected:
float m_fClosest;
// Still intersect with backfacing triangles. While this fixes a few false shadow cases, it
// adds ugly bright seems between caster and shadow, which looks far worse
int intersect_triangle( float orig[3], float dir[3],
float vert0[3], float vert1[3], float vert2[3],
float *t, float *u, float *v);
};//CAnyHit
friend class CRadPoly;
};//CLightScene
//computes a fade for the light average weight
static inline const float LightInfluence(const float cfDot)
{
static const float scfMinInfluence = 0.01f; //influence = 0
static const float scfFullInfluence = 0.2f; //influence = 1
if(cfDot >= scfFullInfluence)
return 1.f;
if(cfDot <= scfMinInfluence)
return 0.f;
return (cfDot - scfMinInfluence) / (scfFullInfluence - scfMinInfluence);
}
#endif // __INDOOR_LIGHT_PATCHES_H__

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,65 @@
// ---------------------------------------------------------------------------------------------
// Crytek CryENGINE source code
// History:
// - Created by Tim Schroeder
// ---------------------------------------------------------------------------------------------
#ifndef __LM_COMP_COMMONH_H__
#define __LM_COMP_COMMONH_H__
#pragma once
struct IndoorBaseInterface
{
ILog *m_pLog;
IRenderer *m_pRenderer;
I3DEngine *m_p3dEngine;
IConsole *m_pConsole;
ISystem *m_pSystem;
};
#include "LMCompStructures.h"
extern ICompilerProgress *g_pIProgress;
_inline void __cdecl _TRACE(std::vector<CString>& rLogInfo, const bool cbDoOutput, const char *sFormat, ... )
{
va_list vl;
static char sTraceString[1024];
va_start(vl, sFormat);
vsprintf(sTraceString, sFormat, vl);
va_end(vl);
rLogInfo.push_back(CString(sTraceString));
if(cbDoOutput == false)
return;
if (g_pIProgress == NULL)
return;
g_pIProgress->Output(sTraceString);
}
_inline void __cdecl _TRACE(const char *sFormat, ... )
{
va_list vl;
static char sTraceString[1024];
va_start(vl, sFormat);
vsprintf(sTraceString, sFormat, vl);
va_end(vl);
if (g_pIProgress == NULL)
return;
g_pIProgress->Output(sTraceString);
}
/*
__inline bool IsLeafBufferEmpty(CLeafBuffer *pLB)
{
return (pLB->m_pSecVertBuffer == NULL && pLB->m_pSecVertBuffer->m_NumVerts == 0 && pLB->m_NumIndices == 0);
}
*/
#endif

View File

@@ -0,0 +1,182 @@
#ifndef __LM_LIGHT_COLLECTION_H__
#define __LM_LIGHT_COLLECTION_H__
#include "LMCompStructures.h"
#include "RasterCube.h"
//! \sa LMCompLight
enum eLightType
{
eSpotlight,
ePoint,
eDirectional, //!< Sun
};
//class CRadPoly;
struct RasterCubeUserT
{
float fVertices[3][3];
Vec3d vNormal; //plane normal of triangle
// CRadPoly *pTriangle;
bool operator == (const RasterCubeUserT& A) const
{ return memcmp(this, &A, sizeof(RasterCubeUserT)) == 0; };
bool operator > (const RasterCubeUserT& A) const
{ return memcmp(this, &A, sizeof(RasterCubeUserT)) == 1; };
};
typedef CRasterCube<RasterCubeUserT, true, false> CRasterCubeImpl;//fast version using only two raster and break after first valid hit encounter
//typedef CRasterCube<RasterCubeUserT> CRasterCubeImpl;//old version with 3 rastertables and no early out
inline void CalcNCombineHash( const DWORD indwValue, DWORD &inoutHash )
{
// inoutHash^=inoutHash*0x1c1a8926 + indwValue + inoutHash;
// inoutHash^=inoutHash%600011 + indwValue;
inoutHash^=(inoutHash%600011) + (inoutHash/600011) + indwValue;
}
inline void CalcNCombineHash( const float infValue, DWORD &inoutHash )
{
CalcNCombineHash(*((DWORD *)(&infValue)),inoutHash);
}
inline void CalcNCombineHash( const Vec3d &invValue, DWORD &inoutHash )
{
CalcNCombineHash(invValue.x,inoutHash);
CalcNCombineHash(invValue.y,inoutHash);
CalcNCombineHash(invValue.z,inoutHash);
}
//! \sa eLightType
struct LMCompLight
{
LMCompLight():m_CompLightID(std::pair<EntityId, EntityId>(0,0))
{
vWorldSpaceLightPos = Vec3d(0.0f, 0.0f, 0.0f);
vDirection = Vec3d(1.0f, 0.0f, 0.0f);
fRadius = 1.0f;
fLightFrustumAngleDegree = 45.0f;
eType = ePoint;
fColor[0]=0;fColor[1]=0;fColor[2]=0;
m_pLastIntersection=0;
// m_pPointLtRC = NULL;
m_pLastIntersectionRasterCube = NULL;
uiFlags = 0;
pVisArea = 0;
m_bFakeRadiosity = false;
m_bDot3 = true;
m_bOcclType = false;
m_bCastShadow = true;
};
~LMCompLight(){}
Vec3d vWorldSpaceLightPos; //!<
Vec3d vDirection; //!< For directional / spotlights (normalized)
float fRadius; //!< >=0
float fLightFrustumAngleDegree; //!< For spotlights
float fColor[3]; //!< [0..,0..,0..]
eLightType eType; //!<
bool m_bDot3; //!< marks the lightsource to be dot3 vector contributing
bool m_bFakeRadiosity; //!< applies a different lighting model
bool m_bOcclType; //!< indicates a occlusion map light source type
bool m_bCastShadow; //!< indicates whether this lights casts shadow into lightmap or not
CString m_Name; //!< name of lightsource
std::pair<EntityId, EntityId> m_CompLightID; //!< id which the light is referred to from GLMIOcclInfo (the serialization index)
// CRasterCubeImpl *m_pPointLtRC; //!< Raster cube convering all geometry in the point light's radius
RasterCubeUserT *m_pLastIntersection; //!< Filled when ReturnElement() has a new intersection - to optimize the shadow rays
CRasterCubeImpl *m_pLastIntersectionRasterCube; //!< stores where cached result belongs to
uint uiFlags;
void * pVisArea;
//! /return hashing value to detect changes in the data
DWORD GetLightHashValue( void ) const
{
DWORD dwRet=0;
CalcNCombineHash(vWorldSpaceLightPos,dwRet);
CalcNCombineHash(vDirection,dwRet);
CalcNCombineHash(fRadius,dwRet);
CalcNCombineHash(fLightFrustumAngleDegree,dwRet);
CalcNCombineHash(fColor[0],dwRet);
CalcNCombineHash(fColor[1],dwRet);
CalcNCombineHash(fColor[2],dwRet);
CalcNCombineHash((DWORD)eType,dwRet);
DWORD ciFlagSum = (m_bFakeRadiosity? 0x11111111 : 0) + (m_bDot3? 0x22222222 : 0) + (m_bCastShadow? 0x44444444 : 0) + (m_bOcclType? 0x88888888 : 0);
CalcNCombineHash(ciFlagSum, dwRet);
return(dwRet);
}
};
// \brief Collection class for passing lights to the compiler, converts 3D engine lights
// to LM compiler lights
// \sa LMCompLight
class CLMLightCollection
{
public:
CLMLightCollection():m_uiOcclLightSize(0){};
std::vector<LMCompLight>& GetLights() { return m_vLights; }
std::vector<CDLight *>& GetSrcLights() { return m_vSrcLights; };
const unsigned int OcclLightSize() {return m_uiOcclLightSize;}
//!
void Release() { delete this; };
//! /param pLight must not be 0
void AddLight(CDLight *pLight, const CString& rName, const EntityId& rID, const bool cbCastShadow = true)
{
assert(pLight);
LMCompLight cNewLight;
cNewLight.fLightFrustumAngleDegree = pLight->m_fLightFrustumAngle;
cNewLight.fColor[0] = pLight->m_Color.r;
cNewLight.fColor[1] = pLight->m_Color.g;
cNewLight.fColor[2] = pLight->m_Color.b;
cNewLight.fRadius = pLight->m_fRadius;
cNewLight.vDirection = pLight->m_Orientation.m_vForward;
cNewLight.vDirection.Normalize();
cNewLight.vWorldSpaceLightPos = pLight->m_Origin;
// for Visible Area
if(pLight->m_pOwner)
{
cNewLight.uiFlags = pLight->m_Flags;
cNewLight.pVisArea = (void *) pLight->m_pOwner->GetEntityVisArea();
}
if (pLight->m_Flags & DLF_PROJECT)
cNewLight.eType = eSpotlight;
else
cNewLight.eType = ePoint;
cNewLight.m_bFakeRadiosity = (pLight->m_Flags & DLF_FAKE_RADIOSITY);
cNewLight.m_bDot3 = (pLight->m_Flags & DLF_LMDOT3);
cNewLight.m_bOcclType = (pLight->m_Flags & DLF_LMOCCL);
cNewLight.m_bCastShadow = cbCastShadow;
if(cNewLight.m_bOcclType) m_uiOcclLightSize++;
cNewLight.m_Name = rName;
cNewLight.m_CompLightID.first = rID;
cNewLight.m_CompLightID.second = m_vLights.size();
m_vLights.push_back(cNewLight);
m_vSrcLights.push_back(pLight);
};
protected:
std::vector<LMCompLight> m_vLights; //!< lightmap lights
std::vector<CDLight *> m_vSrcLights; //!< source entity
unsigned int m_uiOcclLightSize; //!< number of occlusion map light source types
};
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,679 @@
#pragma once
#include "RasterTable.h" // CRasterTable
#include <assert.h> // assert()
#include <set>
#include <vector>
template <class T, class T2> class _Matrix34C;
template <class T> class _Vector2dC;
template < class T > class _Vector3dC
{
public:
T p[3];
// default constructor
_Vector3dC ();
// constructor
_Vector3dC ( T x, T y, T z );
_Vector3dC ( const _Vector3dC<T> &x );
_Vector3dC ( const _Vector2dC<T> &x );
_Vector3dC ( const Vec3d &x ) { p[0]=(T)x.x;p[1]=(T)x.y;p[2]=(T)x.z; }
_Vector3dC ( const T &x );
void Set ( T x, T y, T z );
void Max ( const _Vector3dC<T> &c );
void Min ( const _Vector3dC<T> &c );
const _Vector3dC<T> operator- ( void ) const;
_Vector3dC<T> operator- ( const _Vector3dC<T> &other ) const;
_Vector3dC<T> operator-= ( const _Vector3dC<T> &other );
_Vector3dC<T> operator+ ( const _Vector3dC<T> &other ) const;
_Vector3dC<T> operator+= ( const _Vector3dC<T> &other );
T operator* ( const _Vector3dC<T> &other ) const;
_Vector3dC<T> operator* ( const T fak ) const;
_Vector3dC<T> operator*= ( const T fak );
_Vector3dC<T> operator/= ( const T fak );
bool operator== ( const _Vector3dC<T> &outher ) const;
T Length ( void ) const;
double LengthQuad ( void ) const;
void Normalize ( void );
// a=thumb, b=zeigefinger, b=mittelfinger mit rechter Hand
friend _Vector3dC<T> CrossProd ( const _Vector3dC<T> &a, const _Vector3dC<T> &b ); // Kreuzprodukt
friend _Vector3dC<T> operator* ( const T t, const _Vector3dC<T> &a );
};
template <class T>
_Vector3dC<T>::_Vector3dC()
{}
template <class T>
_Vector3dC<T>::_Vector3dC( T x, T y, T z )
{
p[0]=x;p[1]=y;p[2]=z;
}
template <class T>
_Vector3dC<T>::_Vector3dC( const T &x )
{
assert(x==0); // sonst macht das glaub ich keinen Sinn
p[0]=x;p[1]=x;p[2]=x;
}
template <class T>
_Vector3dC<T>::_Vector3dC( const _Vector3dC<T> &x )
{
p[0]=x.p[0];p[1]=x.p[1];p[2]=x.p[2];
}
template <class T>
_Vector3dC<T>::_Vector3dC( const _Vector2dC<T> &x )
{
p[0]=x.p[0];p[1]=x.p[1];p[2]=0;
}
template <class T>
void _Vector3dC<T>::Set( T x, T y, T z )
{
p[0]=x;p[1]=y;p[2]=z;
}
template <class T>
_Vector3dC<T> _Vector3dC<T>::operator-( const _Vector3dC<T> &b ) const
{
return _Vector3dC<T>(p[0]-b.p[0],p[1]-b.p[1],p[2]-b.p[2] );
};
template <class T>
_Vector3dC<T> _Vector3dC<T>::operator-=( const _Vector3dC<T> &b )
{
p[0]-=b.p[0];p[1]-=b.p[1];p[2]-=b.p[2];
return *this;
}
template <class T>
_Vector3dC<T> _Vector3dC<T>::operator+( const _Vector3dC<T> &b ) const
{
return _Vector3dC<T>(p[0]+b.p[0],p[1]+b.p[1],p[2]+b.p[2] );
}
template <class T>
_Vector3dC<T> _Vector3dC<T>::operator+=( const _Vector3dC<T> &b )
{
p[0]+=b.p[0];p[1]+=b.p[1];p[2]+=b.p[2];
return *this;
}
template <class T>
_Vector3dC<T> _Vector3dC<T>::operator*( const T f ) const
{
return _Vector3dC<T>(p[0]*f,p[1]*f,p[2]*f );
}
template <class T>
T _Vector3dC<T>::operator*( const _Vector3dC<T> &b ) const
{
return(p[0]*b.p[0]+p[1]*b.p[1]+p[2]*b.p[2]);
}
template <class T>
bool _Vector3dC<T>::operator==( const _Vector3dC<T> &other ) const
{
if(p[0]!=other.p[0])return(false);
if(p[1]!=other.p[1])return(false);
if(p[2]!=other.p[2])return(false);
return(true);
}
template <class T>
_Vector3dC<T> _Vector3dC<T>::operator*=( const T f )
{
p[0]*=f;p[1]*=f;p[2]*=f;
return(*this);
}
template <class T>
_Vector3dC<T> _Vector3dC<T>::operator/=( const T f )
{
T fInv=1/f;
p[0]*=fInv;p[1]*=fInv;p[2]*=fInv;
return(*this);
}
template <class T>
_Vector3dC<T> CrossProd( const _Vector3dC<T> &a, const _Vector3dC<T> &b )
{
_Vector3dC<T> ret;
ret.p[0]=a.p[1]*b.p[2] - a.p[2]*b.p[1];
ret.p[1]=a.p[2]*b.p[0] - a.p[0]*b.p[2];
ret.p[2]=a.p[0]*b.p[1] - a.p[1]*b.p[0];
return(ret);
}
template <class T>
void _Vector3dC<T>::Normalize( void )
{
T len=1.0f/Length();
p[0]*=len;p[1]*=len;p[2]*=len;
}
template <class T>
T _Vector3dC<T>::Length( void ) const
{
return( (T)sqrt(p[0]*p[0]+p[1]*p[1]+p[2]*p[2]) );
}
// unary negation
template <class T>
const _Vector3dC<T> _Vector3dC<T>::operator-( void ) const
{
return(_Vector3dC<T>(-p[0],-p[1],-p[2]));
}
typedef _Vector3dC<double> CVector3D;
template <class T>
class CPossibleIntersectionSink
{
public:
//! return one element from the bucket
//! /param inObject
//! /param inoutfRayMaxDist in and out value for max. shooting distance or FLT_MAX, if you found a interesection you may update this because further intersections are no longer interesting
//! /return true=do further processing, false=stop I found what I need (e.g. any intersection for shadow testing)
bool ReturnElement( T &inObject, float &inoutfRayMaxDist );
//!
void EndReturningBucket( void )
{}
};
template <class T, bool bBreakAfterFirstHit = false, bool bUseXRaster = true>
class CRasterCube
{
public:
//! default constructor
CRasterCube( void );
//! destructor
virtual ~CRasterCube( void );
//! alloc raster, after that Phase 1 has started
//! /param invMin minimum values for the bounding box
//! /param invMax maximum values for the bounding box
//! /param inElementCount element count for the data structure size estimation
//! /param infMagnifier scale factor for internal data structures (scales ~ quadratic in memory consumptions)
//! /return true=success, false otherwise
bool Init( const CVector3D invMin, const CVector3D invMax, DWORD inElementCount, float infMagnifier=1.0f );
//! free data
void DeInit( void );
//! Has to be called in Phase1, after that Phase 2 has started
//! /param inbDebug debug output is generated
//! /return true=success, false otherwise
bool PreProcess( const bool inbDebug );
//! Has to be called after Phase2
void Compress( void );
//! call this per triangle after Init and before PreProcess
//! after PreProcess call it again for every triangle
//! /param invVertex
//! /param inpElement
void PutInTriangle( const Vec3d invVertex[3], const T &inElement );
//!
//! /param outdwSize
//! /return memory consumption in bytes O(1)
DWORD CalcMemoryConsumption( DWORD outdwSize[3] );
// this works because m_pExtraMemoryPool is sorted in ascending order
// returns true if inSink.ReturnElement returns false(meaning stop processing), does not bother if bBreakAfterFirstHit = false
template <class Sink>
const bool GatherElementsAt( int iniPos[3], Sink &inSink, float& rfRet )
{
rfRet=FLT_MAX;
DWORD *pElX = 0;
if(bUseXRaster)
{
pElX=m_XRaster.GetElementsAt(iniPos[1],iniPos[2]); // YZ
if(!pElX)return(false);
}
DWORD *pElY=m_YRaster.GetElementsAt(iniPos[2],iniPos[0]); // ZX
if(!pElY)return(false);
DWORD *pElZ=m_ZRaster.GetElementsAt(iniPos[0],iniPos[1]); // XY
if(!pElZ)return(false);
if(bUseXRaster)
assert(*pElX);
assert(*pElY);
assert(*pElZ);
// get the intersection of all three lists
for(;;)
{
// increase pointer to the biggest values
if(bUseXRaster)
{
if(*pElY>*pElX)
{
if(*pElZ>*pElY){ pElZ++;if(*pElZ==0)break; }
else { pElY++;if(*pElY==0)break; }
}
else
{
if(*pElZ>*pElX){ pElZ++;if(*pElZ==0)break; }
else
{
if(*pElX==*pElY && *pElY==*pElZ)
{
if(bBreakAfterFirstHit)
{
if(!inSink.ReturnElement(m_vElements[(*pElX++)-1],rfRet))
{
inSink.EndReturningBucket();
return true;
}
}
else
inSink.ReturnElement(m_vElements[(*pElX++)-1],rfRet); // -1 because 0 is used for termination
if(*pElX==0)break;
pElY++;if(*pElY==0)break;
pElZ++;if(*pElZ==0)break;
}
else { pElX++;if(*pElX==0)break; }
}
}
}
else//bUseXRaster = false
{
if(*pElZ > *pElY)
{
pElZ++;
if(*pElZ==0)
break;
}
else
{
if(*pElZ < *pElY)
{
pElY++;
if(*pElY==0)
break;
}
else //*pElY == *pElZ
{
if(bBreakAfterFirstHit)
{
if(!inSink.ReturnElement(m_vElements[(*pElY++)-1],rfRet))
{
inSink.EndReturningBucket();
return true;
}
}
else
inSink.ReturnElement(m_vElements[(*pElY++)-1],rfRet); // -1 because 0 is used for termination
if(*pElY==0)
break;
pElZ++;
if(*pElZ==0)
break;
}
}
}
}
inSink.EndReturningBucket();
return(false);
}
// raytracing, gather unsorted hits (sorted could give faster access to the first hit)
// call ClearListBefore and GetHitList afterwards
// if bBreakAfterFirstHit=true it returns immediately if GatherElementsAt returns true (this is when inSink.ReturnElement returns false(meaning stop processing))
template <class Sink>
void GatherRayHitsDirection( const CVector3D _invStart, const CVector3D _invDir, Sink &inSink, const float infRayMax=FLT_MAX )
{
CVector3D invStart,vDirRelative; // scaled to the internal representation
// tranform the ray into the RasterCubeSpace
for(int i=0;i<3;i++)
{
double fScale=(((double)(m_iSize[i]))/((double)m_vMax.p[i]-(double)m_vMin.p[i]));
invStart.p[i]=(_invStart.p[i]-m_vMin.p[i])*fScale; vDirRelative.p[i]=_invDir.p[i]*fScale;
}
CVector3D vDir=vDirRelative;
int iPos[3]={ (int)floor(invStart.p[0]),(int)floor(invStart.p[1]),(int)floor(invStart.p[2]) }; // floor done
// integer direction
int iDir[3]={ -1,-1,-1 };
CVector3D vPosInCube( fmod(fabs(invStart.p[0]),1),fmod(fabs(invStart.p[1]),1),fmod(fabs(invStart.p[2]),1) );
if(vDir.p[0]>0){ iDir[0]=1;vDir.p[0]=-vDir.p[0];vPosInCube.p[0]=1.0f-vPosInCube.p[0]; }
if(vDir.p[1]>0){ iDir[1]=1;vDir.p[1]=-vDir.p[1];vPosInCube.p[1]=1.0f-vPosInCube.p[1]; }
if(vDir.p[2]>0){ iDir[2]=1;vDir.p[2]=-vDir.p[2];vPosInCube.p[2]=1.0f-vPosInCube.p[2]; }
// make sure it's in the range 0..1
if(vPosInCube.p[0]<=0)vPosInCube.p[0]++;
if(vPosInCube.p[1]<=0)vPosInCube.p[1]++;
if(vPosInCube.p[2]<=0)vPosInCube.p[2]++;
assert(vPosInCube.p[0]>0);
assert(vPosInCube.p[1]>0);
assert(vPosInCube.p[2]>0);
assert(vPosInCube.p[0]<=1.0f);
assert(vPosInCube.p[1]<=1.0f);
assert(vPosInCube.p[2]<=1.0f);
// calculate T and StepT
double fT[3],fTStep[3];
if(vDir.p[0]!=0.0)
{ fTStep[0]=1.0/vDir.p[0];fT[0]=vPosInCube.p[0]*fTStep[0]; }
else
{ fTStep[0]=0.0;fT[0]=-FLT_MAX; }
if(vDir.p[1]!=0.0)
{ fTStep[1]=1.0/vDir.p[1];fT[1]=vPosInCube.p[1]*fTStep[1]; }
else
{ fTStep[1]=0.0;fT[1]=-FLT_MAX; }
if(vDir.p[2]!=0.0)
{ fTStep[2]=1.0/vDir.p[2];fT[2]=vPosInCube.p[2]*fTStep[2]; }
else
{ fTStep[2]=0.0;fT[2]=-FLT_MAX; }
float fRayMax=infRayMax;
int iEndBorder[3];
CalcNewBorder(invStart,vDirRelative,fRayMax,iDir,iEndBorder);
float fNewRayMax;
for(;;)
{
// get elements if we are in the block
if((DWORD)iPos[0]<(DWORD)m_iSize[0])
if((DWORD)iPos[1]<(DWORD)m_iSize[1])
if((DWORD)iPos[2]<(DWORD)m_iSize[2])
{
if(bBreakAfterFirstHit)
{
if(GatherElementsAt(iPos,inSink, fNewRayMax))
return;//one valid hit found, stop here
}
else
GatherElementsAt(iPos,inSink, fNewRayMax); // Baustelle
// new Border
if(fNewRayMax<fRayMax)
{
fRayMax=fNewRayMax;
CalcNewBorder(invStart,vDirRelative,fRayMax,iDir,iEndBorder);
}
}
// advance position
int iAxis=GetBiggestValue(fT);
fT[iAxis]+=fTStep[iAxis];
iPos[iAxis]+=iDir[iAxis];
// stop on border hit
if(iDir[0]>0){ if(iPos[0]>=iEndBorder[0])break; }
else { if(iPos[0]<=iEndBorder[0])break; }
if(iDir[1]>0){ if(iPos[1]>=iEndBorder[1])break; }
else { if(iPos[1]<=iEndBorder[1])break; }
if(iDir[2]>0){ if(iPos[2]>=iEndBorder[2])break; }
else { if(iPos[2]<=iEndBorder[2])break; }
}
}
template <class Sink>
void GatherRayHitsTo( const CVector3D _invStart, const CVector3D _invEnd, Sink &inSink )
{
CVector3D vDir=_invEnd-_invStart; float fRayMax=(float)vDir.Length();
vDir.Normalize(); // Baustelle
GatherRayHitsDirection(_invStart,vDir,inSink,fRayMax);
}
private:
CVector3D m_vMin; //!< bounding box min from Init() parameters)
CVector3D m_vMax; //!< bounding box max from Init() parameters)
int m_iSize[3]; //!< integer size of the internal raster images
std::vector<T> m_vElements; //!< index to the elements that are put in
CRasterTable<DWORD> m_XRaster; //!< YZ internal raster images - index-1 to m_vElements because 0 is used for termination
CRasterTable<DWORD> m_YRaster; //!< ZX internal raster images - index-1 to m_vElements because 0 is used for termination
CRasterTable<DWORD> m_ZRaster; //!< XY internal raster images - index-1 to m_vElements because 0 is used for termination
void CalcNewBorder( CVector3D &invStart, CVector3D &invDirRelative, float infRayMax, int iniDir[3], int outiNewBorder[3] )
{
if(infRayMax==FLT_MAX)
{
if(iniDir[0]>0)outiNewBorder[0]=m_iSize[0]; else outiNewBorder[0]=-1;
if(iniDir[1]>0)outiNewBorder[1]=m_iSize[1]; else outiNewBorder[1]=-1;
if(iniDir[2]>0)outiNewBorder[2]=m_iSize[2]; else outiNewBorder[2]=-1;
return;
}
CVector3D vNewEnd=invStart + invDirRelative*infRayMax;
for(int i=0;i<3;i++)
{
outiNewBorder[i]=(int)floor(vNewEnd.p[i])+iniDir[i];
if(outiNewBorder[i]<-1)outiNewBorder[i]=-1;
if(outiNewBorder[i]>m_iSize[i])outiNewBorder[i]=m_iSize[i];
}
}
};
// constructor
template <class T, bool bBreakAfterFirstHit, bool bUseXRaster>
CRasterCube<T, bBreakAfterFirstHit, bUseXRaster>::CRasterCube( void )
{
DeInit();
}
// destructor
template <class T, bool bBreakAfterFirstHit, bool bUseXRaster>
CRasterCube<T, bBreakAfterFirstHit, bUseXRaster>::~CRasterCube( void )
{
DeInit();
}
// free data
template <class T, bool bBreakAfterFirstHit, bool bUseXRaster>
void CRasterCube<T, bBreakAfterFirstHit, bUseXRaster>::DeInit( void )
{
if(bUseXRaster)
m_XRaster.DeInit();
m_YRaster.DeInit();
m_ZRaster.DeInit();
m_iSize[0]=m_iSize[1]=m_iSize[2]=0;
}
// alloc raster, after that Phase 1 has started
template <class T, bool bBreakAfterFirstHit, bool bUseXRaster>
bool CRasterCube<T, bBreakAfterFirstHit, bUseXRaster>::Init( const CVector3D invMin, const CVector3D invMax, DWORD inElementCount, float infMagnifier )
{
m_vElements.reserve(inElementCount);
CVector3D vSize;
m_vMin=invMin;
m_vMax=invMax;
vSize=m_vMax-m_vMin;
float fAvgSize=((float)vSize.p[0]+(float)vSize.p[1]+(float)vSize.p[2])/3.0f;
if(fAvgSize==0.0f)return(false);
float fMassSideLength=(float)sqrt((double)inElementCount) * infMagnifier;
m_iSize[0]=(int)(vSize.p[0]/fAvgSize * fMassSideLength)+2;
m_iSize[1]=(int)(vSize.p[1]/fAvgSize * fMassSideLength)+2;
m_iSize[2]=(int)(vSize.p[2]/fAvgSize * fMassSideLength)+2;
/*
char str[256];
sprintf(str,"CRasterCube<T>::Init %dx%dx%d\n",m_iSize[0],m_iSize[1],m_iSize[2]);
OutputDebugString(str);
*/
CVector3D vBorder=CVector3D((vSize.p[0]+1.0f)/(float)m_iSize[0],(vSize.p[1]+1.0f)/(float)m_iSize[1],(vSize.p[2]+1.0f)/(float)m_iSize[2]);
m_vMin-=vBorder*2;
m_vMax+=vBorder*2;
const int iMinimumSize=3;
if(m_iSize[0]<iMinimumSize)m_iSize[0]=iMinimumSize;
if(m_iSize[1]<iMinimumSize)m_iSize[1]=iMinimumSize;
if(m_iSize[2]<iMinimumSize)m_iSize[2]=iMinimumSize;
if(bUseXRaster)
if(!m_XRaster.Init(m_iSize[1],m_iSize[2])) // YZ
return(false);
if(!m_YRaster.Init(m_iSize[2],m_iSize[0])) // ZX
return(false);
if(!m_ZRaster.Init(m_iSize[0],m_iSize[1])) // XY
return(false);
return(true);
}
// call this per triangle after Init and before PreProcess
// after PreProcess call it again for every triangle
template <class T, bool bBreakAfterFirstHit, bool bUseXRaster>
void CRasterCube<T, bBreakAfterFirstHit, bUseXRaster>::PutInTriangle( const Vec3d invVertices[3], const T &inpElement )
{
float fX[3],fY[3];
double fScale[3];
DWORD dwElementNo=(DWORD)(m_vElements.size()+1); // +1 because 0 is used for termination
fScale[0]=(((double)(m_iSize[0]))/(m_vMax.p[0]-m_vMin.p[0]));
fScale[1]=(((double)(m_iSize[1]))/(m_vMax.p[1]-m_vMin.p[1]));
fScale[2]=(((double)(m_iSize[2]))/(m_vMax.p[2]-m_vMin.p[2]));
if(bUseXRaster)
{
fX[0]=(float)((invVertices[0].y-m_vMin.p[1])*fScale[1]);fY[0]=(float)((invVertices[0].z-m_vMin.p[2])*fScale[2]);
fX[1]=(float)((invVertices[1].y-m_vMin.p[1])*fScale[1]);fY[1]=(float)((invVertices[1].z-m_vMin.p[2])*fScale[2]);
fX[2]=(float)((invVertices[2].y-m_vMin.p[1])*fScale[1]);fY[2]=(float)((invVertices[2].z-m_vMin.p[2])*fScale[2]);
m_XRaster.PutInTriangle(fX,fY,dwElementNo);
}
fX[0]=(float)((invVertices[0].z-m_vMin.p[2])*fScale[2]);fY[0]=(float)((invVertices[0].x-m_vMin.p[0])*fScale[0]);
fX[1]=(float)((invVertices[1].z-m_vMin.p[2])*fScale[2]);fY[1]=(float)((invVertices[1].x-m_vMin.p[0])*fScale[0]);
fX[2]=(float)((invVertices[2].z-m_vMin.p[2])*fScale[2]);fY[2]=(float)((invVertices[2].x-m_vMin.p[0])*fScale[0]);
m_YRaster.PutInTriangle(fX,fY,dwElementNo);
fX[0]=(float)((invVertices[0].x-m_vMin.p[0])*fScale[0]);fY[0]=(float)((invVertices[0].y-m_vMin.p[1])*fScale[1]);
fX[1]=(float)((invVertices[1].x-m_vMin.p[0])*fScale[0]);fY[1]=(float)((invVertices[1].y-m_vMin.p[1])*fScale[1]);
fX[2]=(float)((invVertices[2].x-m_vMin.p[0])*fScale[0]);fY[2]=(float)((invVertices[2].y-m_vMin.p[1])*fScale[1]);
m_ZRaster.PutInTriangle(fX,fY,dwElementNo);
m_vElements.push_back(inpElement);
}
// Has to be called in Phase1, after that Phase 2 has started
template <class T, bool bBreakAfterFirstHit, bool bUseXRaster>
bool CRasterCube<T, bBreakAfterFirstHit, bUseXRaster>::PreProcess( const bool inbDebug )
{
if(inbDebug)
{
if(bUseXRaster)
m_XRaster.Debug("CRasterCubeDebugX.tga");
m_YRaster.Debug("CRasterCubeDebugY.tga");
m_ZRaster.Debug("CRasterCubeDebugZ.tga");
}
if(bUseXRaster)
if(!m_XRaster.PreProcess()) return(false);
if(!m_YRaster.PreProcess()) return(false);
if(!m_ZRaster.PreProcess()) return(false);
return(true);
}
// Has to be called in Phase1, after that Phase 2 has started
template <class T, bool bBreakAfterFirstHit, bool bUseXRaster>
void CRasterCube<T, bBreakAfterFirstHit, bUseXRaster>::Compress( void )
{
if(bUseXRaster)
m_XRaster.Compress();
m_YRaster.Compress();
m_ZRaster.Compress();
}
//! /param inT
//! /return 0/1/2 plane axis that was hit
__forceinline int GetBiggestValue( double inT[3] )
{
// get the biggest value
if(inT[0]>inT[1])
{
if(inT[2]>inT[0])
return(2);
else
return(0);
}
else
{
if(inT[2]>inT[1])
return(2);
else
return(1);
}
}
// calculates memory consumption in bytes O(1)
template <class T, bool bBreakAfterFirstHit, bool bUseXRaster>
DWORD CRasterCube<T, bBreakAfterFirstHit, bUseXRaster>::CalcMemoryConsumption( DWORD outdwSize[3] )
{
outdwSize[0]=m_iSize[0];
outdwSize[1]=m_iSize[1];
outdwSize[2]=m_iSize[2];
if(bUseXRaster)
return(m_XRaster.CalcMemoryConsumption()+m_YRaster.CalcMemoryConsumption()+m_ZRaster.CalcMemoryConsumption()+sizeof(*this));
else
return(m_YRaster.CalcMemoryConsumption()+m_ZRaster.CalcMemoryConsumption()+sizeof(*this));
}

View File

@@ -0,0 +1,462 @@
#pragma once
#include <assert.h> // assert()
#include "SimpleTriangleRasterizer.h" // CSimpleTriangleRasterizer
// This class is to speed up raycasting queries.
// each element in a 2D raster has a pointer (or zero) to a few elements in a second memory(elemets: void *) (0 teminated)
// The raster can be used to query elements on a 3D line - if you project the 3D World down to that raster from one side.
// To get the maximum speedup you shoudl do this from 3 sides.
template <class T>
class CRasterTable
{
public:
//! constructor
CRasterTable( void );
//! destructor
virtual ~CRasterTable( void );
//! alloc raster, after that Phase 1 has started
//! /param indwXSize 1..
//! /param indwYSize 1..
//! /return true=success, false otherwise
bool Init( const DWORD indwXSize, const DWORD indwYSize );
//!
//! free data
void DeInit( void );
//! Has to be called in Phase 1, after Phase 2 has started
//! /return true=success, false otherwise
bool PreProcess( void );
//! call this per triangle after Init and before PreProcess
//! after PreProcess call it again for every triangle
//! /param infX[3] x coordiantes of the 3 vertices in raster units
//! /param infX[3] y coordiantes of the 3 vertices in raster units
//! /param inElement element you want to store
void PutInTriangle( float infX[3], float infY[3], T &inElement );
//! returns pointer to zero terminated array of pointers
//! /param iniX 0..m_dwXSize-1
//! /param iniY 0..m_dwYSize-1
//! /return
T *GetElementsAt( int iniX, int iniY );
//!
//! /return memory consumption in bytes O(1)
DWORD CalcMemoryConsumption( void );
//!
//! /param inPathFileName filename with path and extension
void Debug( const char *inPathFileName ) const;
//!
//! /param outPitchInBytes
//! /return
DWORD *GetDebugData( DWORD &outPitchInBytes ) const;
//!
//! /return width in raster elements
DWORD GetWidth( void );
//!
//! /return height in raster elements
DWORD GetHeight( void );
//! to save memory and make it faster - less cache misses)
// IF YOU WANNA USE THIS FUNCTION, CLEAR IT FIRST BECAUSE IT SIMPLY DOES NOT WORK
void Compress( void );
// **************************************************************
union
{
DWORD * m_pDataCounter; //!< used in Phase 1 [m_dwXSize*m_dwYSize]
T ** m_pDataPtr; //!< used in Phase 2 [m_dwXSize*m_dwYSize]
};
private:
T * m_pExtraMemoryPool; //!< for the pointers to store (zero terminated) (sorted in ascending order) [m_ExtraMemorySize]
DWORD m_ExtraMemorySize; //!< extra memroy pool size in StoredElementPtr elements
DWORD m_dwXSize; //!< width of the buffer 1.. if allocated, otherwise 0
DWORD m_dwYSize; //!< height of the buffer 1.. if allocated, otherwise 0
//! callback function
class CPixelIncrement: public CSimpleTriangleRasterizer::IRasterizeSink
{
public:
//! constructor
CPixelIncrement( DWORD *inpBuffer, DWORD indwWidth, DWORD indwHeight )
{
m_pBuffer=inpBuffer;
m_dwWidth=indwWidth;
m_dwHeight=indwHeight;
}
//!
virtual void Line( const float infXLeft, const float infXRight,
const int iniLeft, const int iniRight, const int iniY )
{
assert(iniLeft>=0);
assert(iniY>=0);
assert(iniRight<=(int)m_dwWidth);
assert(iniY<(int)m_dwHeight);
DWORD *pMem=&m_pBuffer[iniLeft+iniY*m_dwWidth];
for(int x=iniLeft;x<iniRight;x++)
{
*pMem=*pMem+1;
pMem++;
}
}
DWORD * m_pBuffer; //!<
DWORD m_dwWidth; //!< x= [0,m_dwWidth[
DWORD m_dwHeight; //!< y= [0,m_dwHeight[
};
//! callback function
template <class T>
class CPixelAddArrayElement: public CSimpleTriangleRasterizer::IRasterizeSink
{
public:
//! constructor
CPixelAddArrayElement( T **inpBuffer, DWORD indwWidth, T *inElementPtr )
{
m_pBuffer=inpBuffer;
m_dwWidth=indwWidth;
m_ElementPtr=inElementPtr;
}
//!
__forceinline void ReturnPixel( T * &rPtr )
{
--rPtr;
*rPtr=*m_ElementPtr;
}
//!
virtual void Line( const float infXLeft, const float infXRight,
const int iniLeft, const int iniRight, const int iniY )
{
T **pMem=&m_pBuffer[iniLeft+iniY*m_dwWidth];
for(int x=iniLeft;x<iniRight;x++)
ReturnPixel(*pMem++);
}
T * m_ElementPtr; //!< element to store
T ** m_pBuffer; //!< pointer to the array buffer
DWORD m_dwWidth; //!< width of the buffer
};
};
// constructor
template <class T>
CRasterTable<T>::CRasterTable( void )
{
assert(sizeof(DWORD)==sizeof(DWORD *)); // only for 32 Bit compiler
m_pDataPtr=0;
m_pExtraMemoryPool=0;
m_ExtraMemorySize=0;
}
// destructor
template <class T>
CRasterTable<T>::~CRasterTable( void )
{
DeInit();
}
// free data
template <class T>
void CRasterTable<T>::DeInit( void )
{
delete [] m_pDataCounter;m_pDataCounter=0;
delete [] m_pExtraMemoryPool;m_pExtraMemoryPool=0;
m_ExtraMemorySize=0;
m_dwXSize=0;
m_dwYSize=0;
}
// alloc raster, after that Phase 1 has started
template <class T>
bool CRasterTable<T>::Init( const DWORD indwXSize, const DWORD indwYSize )
{
assert(!m_pDataCounter);
assert(!m_pExtraMemoryPool); DeInit();
assert(indwXSize);
assert(indwYSize);
m_dwXSize=indwXSize;
m_dwYSize=indwYSize;
assert(sizeof(DWORD)==sizeof(T *));
m_pDataCounter=new DWORD[m_dwXSize*m_dwYSize];
if(m_pDataCounter)
memset(m_pDataCounter,0,m_dwXSize*m_dwYSize*sizeof(DWORD));
return(m_pDataPtr!=0);
}
// Has to be called in Phase1, after that Phase 2 has started
template <class T>
bool CRasterTable<T>::PreProcess( void )
{
assert(m_pDataCounter);
assert(!m_pExtraMemoryPool);
assert(!m_ExtraMemorySize);
DWORD dwSum=0; // extra memroy pool size in StoredElementPtr elements
{
DWORD *ptr=m_pDataCounter;
for(DWORD i=0;i<m_dwXSize*m_dwYSize;i++)
{
DWORD dwNumEl=*ptr++;
if(dwNumEl!=0)
dwSum+=dwNumEl+1; // Elements + NullTermination
}
}
if(dwSum==0)return(true); // no information to store - no problem
m_pExtraMemoryPool=new T[dwSum];
m_ExtraMemorySize=dwSum;
if(!m_pExtraMemoryPool)return(false);
memset(m_pExtraMemoryPool,0,dwSum*sizeof(T));
// build the access structure for compressing the elements in a vector (pointer is in the beginning on the last element)
// after filing in the triangles once again the pointer is on the first element
{
DWORD *ptr1=m_pDataCounter;
T **ptr2=m_pDataPtr;
T *dest=m_pExtraMemoryPool;
for(DWORD i=0;i<m_dwXSize*m_dwYSize;i++)
{
DWORD dwNumEl=*ptr1++;
if(dwNumEl!=0)
{
dest+=dwNumEl;
*ptr2++=dest;
dest++; // Elements + NullTermination
}
else *ptr2++=0;
}
}
return(true);
}
// call this per triangle after Init and before PreProcess
// after PreProcess call it again for every triangle
template <class T>
void CRasterTable<T>::PutInTriangle( float infX[3], float infY[3], T &inElement )
{
float fU[3],fV[3];
for(int i=0;i<3;i++)
{
fU[i]=infX[i];fV[i]=infY[i];
}
CSimpleTriangleRasterizer Rasterizer(m_dwXSize,m_dwYSize);
if(m_pExtraMemoryPool==0) // Phase 1
{
CPixelIncrement pix(m_pDataCounter,m_dwXSize,m_dwYSize);
Rasterizer.CallbackFillConservative(fU,fV,&pix);
}
else // Phase 2
{
CPixelAddArrayElement<T> pix(m_pDataPtr,m_dwXSize,&inElement);
Rasterizer.CallbackFillConservative(fU,fV,&pix);
}
}
// returns pointer to zero terminated array of pointers
template <class T>
T *CRasterTable<T>::GetElementsAt( int iniX, int iniY )
{
assert(iniX>=0);
assert(iniX<(int)m_dwXSize);
assert(iniY>=0);
assert(iniY<(int)m_dwYSize);
T *pRet=m_pDataPtr[iniY*m_dwXSize+iniX];
if(pRet)
assert(*pRet); // no pointer in the raster should point to empty list
return(pRet);
}
// returns pointer to zero terminated array of pointers
// IF YOU WANNA USE THIS FUNCTION, CLEAR IT FIRST BECAUSE IT SIMPLY DOES NOT WORK
template <class T>
void CRasterTable<T>::Compress( void )
{
if(!m_pExtraMemoryPool)return;
T *pDestStart=0;
DWORD dwNewMemorySize=0;
for(DWORD i=0;i<m_dwXSize*m_dwYSize;i++)
{
T *pSrcStart=m_pDataPtr[i]; // pointer from the raster element
if(!pSrcStart)continue; // no elements there
{
bool bAddBucket=true;
T *pSrc=pSrcStart, *pDest=pDestStart;
if(pDestStart)
while(*pSrc == *pDest)
{
if(*pSrc==0)
{
// buckets are fully identical
bAddBucket=false;
break;
}
pDest++;pSrc++;
}
if(bAddBucket)
{
if(!pDestStart) pDestStart=m_pExtraMemoryPool;
else
{
while(*pDestStart)pDestStart++; pDestStart++;
}
// copy bucket
T *pSrc=pSrcStart, *pDest=pDestStart;
while(*pSrc)
{ *pDest++=*pSrc++;dwNewMemorySize++; }
*pDest++=0; // 0 terminate
assert(*pDestStart); // no pointer in the raster should point to empty list
m_pDataPtr[i]=pDestStart;
}
else m_pDataPtr[i]=pDestStart;
}
}
T *newMem=new T[dwNewMemorySize];
if(!newMem)return; // no memory - no compression
// copy in new buffer
memcpy(newMem,m_pExtraMemoryPool,dwNewMemorySize*sizeof(T));
// correct the pointers to the new buffer
for(i=0;i<m_dwXSize*m_dwYSize;i++)
{
if(m_pDataPtr[i])
{
assert(*m_pDataPtr[i]); // no pointer in the raster should point to empty list
m_pDataPtr[i]=(T *)( (DWORD)(m_pDataPtr[i]) - (DWORD)m_pExtraMemoryPool + (DWORD)newMem);
assert(*m_pDataPtr[i]); // no pointer in the raster should point to empty list
}
}
delete [] m_pExtraMemoryPool;
// set new buffer
m_pExtraMemoryPool=newMem;
float fRatio=(float)dwNewMemorySize/(float)m_ExtraMemorySize;
}
// calculates memory consumption in bytes O(1)
template <class T>
DWORD CRasterTable<T>::CalcMemoryConsumption( void )
{
return(m_dwXSize*m_dwYSize*sizeof(m_pDataCounter)+m_ExtraMemorySize*sizeof(T));
}
template <class T>
void CRasterTable<T>::Debug( const char *inPathFileName ) const
{
#ifdef _TGA_LOADER_INCLUDED
for(DWORD i=0;i<m_dwXSize*m_dwYSize;i++)
m_pDataCounter[i]=m_pDataCounter[i]*10+0x0800;
PIX_SaveTGA32(inPathFileName,(unsigned char *)m_pDataCounter,m_dwXSize,m_dwYSize,false,false);
for(DWORD i=0;i<m_dwXSize*m_dwYSize;i++)
m_pDataCounter[i]=(m_pDataCounter[i]-0x0800)/10;
#endif
}
template <class T>
DWORD *CRasterTable<T>::GetDebugData( DWORD &outPitchInBytes ) const
{
outPitchInBytes=m_dwXSize*sizeof(DWORD);
return(m_pDataCounter);
}
template <class T>
DWORD CRasterTable<T>::GetWidth( void )
{
return(m_dwXSize);
}
template <class T>
DWORD CRasterTable<T>::GetHeight( void )
{
return(m_dwYSize);
}

View File

@@ -0,0 +1,354 @@
#include "stdafx.h" // precompiled headers
#include "SimpleTriangleRasterizer.h" // CSimpleTriangleRastizer
#include <math.h> // sqrt()
#define FLT_MAX 3.402823466e+38F
#ifndef min
#define min(a,b) ((a)<(b)?(a):(b))
#endif
#ifndef max
#define max(a,b) ((a)>(b)?(a):(b))
#endif
void CSimpleTriangleRasterizer::lambertHorizlineConservative( float fx1, float fx2, int yy, IRasterizeSink *inpSink )
{
int x1=(int)floorf(fx1),x2=(int)floorf(fx2+1.0f);
if(x1<m_iMinX)x1=m_iMinX;
if(x2>m_iMaxX+1)x2=m_iMaxX+1;
if(x1>m_iMaxX+1)x1=m_iMaxX+1;
if(x2<m_iMinX)x2=m_iMinX;
inpSink->Line(fx1,fx2,x1,x2,yy);
}
void CSimpleTriangleRasterizer::lambertHorizlineSubpixelCorrect( float fx1, float fx2, int yy, IRasterizeSink *inpSink )
{
int x1=(int)floorf(fx1+0.5f),x2=(int)floorf(fx2+0.5f);
if(x1<m_iMinX)x1=m_iMinX;
if(x2>m_iMaxX+1)x2=m_iMaxX+1;
if(x1>m_iMaxX+1)x1=m_iMaxX+1;
if(x2<m_iMinX)x2=m_iMinX;
inpSink->Line(fx1,fx2,x1,x2,yy);
}
// optimizable
void CSimpleTriangleRasterizer::CopyAndSortY( const float infX[3], const float infY[3], float outfX[3], float outfY[3] )
{
outfX[0]=infX[0];outfY[0]=infY[0];
outfX[1]=infX[1];outfY[1]=infY[1];
outfX[2]=infX[2];outfY[2]=infY[2];
// Sort the coordinates, so that (x[1], y[1]) becomes the highest coord
float tmp;
if(outfY[0]>outfY[1])
{
if(outfY[1]>outfY[2])
{
tmp=outfY[0];outfY[0]=outfY[1];outfY[1]=tmp;
tmp=outfX[0];outfX[0]=outfX[1];outfX[1]=tmp;
tmp=outfY[1];outfY[1]=outfY[2];outfY[2]=tmp;
tmp=outfX[1];outfX[1]=outfX[2];outfX[2]=tmp;
if(outfY[0]>outfY[1])
{
tmp=outfY[0];outfY[0]=outfY[1];outfY[1]=tmp;
tmp=outfX[0];outfX[0]=outfX[1];outfX[1]=tmp;
}
}
else
{
tmp=outfY[0];outfY[0]=outfY[1];outfY[1]=tmp;
tmp=outfX[0];outfX[0]=outfX[1];outfX[1]=tmp;
if(outfY[1]>outfY[2])
{
tmp=outfY[1];outfY[1]=outfY[2];outfY[2]=tmp;
tmp=outfX[1];outfX[1]=outfX[2];outfX[2]=tmp;
}
}
}
else
{
if(outfY[1]>outfY[2])
{
tmp=outfY[1];outfY[1]=outfY[2];outfY[2]=tmp;
tmp=outfX[1];outfX[1]=outfX[2];outfX[2]=tmp;
if(outfY[0]>outfY[1])
{
tmp=outfY[0];outfY[0]=outfY[1];outfY[1]=tmp;
tmp=outfX[0];outfX[0]=outfX[1];outfX[1]=tmp;
}
}
}
}
void CSimpleTriangleRasterizer::CallbackFillRectConservative( float _x[3], float _y[3], IRasterizeSink *inpSink )
{
inpSink->Triangle(m_iMinY);
float fMinX=min(_x[0],min(_x[1],_x[2]));
float fMaxX=max(_x[0],max(_x[1],_x[2]));
float fMinY=min(_y[0],min(_y[1],_y[2]));
float fMaxY=max(_y[0],max(_y[1],_y[2]));
int iMinX=max(m_iMinX,(int)floorf(fMinX));
int iMaxX=min(m_iMaxX+1,(int)ceilf(fMaxX));
int iMinY=max(m_iMinY,(int)floorf(fMinY));
int iMaxY=min(m_iMaxY+1,(int)ceilf(fMaxY));
for(int y=iMinY;y<iMaxY;y++)
inpSink->Line(fMinX,fMaxX,iMinX,iMaxX,y);
}
void CSimpleTriangleRasterizer::CallbackFillConservative( float _x[3], float _y[3], IRasterizeSink *inpSink )
{
float x[3],y[3];
CopyAndSortY(_x,_y,x,y);
// Calculate interpolation steps
float fX1toX2step=0.0f; if(y[1]-y[0]!=0.0f)fX1toX2step=(x[1]-x[0])/(float)(y[1]-y[0]);
float fX1toX3step=0.0f; if(y[2]-y[0]!=0.0f)fX1toX3step=(x[2]-x[0])/(float)(y[2]-y[0]);
float fX2toX3step=0.0f; if(y[2]-y[1]!=0.0f)fX2toX3step=(x[2]-x[1])/(float)(y[2]-y[1]);
float fX1toX2=x[0], fX1toX3=x[0], fX2toX3=x[1];
bool bFirstLine=true;
bool bTriangleCallDone=false;
// Go through the scanlines of the triangle
int yy=(int)floorf(y[0]); // was floor
for(; yy<=(int)floorf(y[2]); yy++)
// for(yy=m_iMinY; yy<=m_iMaxY; yy++) // juhu
{
float fSubPixelYStart=0.0f,fSubPixelYEnd=1.0f;
float start,end;
// first line
if(bFirstLine)
{
fSubPixelYStart=y[0]-floorf(y[0]);
start=x[0];end=x[0];bFirstLine=false;
}
else
{
// top part without middle corner line
if(yy<=(int)floorf(y[1]))
{
start=min(fX1toX2,fX1toX3);end=max(fX1toX2,fX1toX3);
}
else
{
start=min(fX2toX3,fX1toX3);end=max(fX2toX3,fX1toX3);
}
}
// middle corner line
if(yy==(int)floorf(y[1]))
{
fSubPixelYEnd=y[1]-floorf(y[1]);
fX1toX3+=fX1toX3step*(fSubPixelYEnd-fSubPixelYStart);
start=min(start,fX1toX3);end=max(end,fX1toX3);
start=min(start,x[1]);end=max(end,x[1]);
fSubPixelYStart=fSubPixelYEnd;fSubPixelYEnd=1.0f;
}
// last line
if(yy==(int)floorf(y[2]))
{
start=min(start,x[2]);end=max(end,x[2]);
}
else
{
// top part without middle corner line
if(yy<(int)floorf(y[1]))
{
fX1toX2+=fX1toX2step*(fSubPixelYEnd-fSubPixelYStart);
start=min(start,fX1toX2);end=max(end,fX1toX2);
}
else
{
fX2toX3+=fX2toX3step*(fSubPixelYEnd-fSubPixelYStart);
start=min(start,fX2toX3);end=max(end,fX2toX3);
}
fX1toX3+=fX1toX3step*(fSubPixelYEnd-fSubPixelYStart);
start=min(start,fX1toX3);end=max(end,fX1toX3);
}
if(yy>=m_iMinY && yy<=m_iMaxY)
{
if(!bTriangleCallDone){ inpSink->Triangle(yy);bTriangleCallDone=true; }
lambertHorizlineConservative(start,end,yy,inpSink);
}
}
}
void CSimpleTriangleRasterizer::CallbackFillSubpixelCorrect( float _x[3], float _y[3], IRasterizeSink *inpSink )
{
float x[3],y[3];
CopyAndSortY(_x,_y,x,y);
if(y[0]-floorf(y[0])==0.0f)y[0]-=0.0001f;
// Calculate interpolation steps
float fX1toX2step=0.0f; if(y[1]-y[0]!=0.0f)fX1toX2step=(x[1]-x[0])/(float)(y[1]-y[0]);
float fX1toX3step=0.0f; if(y[2]-y[0]!=0.0f)fX1toX3step=(x[2]-x[0])/(float)(y[2]-y[0]);
float fX2toX3step=0.0f; if(y[2]-y[1]!=0.0f)fX2toX3step=(x[2]-x[1])/(float)(y[2]-y[1]);
float fX1toX2=x[0], fX1toX3=x[0], fX2toX3=x[1];
bool bFirstLine=true;
bool bTriangleCallDone=false;
y[0]-=0.5f;y[1]-=0.5f;y[2]-=0.5f;
int yy=(int)floorf(y[0]);
for(; yy<=(int)floorf(y[2]); yy++)
{
float fSubPixelYStart=0.0f,fSubPixelYEnd=1.0f;
float start,end;
// first line
if(bFirstLine)
{
fSubPixelYStart=y[0]-floorf(y[0]);
start=x[0];end=x[0];bFirstLine=false;
}
else
{
// top part without middle corner line
if(yy<=(int)floorf(y[1]))
{
start=min(fX1toX2,fX1toX3);end=max(fX1toX2,fX1toX3);
}
else
{
start=min(fX2toX3,fX1toX3);end=max(fX2toX3,fX1toX3);
}
}
// middle corner line
if(yy==(int)floorf(y[1]))
{
fSubPixelYEnd=y[1]-floorf(y[1]);
fX1toX3+=fX1toX3step*(fSubPixelYEnd-fSubPixelYStart);
fSubPixelYStart=fSubPixelYEnd;fSubPixelYEnd=1.0f;
}
// last line
if(yy!=(int)floorf(y[2]))
{
// top part without middle corner line
if(yy<(int)floorf(y[1]))
fX1toX2+=fX1toX2step*(fSubPixelYEnd-fSubPixelYStart);
else
fX2toX3+=fX2toX3step*(fSubPixelYEnd-fSubPixelYStart);
fX1toX3+=fX1toX3step*(fSubPixelYEnd-fSubPixelYStart);
}
if(start!=end)
if(yy>=m_iMinY && yy<=m_iMaxY)
{
if(!bTriangleCallDone){ inpSink->Triangle(yy);bTriangleCallDone=true; }
lambertHorizlineSubpixelCorrect(start,end,yy,inpSink);
}
}
}
// shrink triangle by n pixel, optimizable
void CSimpleTriangleRasterizer::ShrinkTriangle( float inoutfX[3], float inoutfY[3], float infAmount )
{
float fX[3]={ inoutfX[0], inoutfX[1], inoutfX[2] };
float fY[3]={ inoutfY[0], inoutfY[1], inoutfY[2] };
/*
// move edge to opposing vertex
float dx,dy,fLength;
for(int a=0;a<3;a++)
{
int b=a+1;if(b>=3)b=0;
int c=b+1;if(c>=3)c=0;
dx=fX[a]-(fX[b]+fX[c])*0.5f;
dy=fY[a]-(fY[b]+fY[c])*0.5f;
fLength=(float)sqrt(dx*dx+dy*dy);
if(fLength>1.0f)
{
dx/=fLength;dy/=fLength;
inoutfX[b]+=dx;inoutfY[b]+=dy;
inoutfX[c]+=dx;inoutfY[c]+=dy;
}
}
*/
/*
// move vertex to opposing edge
float dx,dy,fLength;
for(int a=0;a<3;a++)
{
int b=a+1;if(b>=3)b=0;
int c=b+1;if(c>=3)c=0;
dx=fX[a]-(fX[b]+fX[c])*0.5f;
dy=fY[a]-(fY[b]+fY[c])*0.5f;
fLength=(float)sqrt(dx*dx+dy*dy);
if(fLength>1.0f)
{
dx/=fLength;dy/=fLength;
inoutfX[a]-=dx;inoutfY[a]-=dy;
}
}
*/
// move vertex to get edges shifted perpendicual for 1 unit
for(int a=0;a<3;a++)
{
float dx1,dy1,dx2,dy2,fLength;
int b=a+1;if(b>=3)b=0;
int c=b+1;if(c>=3)c=0;
dx1=fX[b]-fX[a];
dy1=fY[b]-fY[a];
fLength=(float)sqrt(dx1*dx1+dy1*dy1);
if(infAmount>0)if(fLength<infAmount)continue;
if(fLength==0.0f)continue;
dx1/=fLength;dy1/=fLength;
dx2=fX[c]-fX[a];
dy2=fY[c]-fY[a];
fLength=(float)sqrt(dx2*dx2+dy2*dy2);
if(infAmount>0)if(fLength<infAmount)continue;
if(fLength==0.0f)continue;
dx2/=fLength;dy2/=fLength;
inoutfX[a]+=(dx1+dx2)*infAmount;inoutfY[a]+=(dy1+dy2)*infAmount;
}
}

View File

@@ -0,0 +1,166 @@
#pragma once
// Helper class
//
// clipping is done in integer
//
class CSimpleTriangleRasterizer
{
public:
class IRasterizeSink
{
public:
//! is called once per triangel for the first possible visible line
//! /param iniStartY
virtual void Triangle( const int iniStartY )
{
}
//! callback function
//! /param infXLeft included - not clipped against left and reight border
//! /param infXRight excluded - not clipped against left and reight border
//! /param iniXLeft included
//! /param iniXRight excluded
//! /param iniY
virtual void Line( const float infXLeft, const float infXRight,
const int iniXLeft, const int iniXRight, const int iniY )=0;
};
typedef unsigned long DWORD;
// -----------------------------------------------------
//! implementation sink sample
class CDWORDFlatFill: public IRasterizeSink
{
public:
//! constructor
CDWORDFlatFill( DWORD *inpBuffer, const DWORD indwPitchInPixels, DWORD indwValue )
{
m_dwValue=indwValue;
m_pBuffer=inpBuffer;
m_dwPitchInPixels=indwPitchInPixels;
}
virtual void Triangle( const int iniY )
{
m_pBufferLine=&m_pBuffer[iniY*m_dwPitchInPixels];
}
virtual void Line( const float infXLeft, const float infXRight,
const int iniLeft, const int iniRight, const int iniY )
{
DWORD *mem=&m_pBufferLine[iniLeft];
for(int x=iniLeft;x<iniRight;x++)
*mem++=m_dwValue;
m_pBufferLine+=m_dwPitchInPixels;
}
private:
DWORD m_dwValue; //!< fill value
DWORD *m_pBufferLine; //!< to get rid of the multiplication per line
DWORD m_dwPitchInPixels; //!< in DWORDS, not in Bytes
DWORD *m_pBuffer; //!< pointer to the buffer
};
// -----------------------------------------------------
//! constructor
//! /param iniWidth excluded
//! /param iniHeight excluded
CSimpleTriangleRasterizer( const int iniWidth, const int iniHeight )
{
m_iMinX=0;
m_iMinY=0;
m_iMaxX=iniWidth-1;
m_iMaxY=iniHeight-1;
}
/*
//! constructor
//! /param iniMinX included
//! /param iniMinY included
//! /param iniMaxX included
//! /param iniMaxY included
CSimpleTriangleRasterizer( const int iniMinX, const int iniMinY, const int iniMaxX, const int iniMaxY )
{
m_iMinX=iniMinX;
m_iMinY=iniMinY;
m_iMaxX=iniMaxX;
m_iMaxY=iniMaxY;
}
*/
//! simple triangle filler with clipping (optimizable), not subpixel correct
//! /param pBuffer pointer o the color buffer
//! /param indwWidth width of the color buffer
//! /param indwHeight height of the color buffer
//! /param x array of the x coordiantes of the three vertices
//! /param y array of the x coordiantes of the three vertices
//! /param indwValue value of the triangle
void DWORDFlatFill( DWORD *inpBuffer, const DWORD indwPitchInPixels, float x[3], float y[3], DWORD indwValue, bool inbConservative )
{
CDWORDFlatFill pix(inpBuffer,indwPitchInPixels,indwValue);
if(inbConservative)
CallbackFillConservative(x,y,&pix);
else
CallbackFillSubpixelCorrect(x,y,&pix);
}
// Rectangle around triangle - more stable - use for debugging purpose
void CallbackFillRectConservative( float x[3], float y[3], IRasterizeSink *inpSink );
//! subpixel correct triangle filler (conservative or not conservative)
//! \param pBuffer pointe to the DWORD
//! \param indwWidth width of the buffer pBuffer pointes to
//! \param indwHeight height of the buffer pBuffer pointes to
//! \param x array of the x coordiantes of the three vertices
//! \param y array of the x coordiantes of the three vertices
//! \param inpSink pointer to the sink interface (is called per triangle and per triangle line)
void CallbackFillConservative( float x[3], float y[3], IRasterizeSink *inpSink );
//! subpixel correct triangle filler (conservative or not conservative)
//! \param pBuffer pointe to the DWORD
//! \param indwWidth width of the buffer pBuffer pointes to
//! \param indwHeight height of the buffer pBuffer pointes to
//! \param x array of the x coordiantes of the three vertices
//! \param y array of the x coordiantes of the three vertices
//! \param inpSink pointer to the sink interface (is called per triangle and per triangle line)
void CallbackFillSubpixelCorrect( float x[3], float y[3], IRasterizeSink *inpSink );
//!
//! /param inoutfX
//! /param inoutfY
//! /param infAmount could be positive or negative
static void ShrinkTriangle( float inoutfX[3], float inoutfY[3], float infAmount );
private:
// Clipping Rect;
int m_iMinX; //!< minimum x value included
int m_iMinY; //!< minimum y value included
int m_iMaxX; //!< maximum x value included
int m_iMaxY; //!< maximum x value included
void lambertHorizlineConservative( float fx1, float fx2, int y, IRasterizeSink *inpSink );
void lambertHorizlineSubpixelCorrect( float fx1, float fx2, int y, IRasterizeSink *inpSink );
void CopyAndSortY( const float infX[3], const float infY[3], float outfX[3],float outfY[3] );
};
// extension ideas:
// * callback with coverage mask (possible non ordered sampling)
// * z-buffer behaviour
// * gouraud shading
// * texture mapping with nearest/bicubic/bilinear filter
// * further primitives: thick line, ellipse
// * build a template version
// *

View File

@@ -0,0 +1,78 @@
// ---------------------------------------------------------------------------------------------
// Crytek CryENGINE source code
// History:
// - Created by Michael Glueck
// ---------------------------------------------------------------------------------------------
#include "stdafx.h" // precompiled headers
#include "TexelSampler.h" // CSimpleTriangleRastizer
void CTexelSampler::Init(const EAATYPE ceAAType)
{
m_vSamples.clear();
//process according to desired processing method
//pay attention to add them sorted by u and then v
switch(ceAAType)
{
case NONE:
m_vSamples.push_back(SSample()); //default constructor did everything
break;
case MED:
{
SSample sample;
sample.bTexelCenter = false;
sample.fX = 0.f - 0.5f;
sample.fY = 0.f - 0.5f;
sample.fWeight = 0.125f; //1/8 for corner texels
m_vSamples.push_back(sample);
sample.fX = 1.f - 0.5f;
sample.fY = 0.f - 0.5f;
m_vSamples.push_back(sample);
sample.fX = 0.5f - 0.5f;
sample.fY = 0.5f - 0.5f;
sample.fWeight = 0.5f; //1/2 weight for center texel
sample.bTexelCenter = true;
m_vSamples.push_back(sample);
sample.fX = 0.f - 0.5f;
sample.fY = 1.f - 0.5f;
sample.fWeight = 0.125f;
sample.bTexelCenter = false;
m_vSamples.push_back(sample);
sample.fX = 1.f - 0.5f;
sample.fY = 1.f - 0.5f;
m_vSamples.push_back(sample);
}
break;
case HIGH:
{
//it is assumed to be equal to n^2
SSample sample;
//uniform grid sampling
sample.fWeight = 1.f/(2.0f*(float)(HIGH-1));
static const float scfGrid = sqrtf((float)HIGH);
static const unsigned int scuiCount = (unsigned int)scfGrid;
sample.bTexelCenter = false;
float y = 1.f/(2.f * scfGrid);
for(int v=0;v<scuiCount;v++)
{
float x = 1.f/(2.f * scfGrid);
for(int u=0;u<scuiCount;u++)
{
x += 2.f/(2.f * scfGrid);
sample.fX = x - 0.5f;
sample.fY = y - 0.5f;
m_vSamples.push_back(sample);
}
y += 2.f/(2.f * scfGrid);
}
//alter weight of center sample
m_vSamples[(HIGH-1)/2].fWeight = 0.5f;
m_vSamples[(HIGH-1)/2].bTexelCenter = true;
}
break;
default:
//use no AA in this case (shouldn't happen anyway)
m_vSamples.push_back(SSample()); //default constructor did everything
}
}

View File

@@ -0,0 +1,83 @@
#pragma once
#include <assert.h>
#include <vector>
/**
* class contains the functionality for retrieving samples relative to a texel center
* uses prepared raster table
* texel ranges from -0.5,-0.5 to 0.5,0.5, therefore texel center located at 0.0,0.0
* one is guaranteed to be set as texel center
*/
class CTexelSampler
{
public:
//!< describes one sample
typedef struct SSample
{
float fX; //!< u-position relative to texel center
float fY; //!< v-position relative to texel center
float fWeight; //!< weight for this sample
bool bTexelCenter; //!< true if to use as texel center, default true to guarantee at least one valid sample
SSample():fX(0.f),fY(0.f),fWeight(1.0f),bTexelCenter(true){}
}SSample;
//!< enumerates the available antialiasing approaches
typedef enum EAATYPE
{
NONE = 1, //!< only texel center gets processed
MED = 5, //!< texel center and all corners gets sampled
HIGH = 9 //!< texel center and 8 additional samples get used
}EAATYPE;
/**
* constructor which initializes the sample table
* @param ceAAType desired accuracy for antialiasing (determines number of samples to take)
*/
CTexelSampler(const EAATYPE ceAAType = MED)
{
Init(ceAAType);
}
/**
* reinitializes the sample table
* @param ceAAType desired accuracy for antialiasing (determines number of samples to take)
*/
void Reinitialize(const EAATYPE ceAAType = MED)
{
Init(ceAAType);
}
/**
* returns number of sampels per texel, sorted to make reuse possible
* @return number of samples
*/
const unsigned int NumberOfSamples() const
{
return m_vSamples.size();
}
/**
* retrieves an const iterator to the samples
* @param const iterator to the sample vector
*/
std::vector<SSample>::const_iterator BeginIterator() const
{
return m_vSamples.begin();
}
/**
* retrieves an const iterator to the samples
* @param const iterator to the sample vector
*/
std::vector<SSample>::const_iterator EndIterator() const
{
return m_vSamples.end();
}
private:
/**
* initializes sampler, called from Reinitialize and contructor
* @param ceAAType desired accuracy for antialiasing (determines number of samples to take)
*/
void Init(const EAATYPE ceAAType);
std::vector<SSample> m_vSamples; //!< sample offset related to respective texel center, sorted by u and then v
};
typedef std::vector<CTexelSampler::SSample>::const_iterator SampleIter;