#include "stdafx.h" #include "KPrimitiveSpeedGrass.h" #include "GrassColonyInfo.h" #include #include #include #include "KResourceManager.h" #include "KResourceDX.h" #include "TerrainSpeedGrass.h" // #include "TerrainMapEngine.h" //======================================================== // 풀 효과용 로컬 함수들 //======================================================== #define MIN2(a, b) ((ab)?(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 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(&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 ); }*/ }