1
/*-------------------------------------------------------------------------------------
2
Copyright (c) 2006 John Judnich
3
Modified 2008 by Erik Hjortsberg (erik.hjortsberg@gmail.com)
5
This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software.
6
Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
7
1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
8
2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
9
3. This notice may not be removed or altered from any source distribution.
10
-------------------------------------------------------------------------------------*/
12
#include "GrassLoader.h"
13
#include "PagedGeometry.h"
14
#include "PropertyMaps.h"
17
#include <OgreTimer.h>
18
#include <OgreCamera.h>
19
#include <OgreVector3.h>
20
#include <OgreQuaternion.h>
21
#include <OgreEntity.h>
22
#include <OgreString.h>
23
#include <OgreStringConverter.h>
24
#include <OgreMaterialManager.h>
25
#include <OgreMaterial.h>
26
#include <OgreHardwareBufferManager.h>
27
#include <OgreHardwareBuffer.h>
28
#include <OgreMeshManager.h>
30
#include <OgreSubMesh.h>
31
#include <OgreLogManager.h>
32
#include <OgreTextureManager.h>
33
#include <OgreHardwarePixelBuffer.h>
34
#include <OgreRenderSystem.h>
35
#include <OgreRenderSystemCapabilities.h>
36
#include <OgreHighLevelGpuProgram.h>
37
#include <OgreHighLevelGpuProgramManager.h>
40
namespace PagedGeometry {
42
GrassLayer::GrassLayer(PagedGeometry *geom, GrassLoader<GrassLayer> *ldr)
44
GrassLayer::geom = geom;
45
GrassLayer::parent = ldr;
48
minWidth = 1.0f; maxWidth = 1.0f;
49
minHeight = 1.0f; maxHeight = 1.0f;
51
renderTechnique = GRASSTECH_QUAD;
52
fadeTechnique = FADETECH_ALPHA;
59
shaderNeedsUpdate = true;
62
densityMapFilter = MAPFILTER_BILINEAR;
64
colorMapFilter = MAPFILTER_BILINEAR;
67
GrassLayer::~GrassLayer()
75
unsigned int GrassLayer::calculateMaxGrassCount(float densityFactor, float volume)
77
return density * densityFactor * volume;
80
unsigned int GrassLayer::_populateGrassList(PageInfo page, float *posBuff, unsigned int grassCount)
83
if (densityMap->getFilter() == MAPFILTER_NONE)
84
return _populateGrassList_UnfilteredDM(page, posBuff, grassCount);
85
else if (densityMap->getFilter() == MAPFILTER_BILINEAR)
86
return _populateGrassList_BilinearDM(page, posBuff, grassCount);
88
return _populateGrassList_Uniform(page, posBuff, grassCount);
92
void GrassLayerBase::setMaterialName(const String &matName)
94
if (material.isNull() || matName != material->getName()){
95
material = MaterialManager::getSingleton().getByName(matName);
96
if (material.isNull())
97
OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "The specified grass material does not exist", "GrassLayer::setMaterialName()");
98
shaderNeedsUpdate = true;
102
void GrassLayerBase::setMinimumSize(float width, float height)
108
void GrassLayerBase::setMaximumSize(float width, float height)
111
if (maxHeight != height){
113
shaderNeedsUpdate = true;
117
void GrassLayerBase::setRenderTechnique(GrassTechnique style, bool blendBase)
119
if (blend != blendBase || renderTechnique != style){
121
renderTechnique = style;
122
shaderNeedsUpdate = true;
126
void GrassLayerBase::setFadeTechnique(FadeTechnique style)
128
if (fadeTechnique != style){
129
fadeTechnique = style;
130
shaderNeedsUpdate = true;
134
void GrassLayerBase::setAnimationEnabled(bool enabled)
136
if (animate != enabled){
138
shaderNeedsUpdate = true;
142
void GrassLayer::setDensityMap(const String &mapFile, MapChannel channel)
145
densityMap->unload();
149
densityMap = DensityMap::load(mapFile, channel);
150
densityMap->setMapBounds(mapBounds);
151
densityMap->setFilter(densityMapFilter);
154
void GrassLayer::setDensityMap(Texture *map, MapChannel channel)
157
densityMap->unload();
161
densityMap = DensityMap::load(map, channel);
162
densityMap->setMapBounds(mapBounds);
163
densityMap->setFilter(densityMapFilter);
167
void GrassLayer::setDensityMapFilter(MapFilter filter)
169
densityMapFilter = filter;
171
densityMap->setFilter(densityMapFilter);
174
unsigned int GrassLayer::_populateGrassList_Uniform(PageInfo page, float *posBuff, unsigned int grassCount)
176
float *posPtr = posBuff;
181
for (unsigned int i = 0; i < grassCount; ++i){
182
//Pick a random position
183
float x = Math::RangeRandom(page.bounds.left, page.bounds.right);
184
float z = Math::RangeRandom(page.bounds.top, page.bounds.bottom);
186
//Add to list in within bounds
190
} else if (x >= mapBounds.left && x <= mapBounds.right && z >= mapBounds.top && z <= mapBounds.bottom){
198
if (minY) min = minY; else min = Math::NEG_INFINITY;
199
if (maxY) max = maxY; else max = Math::POS_INFINITY;
201
for (unsigned int i = 0; i < grassCount; ++i){
202
//Pick a random position
203
float x = Math::RangeRandom(page.bounds.left, page.bounds.right);
204
float z = Math::RangeRandom(page.bounds.top, page.bounds.bottom);
207
float y = parent->heightFunction(x, z, parent->heightFunctionUserData);
209
//Add to list if in range
210
if (y >= min && y <= max){
211
//Add to list in within bounds
215
} else if (x >= mapBounds.left && x <= mapBounds.right && z >= mapBounds.top && z <= mapBounds.bottom){
223
grassCount = (posPtr - posBuff) / 2;
227
unsigned int GrassLayer::_populateGrassList_UnfilteredDM(PageInfo page, float *posBuff, unsigned int grassCount)
229
float *posPtr = posBuff;
234
for (unsigned int i = 0; i < grassCount; ++i){
235
//Pick a random position
236
float x = Math::RangeRandom(page.bounds.left, page.bounds.right);
237
float z = Math::RangeRandom(page.bounds.top, page.bounds.bottom);
239
//Determine whether this grass will be added based on the local density.
240
//For example, if localDensity is .32, grasses will be added 32% of the time.
241
if (Math::UnitRandom() < densityMap->_getDensityAt_Unfiltered(x, z)){
250
if (minY) min = minY; else min = Math::NEG_INFINITY;
251
if (maxY) max = maxY; else max = Math::POS_INFINITY;
253
for (unsigned int i = 0; i < grassCount; ++i){
254
//Pick a random position
255
float x = Math::RangeRandom(page.bounds.left, page.bounds.right);
256
float z = Math::RangeRandom(page.bounds.top, page.bounds.bottom);
258
//Determine whether this grass will be added based on the local density.
259
//For example, if localDensity is .32, grasses will be added 32% of the time.
260
if (Math::UnitRandom() < densityMap->_getDensityAt_Unfiltered(x, z)){
262
float y = parent->heightFunction(x, z, parent->heightFunctionUserData);
264
//Add to list if in range
265
if (y >= min && y <= max){
274
grassCount = (posPtr - posBuff) / 2;
278
unsigned int GrassLayer::_populateGrassList_BilinearDM(PageInfo page, float *posBuff, unsigned int grassCount)
280
float *posPtr = posBuff;
284
for (unsigned int i = 0; i < grassCount; ++i){
285
//Pick a random position
286
float x = Math::RangeRandom(page.bounds.left, page.bounds.right);
287
float z = Math::RangeRandom(page.bounds.top, page.bounds.bottom);
289
//Determine whether this grass will be added based on the local density.
290
//For example, if localDensity is .32, grasses will be added 32% of the time.
291
if (Math::UnitRandom() < densityMap->_getDensityAt_Bilinear(x, z)){
300
if (minY) min = minY; else min = Math::NEG_INFINITY;
301
if (maxY) max = maxY; else max = Math::POS_INFINITY;
303
for (unsigned int i = 0; i < grassCount; ++i){
304
//Pick a random position
305
float x = Math::RangeRandom(page.bounds.left, page.bounds.right);
306
float z = Math::RangeRandom(page.bounds.top, page.bounds.bottom);
308
//Determine whether this grass will be added based on the local density.
309
//For example, if localDensity is .32, grasses will be added 32% of the time.
310
if (Math::UnitRandom() < densityMap->_getDensityAt_Bilinear(x, z)){
312
float y = parent->heightFunction(x, z, parent->heightFunctionUserData);
314
//Add to list if in range
315
if (y >= min && y <= max){
324
grassCount = (posPtr - posBuff) / 2;
328
void GrassLayer::setColorMap(const String &mapFile, MapChannel channel)
335
colorMap = ColorMap::load(mapFile, channel);
336
colorMap->setMapBounds(mapBounds);
337
colorMap->setFilter(colorMapFilter);
341
void GrassLayer::setColorMap(Texture *map, MapChannel channel)
348
colorMap = ColorMap::load(map, channel);
349
colorMap->setMapBounds(mapBounds);
350
colorMap->setFilter(colorMapFilter);
354
void GrassLayer::setColorMapFilter(MapFilter filter)
356
colorMapFilter = filter;
358
colorMap->setFilter(colorMapFilter);
361
void GrassLayerBase::_updateShaders()
363
if (shaderNeedsUpdate){
364
shaderNeedsUpdate = false;
366
//Proceed only if there is no custom vertex shader and the user's computer supports vertex shaders
367
const RenderSystemCapabilities *caps = Root::getSingleton().getRenderSystem()->getCapabilities();
368
if (caps->hasCapability(RSC_VERTEX_PROGRAM)){
369
//Generate a string ID that identifies the current set of vertex shader options
370
StringUtil::StrStreamType tmpName;
371
tmpName << "GrassVS_";
376
tmpName << renderTechnique << "_";
377
tmpName << fadeTechnique << "_";
378
if (fadeTechnique == FADETECH_GROW || fadeTechnique == FADETECH_ALPHAGROW)
379
tmpName << maxHeight << "_";
381
const String vsName = tmpName.str();
383
//Generate a string ID that identifies the material combined with the vertex shader
384
const String matName = material->getName() + "_" + vsName;
386
//Check if the desired material already exists (if not, create it)
387
MaterialPtr tmpMat = MaterialManager::getSingleton().getByName(matName);
388
if (tmpMat.isNull()){
389
//Clone the original material
390
tmpMat = material->clone(matName);
393
tmpMat->setLightingEnabled(false);
394
//tmpMat->setReceiveShadows(false);
396
//Check if the desired shader already exists (if not, compile it)
397
HighLevelGpuProgramPtr vertexShader = HighLevelGpuProgramManager::getSingleton().getByName(vsName);
398
if (vertexShader.isNull()){
399
//Generate the grass shader
400
String vertexProgSource;
403
" float4 iPosition : POSITION, \n"
404
" float4 iColor : COLOR, \n"
405
" float2 iUV : TEXCOORD0, \n"
406
" out float4 oPosition : POSITION, \n"
407
" out float4 oColor : COLOR, \n"
408
" out float2 oUV : TEXCOORD0, \n";
410
if (animate) vertexProgSource +=
411
" uniform float time, \n"
412
" uniform float frequency, \n"
413
" uniform float4 direction, \n";
415
if (fadeTechnique == FADETECH_GROW || fadeTechnique == FADETECH_ALPHAGROW) vertexProgSource +=
416
" uniform float grassHeight, \n";
418
if (renderTechnique == GRASSTECH_SPRITE) vertexProgSource +=
419
" float4 iNormal : NORMAL, \n";
422
" uniform float4x4 worldViewProj, \n"
423
" uniform float3 camPos, \n"
424
" uniform float fadeRange ) \n"
426
" oColor.rgb = iColor.rgb; \n"
427
" float4 position = iPosition; \n"
428
" float dist = distance(camPos.xz, position.xz); \n";
430
if (fadeTechnique == FADETECH_ALPHA || fadeTechnique == FADETECH_ALPHAGROW) vertexProgSource +=
431
//Fade out in the distance
432
" oColor.a = 2.0f - (2.0f * dist / fadeRange); \n";
433
else vertexProgSource +=
434
" oColor.a = 1.0f; \n";
437
" float oldposx = position.x; \n";
439
if (renderTechnique == GRASSTECH_SPRITE) vertexProgSource +=
441
" float3 dirVec = (float3)position - (float3)camPos; \n"
442
" float3 p = normalize(cross(float4(0,1,0,0), dirVec)); \n"
443
" position += float4(p.x * iNormal.x, iNormal.y, p.z * iNormal.x, 0); \n";
445
if (animate) vertexProgSource +=
446
" if (iUV.y == 0.0f){ \n"
447
//Wave grass in breeze
448
" float offset = sin(time + oldposx * frequency); \n"
449
" position += direction * offset; \n"
452
if (blend && animate) vertexProgSource +=
454
else if (blend) vertexProgSource +=
455
" if (iUV.y != 0.0f){ \n";
457
if (blend) vertexProgSource +=
458
//Blend the base of nearby grass into the terrain
459
" if (oColor.a >= 1.0f) \n"
460
" oColor.a = 4.0f * ((dist / fadeRange) - 0.1f); \n"
463
if (fadeTechnique == FADETECH_GROW || fadeTechnique == FADETECH_ALPHAGROW) vertexProgSource +=
464
" float offset = (2.0f * dist / fadeRange) - 1.0f; \n"
465
" position.y -= grassHeight * clamp(offset, 0, 1); ";
468
" oPosition = mul(worldViewProj, position); \n";
474
vertexShader = HighLevelGpuProgramManager::getSingleton().createProgram(
476
ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
477
"cg", GPT_VERTEX_PROGRAM);
479
vertexShader->setSource(vertexProgSource);
480
vertexShader->setParameter("profiles", "vs_1_1 arbvp1");
481
vertexShader->setParameter("entry_point", "main");
482
vertexShader->load();
484
//Now the vertex shader (vertexShader) has either been found or just generated
485
//(depending on whether or not it was already generated).
487
//Apply the shader to the material
488
Pass *pass = tmpMat->getTechnique(0)->getPass(0);
489
pass->setVertexProgram(vsName);
490
GpuProgramParametersSharedPtr params = pass->getVertexProgramParameters();
492
params->setNamedAutoConstant("worldViewProj", GpuProgramParameters::ACT_WORLDVIEWPROJ_MATRIX);
493
params->setNamedAutoConstant("camPos", GpuProgramParameters::ACT_CAMERA_POSITION_OBJECT_SPACE);
494
params->setNamedAutoConstant("fadeRange", GpuProgramParameters::ACT_CUSTOM, 1);
497
params->setNamedAutoConstant("time", GpuProgramParameters::ACT_CUSTOM, 1);
498
params->setNamedAutoConstant("frequency", GpuProgramParameters::ACT_CUSTOM, 1);
499
params->setNamedAutoConstant("direction", GpuProgramParameters::ACT_CUSTOM, 4);
502
if (fadeTechnique == FADETECH_GROW || fadeTechnique == FADETECH_ALPHAGROW){
503
params->setNamedAutoConstant("grassHeight", GpuProgramParameters::ACT_CUSTOM, 1);
504
params->setNamedConstant("grassHeight", maxHeight * 1.05f);
507
float farViewDist = geom->getDetailLevels().front()->getFarRange();
508
pass->getVertexProgramParameters()->setNamedConstant("fadeRange", farViewDist / 1.225f);
509
//Note: 1.225 ~= sqrt(1.5), which is necessary since the far view distance is measured from the centers
510
//of pages, while the vertex shader needs to fade grass completely out (including the closest corner)
511
//before the page center is out of range.
513
//Now the material (tmpMat) has either been found or just created (depending on whether or not it was already
514
//created). The appropriate vertex shader should be applied and the material is ready for use.
516
//Apply the new material
523
unsigned long GrassPage::GUID = 0;
525
void GrassPage::init(PagedGeometry *geom)
527
sceneMgr = geom->getSceneManager();
528
rootNode = geom->getSceneNode();
531
GrassPage::~GrassPage()
536
void GrassPage::addEntity(Entity *entity, const Vector3 &position, const Quaternion &rotation, const Vector3 &scale, const Ogre::ColourValue &color)
538
SceneNode *node = rootNode->createChildSceneNode();
539
node->setPosition(position);
540
nodeList.push_back(node);
542
Entity *ent = entity->clone(getUniqueID());
543
ent->setCastShadows(false);
544
ent->setRenderQueueGroup(entity->getRenderQueueGroup());
545
node->attachObject(ent);
548
void GrassPage::removeEntities()
550
std::list<SceneNode*>::iterator i;
551
for (i = nodeList.begin(); i != nodeList.end(); ++i){
552
SceneNode *node = *i;
553
sceneMgr->destroyEntity(static_cast<Entity*>(node->getAttachedObject(0)));
554
sceneMgr->destroySceneNode(node->getName());
559
void GrassPage::setVisible(bool visible)
561
std::list<SceneNode*>::iterator i;
562
for (i = nodeList.begin(); i != nodeList.end(); ++i){
563
SceneNode *node = *i;
564
node->setVisible(visible);