You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
390 lines
12 KiB
390 lines
12 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// $Id:$ |
|
|
|
#ifndef RAYTRACE_H |
|
#define RAYTRACE_H |
|
|
|
#include <tier0/platform.h> |
|
#include <mathlib/vector.h> |
|
#include <mathlib/ssemath.h> |
|
#include <mathlib/lightdesc.h> |
|
#include <assert.h> |
|
#include <tier1/utlvector.h> |
|
#include <mathlib/mathlib.h> |
|
#include <bspfile.h> |
|
|
|
// fast SSE-ONLY ray tracing module. Based upon various "real time ray tracing" research. |
|
//#define DEBUG_RAYTRACE 1 |
|
|
|
class FourRays |
|
{ |
|
public: |
|
FourVectors origin; |
|
FourVectors direction; |
|
|
|
inline void Check(void) const |
|
{ |
|
// in order to be valid to trace as a group, all four rays must have the same signs in all |
|
// of their direction components |
|
#ifndef NDEBUG |
|
for(int c=1;c<4;c++) |
|
{ |
|
Assert(direction.X(0)*direction.X(c)>=0); |
|
Assert(direction.Y(0)*direction.Y(c)>=0); |
|
Assert(direction.Z(0)*direction.Z(c)>=0); |
|
} |
|
#endif |
|
} |
|
// returns direction sign mask for 4 rays. returns -1 if the rays can not be traced as a |
|
// bundle. |
|
int CalculateDirectionSignMask(void) const; |
|
|
|
}; |
|
|
|
/// The format a triangle is stored in for intersections. size of this structure is important. |
|
/// This structure can be in one of two forms. Before the ray tracing environment is set up, the |
|
/// ProjectedEdgeEquations hold the coordinates of the 3 vertices, for facilitating bounding box |
|
/// checks needed while building the tree. afterwards, they are changed into the projected ege |
|
/// equations for intersection purposes. |
|
enum triangleflags |
|
{ |
|
FCACHETRI_TRANSPARENT = 0x01, |
|
FCACHETRI_NEGATIVE_NORMAL = 0x02, |
|
}; |
|
|
|
|
|
struct TriIntersectData_t |
|
{ |
|
// this structure is 16longs=64 bytes for cache line packing. |
|
float m_flNx, m_flNy, m_flNz; // plane equation |
|
float m_flD; |
|
|
|
int32 m_nTriangleID; // id of the triangle. |
|
|
|
float m_ProjectedEdgeEquations[6]; // A,B,C for each edge equation. a |
|
// point is inside the triangle if |
|
// a*c1+b*c2+c is negative for all 3 |
|
// edges. |
|
|
|
uint8 m_nCoordSelect0,m_nCoordSelect1; // the triangle is projected onto a 2d |
|
// plane for edge testing. These are |
|
// the indices (0..2) of the |
|
// coordinates preserved in the |
|
// projection |
|
|
|
uint8 m_nFlags; // triangle flags |
|
uint8 m_unused0; // no longer used |
|
}; |
|
|
|
|
|
struct TriGeometryData_t |
|
{ |
|
int32 m_nTriangleID; // id of the triangle. |
|
|
|
float m_VertexCoordData[9]; // can't use a vector in a union |
|
|
|
uint8 m_nFlags; // triangle flags |
|
signed char m_nTmpData0; // used by kd-tree builder |
|
signed char m_nTmpData1; // used by kd-tree builder |
|
|
|
|
|
// accessors to get around union annoyance |
|
FORCEINLINE Vector &Vertex(int idx) |
|
{ |
|
return * ( reinterpret_cast<Vector *> ( m_VertexCoordData+3*idx ) ); |
|
} |
|
|
|
}; |
|
|
|
|
|
struct CacheOptimizedTriangle |
|
{ |
|
|
|
union |
|
{ |
|
TriIntersectData_t m_IntersectData; |
|
TriGeometryData_t m_GeometryData; |
|
} m_Data; |
|
|
|
// accessors to get around union annoyance |
|
FORCEINLINE Vector &Vertex(int idx) |
|
{ |
|
return * ( reinterpret_cast<Vector *> (m_Data.m_GeometryData.m_VertexCoordData+3*idx ) ); |
|
} |
|
|
|
FORCEINLINE const Vector &Vertex(int idx) const |
|
{ |
|
return * ( reinterpret_cast<const Vector *> (m_Data.m_GeometryData.m_VertexCoordData+3*idx ) ); |
|
} |
|
|
|
void ChangeIntoIntersectionFormat(void); // change information storage format for |
|
// computing intersections. |
|
|
|
int ClassifyAgainstAxisSplit(int split_plane, float split_value); // PLANECHECK_xxx below |
|
|
|
}; |
|
|
|
#define PLANECHECK_POSITIVE 1 |
|
#define PLANECHECK_NEGATIVE -1 |
|
#define PLANECHECK_STRADDLING 0 |
|
|
|
#define KDNODE_STATE_XSPLIT 0 // this node is an x split |
|
#define KDNODE_STATE_YSPLIT 1 // this node is a ysplit |
|
#define KDNODE_STATE_ZSPLIT 2 // this node is a zsplit |
|
#define KDNODE_STATE_LEAF 3 // this node is a leaf |
|
|
|
struct CacheOptimizedKDNode |
|
{ |
|
// this is the cache intensive data structure. "Tricks" are used to fit it into 8 bytes: |
|
// |
|
// A) the right child is always stored after the left child, which means we only need one |
|
// pointer |
|
// B) The type of node (KDNODE_xx) is stored in the lower 2 bits of the pointer. |
|
// C) for leaf nodes, we store the number of triangles in the leaf in the same place as the floating |
|
// point splitting parameter is stored in a non-leaf node |
|
|
|
int32 Children; // child idx, or'ed with flags above |
|
float SplittingPlaneValue; // for non-leaf nodes, the nodes on the |
|
// "high" side of the splitting plane |
|
// are on the right |
|
|
|
#ifdef DEBUG_RAYTRACE |
|
Vector vecMins; |
|
Vector vecMaxs; |
|
#endif |
|
|
|
inline int NodeType(void) const |
|
|
|
{ |
|
return Children & 3; |
|
} |
|
|
|
inline int32 TriangleIndexStart(void) const |
|
{ |
|
assert(NodeType()==KDNODE_STATE_LEAF); |
|
return Children>>2; |
|
} |
|
|
|
inline int LeftChild(void) const |
|
{ |
|
assert(NodeType()!=KDNODE_STATE_LEAF); |
|
return Children>>2; |
|
} |
|
|
|
inline int RightChild(void) const |
|
{ |
|
return LeftChild()+1; |
|
} |
|
|
|
inline int NumberOfTrianglesInLeaf(void) const |
|
{ |
|
assert(NodeType()==KDNODE_STATE_LEAF); |
|
return *((int32 *) &SplittingPlaneValue); |
|
} |
|
|
|
inline void SetNumberOfTrianglesInLeafNode(int n) |
|
{ |
|
*((int32 *) &SplittingPlaneValue)=n; |
|
} |
|
|
|
protected: |
|
|
|
|
|
}; |
|
|
|
|
|
struct RayTracingSingleResult |
|
{ |
|
Vector surface_normal; // surface normal at intersection |
|
int32 HitID; // -1=no hit. otherwise, triangle index |
|
float HitDistance; // distance to intersection |
|
float ray_length; // leng of initial ray |
|
}; |
|
|
|
struct RayTracingResult |
|
{ |
|
FourVectors surface_normal; // surface normal at intersection |
|
ALIGN16 int32 HitIds[4] ALIGN16_POST; // -1=no hit. otherwise, triangle index |
|
fltx4 HitDistance; // distance to intersection |
|
}; |
|
|
|
|
|
class RayTraceLight |
|
{ |
|
public: |
|
FourVectors Position; |
|
FourVectors Intensity; |
|
}; |
|
|
|
|
|
#define RTE_FLAGS_FAST_TREE_GENERATION 1 |
|
#define RTE_FLAGS_DONT_STORE_TRIANGLE_COLORS 2 // saves memory if not needed |
|
#define RTE_FLAGS_DONT_STORE_TRIANGLE_MATERIALS 4 |
|
|
|
enum RayTraceLightingMode_t { |
|
DIRECT_LIGHTING, // just dot product lighting |
|
DIRECT_LIGHTING_WITH_SHADOWS, // with shadows |
|
GLOBAL_LIGHTING // global light w/ shadows |
|
}; |
|
|
|
|
|
class RayStream |
|
{ |
|
friend class RayTracingEnvironment; |
|
|
|
RayTracingSingleResult *PendingStreamOutputs[8][4]; |
|
int n_in_stream[8]; |
|
FourRays PendingRays[8]; |
|
|
|
public: |
|
RayStream(void) |
|
{ |
|
memset(n_in_stream,0,sizeof(n_in_stream)); |
|
} |
|
}; |
|
|
|
// When transparent triangles are in the list, the caller can provide a callback that will get called at each triangle |
|
// allowing the callback to stop processing if desired. |
|
// UNDONE: This is not currently SIMD - it really only supports single rays |
|
// Also for efficiency FourRays really needs some kind of active mask for the cases where rays get unbundled |
|
class ITransparentTriangleCallback |
|
{ |
|
public: |
|
virtual bool VisitTriangle_ShouldContinue( const TriIntersectData_t &triangle, const FourRays &rays, fltx4 *hitMask, fltx4 *b0, fltx4 *b1, fltx4 *b2, int32 hitID ) = 0; |
|
}; |
|
|
|
class RayTracingEnvironment |
|
{ |
|
public: |
|
uint32 Flags; // RTE_FLAGS_xxx above |
|
Vector m_MinBound; |
|
Vector m_MaxBound; |
|
|
|
FourVectors BackgroundColor; //< color where no intersection |
|
CUtlVector<CacheOptimizedKDNode> OptimizedKDTree; //< the packed kdtree. root is 0 |
|
CUtlBlockVector<CacheOptimizedTriangle> OptimizedTriangleList; //< the packed triangles |
|
CUtlVector<int32> TriangleIndexList; //< the list of triangle indices. |
|
CUtlVector<LightDesc_t> LightList; //< the list of lights |
|
CUtlVector<Vector> TriangleColors; //< color of tries |
|
CUtlVector<int32> TriangleMaterials; //< material index of tries |
|
|
|
public: |
|
RayTracingEnvironment() : OptimizedTriangleList( 1024 ) |
|
{ |
|
BackgroundColor.DuplicateVector(Vector(1,0,0)); // red |
|
Flags=0; |
|
} |
|
|
|
|
|
// call AddTriangle to set up the world |
|
void AddTriangle(int32 id, const Vector &v1, const Vector &v2, const Vector &v3, |
|
const Vector &color); |
|
|
|
// Adds a triangle with flags & material |
|
void AddTriangle(int32 id, const Vector &v1, const Vector &v2, const Vector &v3, |
|
const Vector &color, uint16 flags, int32 materialIndex); |
|
|
|
|
|
void AddQuad(int32 id, const Vector &v1, const Vector &v2, const Vector &v3, |
|
const Vector &v4, // specify vertices in cw or ccw order |
|
const Vector &color); |
|
|
|
// for ease of testing. |
|
void AddAxisAlignedRectangularSolid(int id,Vector mincoord, Vector Maxcoord, |
|
const Vector &color); |
|
|
|
|
|
// SetupAccelerationStructure to prepare for tracing |
|
void SetupAccelerationStructure(void); |
|
|
|
|
|
// lowest level intersection routine - fire 4 rays through the scene. all 4 rays must pass the |
|
// Check() function, and t extents must be initialized. skipid can be set to exclude a |
|
// particular id (such as the origin surface). This function finds the closest intersection. |
|
void Trace4Rays(const FourRays &rays, fltx4 TMin, fltx4 TMax,int DirectionSignMask, |
|
RayTracingResult *rslt_out, |
|
int32 skip_id=-1, ITransparentTriangleCallback *pCallback = NULL); |
|
|
|
// higher level intersection routine that handles computing the mask and handling rays which do not match in direciton sign |
|
void Trace4Rays(const FourRays &rays, fltx4 TMin, fltx4 TMax, |
|
RayTracingResult *rslt_out, |
|
int32 skip_id=-1, ITransparentTriangleCallback *pCallback = NULL); |
|
|
|
// compute virtual light sources to model inter-reflection |
|
void ComputeVirtualLightSources(void); |
|
|
|
|
|
// high level interface - pass viewing parameters, rendering flags, and a destination frame |
|
// buffer, and get a ray traced scene in 32-bit rgba format |
|
void RenderScene(int width, int height, // width and height of desired rendering |
|
int stride, // actual width in pixels of target buffer |
|
uint32 *output_buffer, // pointer to destination |
|
Vector CameraOrigin, // eye position |
|
Vector ULCorner, // word space coordinates of upper left |
|
// monitor corner |
|
Vector URCorner, // top right corner |
|
Vector LLCorner, // lower left |
|
Vector LRCorner, // lower right |
|
RayTraceLightingMode_t lightmode=DIRECT_LIGHTING); |
|
|
|
|
|
/// raytracing stream - lets you trace an array of rays by feeding them to this function. |
|
/// results will not be returned until FinishStream is called. This function handles sorting |
|
/// the rays by direction, tracing them 4 at a time, and de-interleaving the results. |
|
|
|
void AddToRayStream(RayStream &s, |
|
Vector const &start,Vector const &end,RayTracingSingleResult *rslt_out); |
|
|
|
inline void FlushStreamEntry(RayStream &s,int msk); |
|
|
|
/// call this when you are done. handles all cleanup. After this is called, all rslt ptrs |
|
/// previously passed to AddToRaySteam will have been filled in. |
|
void FinishRayStream(RayStream &s); |
|
|
|
|
|
int MakeLeafNode(int first_tri, int last_tri); |
|
|
|
|
|
float CalculateCostsOfSplit( |
|
int split_plane,int32 const *tri_list,int ntris, |
|
Vector MinBound,Vector MaxBound, float &split_value, |
|
int &nleft, int &nright, int &nboth); |
|
|
|
void RefineNode(int node_number,int32 const *tri_list,int ntris, |
|
Vector MinBound,Vector MaxBound, int depth); |
|
|
|
void CalculateTriangleListBounds(int32 const *tris,int ntris, |
|
Vector &minout, Vector &maxout); |
|
|
|
void AddInfinitePointLight(Vector position, // light center |
|
Vector intensity); // rgb amount |
|
|
|
// use the global variables set by LoadBSPFile to populated the RayTracingEnvironment with |
|
// faces. |
|
void InitializeFromLoadedBSP(void); |
|
|
|
void AddBSPFace(int id,dface_t const &face); |
|
|
|
// MakeRoomForTriangles - a hint telling it how many triangles we are going to add so that |
|
// the utl vectors used can be pre-allocated |
|
void MakeRoomForTriangles( int ntriangles ); |
|
|
|
const CacheOptimizedTriangle &GetTriangle( int32 triID ) |
|
{ |
|
return OptimizedTriangleList[triID]; |
|
} |
|
|
|
int32 GetTriangleMaterial( int32 triID ) |
|
{ |
|
return TriangleMaterials[triID]; |
|
} |
|
const Vector &GetTriangleColor( int triID ) |
|
{ |
|
return TriangleColors[triID]; |
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
#endif
|
|
|