2020-09-30 17:17:25 +02:00

988 lines
31 KiB
C++

///////////////////////////////////////////////////////////////////////////////
// File: VBlob.cpp
//
// Copyright 2001 Pipeworks Software
///////////////////////////////////////////////////////////////////////////////
#include "precomp.h"
#include "xbs_math.h"
#include "xbs_app.h"
#include "qrand.h"
#include "VBlob.h"
#include "tex_gen.h"
///////////////////////////////////////////////////////////////////////////////
const float MIN_SPAWN_MAGN = (0.5f);
#define BLOBLET_DIM (8)
#define BLOB_DIM (32)
///////////////////////////////////////////////////////////////////////////////
QRand VBlob::m_QRand;
VBlob* gpVBlob;
///////////////////////////////////////////////////////////////////////////////
void VBlob::Init()
{
int i;
for (i = 0; i < MAX_BLOBBUMPS; i++)
{
m_BlobBumps[i].Init();
}
for (i = 0; i < MAX_BLOBLETS; i++)
{
m_Bloblets[i].Init();
}
m_QRand.Init();
m_pBlobletVB = m_pBlobVBConst = m_pBlobVBChangingR = m_pBlobVBChangingU = NULL;
m_pBlobletIB = m_pBlobIB = NULL;
m_pUnitSphereNormals = NULL;
m_dwNumBlobletVertices = m_dwNumBlobletIndices = m_NumVertsPerFace = m_dwNumVertices = m_dwNumIndices = 0;
m_dwVShaderBlob = m_dwVShaderBloblet = 0;
m_dwPShaderBlob = m_dwPShaderBloblet = 0;
m_NumBlobBumps = m_NumBloblets = 0;
Set(&m_BlobColor, 0.25f, 1.0f, 0.15f, 1.0f);
Set(&m_Pos, 0.0f, 0.0f, 0.0f);
Set(&m_Scale, 1.0f, 1.0f, 1.0f);
m_fRadius = 2.3f;
}
///////////////////////////////////////////////////////////////////////////////
void VBlob::create()
{
gpVBlob = this;
gpd3dDev->CreateVertexBuffer(4 * sizeof(float)*5, 0, 0, 0, &pHaloQuadVB);
//MTS generateUnitSphere(2, &m_pBlobletVB, &m_pBlobletIB, NULL, &m_dwNumBlobletVertices, &m_dwNumBlobletIndices);
generateUnitSphere(BLOBLET_DIM, &m_pBlobletVB, &m_pBlobletIB, NULL, &m_dwNumBlobletVertices, &m_dwNumBlobletIndices);
generateUnitSphere(BLOB_DIM, &m_pBlobVBConst, &m_pBlobIB, &m_pUnitSphereNormals, &m_dwNumVertices, &m_dwNumIndices);
gpd3dDev->CreateVertexBuffer(m_dwNumVertices * sizeof(VBlobChangingVertex), 0, 0, 0, &m_pBlobVBChangingR);
gpd3dDev->CreateVertexBuffer(m_dwNumVertices * sizeof(VBlobChangingVertex), 0, 0, 0, &m_pBlobVBChangingU);
m_NumVertsPerFace = m_dwNumVertices / 6;
restart();
// Initialize the pixel shaders.
if( m_dwPShaderBlob )
{
gpd3dDev->DeletePixelShader( m_dwPShaderBlob );
m_dwPShaderBlob = 0;
}
#ifndef BINARY_RESOURCE
m_dwPShaderBlob = gApp.loadPixelShader("D:\\Shaders\\VBlob.xpu");
#else // BINARY_RESOURCE
m_dwPShaderBlob = gApp.loadPixelShader(g_vblob_xpu);
#endif // BINARY_RESOURCE
if( m_dwPShaderBloblet )
{
gpd3dDev->DeletePixelShader( m_dwPShaderBloblet );
m_dwPShaderBloblet = 0;
}
#ifndef BINARY_RESOURCE
m_dwPShaderBloblet = gApp.loadPixelShader("D:\\Shaders\\VBloblet.xpu");
#else // BINARY_RESOURCE
m_dwPShaderBloblet = gApp.loadPixelShader(g_vbloblet_xpu);
#endif // BINARY_RESOURCE
// Initialize the vertex shaders.
DWORD dwBlobShaderVertexDecl[] =
{
D3DVSD_STREAM( 0 ),
D3DVSD_REG( 0, D3DVSDT_FLOAT3 ), // unit sphere normal
D3DVSD_STREAM( 1 ),
D3DVSD_REG( 1, D3DVSDT_FLOAT4 ), // vertex normal, w is displacement
D3DVSD_END()
};
if (m_dwVShaderBlob)
{
gpd3dDev->DeleteVertexShader( m_dwVShaderBlob );
m_dwVShaderBlob = 0;
}
#ifndef BINARY_RESOURCE
m_dwVShaderBlob = gApp.loadVertexShader("D:\\Shaders\\VBlob.xvu", dwBlobShaderVertexDecl);
#else // BINARY_RESOURCE
m_dwVShaderBlob = gApp.loadVertexShader(g_vblob_xvu, dwBlobShaderVertexDecl);
#endif // BINARY_RESOURCE
DWORD dwBlobletShaderVertexDecl[] =
{
D3DVSD_STREAM( 0 ),
D3DVSD_REG( 0, D3DVSDT_FLOAT3 ), // unit sphere normal
D3DVSD_END()
};
if (m_dwVShaderBloblet)
{
gpd3dDev->DeleteVertexShader( m_dwVShaderBloblet );
m_dwVShaderBloblet = 0;
}
#ifndef BINARY_RESOURCE
m_dwVShaderBloblet = gApp.loadVertexShader("D:\\Shaders\\VBloblet.xvu", dwBlobletShaderVertexDecl);
#else // BINARY_RESOURCE
m_dwVShaderBloblet = gApp.loadVertexShader(g_vbloblet_xvu, dwBlobletShaderVertexDecl);
#endif // BINARY_RESOURCE
}
///////////////////////////////////////////////////////////////////////////////
void VBlob::destroy()
{
m_pBlobletVB->Release();
m_pBlobletIB->Release();
m_pBlobVBConst->Release();
m_pBlobVBChangingR->Release();
m_pBlobVBChangingU->Release();
m_pBlobIB->Release();
MemFree(m_pUnitSphereNormals);
pHaloQuadVB->Release();
if (m_dwPShaderBlob) gpd3dDev->DeletePixelShader(m_dwPShaderBlob);
if (m_dwPShaderBloblet) gpd3dDev->DeletePixelShader(m_dwPShaderBloblet);
if (m_dwVShaderBlob) gpd3dDev->DeleteVertexShader(m_dwVShaderBlob);
if (m_dwVShaderBloblet) gpd3dDev->DeleteVertexShader(m_dwVShaderBloblet);
m_pBlobletVB = NULL;
m_pBlobletIB = NULL;
m_pBlobVBConst = NULL;
m_pBlobVBChangingU = m_pBlobVBChangingR = NULL;
m_pBlobIB = NULL;
m_pUnitSphereNormals = NULL;
}
///////////////////////////////////////////////////////////////////////////////
void VBlob::render()
{
// Set default states
gpd3dDev->SetRenderState( D3DRS_LIGHTING, FALSE );
gpd3dDev->SetRenderState( D3DRS_ZENABLE, TRUE );
gpd3dDev->SetTextureStageState( 0, D3DTSS_MINFILTER, D3DTEXF_LINEAR );
gpd3dDev->SetTextureStageState( 0, D3DTSS_MAGFILTER, D3DTEXF_LINEAR );
gpd3dDev->SetTextureStageState( 0, D3DTSS_MIPFILTER, D3DTEXF_NONE );
gpd3dDev->SetTextureStageState( 0, D3DTSS_ADDRESSU, D3DTADDRESS_CLAMP);
gpd3dDev->SetTextureStageState( 0, D3DTSS_ADDRESSV, D3DTADDRESS_CLAMP);
gpd3dDev->SetTextureStageState( 0, D3DTSS_ADDRESSW, D3DTADDRESS_CLAMP);
gpd3dDev->SetTextureStageState( 1, D3DTSS_MINFILTER, D3DTEXF_LINEAR );
gpd3dDev->SetTextureStageState( 1, D3DTSS_MAGFILTER, D3DTEXF_LINEAR );
gpd3dDev->SetTextureStageState( 1, D3DTSS_MIPFILTER, D3DTEXF_NONE );
gpd3dDev->SetTextureStageState( 1, D3DTSS_ADDRESSU, D3DTADDRESS_CLAMP);
gpd3dDev->SetTextureStageState( 1, D3DTSS_ADDRESSV, D3DTADDRESS_CLAMP);
gpd3dDev->SetTextureStageState( 1, D3DTSS_ADDRESSW, D3DTADDRESS_CLAMP);
gpd3dDev->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE );
gpd3dDev->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA );
gpd3dDev->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );
// Render halo after blob, but before bloblets.
gpd3dDev->SetVertexShader(D3DFVF_XYZ|D3DFVF_TEX1);
gpd3dDev->SetPixelShader(NULL);
D3DMATRIX id_mat;
SetIdentity(&id_mat);
gpd3dDev->SetTransform(D3DTS_WORLD,&id_mat);
gpd3dDev->SetTransform(D3DTS_VIEW,&gApp.theCamera.matWTC);
gpd3dDev->SetTransform(D3DTS_PROJECTION,&gApp.theCamera.matProj);
gpd3dDev->SetTextureStageState(0,D3DTSS_COLOROP,D3DTOP_MODULATE);
gpd3dDev->SetTextureStageState(0,D3DTSS_COLORARG1,D3DTA_TEXTURE);
gpd3dDev->SetTextureStageState(0,D3DTSS_COLORARG2,D3DTA_TFACTOR);
gpd3dDev->SetTextureStageState(0,D3DTSS_ALPHAOP,D3DTOP_SELECTARG1);
gpd3dDev->SetTextureStageState(0,D3DTSS_ALPHAARG1,D3DTA_TFACTOR);
gpd3dDev->SetTextureStageState(1,D3DTSS_COLOROP,D3DTOP_DISABLE);
float cur_rad = m_fRadius * (1.0f + 1.3f * fast_sqrt(gApp.getPulseIntensity()));
DWORD dw_alpha;
float f_alpha = min(gApp.getBlobIntensity(),1.f) * 255.f;
__asm
{
cvttss2si eax, f_alpha
shl eax,24
mov dw_alpha,eax
};
DWORD dw_tf = 0x00a0ff40 | dw_alpha;
gpd3dDev->SetRenderState( D3DRS_TEXTUREFACTOR,dw_tf);
gpd3dDev->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA );
gpd3dDev->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_ONE );
gpd3dDev->SetRenderState( D3DRS_ZWRITEENABLE, FALSE );
gpd3dDev->SetTexture(0,gApp.pGlowMap);
struct _decalvert
{
D3DVECTOR p;
float u,v;
};
_decalvert * phalo_verts = NULL;
pHaloQuadVB->Lock(0,sizeof(_decalvert) * 4, (BYTE **)&phalo_verts,NULL);
float f_rad = cur_rad * 5.2f;
D3DMATRIX & ctw = gApp.theCamera.matCTW;
Sub(*(D3DVECTOR *)&ctw._21,*(D3DVECTOR *)&ctw._11,&phalo_verts[0].p);
Scale(&phalo_verts[0].p,f_rad);
phalo_verts[0].u = 0.f;
phalo_verts[0].v = 1.f;
Add(*(D3DVECTOR *)&ctw._21,*(D3DVECTOR *)&ctw._11,&phalo_verts[1].p);
Scale(&phalo_verts[1].p,f_rad);
phalo_verts[1].u = 1.f;
phalo_verts[1].v = 1.f;
Sub(*(D3DVECTOR *)&ctw._11,*(D3DVECTOR *)&ctw._21,&phalo_verts[2].p);
Scale(&phalo_verts[2].p,f_rad);
phalo_verts[2].u = 1.f;
phalo_verts[2].v = 0.f;
Set(&phalo_verts[3].p,-ctw._11-ctw._21,-ctw._12-ctw._22,-ctw._13-ctw._23);
Scale(&phalo_verts[3].p,f_rad);
phalo_verts[3].u = 0.f;
phalo_verts[3].v = 0.f;
pHaloQuadVB->Unlock();
gpd3dDev->SetStreamSource( 0, pHaloQuadVB, sizeof(_decalvert));
gpd3dDev->DrawPrimitive(D3DPT_TRIANGLEFAN,0,2);
// Restore state
gpd3dDev->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );
gpd3dDev->SetRenderState( D3DRS_ZWRITEENABLE, TRUE );
//MTS gpd3dDev->SetRenderState( D3DRS_FILLMODE, D3DFILL_WIREFRAME );
gpd3dDev->SetTexture( 0, gApp.pNormalCubeMapLoRes );
gpd3dDev->SetTexture( 1, gApp.pNormalCubeMapLoRes );
gpd3dDev->SetVertexShader( m_dwVShaderBlob );
gpd3dDev->SetPixelShader( m_dwPShaderBlob );
// Note: when passing matrices to a vertex shader, we transpose them, since
// matrix multiplies are done with dot product operations on the matrix rows.
D3DMATRIX matFinal,matTmp;
MulMats(gApp.theCamera.matWTC,gApp.theCamera.matProj,&matTmp);
SetTranspose(matTmp,&matFinal);
gpd3dDev->SetVertexShaderConstant(4,(CONST void *)&matFinal,4);
//; Expected vertex shaders constants
//; c0-c3 = Transpose of world matrix
//; c4-c7 = Transpose of view*projection matrix
//; c8 = some constants, x=0, y=1, z=2, w=0.5
//; c9 = eye location
//; c10 = blob scaling
//; c11 = 1/ blob scaling
//; c12 = blob center
D3DVECTOR4 val[5];
// Constants
Set(&val[0], 0.0f, 1.0f, 2.0f, 0.5f);
// Eye position
gApp.theCamera.getCameraPos(&val[1]);
// Scaling
Set(&val[2], cur_rad*m_Scale.x, cur_rad*m_Scale.y, cur_rad*m_Scale.z, 1.0f);
// 1 / blob scaling
val[3].x = 1.0f / val[2].x;
val[3].y = 1.0f / val[2].y;
val[3].z = 1.0f / val[2].z;
// Blob center.
//MTS float f_jitter_mag = (gApp.getElapsedTime() - BLOB_JITTER_START) * OO_BLOB_JITTER_DELTA;
//MTS f_jitter_mag *= f_jitter_mag;
//MTS float cOffsets[3] = { 0.2f, 0.7f, 4.2f };
//MTS float cScales[3] = { 20.0f, 28.0f, 44.0f };
D3DVECTOR4 cur_pos;
//MTS cur_pos.x = m_Pos.x + f_jitter_mag * sinf((gApp.getElapsedTime() - cOffsets[0])*cScales[0]);
//MTS cur_pos.y = m_Pos.y + f_jitter_mag * sinf((gApp.getElapsedTime() - cOffsets[1])*cScales[1]);
//MTS cur_pos.z = m_Pos.z + f_jitter_mag * sinf((gApp.getElapsedTime() - cOffsets[2])*cScales[2]);
cur_pos.x = m_Pos.x;
cur_pos.y = m_Pos.y;
cur_pos.z = m_Pos.z;
cur_pos.w = 0.0f;
Set(&val[4], cur_pos.x, cur_pos.y, cur_pos.z, 0.0f);
gpd3dDev->SetVertexShaderConstant( 8, &val, 5 );
// Make the object to World transform. Transpose it.
D3DMATRIX matT, mat;
SetIdentity( &mat );
mat.m[0][0] = m_Scale.x * cur_rad;
mat.m[1][1] = m_Scale.y * cur_rad;
mat.m[2][2] = m_Scale.z * cur_rad;
mat.m[3][0] = cur_pos.x;
mat.m[3][1] = cur_pos.y;
mat.m[3][2] = cur_pos.z;
SetTranspose( mat, &matT );
gpd3dDev->SetVertexShaderConstant( 0, &matT, 4 );
// Expected pixel shader constants
//; c0 = base blob color
//; c1 = ambient color
// Blob color
val[0] = m_BlobColor;
float f_color_intensity = BLOB_BASE_INTENSITY + 4.0f * (1.2f * gApp.getBaseBlobIntensity() + 0.8f * gApp.getPulseIntensity() );
//MTS f_color_intensity *= min(1.0f, gApp.getElapsedTime() * OO_BLOB_STATIC_END_TIME);
f_color_intensity *= min(1.0f, gApp.getElapsedTime() * 4.0f);
Scale(&val[0], f_color_intensity);
// Ambient light
val[1] = m_BlobColor;
Scale(&val[1], 0.0f);
gpd3dDev->SetPixelShaderConstant( 0, &val[0], 2 );
gpd3dDev->SetIndices( m_pBlobIB, 0 );
gpd3dDev->SetStreamSource( 0, m_pBlobVBConst, sizeof(VBlobConstantVertex) );
// Get the Update vertex buffer and use it to render.
swapChangingVertices();
gpd3dDev->SetStreamSource( 1, m_pBlobVBChangingR, sizeof(VBlobChangingVertex) );
gpd3dDev->DrawIndexedPrimitive( D3DPT_TRIANGLESTRIP, 0, m_dwNumVertices, 0, m_dwNumIndices-2 );
// Render the bloblets.
gpd3dDev->SetVertexShader( m_dwVShaderBloblet );
gpd3dDev->SetPixelShader( m_dwPShaderBloblet );
gpd3dDev->SetVertexShaderConstant(4,(CONST void *)&matFinal,4);
// Constants
Set(&val[0], 0.0f, 1.0f, 2.0f, 0.5f);
// Eye position
gApp.theCamera.getCameraPos(&val[1]);
gpd3dDev->SetVertexShaderConstant( 8, &val, 2 );
// Bloblet color
val[0] = m_BlobColor;
Scale(&val[0], 0.3f * gApp.getBlobIntensity());
// Ambient light
val[1] = m_BlobColor;
Scale(&val[1], 0.2f);
Set(&val[2], 2.0f, 2.0f, 2.0f, 2.0f);
// 1/val is the level of brightness that is full alpha
gpd3dDev->SetPixelShaderConstant( 0, &val, 3 );
gpd3dDev->SetIndices( m_pBlobletIB, 0 );
gpd3dDev->SetStreamSource( 0, m_pBlobletVB, sizeof(VBlobConstantVertex) );
for (int i=0; i<m_NumBloblets; i++)
{
//; c9 = eye location
//; c10 = blob center
//; c11 = direction of scaling
//; c12 = scaling perpendicular to direction
//; c13 = parallel minus perpendicular scaling multiplied by scaling direction
VBloblet* p_bloblet = &m_Bloblets[i];
// Bloblet center.
Set(&val[0], p_bloblet->vPosition.x, p_bloblet->vPosition.y, p_bloblet->vPosition.z, 0.0f);
// Direction of scaling.
Set(&val[1], p_bloblet->vDirection.x, p_bloblet->vDirection.y, p_bloblet->vDirection.z, 1.0f);
// Scaling perpendicular to direction
float fval = p_bloblet->fRadius / fast_sqrt(p_bloblet->fWobble);
Set(&val[2], fval, fval, fval, 1.0f);
// Parallel minus perpendicular scaling multiplied by scaling direction
fval = p_bloblet->fRadius * p_bloblet->fWobble - fval;
Set(&val[3], fval*p_bloblet->vDirection.x, fval*p_bloblet->vDirection.y, fval*p_bloblet->vDirection.z, 1.0f);
gpd3dDev->SetVertexShaderConstant( 10, &val, 4 );
gpd3dDev->DrawIndexedPrimitive( D3DPT_TRIANGLESTRIP, 0, m_dwNumBlobletVertices, 0, m_dwNumBlobletIndices-2 );
}
// Restore the state
gpd3dDev->SetPixelShader( NULL );
gpd3dDev->SetVertexShader( NULL );
gpd3dDev->SetTexture(0, NULL);
gpd3dDev->SetTexture(1, NULL);
gpd3dDev->SetRenderState( D3DRS_ALPHABLENDENABLE, FALSE );
}
///////////////////////////////////////////////////////////////////////////////
void VBlob::zeroChangingVertices()
{
VBlobChangingVertex* p_verts;
const D3DVECTOR* p_us_normals = m_pUnitSphereNormals;
m_pBlobVBChangingU->Lock(0, 0, (BYTE**)&p_verts, 0); // D3DLOCK_DISCARD not on XBox!
for (int face=0; face<6; face++)
{
for (int i=0; i<m_NumVertsPerFace; i++, p_verts++, p_us_normals++)
{
p_verts->normal.x = p_us_normals->x;
p_verts->normal.y = p_us_normals->y;
p_verts->normal.z = p_us_normals->z;
p_verts->normal.w = 1.0f;
}
}
m_pBlobVBChangingU->Unlock();
}
///////////////////////////////////////////////////////////////////////////////
void VBlob::prepareChangingVertices()
{
VBlobChangingVertex* p_verts;
const D3DVECTOR* p_us_normals = m_pUnitSphereNormals;
m_pBlobVBChangingU->Lock(0, 0, (BYTE**)&p_verts, 0); // D3DLOCK_DISCARD not on XBox!
/*
// For spiky blobs mark 2.
const float cA = 1.0f;
const float cB = 0.1f;
const float cD = 1.0f / ( (1.0f/cB) - (1.0f / (cA+cB)) );
const float cE = cA / cD;
const float cF = cB / cD;
const float cG = - cD / (cA+cB);
float sd_frac = max(1.0f, gApp.getElapsedTime() / 6.0f);
float bd_frac = 1.0f - sd_frac;
*/
for (int face=0; face<6; face++)
{
const VBlobBump* vp_blobs_of_interest[MAX_BLOBBUMPS];
int i;
int num_boi = 0;
for (i=0; i<m_NumBlobBumps; i++)
{
if (m_BlobBumps[i].facesOfInterest & (1<<face))
{
vp_blobs_of_interest[num_boi++] = &m_BlobBumps[i];
}
}
for (int i=0; i<m_NumVertsPerFace; i++, p_verts++)
{
const D3DVECTOR& us_normal = *(p_us_normals++);
D3DVECTOR4& accum_normal = p_verts->normal;
//MTS D3DVECTOR4 accum_normal;
Set(&accum_normal, us_normal.x, us_normal.y, us_normal.z, 0.0f);
for (int j=num_boi-1; j>=0; j--)
{
const VBlobBump* p_blob = vp_blobs_of_interest[j];
D3DVECTOR delta;
Sub(us_normal, p_blob->vPosition, &delta);
//MTS float dist2 = Length2(delta);
float dist2 = delta.x*delta.x + delta.y*delta.y + delta.z*delta.z;
//MTS float dist2 = Distance2(us_normal, p_blob->vPosition);
if (dist2 < p_blob->fRadius2)
{
//MTS if ((p_blob->facesOfInterest & (1<<face)) == 0)
//MTS {
//MTS int a = 0;
//MTS }
float dist2_mo = dist2 * p_blob->fOORadius2 - 1.0f;
float displacement = m_fRadius*p_blob->fMagnitude * dist2_mo*dist2_mo;
/*
// Spiky blob bumps mark 2.
float bumpy_displacement = m_fRadius*p_blob->fMagnitude * dist2_mo*dist2_mo;
float spiky_displacement = m_fRadius * p_blob->fMagnitude * ( 1.0f / (cE*dist2*p_blob->fOORadius2 + cF) + cG );
spiky_displacement *= 1.3f;
float displacement = bd_frac*bumpy_displacement + sd_frac*spiky_displacement;
*/
float perturb_amount = -4.0f * p_blob->fMagnitude * p_blob->fOORadius2 * dist2_mo;
// lnorm = us_normal + perturb_amount*(us_normal - p_blob->vPosition)
// This should make it faster, but VTune suggests it doesn't. Maybe in Release...
//MTS D3DVECTOR lnorm;
//MTS float oppa = 1.0f + perturb_amount;
//MTS lnorm.x = us_normal.x * oppa - perturb_amount * p_blob->vPosition.x;
//MTS lnorm.y = us_normal.y * oppa - perturb_amount * p_blob->vPosition.y;
//MTS lnorm.z = us_normal.z * oppa - perturb_amount * p_blob->vPosition.z;
D3DVECTOR lnorm = us_normal;
//MTS Set(&lnorm, 0.0f, 0.0f, 0.0f);
//MTS D3DVECTOR delta;
//MTS Sub(us_normal, p_blob->vPosition, &delta);
AddScaled(&lnorm, delta, perturb_amount);
QuickNormalize(&lnorm);
accum_normal.x += lnorm.x;
accum_normal.y += lnorm.y;
accum_normal.z += lnorm.z;
accum_normal.w += displacement;
}
}
p_verts->normal = accum_normal;
}
}
m_pBlobVBChangingU->Unlock();
}
///////////////////////////////////////////////////////////////////////////////
void VBlob::advanceTime(float fElapsedTime, float fDt)
{
if (fElapsedTime < BLOB_STATIC_END_TIME)
{
if (fElapsedTime - fDt > BLOB_STATIC_END_TIME)
{
zeroChangingVertices();
swapChangingVertices();
zeroChangingVertices();
}
return;
}
for (int i=0; i<m_NumBloblets; i++)
{
if (!m_Bloblets[i].update(fElapsedTime, fDt))
{
//MTS m_Bloblets[i] = m_Bloblets[--m_NumBloblets];
//MTS i--;
// better notify the bump that owned it...
}
}
for (int i=0; i<m_NumBlobBumps; i++)
{
if (m_BlobBumps[i].update(
fElapsedTime,
fDt,
(m_NumBloblets<MAX_BLOBLETS) ? &m_Bloblets[m_NumBloblets] : NULL)
)
{
m_NumBloblets++;
}
}
//MTS m_pBlobVBChangingU->BlockUntilNotBusy(); // this causes it to block here, rather than in prepareChangingVertices
prepareChangingVertices();
}
///////////////////////////////////////////////////////////////////////////////
void VBlob::restart()
{
m_NumBloblets = 0;
m_NumBlobBumps = 0;
while (m_NumBlobBumps<MAX_BLOBBUMPS)
{
if (m_BlobBumps[m_NumBlobBumps++].create(
-0.3f,
(m_NumBloblets < MAX_BLOBLETS) ? &m_Bloblets[m_NumBloblets] : NULL
)) // pass negative time so the blobs get a head start
{
m_NumBloblets++;
}
}
zeroChangingVertices();
swapChangingVertices();
zeroChangingVertices();
}
///////////////////////////////////////////////////////////////////////////////
void VBlob::getLightForPosition(D3DVECTOR* p_light_pos, float* p_intensity, D3DVECTOR position)
{
float total_weights = 0.0f;
D3DVECTOR av_pos;
float av_intensity = 0.0f;
Set(&av_pos, 0.0f, 0.0f, 0.0f);
float light_intensity = gApp.getBlobIntensity();
{
float dist2 = Distance2(position, m_Pos);
float dist4 = dist2 * dist2;
float weight = 1.0f / dist2;
av_intensity += 4.0f * light_intensity * weight;
AddScaled(&av_pos, m_Pos, weight);
total_weights += weight;
}
for (int i=0; i<m_NumBloblets; i++)
{
float dist2 = Distance2(position, m_Bloblets[i].vPosition);
float dist4 = dist2 * dist2;
float weight = 1.0f / dist2;
av_intensity += light_intensity * weight;
AddScaled(&av_pos, m_Bloblets[i].vPosition, weight);
total_weights += weight;
}
float oo_total_weights = 1.0f / total_weights;
Set(p_light_pos, oo_total_weights * av_pos.x, oo_total_weights * av_pos.y, oo_total_weights * av_pos.z);
*p_intensity = oo_total_weights * av_intensity;
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
bool VBlob::generateUnitSphere( int resolution,
LPDIRECT3DVERTEXBUFFER8* pp_vb,
LPDIRECT3DINDEXBUFFER8* pp_ib,
D3DVECTOR** pp_us,
DWORD* num_verts, DWORD* num_indices)
{
int subdiv = max(1, resolution / 2);
float f_division_step = 2.0f / ((float)subdiv);
*num_verts = 6*(subdiv+1)*(subdiv+1);
*num_indices = 6*GetNumberOfIndicesForTristripMesh(subdiv,subdiv) + 2*5; // 5 double-taps
gpd3dDev->CreateVertexBuffer( *num_verts * sizeof(VBlobConstantVertex), 0, 0, 0, pp_vb);
gpd3dDev->CreateIndexBuffer( *num_indices * sizeof(WORD), D3DUSAGE_WRITEONLY, D3DFMT_INDEX16, D3DPOOL_DEFAULT, pp_ib);
if (pp_us) *pp_us = (D3DVECTOR*)MemAlloc(sizeof(D3DVECTOR)*(*num_verts));
VBlobConstantVertex* p_verts;
WORD* p_indices;
(*pp_vb)->Lock(0, 0, (BYTE**)&p_verts, 0);
(*pp_ib)->Lock(0, 0, (BYTE**)&p_indices, 0);
D3DVECTOR* p_us = ((pp_us) ? *pp_us : NULL);
// Create vertices.
VBlobConstantVertex* p_vert = &p_verts[0];
for (int k=0; k<6; k++)
{
for (int j=0; j<=subdiv; j++)
{
for (int i=0; i<=subdiv; i++)
{
D3DVECTOR& pos = p_vert->unit_sphere_normal;
float fu = ((i==subdiv) ? +1.0f : (-1.0f + f_division_step * ((float)i)));
float fv = ((j==subdiv) ? +1.0f : (-1.0f + f_division_step * ((float)j)));
switch(k)
{
case 0: Set(&pos, -1.0f, -fu, +fv); break;
case 1: Set(&pos, +fv, -1.0f, -fu); break;
case 2: Set(&pos, -fu, +fv, -1.0f); break;
case 3: Set(&pos, +1.0f, +fu, +fv); break;
case 4: Set(&pos, +fv, +1.0f, +fu); break;
case 5: Set(&pos, +fu, +fv, +1.0f); break;
}
Normalize(&pos);
if (p_us)
{
*p_us = pos;
p_us++;
}
p_vert++;
}
}
}
// Create indices.
int j = 0;
for (int i=0; i<6; i++)
{
j += CreateTristripForMesh(&p_indices[j], subdiv, subdiv, (i>0), (i<5), i*(subdiv+1)*(subdiv+1));
}
(*pp_ib)->Unlock();
(*pp_vb)->Unlock();
return true;
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
void VBlobBump::Init()
{
pMyBloblet = NULL;
}
///////////////////////////////////////////////////////////////////////////////
void VBlobBump::UnInit()
{
}
///////////////////////////////////////////////////////////////////////////////
// randomly creates a blob bump
bool VBlobBump::create(float cur_time, VBloblet* p_bloblet)
{
if (cur_time < 0.0f) pMyBloblet = NULL;
vDirection.x = VBlob::fRand11();
vDirection.y = VBlob::fRand11();
vDirection.z = VBlob::fRand11();
if (Length2(vDirection) < 0.001f)
{
vDirection.x = VBlob::fRand11();
vDirection.y = VBlob::fRand11();
vDirection.z = 1.0f;
}
Set(&vPosition, 0.0f, 0.0f, 0.0f);
QuickNormalize(&vDirection);
float time_prog = max(0.0f, (cur_time-BLOB_STATIC_END_TIME) * OO_MAX_INTENSITY_DELTA);
float rad_mag_rand = VBlob::fRand01();
fRadius = rad_mag_rand * 0.4f + 0.4f;
fRadius2 = fRadius*fRadius;
fOORadius2 = 1.0f / fRadius2;
fMagnitude = 0.0f;
recalculateFacesOfInterest();
fStartTime = cur_time + 0.4f * VBlob::fRand01();
fMaxMagnitude = (1.0f - rad_mag_rand) * 0.5f + 0.2f;
fMaxMagnitude *= 0.5f + 0.5f * time_prog;
if (!pMyBloblet) pMyBloblet = p_bloblet;
if (pMyBloblet)
{
// Paired with a bloblet.
float f_main_rad = gpVBlob->getRadius();
pMyBloblet->fRadius = (VBlob::fRand01()+1.0f) * 0.25f * f_main_rad * fRadius;
pMyBloblet->vDirection = vDirection;
pMyBloblet->fMaxDist = f_main_rad * (5.0f + VBlob::fRand11() * 2.0f);
pMyBloblet->fMaxDist *= 0.6f;
pMyBloblet->fStartTime = (cur_time<-1.0f) ? -VBlob::fRand01() * 0.3f : cur_time;
float period = 0.8f + 0.3f * VBlob::fRand01();
period *= 1.0f / 0.6f;
pMyBloblet->fTimeMultiple = 2.0f * Pi / period;
pMyBloblet->fWobble = 1.2f;
pMyBloblet->fWobbleDirection = 0.0f;
bStillAttachedToBloblet = (cur_time-fStartTime < 0.4f*period);
// Set my properties from the bloblet.
pMyBloblet->update(cur_time, 0.0f);
update(cur_time, 0.0f, NULL);
}
else
{
// Not paired with a bloblet.
float sequence_len = fMaxMagnitude * 0.3f + VBlob::fRand01() * 0.3f;
fTimeMul = Pi / sequence_len;
fTimeMul *= time_prog*0.2f + 0.8f;
if (cur_time < -1.0f) fStartTime = -VBlob::fRand01() * Pi / fTimeMul;
}
return (pMyBloblet!=NULL);
}
///////////////////////////////////////////////////////////////////////////////
// returns true if it initialized a new Bloblet
bool VBlobBump::update(float elapsed_time, float dt, VBloblet* p_bloblet)
{
if (pMyBloblet)
{
float f_b_mag = (fast_fabs(pMyBloblet->fCurDist) + pMyBloblet->fRadius) / gpVBlob->getRadius();
fMagnitude = min( 2.0f, max(0.0f, f_b_mag - 1.0f) );
if (bStillAttachedToBloblet)
{
if (fMagnitude > 0.8f)
{
// Lost attachment
bStillAttachedToBloblet = false;
//MTS fMaxMagnitude = max(0.0f, fMagnitude - 2.0f * pMyBloblet->fRadius / gpVBlob->getRadius());
fMaxMagnitude = fMagnitude;
float sequence_len = 0.3f * fMagnitude;
fTimeMul = 2.0f * Pi / sequence_len;
fStartTime = elapsed_time - 0.25f * sequence_len;
pMyBloblet->fWobble = max(0.6f, min(0.8f, fMagnitude-0.5f)); // more wobble when there is less energy
pMyBloblet->fWobbleDirection = 0.0f;
}
else
{
// Still attached.
if ( (Dot(vDirection, pMyBloblet->vDirection) < 0.0f) != pMyBloblet->bFarSide)
{
// Must reverse direction.
Scale(&vDirection, -1.0f);
vPosition = vDirection;
recalculateFacesOfInterest();
}
return false;
}
}
// May have just lost attachment.
if (!bStillAttachedToBloblet)
{
if (f_b_mag < 0.9f)
{
// Blob is inside sphere, reattach.
bStillAttachedToBloblet = true;
}
}
}
float t = (elapsed_time - fStartTime) * fTimeMul;
if (t>Pi)
{
if (pMyBloblet==NULL)
{
return create(elapsed_time, p_bloblet);
}
fMagnitude = 0.0f;
return false;
}
if (t<0.0f) return false;
float sin_val = fast_sin(t);
fMagnitude = fMaxMagnitude * sin_val;
//MTS vPosition.x = vDirection.x * sin_val;
//MTS vPosition.y = vDirection.y * sin_val;
//MTS vPosition.z = vDirection.z * sin_val;
vPosition = vDirection;
recalculateFacesOfInterest();
//MTS if ((bNotYetSpawned) && (t > 0.5f * Pi) && (p_bloblet))
//MTS {
//MTS bNotYetSpawned = false;
//MTS
//MTS float f_main_rad = gpVBlob->getRadius();
//MTS p_bloblet->fRadius = 0.7f * f_main_rad * fRadius;
//MTS
//MTS float displ = f_main_rad * (1.0f + fMagnitude) - p_bloblet->fRadius;
//MTS p_bloblet->vPosition = gpVBlob->getCenter();
//MTS p_bloblet->vPosition.x += vDirection.x * displ;
//MTS p_bloblet->vPosition.y += vDirection.y * displ;
//MTS p_bloblet->vPosition.z += vDirection.z * displ;
//MTS
//MTS
//MTS
//MTS
//MTS
//MTS float vel = f_main_rad * (fMagnitude-MIN_SPAWN_MAGN) * 100.0f + 20.0f;
//MTS p_bloblet->vVelocity.x = vDirection.x * vel;
//MTS p_bloblet->vVelocity.y = vDirection.y * vel;
//MTS p_bloblet->vVelocity.z = vDirection.z * vel;
//MTS
//MTS p_bloblet->vAttachedTo = gpVBlob->getCenter();
//MTS
//MTS
//MTS // Set up a spring as per 2001.05.01 of my notes.
//MTS
//MTS // Use a spring model for now. Period is 2*Pi/sqrt(k/m), so k/m = (2*Pi/period)^2
//MTS float sequence_time_mul = 5.0f;
//MTS p_bloblet->fRestoreForceConstant = 2.0f * Pi / (sequence_time_mul*(elapsed_time - fStartTime));
//MTS p_bloblet->fRestoreForceConstant *= p_bloblet->fRestoreForceConstant;
//MTS
//MTS p_bloblet->fEndDist2 = f_main_rad - p_bloblet->fRadius;
//MTS p_bloblet->fEndDist2 *= p_bloblet->fEndDist2;
//MTS
//MTS return true;
//MTS }
return false;
}
///////////////////////////////////////////////////////////////////////////////
void VBlobBump::recalculateFacesOfInterest()
{
// Bitfield indicating the faces (0,1,2,4,5) <-> (-x,-y,-z,+x,+y,+z) the blob points most be checked with.
facesOfInterest = ((vDirection.x - fRadius < -0.57735f) ? 0x0001 : 0) +
((vDirection.y - fRadius < -0.57735f) ? 0x0002 : 0) +
((vDirection.z - fRadius < -0.57735f) ? 0x0004 : 0) +
((vDirection.x + fRadius > +0.57735f) ? 0x0008 : 0) +
((vDirection.y + fRadius > +0.57735f) ? 0x0010 : 0) +
((vDirection.z + fRadius > +0.57735f) ? 0x0020 : 0) ;
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
const float WOBBLE_ACCEL = (1000.0f);
// returns false if it should be deleted
bool VBloblet::update(float elapsed_time, float dt)
{
fWobble = min(2.0f, max(0.5f, fWobble + fWobbleDirection * dt));
if (fWobbleDirection > 0.0f)
{
if ((fWobble < 0.95f) || (fWobble > 1.0f))
{
fWobbleDirection -= (fWobble-1.0f) * dt * WOBBLE_ACCEL;
}
}
else
{
if ((fWobble < 1.0f) || (fWobble > 1.05f))
{
fWobbleDirection -= (fWobble-1.0f) * dt * WOBBLE_ACCEL;
}
}
float time_prog = max(0.0f, (elapsed_time-BLOB_STATIC_END_TIME) * OO_MAX_INTENSITY_DELTA);
float t = fTimeMultiple * (elapsed_time - fStartTime);
t *= 1.4f * (1.0f + elapsed_time / 10.0f); // speed up over time
float s = fast_sin(t);
float sm = fast_fabs(s);
sm = 1.0f - (1.0f-sm)*fast_sqrt(1.0f-sm);
s = (s>0.0f) ? sm : -sm;
fCurDist = fMaxDist * s * time_prog;
bFarSide = (fCurDist<0.0f);
vPosition = gpVBlob->getCenter();
AddScaled(&vPosition, vDirection, fCurDist);
return (fast_fabs(fCurDist) + fRadius < gpVBlob->getRadius() * 0.5f);
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////