Files
Leviathan/Client/Game/engine/Renderer/KPrimitiveSpeedGrass.cpp
T
2026-06-01 12:46:52 +02:00

534 lines
14 KiB
C++

#include "stdafx.h"
#include "KPrimitiveSpeedGrass.h"
#include "GrassColonyInfo.h"
#include <SpeedGrass/Random.h>
#include <SpeedGrass/SpeedGrassConstants.h>
#include <SpeedGrass/VertexShaders.h>
#include "KResourceManager.h"
#include "KResourceDX.h"
#include "TerrainSpeedGrass.h"
// #include "TerrainMapEngine.h"
//========================================================
// 풀 효과용 로컬 함수들
//========================================================
#define MIN2(a, b) ((a<b)?(a):(b))
#define MAX2(a, b) ((a>b)?(a):(b))
#define MIN3(a, b, c) (MIN2(MIN2(a,b), c))
#define MAX3(a, b, c) (MAX2(MAX2(a,b), c))
// normally-distributed random
double gaussianRandom() // Normal distribution with 0-mean, 1.0 variation, based on Box-Muller Transformation
// http://www.taygeta.com/random/gaussian.html
{
static int flag=0;
static double y1, y2; // Store calculated values
if (flag==0) {
// Get two new random numbers
double x1, x2;
double w;
do {
x1=GetRandom(-1.0f, 1.0f);
x2=GetRandom(-1.0f, 1.0f);
w=x1*x1+x2*x2;
} while (w>=1.0);
w=sqrt((-2.0*log(w))/w);
y1=x1*w; // Box-Muller transformation generates 2 values from 2 parameters
y2=x2*w;
flag=1;
return y1;
}
// use previously calculated number
flag=0;
return y2;
}
//
// RGB-HSV color conversion. (풀색깔 변화용)
// http://www.cs.rit.edu/~ncs/color/t_convert.html
//
// r,g,b values are from 0 to 1
// h = [0,360], s = [0,1], v = [0,1]
// if s == 0, then h = -1 (undefined)
void RGB2HSV( float r, float g, float b, float *h, float *s, float *v )
{
float min, max, delta;
min = MIN3( r, g, b );
max = MAX3( r, g, b );
*v = max; // v
delta = max - min;
if( max != 0 )
*s = delta / max; // s
else {
// r = g = b = 0 // s = 0, v is undefined
*s = 0;
*h = -1;
return;
}
if( r == max )
*h = ( g - b ) / delta; // between yellow & magenta
else if( g == max )
*h = 2 + ( b - r ) / delta; // between cyan & yellow
else
*h = 4 + ( r - g ) / delta; // between magenta & cyan
*h *= 60; // degrees
if( *h < 0 )
*h += 360;
}
void HSV2RGB( float h, float s, float v, float *r, float *g, float *b )
{
int i;
float f, p, q, t;
if( s == 0 ) {
// achromatic (grey)
*r = *g = *b = v;
return;
}
h /= 60; // sector 0 to 5
i = (int) floor( h );
f = h - i; // factorial part of h
p = v * ( 1 - s );
q = v * ( 1 - s * f );
t = v * ( 1 - s * ( 1 - f ) );
switch( i ) {
case 0:
*r = v;
*g = t;
*b = p;
break;
case 1:
*r = q;
*g = v;
*b = p;
break;
case 2:
*r = p;
*g = v;
*b = t;
break;
case 3:
*r = p;
*g = q;
*b = v;
break;
case 4:
*r = t;
*g = p;
*b = v;
break;
default: // case 5:
*r = v;
*g = p;
*b = q;
break;
}
}
#define LIMITBETWEENZEROANDONE(x) { if (x<0.) x=0.; else if (x>1.) x=1.; }
#define K3DVECTOR_LIMITBETWEENZEROANDONE(v) { LIMITBETWEENZEROANDONE(v.x); LIMITBETWEENZEROANDONE(v.y); LIMITBETWEENZEROANDONE(v.z); }
void RandomizeGrassColor(float r, float g, float b
, float colorVariation, float saturationVariation, float intensityVariation
, float *newr, float *newg, float *newb)
{
// Convert to HSV
float h, s, v;
RGB2HSV(r, g, b, &h, &s, &v);
//
h+=(float)gaussianRandom()*colorVariation;
s+=(float)gaussianRandom()*saturationVariation;
v+=(float)gaussianRandom()*intensityVariation;
if (h<0.f) h+=360.f; if (h>=360.f) h-=360.f;
LIMITBETWEENZEROANDONE(s);
LIMITBETWEENZEROANDONE(v);
// Return to RGB
HSV2RGB(h, s, v, newr, newg, newb);
}
void RandomizeGrassColor_Default(float r, float g, float b // test용. 각 var값을 scene에 따라 잘 조절할 것
, float *newr, float *newg, float *newb)
{
static float COLORV=8.f;
static float SATURATIONV=0.08f;
static float INTENSITYV=0.15f;
RandomizeGrassColor(r, g, b, COLORV, SATURATIONV, INTENSITYV, newr, newg, newb);
}
//---------------------------------------------------
// SpeedGrass Main
//---------------------------------------------------
//이게 무슨 일일까?
KPrimitiveSpeedGrass::KPrimitiveSpeedGrass() : m_fRegionSize( 100.0f ),
m_pTexture( NULL )
{
}
KPrimitiveSpeedGrass::~KPrimitiveSpeedGrass()
{
Clear();
}
void KPrimitiveSpeedGrass::Clear()
{
}
bool KPrimitiveSpeedGrass::Load( K3DRenderDevice* pDev, CTerrainSpeedGrass* pSpeedGrass, GrassColonyInfo* pGrassColonyInfo, CTerrainMapEngine *pTerrainMapEngine )
{
//_oprint("KPrimitiveSpeedGrass::Load()\n");
bool bSuccess = true;
Clear();
// initialize bounding box
m_afBoundingBox[0] = m_afBoundingBox[1] = m_afBoundingBox[2] = FLT_MAX;
m_afBoundingBox[3] = m_afBoundingBox[4] = m_afBoundingBox[5] = -FLT_MAX;
// read file
string strError;
std::vector<SBlade> vSceneBlades;
// how many nodes are stored in file? - 그런 거 업다
// length of this node's name - 그딴 거 엄따
// node name - 글니까 업다구
// number of instances within this node - 실제 풀 개수
int nNumInstances = pSpeedGrass->GetSceneBladesSize();
K3DVector vPosition, vNormal;
// 버텍스 컬러
K3DVector basecolor, topcolor, bottomcolor, tempcolor;
K3DVector referenceColor;
KTripleColor terrainColor;
// read all instances
for (int nInstance = 0; nInstance < nNumInstances && bSuccess; ++nInstance)
{
SBlade sBlade;
// read blade position : 툴에서 준다
vPosition = pSpeedGrass->GetPosition( nInstance );
sBlade.m_afPos[ 0 ] = vPosition.x;
sBlade.m_afPos[ 1 ] = vPosition.y;
sBlade.m_afPos[ 2 ] = vPosition.z;
// check against overall scene bounding box
for (int nAxis = 0; nAxis < 3; ++nAxis)
{
m_afBoundingBox[nAxis] = min(m_afBoundingBox[nAxis], sBlade.m_afPos[nAxis]);
m_afBoundingBox[nAxis+3] = max(m_afBoundingBox[nAxis+3], sBlade.m_afPos[nAxis]);
}
/* if( pInfo->vNormal.empty() )
{
sBlade.m_afNormal[ 0 ] = 0.f;
sBlade.m_afNormal[ 1 ] = 0.f;
sBlade.m_afNormal[ 2 ] = 1.f;
}
else if( (int)pInfo->vNormal.size() > nInstance )*/
{
// read blade normal : 노말도 툴에서 계산해 준다
vNormal = pSpeedGrass->GetNormal( nInstance );
sBlade.m_afNormal[ 0 ] = vNormal.x;
sBlade.m_afNormal[ 1 ] = vNormal.y;
sBlade.m_afNormal[ 2 ] = vNormal.z;
}
// const float GROUNDCOLORMATCH=0.6f; // 바닥 색깔을 반영하는 비율
// ->fColorRatio로 변경
/*!!SKIPPED if (pTerrainMapEngine->GetTerrainColor(terX, terY, terrainColor)) { // 바닥 색(diffuse color) 읽어오기
referenceColor.x = pInfo->Color.r/255.0f * (pInfo->fColorRatio *terrainColor.r/255.0f + (1-GROUNDCOLORMATCH));
referenceColor.y = pInfo->Color.g/255.0f * (pInfo->fColorRatio *terrainColor.g/255.0f + (1-GROUNDCOLORMATCH));
referenceColor.z = pInfo->Color.b/255.0f * (pInfo->fColorRatio *terrainColor.b/255.0f + (1-GROUNDCOLORMATCH));
} else */ {
// 색을 100% 반영
referenceColor.x=pGrassColonyInfo->Color.r/255.0f;
referenceColor.y=pGrassColonyInfo->Color.g/255.0f;
referenceColor.z=pGrassColonyInfo->Color.b/255.0f;
}
// 색 랜덤하게 재배치
RandomizeGrassColor(referenceColor.x, referenceColor.y, referenceColor.z
, pGrassColonyInfo->fColorTone, pGrassColonyInfo->fChroma, pGrassColonyInfo->fBrightness // , float colorVariation, float saturationVariation, float intensityVariation
, &(basecolor.x), &(basecolor.y), &(basecolor.z)
);
// 꼭대기 색
tempcolor.x=basecolor.x*0.1f;
tempcolor.y=basecolor.y*0.1f;
tempcolor.z=basecolor.z*0.1f;
topcolor=basecolor+tempcolor; // lighter
// 밑둥 색
tempcolor.x=basecolor.x*0.2f;
tempcolor.y=basecolor.y*0.2f;
tempcolor.z=basecolor.z*0.2f;
bottomcolor=basecolor-tempcolor; // darker
K3DVECTOR_LIMITBETWEENZEROANDONE(topcolor);
K3DVECTOR_LIMITBETWEENZEROANDONE(bottomcolor);
sBlade.m_afBottomColor[0] = bottomcolor.x;
sBlade.m_afBottomColor[1] = bottomcolor.y;
sBlade.m_afBottomColor[2] = bottomcolor.z;
sBlade.m_afTopColor[0] = topcolor.x;
sBlade.m_afTopColor[1] = topcolor.y;
sBlade.m_afTopColor[2] = topcolor.z;
//
// 텍스처를 랜덤하게 재배치
// Note : 나중에 식물의 식생 분포에 따라 확률적으로 바뀌도록????
//
float fRand = GetRandom(0.0f, 1.0f);
if (fRand > 0.99f)
sBlade.m_ucWhichTexture = 2;
else
sBlade.m_ucWhichTexture = GetRandom(0, 1);
// compute wind effects
sBlade.m_fNoise = GetRandom(c_fMinBladeNoise, c_fMaxBladeNoise);
sBlade.m_fThrow = GetRandom(c_fMinBladeThrow, c_fMaxBladeThrow);
// compute dimensions
// 잎 크기
sBlade.m_fSize = (float) (pGrassColonyInfo->fSize * ((1.0f-pGrassColonyInfo->fHeightM) + (pGrassColonyInfo->fHeightP+pGrassColonyInfo->fHeightM) * gaussianRandom())); // 0.4~1.2 범위에서 사이즈 변화
// store all blades together
vSceneBlades.push_back(sBlade);
}
if (bSuccess)
{
float fCX = m_afBoundingBox[ 3 ] - m_afBoundingBox[ 0 ];
float fCY = m_afBoundingBox[ 4 ] - m_afBoundingBox[ 1 ];
// copy region settings
m_nNumRegionCols = ( int ) ( fCX / m_fRegionSize ) + 1;
m_nNumRegionRows = ( int ) ( fCY / m_fRegionSize ) + 1;
CreateRegions(vSceneBlades);
CreateVertexBuffer( pDev );
// LoadTexture( pGrassColonyInfo->strTexFileName.c_str() );
if( pGrassColonyInfo->m_spTexture )
m_pTexture = pGrassColonyInfo->m_spTexture;
}
else
{
assert( 0 );
}
return bSuccess;
}
void KPrimitiveSpeedGrass::Render( KViewportObject *viewport, class K3DRenderDevice *dev, bool bUseAccum )
{
int nTriangleCount = 0;
Cull();
dev->SetTexture( 0, m_pTexture );
int nRegionsDrawn = 0;
for (int nRegion = 0, nVB = 0; nRegion < m_nNumRegions; ++nRegion)
//size_t numVBs = m_vVB.size();
//for( size_t i = 0; i < numVBs; i++ )
{
SRegion* pRegion = m_pRegions + nRegion;
const unsigned int uiBladeCount = (unsigned int)pRegion->m_vBlades.size( );
if( uiBladeCount > 0 )
{
if( !pRegion->m_bCulled )
{
//m_pDx->SetStreamSource(0, pRegion->m_pVertexBuffer, 0, sizeof(SFVFGrassVertex));
//m_pDx->DrawPrimitive(D3DPT_TRIANGLELIST, 0, uiBladeCount * 2);
//dev->SetTexture( 0, m_vTex.at( nRegion ) );
//dev->DrawTriangleVB( m_vVB.at( nRegion ) );
dev->DrawTriangleVB( m_vVB.at( nVB ) );
nTriangleCount += uiBladeCount * 2;
nRegionsDrawn++;
}
nVB++;
}
}
//return nTriangleCount;
}
void KPrimitiveSpeedGrass::CreateVertexBuffer( K3DRenderDevice* pDev )
{
// set up constants
const short anVertexIndices[6] = { 0, 1, 2, 0, 2, 3 };
// prepare static vertex buffers
for (int nRegion = 0; nRegion < m_nNumRegions; ++nRegion)
{
SRegion* pRegion = m_pRegions + nRegion;
const unsigned int uiBladeCount = (unsigned int)pRegion->m_vBlades.size( );
if (uiBladeCount > 0)
{
//m_pDx->CreateVertexBuffer(6 * uiBladeCount * sizeof(SFVFGrassVertex), D3DUSAGE_WRITEONLY, D3DFVF_SPEEDGRASS_VERTEX, D3DPOOL_MANAGED, &pRegion->m_pVertexBuffer, NULL);
K3DVertexBuffer* pVB = pDev->CreateVertexBufferSpeedGrass( sizeof(SFVFGrassVertex), D3DUSAGE_WRITEONLY, D3DFVF_SPEEDGRASS_VERTEX, uiBladeCount * 6 );
if( pVB == NULL || !pVB->IsValidVtx() ) continue;
SFVFGrassVertex* pBuffer = NULL;
int size;
//pRegion->m_pVertexBuffer->Lock(0, 0, reinterpret_cast<void**>(&pBuffer), 0);
//m_vVB.at( nRegion )->Lock( ( void** ) &pBuffer, size );
pVB->Lock( ( void** ) &pBuffer, size );
if( !pBuffer )
{
assert( 0 );
return;
}
SFVFGrassVertex* pVertex = pBuffer;
unsigned int nBlade;
for (nBlade = 0; nBlade < uiBladeCount; ++nBlade)
{
SBlade* pBlade = &pRegion->m_vBlades[nBlade];
for (int nCorner = 0; nCorner < 6; ++nCorner)
{
// position (all corners have the same point, to be offset
// in the vertex shader)
memcpy(pVertex->m_vPosition, pBlade->m_afPos, sizeof(pVertex->m_vPosition));
// normal
memcpy(pVertex->m_vNormal, pBlade->m_afNormal, sizeof(pVertex->m_vNormal));
// color
unsigned long ulColor = 0;
switch (anVertexIndices[nCorner])
{
case 0: case 1:
ulColor = (int(pBlade->m_afTopColor[0] * 255.0f) << 16) +
(int(pBlade->m_afTopColor[1] * 255.0f) << 8) +
(int(pBlade->m_afTopColor[2] * 255.0f) << 0) +
0xff000000;
break;
case 2: case 3:
ulColor = (int(pBlade->m_afBottomColor[0] * 255.0f) << 16) +
(int(pBlade->m_afBottomColor[1] * 255.0f) << 8) +
(int(pBlade->m_afBottomColor[2] * 255.0f) << 0) +
0xff000000;
break;
}
pVertex->m_dwDiffuseColor = ulColor;
// texcoord0
const float c_fClamp = 2.0f / 256.0f;
float fS1 = float(pBlade->m_ucWhichTexture) / c_nNumBladeMaps + c_fClamp;
float fS2 = float(pBlade->m_ucWhichTexture + 1) / c_nNumBladeMaps - c_fClamp;
switch (anVertexIndices[nCorner])
{
case 0:
pVertex->m_fTexCoords[0] = fS2;
pVertex->m_fTexCoords[1] = 0.0f + c_fClamp;
break;
case 1:
pVertex->m_fTexCoords[0] = fS1;
pVertex->m_fTexCoords[1] = 0.0f + c_fClamp;
break;
case 2:
pVertex->m_fTexCoords[0] = fS1;
pVertex->m_fTexCoords[1] = 1.0f - c_fClamp;
break;
case 3:
pVertex->m_fTexCoords[0] = fS2;
pVertex->m_fTexCoords[1] = 1.0f - c_fClamp;
break;
}
// texcoord1
pVertex->m_fVertexIndex = anVertexIndices[nCorner];
pVertex->m_fBladeSize = pBlade->m_fSize;
// texcoord2
switch (anVertexIndices[nCorner])
{
case 0: case 1:
pVertex->m_fWindWeight = pBlade->m_fThrow;
break;
case 2: case 3:
pVertex->m_fWindWeight = 0.0f;
break;
}
pVertex->m_fNoiseFactor = pBlade->m_fNoise;
++pVertex;
}
}
assert(pVertex - pBuffer == 6 * uiBladeCount);
//pRegion->m_pVertexBuffer->Unlock( );
//m_vVB.at( nRegion )->Unlock();
pVB->Unlock();
pVB->Backup();
m_vVB.push_back( K3DVertexBufferSPtr(pVB) );
}
}
}
void KPrimitiveSpeedGrass::LoadTexture( const char* szTextureFileName )
{
/* NX3LoadPack loadpack;
loadpack.Init();
if( KTextureManager::GetManager()->IsExistTexture( szTextureFileName, &loadpack ) )
{
m_spTex = KTextureManager::GetManager()->GetTexture( szTextureFileName, &loadpack, true, KTextureManager::GetManager()->GetMipMapBiasLevel() );
}
else
{
_oprint( "Resource Error : Speed Grass Texture Not Found - %s\n", szTextureFileName );
}*/
}