xbox-kernel/private/ntos/ani2/BlobRenderer.cpp
2020-09-30 17:17:25 +02:00

396 lines
13 KiB
C++

///////////////////////////////////////////////////////////////////////////////
// File: BlobRenderer.cpp
//
// Copyright 2001 Pipeworks Software
//
// WORK HALTED: Patent issues
//
///////////////////////////////////////////////////////////////////////////////
#include <xtl.h>
#include "xbs_math.h"
#include "xbs_app.h"
#include "Blobs.h"
#include "BlobRenderer.h"
///////////////////////////////////////////////////////////////////////////////
#define LLI_RAND_MAX 0x00010000
#define LLI_RAND_MASK 0x0000FFFF
///////////////////////////////////////////////////////////////////////////////
float FRand01()
{
static QRand qrand;
static float mul = 1.0f / ((float)LLI_RAND_MAX);
return ((float)(qrand.Rand()&LLI_RAND_MASK)) * mul;
}
///////////////////////////////////////////////////////////////////////////////
float FRand11()
{
static QRand qrand;
static float mul = 2.0f / ((float)LLI_RAND_MAX);
return (((float)(qrand.Rand()&LLI_RAND_MASK)) * mul) - 1.0f;
}
///////////////////////////////////////////////////////////////////////////////
BlobRenderer::BlobRenderer()
{
m_pLLI = NULL;
m_pBlobArray = NULL;
m_NumBlobs = 0;
m_Threshhold = 1.0f;
m_dwNumVertices = 0;
m_dwNumIndices = 0;
m_pBlobVB = NULL;
m_pBlobIB = NULL;
m_pField = NULL;
m_pVertexIndices = NULL;
}
///////////////////////////////////////////////////////////////////////////////
BlobRenderer::~BlobRenderer()
{
destroy();
}
///////////////////////////////////////////////////////////////////////////////
void BlobRenderer::destroy()
{
if (m_pBlobVB) m_pBlobVB->Release();
if (m_pBlobIB) m_pBlobIB->Release();
delete m_pField;
delete m_pVertexIndices;
m_pBlobArray = NULL;
m_pLLI = NULL;
m_pBlobArray = NULL;
m_NumBlobs = 0;
m_dwNumVertices = 0;
m_dwNumIndices = 0;
m_pBlobVB = NULL;
m_pBlobIB = NULL;
m_pField = NULL;
m_pVertexIndices = NULL;
}
///////////////////////////////////////////////////////////////////////////////
void BlobRenderer::render()
{
int next_affiliation = m_pSources[0]->dAffiliation;
int i;
for (i=1; i<m_NumBlobs; i++)
{
next_affiliation = min(next_affiliation, m_pSources[i]->dAffiliation);
}
for (int affiliation = next_affiliation; affiliation < m_NumBlobs; affiliation = next_affiliation)
{
next_affiliation = m_NumBlobs;
D3DVECTOR pt_min, pt_max;
pt_min = pt_max = bs.ptPosition;
bool b_unset = true;
// For this affiliation, iterate through all the sources, adjusting
// the min/max for any blobs with the correct affiliation.
for (int i=0; i<m_NumBlobs; i++)
{
BlobSource& bs = m_pSources[i];
// Keep track of what the smallest affiliation larger than the current affiliation is.
int aff = bs.dAffiliation;
if (aff > affiliation) next_affiliation = min(aff, next_affiliation);
if (aff == affiliation)
{
if (b_unset)
{
b_unset = false;
pt_min = pt_max = bs.ptPosition;
pt_min.x -= bs.fConsiderationRadius;
pt_min.y -= bs.fConsiderationRadius;
pt_min.z -= bs.fConsiderationRadius;
pt_max.x += bs.fConsiderationRadius;
pt_max.y += bs.fConsiderationRadius;
pt_max.z += bs.fConsiderationRadius;
}
else
{
pt_min.x = min(pt_min.x, bs.ptPosition.x - bs.fConsiderationRadius);
pt_min.y = min(pt_min.y, bs.ptPosition.y - bs.fConsiderationRadius);
pt_min.z = min(pt_min.z, bs.ptPosition.z - bs.fConsiderationRadius);
pt_max.x = min(pt_max.x, bs.ptPosition.x + bs.fConsiderationRadius);
pt_max.y = min(pt_max.y, bs.ptPosition.y + bs.fConsiderationRadius);
pt_max.z = min(pt_max.z, bs.ptPosition.z + bs.fConsiderationRadius);
}
}
}
// Now have the boundaries for the rendering region. Blobs outside the area may
// affect the energy threshold, but no polygons will be drawn outside it.
int sx, sy, sz, ex, ey, ez;
getCoords(pt_min, &sx, &sy, &sz);
getCoords(pt_max, &ex, &ey, &ez);
ex++; ey++; ez++;
int len_x = ex - sx + 1;
int len_y = ey - sy + 1;
int len_z = ez - sz + 1;
int len_xy = len_x*len_y;
int len_xyz = len_xy * len_z;
if (len_x*len_y*len_z > m_FieldX*m_FieldY*m_FieldZ)
{
continue; // can't render it, it is too big
}
// Clear the part of the field that will be used. Note that this does not
// share the rows/columns/levels that would be used if the entire field
// was used. Instead, the minimum amount of memory is used in a contiguous
// fashion.
for (i=0; i<len_xyz; i++) m_pField[i] = 0.0f;
// For each blob, isolate the box within influence and iterate for each point.
// The box of influence can (and must) be clipped to the previously determined
// region.
for (i=0; i<m_NumBlobs; i++)
{
BlobSource& bs = m_pSources[i];
D3DVECTOR pt_start, pt_end;
Set(&pt_start,
max(pt_min.x, bs.ptPosition.x - bs.fConsiderationRadius),
max(pt_min.y, bs.ptPosition.y - bs.fConsiderationRadius),
max(pt_min.z, bs.ptPosition.z - bs.fConsiderationRadius)
);
Set(&pt_end,
min(pt_max.x, bs.ptPosition.x + bs.fConsiderationRadius),
min(pt_max.y, bs.ptPosition.y + bs.fConsiderationRadius),
min(pt_max.z, bs.ptPosition.z + bs.fConsiderationRadius)
);
int bsx, bsy, bsz, bex, bey, bez;
getCoords(pt_start, &bsx, &bsy, &bsz);
getCoords(pt_end , &bex, &bey, &bez);
bex++; bey++; bez++;
// The coordinates have already been capped to the rendering region.
if ( (bsx<=bex) && (bsy<=bey) && (bsz<=bez) )
{
D3DVECTOR pos, posll;
getWorldPos(&posll, bsx, bsy, bsz);
pos.z = posll.z;
for (int w=bsz; w<=bez; w++, pos.z += m_FieldToWorld.z)
{
pos.y = posll.y;
for (int v=bsy; v<=bey; v++, pos.y += m_FieldToWorld.y)
{
pos.x = posll.x;
float* p_field = &m_pField[w*len_xy + v*len_x + bsx];
for (int u=bsx; u<=bex; u++, pos.x += m_FieldToWorld.x)
{
*(p_field++) += bs.calculate(affiliation, pos);
}
}
}
}
}
// It is legitimate for the outermost points to have a negative energy accumulation,
// but nothing should be giving it a positive value. It would be nice to enforce this,
// but I'm not sure it's worth the trouble.
// Woohoo! The field is complete for this affiliation. Start rendering.
// Lock the buffers.
int max_tris = len_xyz * 5 * 2; // for a Sierpinski cube relative, maybe...
BlobVertex* p_verts;
u16* p_indices;
if (D3D_OK != m_pBlobVB->Lock(0, len_xyz*6, (BYTE**) &pbv_buf, D3DLOCK_DISCARD))
{
continue;
}
if (D3D_OK != m_pBlobIB->Lock(0, max_tris*3, (BYTE**) &pbv_buf, D3DLOCK_DISCARD))
{
m_pBlobVB->Unlock();
continue;
}
int num_tris = 0;
int num_verts = 0;
BlobVertex* p_vert = p_verts;
u16* p_index = p_indices;
// This buffer stores the vertex indices (or -1) for the vertex on each segment
// of a voxel. The offsets march through the buffer, returning to zero when they
// reach the voxel_wrap value. m_pVertexIndices[voxel_offset] is for the voxel
// with the current point in the max_pt position.
int voxel_offset = 0;
int voxel_offset_x = 6*1;
int voxel_offset_y = 6*len_x;
int voxel_offset_z = 6*len_xy;
int voxel_wrap = 6*(len_xy+1);
memset(m_pVertexIndices, -1, sizeof(int) * voxel_wrap);
D3DVECTOR pos;
pos.z = pt_min.z;
for (int w=sz; w<ez; w++, pos.z += m_FieldToWorld.z)
{
pos.y = pt_min.y;
for (int v=sy; v<ey; v++, pos.y += m_FieldToWorld.y)
{
pos.x = pt_min.x;
p_field = &m_pField[w*len_xy + v*len_x];
for (int u=sx; u<ex; u++, pos.x += m_FieldToWorld.x)
{
u32 in_or_out;
if (p_field[ 0] > m_Threshhold) in_or_out |= 0x0001;
if (p_field[ 1] > m_Threshhold) in_or_out |= 0x0002;
if (p_field[ len_x ] > m_Threshhold) in_or_out |= 0x0004;
if (p_field[ len_x+1] > m_Threshhold) in_or_out |= 0x0008;
if (p_field[len_xy ] > m_Threshhold) in_or_out |= 0x0010;
if (p_field[len_xy+ 1] > m_Threshhold) in_or_out |= 0x0020;
if (p_field[len_xy+len_x ] > m_Threshhold) in_or_out |= 0x0040;
if (p_field[len_xy+len_x+1] > m_Threshhold) in_or_out |= 0x0080;
if ((in_or_out!=0) && (in_or_out!=0x00FF))
{
// Not all corners are the same. Analyze the five tetrahedrons.
// The five tetrahedrons are defined as corners:
const int odd_tetrahedrons[20] = { 0,1,2,4, 5,1,4,7, 3,1,2,7, 6,2,7,4, 1,2,4,7 };
const int even_tetrahedrons[20] = { 1,0,5,3, 7,3,5,6, 2,0,6,3, 4,0,6,5, 0,6,5,3 };
const int* tetrahedrons = ((u+v+w)&0x01) ? odd_tetrahedrons : even_tetrahedrons;
for (int tet=0; tet<5; tet++)
{
int mask = 0;
mask |= (1<<(tetrahedrons[4*tet+0]));
mask |= (1<<(tetrahedrons[4*tet+1]));
mask |= (1<<(tetrahedrons[4*tet+2]));
mask |= (1<<(tetrahedrons[4*tet+3]));
int masked = in_or_out & mask;
if ((masked!=0) && (masked!=mask))
{
// Mixed corners for this tetrahedron. Triangles are needed.
int ins[4];
int outs[4];
int num_in = 0;
int num_out = 0;
if (mask & 0x0001) ins[num_in++] = 0;
if (mask & 0x0002) ins[num_in++] = 1;
if (mask & 0x0004) ins[num_in++] = 2;
if (mask & 0x0008) ins[num_in++] = 3;
if (mask & 0x0010) ins[num_in++] = 4;
if (mask & 0x0020) ins[num_in++] = 5;
if (mask & 0x0040) ins[num_in++] = 6;
if (mask & 0x0080) ins[num_in++] = 7;
if (num_in==1)
{
// One is in, three are out.
D3DVECTOR pos_in = pos;
if (ins[0] & 0x01) pos_in.x += m_FieldToWorld.x * 0.5f;
if (ins[0] & 0x02) pos_in.y += m_FieldToWorld.y * 0.5f;
if (ins[0] & 0x04) pos_in.z += m_FieldToWorld.z * 0.5f;
}
}
}
}
}
}
}
// Render.
m_pBlobIB->Unlock();
m_pBlobVB->Unlock();
}
}
///////////////////////////////////////////////////////////////////////////////
void BlobRenderer::init( const BlobSource* p_blob_sources, int num_blobs,
float xy_spacing, float z_spacing,
const D3DVECTOR& center, const D3DVECTOR& half_dim)
{
m_FieldX = (int)((half_dim.x * 2.0f / xy_spacing) + 2.0f);
m_FieldY = (int)((half_dim.y * 2.0f / xy_spacing) + 2.0f);
m_FieldZ = (int)((half_dim.z * 2.0f / z_spacing) + 2.0f);
m_FieldXY = m_FieldX * m_FieldY;
m_pField = new float[m_FieldXY * m_FieldZ];
m_pVertexIndices = new int[(m_FieldXY+1) * 6];
Sub(&m_LowerLeftCorner, center, half_dim);
m_FieldToWorld.x = xy_spacing;
m_FieldToWorld.y = xy_spacing;
m_FieldToWorld.z = z_spacing;
m_WorldToField.x = 1.0f / xy_spacing;
m_WorldToField.y = 1.0f / xy_spacing;
m_WorldToField.z = 1.0f / z_spacing;
// Create a vertex buffer, too.
}
///////////////////////////////////////////////////////////////////////////////
void BlobRenderer::getCoords(const D3DVECTOR& pos, int* p_x, int* p_y, int* p_z, D3DVECTOR* p_remainder)
{
float fx = (pos.x - m_LowerLeftCorner.x) * m_WorldToField.x;
float fy = (pos.y - m_LowerLeftCorner.y) * m_WorldToField.y;
float fz = (pos.z - m_LowerLeftCorner.z) * m_WorldToField.z;
*p_x = (int) (fx);
*p_y = (int) (fy);
*p_z = (int) (fz);
if (p_remainder)
{
p_remainder->x = fx - ((float)*p_x);
p_remainder->y = fy - ((float)*p_y);
p_remainder->z = fz - ((float)*p_z);
}
return (*p_z * m_FieldXY) + (*p_y * m_FieldX) + (*p_x);
}
///////////////////////////////////////////////////////////////////////////////
void BlobRenderer::getWorldPos(D3DVECTOR* pos, int x, int y, int z)
{
pos->x = m_LowerLeftCorner.x + ((float)x) * m_FieldToWorld.x;
pos->y = m_LowerLeftCorner.y + ((float)y) * m_FieldToWorld.y;
pos->z = m_LowerLeftCorner.z + ((float)z) * m_FieldToWorld.z;
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
TestBlobRenderer::TestBlobRenderer()
{
}
///////////////////////////////////////////////////////////////////////////////
TestBlobRenderer::~TestBlobRenderer()
{
destroy();
}
///////////////////////////////////////////////////////////////////////////////
void TestBlobRenderer::create()
{
for (int i=0; i<NUM_BLOBS; i++)
{
Set(&m_Sources[i].ptPosition, FRand01() * 0.2f, FRand01() * 0.2f, FRand01() * 0.2f);
m_Sources[i].fStrength = 1.0f;
m_Sources[i].fConsiderationRadius = 0.09f;
m_Sources[i].fConsiderationRadius2 = m_Sources[i].fConsiderationRadius * m_Sources[i].fConsiderationRadius;
m_Sources[i].dAffiliation = 0;
}
D3DVECTOR center, hd;
Set(&center, 0.0f, 0.0f, 0.0f);
Set(&hd, 1.0f, 1.0f, 1.0f);
init(&m_Sources[i], NUM_BLOBS, 0.005f, 0.005f, center, hd);
}
///////////////////////////////////////////////////////////////////////////////
void TestBlobRenderer::advanceTime(float fElapsedTime, float fDt)
{
}
///////////////////////////////////////////////////////////////////////////////