396 lines
13 KiB
C++
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(¢er, 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)
|
|
{
|
|
}
|
|
///////////////////////////////////////////////////////////////////////////////
|