Rigs of Rods 2026.01
Soft-body Physics Simulation
Loading...
Searching...
No Matches
GrassLoader.cpp
Go to the documentation of this file.
1/*-------------------------------------------------------------------------------------
2Copyright (c) 2006 John Judnich
3
4This 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.
5Permission 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:
6 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.
7 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
8 3. This notice may not be removed or altered from any source distribution.
9-------------------------------------------------------------------------------------*/
10
11#include "OgreRoot.h"
12#include "OgreTimer.h"
13#include "OgreCamera.h"
14#include "OgreVector3.h"
15#include "OgreQuaternion.h"
16#include "OgreEntity.h"
17#include "OgreString.h"
18#include "OgreStringConverter.h"
19#include "OgreMaterialManager.h"
20#include "OgreMaterial.h"
21#include "OgreHardwareBufferManager.h"
22#include "OgreHardwareBuffer.h"
23#include "OgreMeshManager.h"
24#include "OgreMesh.h"
25#include "OgreSubMesh.h"
26#include "OgreLogManager.h"
27#include "OgreTextureManager.h"
28#include "OgreHardwarePixelBuffer.h"
29#include "OgreRenderSystem.h"
30#include "OgreRenderSystemCapabilities.h"
31#include "OgreHighLevelGpuProgram.h"
32#include "OgreHighLevelGpuProgramManager.h"
33#include <OgreTechnique.h>
34#include <OgreSceneNode.h>
35
36#include "GrassLoader.h"
37#include "PagedGeometry.h"
38#include "PropertyMaps.h"
39#include "RandomTable.h"
40
41#include <limits> //for numeric_limits
42
43using namespace Ogre;
44
45namespace Forests {
46
47unsigned long GrassLoader::GUID = 0;
48
50{
52
53 // generate some random numbers
54 rTable = new RandomTable();
55
56 heightFunction = NULL;
58
59 windDir = Vector3::UNIT_X;
60 densityFactor = 1.0f;
62
63 windTimer.reset();
64 lastTime = 0;
65}
66
68{
69 std::list<GrassLayer*>::iterator it;
70 for (it = layerList.begin(); it != layerList.end(); ++it){
71 delete *it;
72 }
73 layerList.clear();
74
75 if(rTable)
76 {
77 delete(rTable);
78 rTable=0;
79 }
80}
81
82GrassLayer *GrassLoader::addLayer(const String &material)
83{
84 GrassLayer *layer = new GrassLayer(geom, this);
85 layer->setMaterialName(material);
86 layerList.push_back(layer);
87
88 return layer;
89}
90
92{
93 layerList.remove(layer);
94 delete layer;
95}
96
98{
99 unsigned long currentTime = windTimer.getMilliseconds();
100 unsigned long ellapsedTime = currentTime - lastTime;
101 lastTime = currentTime;
102
103 float ellapsed = ellapsedTime / 1000.0f;
104
105 //Update the vertex shader parameters
106 std::list<GrassLayer*>::iterator it;
107 for (it = layerList.begin(); it != layerList.end(); ++it){
108 GrassLayer *layer = *it;
109
110 layer->_updateShaders();
111
112 GpuProgramParametersSharedPtr params = layer->material->getTechnique(0)->getPass(0)->getVertexProgramParameters();
113 if (layer->animate){
114 //Increment animation frame
115 layer->waveCount += ellapsed * (layer->animSpeed * Math::PI);
116 if (layer->waveCount > Math::PI*2) layer->waveCount -= Math::PI*2;
117
118 //Set vertex shader parameters
119 params->setNamedConstant("time", layer->waveCount);
120 params->setNamedConstant("frequency", layer->animFreq);
121
122 Vector3 direction = windDir * layer->animMag;
123 params->setNamedConstant("direction", Vector4(direction.x, direction.y, direction.z, 0));
124
125 }
126 }
127}
128
130{
131 //Generate meshes
132 std::list<GrassLayer*>::iterator it;
133 for (it = layerList.begin(); it != layerList.end(); ++it){
134 GrassLayer *layer = *it;
135
136 // Continue to the next layer if the current page is outside of the layers map boundaries.
137 if(layer->mapBounds.right < page.bounds.left || layer->mapBounds.left > page.bounds.right ||
138 layer->mapBounds.bottom < page.bounds.top || layer->mapBounds.top > page.bounds.bottom)
139 {
140 continue;
141 }
142
143 //Calculate how much grass needs to be added
144 Ogre::Real volume = page.bounds.width() * page.bounds.height();
145 unsigned int grassCount = (unsigned int)(layer->density * densityFactor * volume);
146
147 //The vertex buffer can't be allocated until the exact number of polygons is known,
148 //so the locations of all grasses in this page must be precalculated.
149
150 //Precompute grass locations into an array of floats. A plain array is used for speed;
151 //there's no need to use a dynamic sized array since a maximum size is known.
152 float *position = new float[grassCount*4];
153 if (layer->densityMap){
154 if (layer->densityMap->getFilter() == MAPFILTER_NONE)
155 grassCount = layer->_populateGrassList_UnfilteredDM(page, position, grassCount);
156 else if (layer->densityMap->getFilter() == MAPFILTER_BILINEAR)
157 grassCount = layer->_populateGrassList_BilinearDM(page, position, grassCount);
158 } else {
159 grassCount = layer->_populateGrassList_Uniform(page, position, grassCount);
160 }
161
162 //Don't build a mesh unless it contains something
163 if (grassCount != 0){
164 Mesh *mesh = NULL;
165 switch (layer->renderTechnique){
166 case GRASSTECH_QUAD:
167 mesh = generateGrass_QUAD(page, layer, position, grassCount);
168 break;
170 mesh = generateGrass_CROSSQUADS(page, layer, position, grassCount);
171 break;
172 case GRASSTECH_SPRITE:
173 mesh = generateGrass_SPRITE(page, layer, position, grassCount);
174 break;
175 }
176 assert(mesh);
177
178 //Add the mesh to PagedGeometry
179 Entity *entity = geom->getCamera()->getSceneManager()->createEntity(getUniqueID(), mesh->getName());
180 entity->setRenderQueueGroup(renderQueue);
181 entity->setCastShadows(false);
182 addEntity(entity, page.centerPoint, Quaternion::IDENTITY, Vector3::UNIT_SCALE);
183
184 //Store the mesh pointer
185 page.meshList.push_back(mesh);
186 }
187
188 //Delete the position list
189 delete[] position;
190 }
191}
192
194{
195 // we unload the page in the page's destructor
196}
197
198Mesh *GrassLoader::generateGrass_QUAD(PageInfo &page, GrassLayer *layer, const float *grassPositions, unsigned int grassCount)
199{
200 //Calculate the number of quads to be added
201 unsigned int quadCount;
202 quadCount = grassCount;
203
204 // check for overflows of the uint16's
205 unsigned int maxUInt16 = std::numeric_limits<uint16>::max();
206 if(grassCount > maxUInt16)
207 {
208 LogManager::getSingleton().logMessage("grass count overflow: you tried to use more than " + StringConverter::toString(maxUInt16) + " (thats the maximum) grass meshes for one page");
209 return 0;
210 }
211 if(quadCount > maxUInt16)
212 {
213 LogManager::getSingleton().logMessage("quad count overflow: you tried to use more than " + StringConverter::toString(maxUInt16) + " (thats the maximum) grass meshes for one page");
214 return 0;
215 }
216
217 //Create manual mesh to store grass quads
218 MeshPtr mesh = MeshManager::getSingleton().createManual(getUniqueID(), ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
219 SubMesh *subMesh = mesh->createSubMesh();
220 subMesh->useSharedVertices = false;
221
222 //Setup vertex format information
223 subMesh->vertexData = new VertexData;
224 subMesh->vertexData->vertexStart = 0;
225 subMesh->vertexData->vertexCount = 4 * quadCount;
226
227 VertexDeclaration* dcl = subMesh->vertexData->vertexDeclaration;
228 size_t offset = 0;
229 dcl->addElement(0, offset, VET_FLOAT3, VES_POSITION);
230 offset += VertexElement::getTypeSize(VET_FLOAT3);
231 dcl->addElement(0, offset, VET_COLOUR, VES_DIFFUSE);
232 offset += VertexElement::getTypeSize(VET_COLOUR);
233 dcl->addElement(0, offset, VET_FLOAT2, VES_TEXTURE_COORDINATES);
234 offset += VertexElement::getTypeSize(VET_FLOAT2);
235
236 //Populate a new vertex buffer with grass
237 HardwareVertexBufferSharedPtr vbuf = HardwareBufferManager::getSingleton()
238 .createVertexBuffer(offset, subMesh->vertexData->vertexCount, HardwareBuffer::HBU_STATIC_WRITE_ONLY, false);
239 float* pReal = static_cast<float*>(vbuf->lock(HardwareBuffer::HBL_DISCARD));
240
241 //Calculate size variance
242 Ogre::Real rndWidth = layer->maxWidth - layer->minWidth;
243 Ogre::Real rndHeight = layer->maxHeight - layer->minHeight;
244
245 Ogre::Real minY = Math::POS_INFINITY, maxY = Math::NEG_INFINITY;
246 const float *posPtr = grassPositions; //Position array "iterator"
247 for (uint16 i = 0; i < grassCount; ++i)
248 {
249 //Get the x and z positions from the position array
250 float x = *posPtr++;
251 float z = *posPtr++;
252
253 //Get the color at the grass position
254 uint32 color;
255 if (layer->colorMap)
256 color = layer->colorMap->getColorAt(x, z, layer->mapBounds);
257 else
258 color = 0xFFFFFFFF;
259
260 //Calculate size
261 Ogre::Real rnd = *posPtr++; //The same rnd value is used for width and height to maintain aspect ratio
262 Ogre::Real halfScaleX = (layer->minWidth + rndWidth * rnd) * 0.5f;
263 Ogre::Real scaleY = (layer->minHeight + rndHeight * rnd);
264
265 //Calculate rotation
266 Ogre::Real angle = *posPtr++;
267 Ogre::Real xTrans = Math::Cos(angle) * halfScaleX;
268 Ogre::Real zTrans = Math::Sin(angle) * halfScaleX;
269
270 //Calculate heights and edge positions
271 Ogre::Real x1 = x - xTrans, z1 = z - zTrans;
272 Ogre::Real x2 = x + xTrans, z2 = z + zTrans;
273
274 Ogre::Real y1 = 0.f, y2 = 0.f;
275 if (heightFunction)
276 {
279
280 if (layer->getMaxSlope() < (Math::Abs(y1 - y2) / (halfScaleX * 2))) {
281 //Degenerate the face
282 x2 = x1;
283 y2 = y1;
284 z2 = z1;
285 }
286 }
287
288 //Add vertices
289 *pReal++ = float(x1 - page.centerPoint.x);
290 *pReal++ = float(y1 + scaleY);
291 *pReal++ = float(z1 - page.centerPoint.z); //pos
292
293 *((uint32*)pReal++) = color; //color
294 *pReal++ = 0.f; *pReal++ = 0.f; //uv
295
296 *pReal++ = float(x2 - page.centerPoint.x);
297 *pReal++ = float(y2 + scaleY);
298 *pReal++ = float(z2 - page.centerPoint.z); //pos
299 *((uint32*)pReal++) = color; //color
300 *pReal++ = 1.f; *pReal++ = 0.f; //uv
301
302 *pReal++ = float(x1 - page.centerPoint.x);
303 *pReal++ = float(y1);
304 *pReal++ = float(z1 - page.centerPoint.z); //pos
305 *((uint32*)pReal++) = color; //color
306 *pReal++ = 0.f; *pReal++ = 1.f; //uv
307
308 *pReal++ = float(x2 - page.centerPoint.x);
309 *pReal++ = float(y2);
310 *pReal++ = float(z2 - page.centerPoint.z); //pos
311 *((uint32*)pReal++) = color; //color
312 *pReal++ = 1.f; *pReal++ = 1.f; //uv
313
314 //Update bounds
315 if (y1 < minY) minY = y1;
316 if (y2 < minY) minY = y2;
317 if (y1 + scaleY > maxY) maxY = y1 + scaleY;
318 if (y2 + scaleY > maxY) maxY = y2 + scaleY;
319 }
320
321 vbuf->unlock();
322 subMesh->vertexData->vertexBufferBinding->setBinding(0, vbuf);
323
324 //Populate index buffer
325 subMesh->indexData->indexStart = 0;
326 subMesh->indexData->indexCount = 6 * quadCount;
327 subMesh->indexData->indexBuffer = HardwareBufferManager::getSingleton()
328 .createIndexBuffer(HardwareIndexBuffer::IT_16BIT, subMesh->indexData->indexCount, HardwareBuffer::HBU_STATIC_WRITE_ONLY);
329 uint16* pI = static_cast<uint16*>(subMesh->indexData->indexBuffer->lock(HardwareBuffer::HBL_DISCARD));
330 for (uint16 i = 0; i < quadCount; ++i)
331 {
332 uint16 offset = i * 4;
333
334 *pI++ = 0 + offset;
335 *pI++ = 2 + offset;
336 *pI++ = 1 + offset;
337
338 *pI++ = 1 + offset;
339 *pI++ = 2 + offset;
340 *pI++ = 3 + offset;
341 }
342
343 subMesh->indexData->indexBuffer->unlock();
344
345 //Finish up mesh
346 AxisAlignedBox bounds(page.bounds.left - page.centerPoint.x, minY, page.bounds.top - page.centerPoint.z,
347 page.bounds.right - page.centerPoint.x, maxY, page.bounds.bottom - page.centerPoint.z);
348 mesh->_setBounds(bounds);
349 Vector3 temp = bounds.getMaximum() - bounds.getMinimum();
350 mesh->_setBoundingSphereRadius(temp.length() * 0.5f);
351
352 LogManager::getSingleton().setLogDetail(static_cast<LoggingLevel>(0));
353 mesh->load();
354 LogManager::getSingleton().setLogDetail(LL_NORMAL);
355
356 //Apply grass material to mesh
357 subMesh->setMaterialName(layer->material->getName());
358
359 //Return the mesh
360 return mesh.get();
361}
362
363Mesh *GrassLoader::generateGrass_CROSSQUADS(PageInfo &page, GrassLayer *layer, const float *grassPositions, unsigned int grassCount)
364{
365 //Calculate the number of quads to be added
366 unsigned int quadCount;
367 quadCount = grassCount * 2;
368
369 // check for overflows of the uint16's
370 unsigned int maxUInt16 = std::numeric_limits<uint16>::max();
371 if(grassCount > maxUInt16)
372 {
373 LogManager::getSingleton().logMessage("grass count overflow: you tried to use more than " + StringConverter::toString(maxUInt16) + " (thats the maximum) grass meshes for one page");
374 return 0;
375 }
376 if(quadCount > maxUInt16)
377 {
378 LogManager::getSingleton().logMessage("quad count overflow: you tried to use more than " + StringConverter::toString(maxUInt16) + " (thats the maximum) grass meshes for one page");
379 return 0;
380 }
381
382 //Create manual mesh to store grass quads
383 MeshPtr mesh = MeshManager::getSingleton().createManual(getUniqueID(), ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
384 SubMesh *subMesh = mesh->createSubMesh();
385 subMesh->useSharedVertices = false;
386
387 //Setup vertex format information
388 subMesh->vertexData = new VertexData;
389 subMesh->vertexData->vertexStart = 0;
390 subMesh->vertexData->vertexCount = 4 * quadCount;
391
392 VertexDeclaration* dcl = subMesh->vertexData->vertexDeclaration;
393 size_t offset = 0;
394 dcl->addElement(0, offset, VET_FLOAT3, VES_POSITION);
395 offset += VertexElement::getTypeSize(VET_FLOAT3);
396 dcl->addElement(0, offset, VET_COLOUR, VES_DIFFUSE);
397 offset += VertexElement::getTypeSize(VET_COLOUR);
398 dcl->addElement(0, offset, VET_FLOAT2, VES_TEXTURE_COORDINATES);
399 offset += VertexElement::getTypeSize(VET_FLOAT2);
400
401 //Populate a new vertex buffer with grass
402 HardwareVertexBufferSharedPtr vbuf = HardwareBufferManager::getSingleton()
403 .createVertexBuffer(offset, subMesh->vertexData->vertexCount, HardwareBuffer::HBU_STATIC_WRITE_ONLY, false);
404 float* pReal = static_cast<float*>(vbuf->lock(HardwareBuffer::HBL_DISCARD));
405
406 //Calculate size variance
407 Ogre::Real rndWidth = layer->maxWidth - layer->minWidth;
408 Ogre::Real rndHeight = layer->maxHeight - layer->minHeight;
409 Ogre::Real minY = Math::POS_INFINITY, maxY = Math::NEG_INFINITY;
410
411 const float *posPtr = grassPositions; //Position array "iterator"
412 for (uint16 i = 0; i < grassCount; ++i)
413 {
414 //Get the x and z positions from the position array
415 Ogre::Real x = *posPtr++;
416 Ogre::Real z = *posPtr++;
417
418 //Get the color at the grass position
419 uint32 color;
420 if (layer->colorMap)
421 color = layer->colorMap->getColorAt(x, z, layer->mapBounds);
422 else
423 color = 0xFFFFFFFF;
424
425 //Calculate size
426 Ogre::Real rnd = *posPtr++; //The same rnd value is used for width and height to maintain aspect ratio
427 Ogre::Real halfScaleX = (layer->minWidth + rndWidth * rnd) * 0.5f;
428 Ogre::Real scaleY = (layer->minHeight + rndHeight * rnd);
429
430 //Calculate rotation
431 Ogre::Real angle = *posPtr++;
432 Ogre::Real xTrans = Math::Cos(angle) * halfScaleX;
433 Ogre::Real zTrans = Math::Sin(angle) * halfScaleX;
434
435 //Calculate heights and edge positions
436 Ogre::Real x1 = x - xTrans, z1 = z - zTrans;
437 Ogre::Real x2 = x + xTrans, z2 = z + zTrans;
438
439 Ogre::Real y1 = 0.f, y2 = 0.f;
440 if (heightFunction)
441 {
444
445 if (layer->getMaxSlope() < (Math::Abs(y1 - y2) / (halfScaleX * 2)))
446 {
447 //Degenerate the face
448 x2 = x1;
449 y2 = y1;
450 z2 = z1;
451 }
452 }
453
454 //Add vertices
455 *pReal++ = float(x1 - page.centerPoint.x);
456 *pReal++ = float(y1 + scaleY);
457 *pReal++ = float(z1 - page.centerPoint.z); //pos
458 *((uint32*)pReal++) = color; //color
459 *pReal++ = 0.f; *pReal++ = 0.f; //uv
460
461 *pReal++ = float(x2 - page.centerPoint.x);
462 *pReal++ = float(y2 + scaleY);
463 *pReal++ = float(z2 - page.centerPoint.z); //pos
464 *((uint32*)pReal++) = color; //color
465 *pReal++ = 1.f; *pReal++ = 0.f; //uv
466
467 *pReal++ = float(x1 - page.centerPoint.x);
468 *pReal++ = float(y1);
469 *pReal++ = float(z1 - page.centerPoint.z); //pos
470 *((uint32*)pReal++) = color; //color
471 *pReal++ = 0.f; *pReal++ = 1.f; //uv
472
473 *pReal++ = float(x2 - page.centerPoint.x);
474 *pReal++ = float(y2);
475 *pReal++ = float(z2 - page.centerPoint.z); //pos
476 *((uint32*)pReal++) = color; //color
477 *pReal++ = 1.f; *pReal++ = 1.f; //uv
478
479 //Update bounds
480 if (y1 < minY) minY = y1;
481 if (y2 < minY) minY = y2;
482 if (y1 + scaleY > maxY) maxY = y1 + scaleY;
483 if (y2 + scaleY > maxY) maxY = y2 + scaleY;
484
485 //Calculate heights and edge positions
486 Ogre::Real x3 = x + zTrans, z3 = z - xTrans;
487 Ogre::Real x4 = x - zTrans, z4 = z + xTrans;
488
489 Ogre::Real y3 = 0.f, y4 = 0.f;
490 if (heightFunction)
491 {
492 if (layer->getMaxSlope() < (Math::Abs(y1 - y2) / (halfScaleX * 2)))
493 {
494 //Degenerate the face
495 x2 = x1;
496 y2 = y1;
497 z2 = z1;
498 }
499
502 }
503
504 //Add vertices
505 *pReal++ = float(x3 - page.centerPoint.x);
506 *pReal++ = float(y3 + scaleY);
507 *pReal++ = float(z3 - page.centerPoint.z); //pos
508 *((uint32*)pReal++) = color; //color
509 *pReal++ = 0.f; *pReal++ = 0.f; //uv
510
511 *pReal++ = float(x4 - page.centerPoint.x);
512 *pReal++ = float(y4 + scaleY);
513 *pReal++ = float(z4 - page.centerPoint.z); //pos
514 *((uint32*)pReal++) = color; //color
515 *pReal++ = 1.f; *pReal++ = 0.f; //uv
516
517 *pReal++ = float(x3 - page.centerPoint.x);
518 *pReal++ = float(y3);
519 *pReal++ = float(z3 - page.centerPoint.z); //pos
520 *((uint32*)pReal++) = color; //color
521 *pReal++ = 0.f; *pReal++ = 1.f; //uv
522
523 *pReal++ = float(x4 - page.centerPoint.x);
524 *pReal++ = float(y4);
525 *pReal++ = float(z4 - page.centerPoint.z); //pos
526 *((uint32*)pReal++) = color; //color
527 *pReal++ = 1.f; *pReal++ = 1.f; //uv
528
529 //Update bounds
530 if (y3 < minY) minY = y1;
531 if (y4 < minY) minY = y2;
532 if (y3 + scaleY > maxY) maxY = y3 + scaleY;
533 if (y4 + scaleY > maxY) maxY = y4 + scaleY;
534 }
535
536 vbuf->unlock();
537 subMesh->vertexData->vertexBufferBinding->setBinding(0, vbuf);
538
539 //Populate index buffer
540 subMesh->indexData->indexStart = 0;
541 subMesh->indexData->indexCount = 6 * quadCount;
542 subMesh->indexData->indexBuffer = HardwareBufferManager::getSingleton()
543 .createIndexBuffer(HardwareIndexBuffer::IT_16BIT, subMesh->indexData->indexCount, HardwareBuffer::HBU_STATIC_WRITE_ONLY);
544 uint16* pI = static_cast<uint16*>(subMesh->indexData->indexBuffer->lock(HardwareBuffer::HBL_DISCARD));
545 for (uint16 i = 0; i < quadCount; ++i)
546 {
547 uint16 offset = i * 4;
548
549 *pI++ = 0 + offset;
550 *pI++ = 2 + offset;
551 *pI++ = 1 + offset;
552
553 *pI++ = 1 + offset;
554 *pI++ = 2 + offset;
555 *pI++ = 3 + offset;
556 }
557
558 subMesh->indexData->indexBuffer->unlock();
559
560
561 //Finish up mesh
562 AxisAlignedBox bounds(page.bounds.left - page.centerPoint.x, minY, page.bounds.top - page.centerPoint.z,
563 page.bounds.right - page.centerPoint.x, maxY, page.bounds.bottom - page.centerPoint.z);
564 mesh->_setBounds(bounds);
565 Vector3 temp = bounds.getMaximum() - bounds.getMinimum();
566 mesh->_setBoundingSphereRadius(temp.length() * 0.5f);
567
568 LogManager::getSingleton().setLogDetail(static_cast<LoggingLevel>(0));
569 mesh->load();
570 LogManager::getSingleton().setLogDetail(LL_NORMAL);
571
572 //Apply grass material to mesh
573 subMesh->setMaterialName(layer->material->getName());
574
575 //Return the mesh
576 return mesh.get();
577}
578
579Mesh *GrassLoader::generateGrass_SPRITE(PageInfo &page, GrassLayer *layer, const float *grassPositions, unsigned int grassCount)
580{
581 //Calculate the number of quads to be added
582 unsigned int quadCount;
583 quadCount = grassCount;
584
585 // check for overflows of the uint16's
586 unsigned int maxUInt16 = std::numeric_limits<uint16>::max();
587 if(grassCount > maxUInt16)
588 {
589 LogManager::getSingleton().logMessage("grass count overflow: you tried to use more than " + StringConverter::toString(maxUInt16) + " (thats the maximum) grass meshes for one page");
590 return 0;
591 }
592 if(quadCount > maxUInt16)
593 {
594 LogManager::getSingleton().logMessage("quad count overflow: you tried to use more than " + StringConverter::toString(maxUInt16) + " (thats the maximum) grass meshes for one page");
595 return 0;
596 }
597
598 //Create manual mesh to store grass quads
599 MeshPtr mesh = MeshManager::getSingleton().createManual(getUniqueID(), ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
600 SubMesh *subMesh = mesh->createSubMesh();
601 subMesh->useSharedVertices = false;
602
603 //Setup vertex format information
604 subMesh->vertexData = new VertexData;
605 subMesh->vertexData->vertexStart = 0;
606 subMesh->vertexData->vertexCount = 4 * quadCount;
607
608 VertexDeclaration* dcl = subMesh->vertexData->vertexDeclaration;
609 size_t offset = 0;
610 dcl->addElement(0, offset, VET_FLOAT3, VES_POSITION);
611 offset += VertexElement::getTypeSize(VET_FLOAT3);
612 dcl->addElement(0, offset, VET_FLOAT4, VES_NORMAL);
613 offset += VertexElement::getTypeSize(VET_FLOAT4);
614 dcl->addElement(0, offset, VET_COLOUR, VES_DIFFUSE);
615 offset += VertexElement::getTypeSize(VET_COLOUR);
616 dcl->addElement(0, offset, VET_FLOAT2, VES_TEXTURE_COORDINATES);
617 offset += VertexElement::getTypeSize(VET_FLOAT2);
618
619 //Populate a new vertex buffer with grass
620 HardwareVertexBufferSharedPtr vbuf = HardwareBufferManager::getSingleton()
621 .createVertexBuffer(offset, subMesh->vertexData->vertexCount, HardwareBuffer::HBU_STATIC_WRITE_ONLY, false);
622 float* pReal = static_cast<float*>(vbuf->lock(HardwareBuffer::HBL_DISCARD));
623
624 //Calculate size variance
625 Ogre::Real rndWidth = layer->maxWidth - layer->minWidth;
626 Ogre::Real rndHeight = layer->maxHeight - layer->minHeight;
627 Ogre::Real minY = Math::POS_INFINITY, maxY = Math::NEG_INFINITY;
628
629 const float *posPtr = grassPositions; //Position array "iterator"
630 for (uint16 i = 0; i < grassCount; ++i)
631 {
632 //Get the x and z positions from the position array
633 float x = *posPtr++;
634 float z = *posPtr++;
635 float y = heightFunction ? (float)heightFunction(x, z, heightFunctionUserData) : 0.f;
636
637 float x1 = float(x - page.centerPoint.x);
638 float z1 = float(z - page.centerPoint.z);
639
640 //Get the color at the grass position
641 uint32 color = layer->colorMap ? layer->colorMap->getColorAt(x, z, layer->mapBounds) : 0xFFFFFFFF;
642
643 //Calculate size
644 float rnd = *posPtr++; //The same rnd value is used for width and height to maintain aspect ratio
645 float halfXScale = float(layer->minWidth + rndWidth * rnd) * 0.5f;
646 float scaleY = float(layer->minHeight + rndHeight * rnd);
647
648 //Randomly mirror grass textures
649 float uvLeft = 1.f, uvRight = 0.f;
650 if (*posPtr++ > 0.5f)
651 {
652 uvLeft = 0.f;
653 uvRight = 1.f;
654 }
655
656 //Add vertices
657 *pReal++ = x1; *pReal++ = y; *pReal++ = z1; //center position
658 *pReal++ = -halfXScale; *pReal++ = scaleY; *pReal++ = 0.f; *pReal++ = 0.f; //normal (used to store relative corner positions)
659 *((uint32*)pReal++) = color; //color
660 *pReal++ = uvLeft; *pReal++ = 0.f; //uv
661
662 *pReal++ = x1; *pReal++ = y; *pReal++ = z1; //center position
663 *pReal++ = +halfXScale; *pReal++ = scaleY; *pReal++ = 0.f; *pReal++ = 0.f; //normal (used to store relative corner positions)
664 *((uint32*)pReal++) = color; //color
665 *pReal++ = uvRight; *pReal++ = 0.f; //uv
666
667 *pReal++ = x1; *pReal++ = y; *pReal++ = z1; //center position
668 *pReal++ = -halfXScale; *pReal++ = 0.f; *pReal++ = 0.f; *pReal++ = 0.f; //normal (used to store relative corner positions)
669 *((uint32*)pReal++) = color; //color
670 *pReal++ = uvLeft; *pReal++ = 1.f; //uv
671
672 *pReal++ = x1; *pReal++ = y; *pReal++ = z1; //center position
673 *pReal++ = +halfXScale; *pReal++ = 0.f; *pReal++ = 0.f; *pReal++ = 0.f; //normal (used to store relative corner positions)
674 *((uint32*)pReal++) = color; //color
675 *pReal++ = uvRight; *pReal++ = 1.f; //uv
676
677 //Update bounds
678 if (y < minY) minY = y;
679 if (y + scaleY > maxY) maxY = y + scaleY;
680 }
681
682 vbuf->unlock();
683 subMesh->vertexData->vertexBufferBinding->setBinding(0, vbuf);
684
685 //Populate index buffer
686 subMesh->indexData->indexStart = 0;
687 subMesh->indexData->indexCount = 6 * quadCount;
688 subMesh->indexData->indexBuffer = HardwareBufferManager::getSingleton()
689 .createIndexBuffer(HardwareIndexBuffer::IT_16BIT, subMesh->indexData->indexCount, HardwareBuffer::HBU_STATIC_WRITE_ONLY);
690 uint16* pI = static_cast<uint16*>(subMesh->indexData->indexBuffer->lock(HardwareBuffer::HBL_DISCARD));
691 for (uint16 i = 0; i < quadCount; ++i)
692 {
693 uint16 offset = i * 4;
694
695 *pI++ = 0 + offset;
696 *pI++ = 2 + offset;
697 *pI++ = 1 + offset;
698
699 *pI++ = 1 + offset;
700 *pI++ = 2 + offset;
701 *pI++ = 3 + offset;
702 }
703
704 subMesh->indexData->indexBuffer->unlock();
705
706 //Finish up mesh
707 AxisAlignedBox bounds(page.bounds.left - page.centerPoint.x, minY, page.bounds.top - page.centerPoint.z,
708 page.bounds.right - page.centerPoint.x, maxY, page.bounds.bottom - page.centerPoint.z);
709 mesh->_setBounds(bounds);
710 Vector3 temp = bounds.getMaximum() - bounds.getMinimum();
711 mesh->_setBoundingSphereRadius(temp.length() * 0.5f);
712
713 LogManager::getSingleton().setLogDetail(static_cast<LoggingLevel>(0));
714 mesh->load();
715 LogManager::getSingleton().setLogDetail(LL_NORMAL);
716
717 //Apply grass material to mesh
718 subMesh->setMaterialName(layer->material->getName());
719
720 //Return the mesh
721 return mesh.get();
722}
723
725{
727 GrassLayer::parent = ldr;
728
729 density = 1.0f;
730 minWidth = 1.0f; maxWidth = 1.0f;
731 minHeight = 1.0f; maxHeight = 1.0f;
732 minY = 0; maxY = 0;
733 maxSlope = 1000;
736 animMag = 1.0f;
737 animSpeed = 1.0f;
738 animFreq = 1.0f;
739 waveCount = 0.0f;
740 animate = false;
741 blend = false;
742 lighting = false;
743 shaderNeedsUpdate = true;
744
745 densityMap = NULL;
747 colorMap = NULL;
749}
750
752{
753 if (densityMap)
755 if (colorMap)
756 colorMap->unload();
757}
758
759void GrassLayer::setMaterialName(const String &matName)
760{
761 if (!material || matName != material->getName()){
762 material = Ogre::static_pointer_cast<Material>(MaterialManager::getSingleton().getByName(matName));
763 if (!material)
764 OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "The specified grass material does not exist", "GrassLayer::setMaterialName()");
765 shaderNeedsUpdate = true;
766 }
767}
768
769void GrassLayer::setMinimumSize(float width, float height)
770{
771 minWidth = width;
772 minHeight = height;
773}
774
775void GrassLayer::setMaximumSize(float width, float height)
776{
777 maxWidth = width;
778 if (maxHeight != height){
779 maxHeight = height;
780 shaderNeedsUpdate = true;
781 }
782}
783
785{
786 ROR_ASSERT(style>=GRASSTECH_MIN && style<=GRASSTECH_MAX);
787
788 if (blend != blendBase || renderTechnique != style){
789 blend = blendBase;
790 renderTechnique = style;
791 shaderNeedsUpdate = true;
792 }
793}
794
796{
797 if (fadeTechnique != style){
798 fadeTechnique = style;
799 shaderNeedsUpdate = true;
800 }
801}
802
804{
805 if (animate != enabled){
806 animate = enabled;
807 shaderNeedsUpdate = true;
808 }
809}
810
812{
813 lighting = enabled;
814}
815
816void GrassLayer::setDensityMap(const String &mapFile, MapChannel channel)
817{
818 if (densityMap){
820 densityMap = NULL;
821 }
822 if (mapFile != ""){
823 densityMap = DensityMap::load(mapFile, channel);
825 }
826}
827void GrassLayer::setDensityMap(TexturePtr map, MapChannel channel)
828{
829 if (densityMap){
831 densityMap = NULL;
832 }
833 if (map){
834 densityMap = DensityMap::load(map, channel);
836 }
837}
838
845
846unsigned int GrassLayer::_populateGrassList_Uniform(PageInfo page, float *posBuff, unsigned int grassCount)
847{
848 float *posPtr = posBuff;
849
851
852 //No density map
853 if (!minY && !maxY)
854 {
855 //No height range
856 for (unsigned int i = 0; i < grassCount; ++i)
857 {
858 //Pick a random position
859 float x = parent->rTable->getRangeRandom((float)page.bounds.left, (float)page.bounds.right);
860 float z = parent->rTable->getRangeRandom((float)page.bounds.top, (float)page.bounds.bottom);
861
862 //Add to list in within bounds
863 if (!colorMap)
864 {
865 *posPtr++ = x;
866 *posPtr++ = z;
867 }
868 else if (x >= mapBounds.left && x <= mapBounds.right && z >= mapBounds.top && z <= mapBounds.bottom)
869 {
870 *posPtr++ = x;
871 *posPtr++ = z;
872 }
873 *posPtr++ = parent->rTable->getUnitRandom();
874 *posPtr++ = parent->rTable->getRangeRandom(0, (float)Math::TWO_PI);
875 }
876 }
877 else
878 {
879 //Height range
880 Real min, max;
881 if (minY) min = minY; else min = Math::NEG_INFINITY;
882 if (maxY) max = maxY; else max = Math::POS_INFINITY;
883
884 for (unsigned int i = 0; i < grassCount; ++i){
885 //Pick a random position
886 float x = parent->rTable->getRangeRandom((float)page.bounds.left, (float)page.bounds.right);
887 float z = parent->rTable->getRangeRandom((float)page.bounds.top, (float)page.bounds.bottom);
888
889 //Calculate height
891
892 //Add to list if in range
893 if (y >= min && y <= max){
894 //Add to list in within bounds
895 if (!colorMap){
896 *posPtr++ = x;
897 *posPtr++ = z;
898 *posPtr++ = parent->rTable->getUnitRandom();
899 *posPtr++ = parent->rTable->getRangeRandom(0, (float)Math::PI);
900 } else if (x >= mapBounds.left && x <= mapBounds.right && z >= mapBounds.top && z <= mapBounds.bottom){
901 *posPtr++ = x;
902 *posPtr++ = z;
903 *posPtr++ = parent->rTable->getUnitRandom();
904 *posPtr++ = parent->rTable->getRangeRandom(0, (float)Math::PI);
905 }
906 }
907 }
908 }
909
910 grassCount = (posPtr - posBuff) / 4;
911 return grassCount;
912}
913
914unsigned int GrassLayer::_populateGrassList_UnfilteredDM(PageInfo page, float *posBuff, unsigned int grassCount)
915{
916 float *posPtr = posBuff;
917
919
920 //Use density map
921 if (!minY && !maxY){
922 //No height range
923 for (unsigned int i = 0; i < grassCount; ++i){
924 //Pick a random position
925 float x = parent->rTable->getRangeRandom((float)page.bounds.left, (float)page.bounds.right);
926 float z = parent->rTable->getRangeRandom((float)page.bounds.top, (float)page.bounds.bottom);
927
928 //Determine whether this grass will be added based on the local density.
929 //For example, if localDensity is .32, grasses will be added 32% of the time.
931 //Add to list
932 *posPtr++ = x;
933 *posPtr++ = z;
934 *posPtr++ = parent->rTable->getUnitRandom();
935 *posPtr++ = parent->rTable->getRangeRandom(0, (float)Math::TWO_PI);
936 }
937 else
938 {
941 }
942
943 }
944 } else {
945 //Height range
946 Real min, max;
947 if (minY) min = minY; else min = Math::NEG_INFINITY;
948 if (maxY) max = maxY; else max = Math::POS_INFINITY;
949
950 for (unsigned int i = 0; i < grassCount; ++i){
951 //Pick a random position
952 float x = parent->rTable->getRangeRandom((float)page.bounds.left, (float)page.bounds.right);
953 float z = parent->rTable->getRangeRandom((float)page.bounds.top, (float)page.bounds.bottom);
954
955 //Determine whether this grass will be added based on the local density.
956 //For example, if localDensity is .32, grasses will be added 32% of the time.
958 //Calculate height
960
961 //Add to list if in range
962 if (y >= min && y <= max){
963 //Add to list
964 *posPtr++ = x;
965 *posPtr++ = z;
966 *posPtr++ = parent->rTable->getUnitRandom();
967 *posPtr++ = parent->rTable->getRangeRandom(0, (float)Math::TWO_PI);
968 }
969 else
970 {
973 }
974 }
975 else
976 {
979 }
980 }
981 }
982
983 grassCount = (posPtr - posBuff) / 4;
984 return grassCount;
985}
986
987unsigned int GrassLayer::_populateGrassList_BilinearDM(PageInfo page, float *posBuff, unsigned int grassCount)
988{
989 float *posPtr = posBuff;
990
992
993 if (!minY && !maxY){
994 //No height range
995 for (unsigned int i = 0; i < grassCount; ++i){
996 //Pick a random position
997 float x = parent->rTable->getRangeRandom((float)page.bounds.left, (float)page.bounds.right);
998 float z = parent->rTable->getRangeRandom((float)page.bounds.top, (float)page.bounds.bottom);
999
1000 //Determine whether this grass will be added based on the local density.
1001 //For example, if localDensity is .32, grasses will be added 32% of the time.
1003 //Add to list
1004 *posPtr++ = x;
1005 *posPtr++ = z;
1006 *posPtr++ = parent->rTable->getUnitRandom();
1007 *posPtr++ = parent->rTable->getRangeRandom(0, (float)Math::TWO_PI);
1008 }
1009 else
1010 {
1011 // why???????????????????????????????????
1014 }
1015 }
1016 } else {
1017 //Height range
1018 Real min, max;
1019 if (minY) min = minY; else min = Math::NEG_INFINITY;
1020 if (maxY) max = maxY; else max = Math::POS_INFINITY;
1021
1022 for (unsigned int i = 0; i < grassCount; ++i){
1023 //Pick a random position
1024 float x = parent->rTable->getRangeRandom((float)page.bounds.left, (float)page.bounds.right);
1025 float z = parent->rTable->getRangeRandom((float)page.bounds.top, (float)page.bounds.bottom);
1026
1027 //Determine whether this grass will be added based on the local density.
1028 //For example, if localDensity is .32, grasses will be added 32% of the time.
1030 //Calculate height
1032
1033 //Add to list if in range
1034 if (y >= min && y <= max){
1035 //Add to list
1036 *posPtr++ = x;
1037 *posPtr++ = z;
1038 *posPtr++ = parent->rTable->getUnitRandom();
1039 *posPtr++ = parent->rTable->getRangeRandom(0, (float)Math::TWO_PI);
1040 }
1041 else
1042 {
1045 }
1046 }
1047 else
1048 {
1051 }
1052 }
1053 }
1054
1055 grassCount = (posPtr - posBuff) / 4;
1056 return grassCount;
1057}
1058
1059void GrassLayer::setColorMap(const String &mapFile, MapChannel channel)
1060{
1061 if (colorMap){
1062 colorMap->unload();
1063 colorMap = NULL;
1064 }
1065 if (mapFile != ""){
1066 colorMap = ColorMap::load(mapFile, channel);
1068 }
1069}
1070
1071void GrassLayer::setColorMap(TexturePtr map, MapChannel channel)
1072{
1073 if (colorMap){
1074 colorMap->unload();
1075 colorMap = NULL;
1076 }
1077 if (map){
1078 colorMap = ColorMap::load(map, channel);
1080 }
1081}
1082
1084{
1085 colorMapFilter = filter;
1086 if (colorMap)
1088}
1089
1091{
1092 if (shaderNeedsUpdate){
1093 shaderNeedsUpdate = false;
1094
1095 //Proceed only if there is no custom vertex shader and the user's computer supports vertex shaders
1096 const RenderSystemCapabilities *caps = Root::getSingleton().getRenderSystem()->getCapabilities();
1097 if (caps->hasCapability(RSC_VERTEX_PROGRAM) && geom->getShadersEnabled())
1098 {
1099 //Calculate fade range
1100 float farViewDist = (float)geom->getDetailLevels().front()->getFarRange();
1101 float fadeRange = farViewDist / 1.2247449f;
1102 //Note: 1.2247449 ~= sqrt(1.5), which is necessary since the far view distance is measured from the centers
1103 //of pages, while the vertex shader needs to fade grass completely out (including the closest corner)
1104 //before the page center is out of range.
1105
1106 //Generate a string ID that identifies the current set of vertex shader options
1107 Ogre::StringStream tmpName;
1108 tmpName << "GrassVS_";
1109 if (animate)
1110 tmpName << "anim_";
1111 if (blend)
1112 tmpName << "blend_";
1113 if (lighting)
1114 tmpName << "lighting_";
1115 tmpName << renderTechnique << "_";
1116 tmpName << fadeTechnique << "_";
1118 tmpName << maxHeight << "_";
1119 tmpName << farViewDist << "_";
1120 tmpName << "vp";
1121 const String vsName = tmpName.str();
1122
1123 //Generate a string ID that identifies the material combined with the vertex shader
1124 const String matName = material->getName() + "_" + vsName;
1125
1126 //Check if the desired material already exists (if not, create it)
1127 MaterialPtr tmpMat = MaterialManager::getSingleton().getByName(matName);
1128 if (!tmpMat)
1129 {
1130 //Clone the original material
1131 tmpMat = material->clone(matName);
1132
1133 //Disable lighting
1134 tmpMat->setLightingEnabled(false);
1135 //tmpMat->setReceiveShadows(false);
1136
1137 //Check if the desired shader already exists (if not, compile it)
1138 String shaderLanguage;
1139 HighLevelGpuProgramPtr vertexShader = Ogre::static_pointer_cast<HighLevelGpuProgram>(HighLevelGpuProgramManager::getSingleton().getByName(vsName));
1140 if (!vertexShader)
1141 {
1142 HighLevelGpuProgramManager& mgr = HighLevelGpuProgramManager::getSingleton();
1143 if (mgr.isLanguageSupported("hlsl"))
1144 shaderLanguage = "hlsl";
1145 else if(mgr.isLanguageSupported("glsl"))
1146 shaderLanguage = "glsl";
1147 else
1148 shaderLanguage = "cg";
1149
1150 //Generate the grass shader
1151 String vertexProgSource;
1152
1153 if(!shaderLanguage.compare("hlsl") || !shaderLanguage.compare("cg"))
1154 {
1155
1156 vertexProgSource =
1157 "void main( \n"
1158 " float4 iPosition : POSITION, \n"
1159 " float4 iColor : COLOR, \n"
1160 " float2 iUV : TEXCOORD0, \n"
1161 " out float4 oPosition : POSITION, \n"
1162 " out float4 oColor : COLOR, \n"
1163 " out float2 oUV : TEXCOORD0, \n";
1164
1165 if (lighting) vertexProgSource +=
1166 " uniform float4 objSpaceLight, \n"
1167 " uniform float4 lightDiffuse, \n"
1168 " uniform float4 lightAmbient, \n";
1169
1170 if (animate) vertexProgSource +=
1171 " uniform float time, \n"
1172 " uniform float frequency, \n"
1173 " uniform float4 direction, \n";
1174
1175 if (fadeTechnique == FADETECH_GROW || fadeTechnique == FADETECH_ALPHAGROW) vertexProgSource +=
1176 " uniform float grassHeight, \n";
1177
1178 if (renderTechnique == GRASSTECH_SPRITE || lighting) vertexProgSource +=
1179 " float4 iNormal : NORMAL, \n";
1180
1181 vertexProgSource +=
1182 " uniform float4x4 worldViewProj, \n"
1183 " uniform float3 camPos, \n"
1184 " uniform float fadeRange ) \n"
1185 "{ \n"
1186 " oColor.rgb = iColor.rgb; \n"
1187 " float4 position = iPosition; \n"
1188 " float dist = distance(camPos.xz, position.xz); \n";
1189
1190 if (lighting)
1191 {
1192 vertexProgSource +=
1193 " float3 light = normalize(objSpaceLight.xyz - (iPosition.xyz * objSpaceLight.w)); \n"
1194 " float diffuseFactor = max(dot(float4(0,1,0,0), light), 0); \n"
1195 " oColor = (lightAmbient + diffuseFactor * lightDiffuse) * iColor; \n";
1196 }
1197 else
1198 {
1199 vertexProgSource +=
1200 " oColor.rgb = iColor.rgb; \n";
1201 }
1202
1203 if (fadeTechnique == FADETECH_ALPHA || fadeTechnique == FADETECH_ALPHAGROW) vertexProgSource +=
1204 //Fade out in the distance
1205 " oColor.a = 2.0f - (2.0f * dist / fadeRange); \n";
1206 else vertexProgSource +=
1207 " oColor.a = 1.0f; \n";
1208
1209 vertexProgSource +=
1210 " float oldposx = position.x; \n";
1211
1212 if (renderTechnique == GRASSTECH_SPRITE) vertexProgSource +=
1213 //Face the camera
1214 " float3 dirVec = (float3)position - (float3)camPos; \n"
1215 " float3 p = normalize(cross(float4(0,1,0,0), dirVec)); \n"
1216 " position += float4(p.x * iNormal.x, iNormal.y, p.z * iNormal.x, 0); \n";
1217
1218 if (animate) vertexProgSource +=
1219 " if (iUV.y == 0.0f){ \n"
1220 //Wave grass in breeze
1221 " float offset = sin(time + oldposx * frequency); \n"
1222 " position += direction * offset; \n"
1223 " } \n";
1224
1225 if (blend && animate) vertexProgSource +=
1226 " else { \n";
1227 else if (blend) vertexProgSource +=
1228 " if (iUV.y != 0.0f){ \n";
1229
1230 if (blend) vertexProgSource +=
1231 //Blend the base of nearby grass into the terrain
1232 " oColor.a = clamp(oColor.a, 0, 1) * 4.0f * ((dist / fadeRange) - 0.1f); \n"
1233 " } \n";
1234
1235 if (fadeTechnique == FADETECH_GROW || fadeTechnique == FADETECH_ALPHAGROW) vertexProgSource +=
1236 " float offset = (2.0f * dist / fadeRange) - 1.0f; \n"
1237 " position.y -= grassHeight * clamp(offset, 0, 1); ";
1238
1239 vertexProgSource +=
1240 " oPosition = mul(worldViewProj, position); \n";
1241
1242 vertexProgSource +=
1243 " oUV = iUV;\n"
1244 "}";
1245 }
1246 else
1247 {
1248 //Must be glsl
1249 if (lighting)
1250 {
1251 vertexProgSource =
1252 "uniform vec4 objSpaceLight; \n"
1253 "uniform vec4 lightDiffuse; \n"
1254 "uniform vec4 lightAmbient; \n";
1255 }
1256
1257 if (animate)
1258 {
1259 vertexProgSource +=
1260 "uniform float time; \n"
1261 "uniform float frequency; \n"
1262 "uniform vec4 direction; \n";
1263 }
1264
1266 {
1267 vertexProgSource +=
1268 "uniform float grassHeight; \n";
1269 }
1270
1271 vertexProgSource +=
1272 "uniform mat4 worldViewProj; \n"
1273 "uniform vec3 camPos; \n"
1274 "uniform float fadeRange; \n"
1275 "\n"
1276 "void main()"
1277 "{ \n"
1278 " vec4 color = gl_Color; \n"
1279 " vec4 position = gl_Vertex; \n"
1280 " float dist = distance(camPos.xz, position.xz); \n";
1281
1282 if (lighting)
1283 {
1284 vertexProgSource +=
1285 " vec3 light = normalize(objSpaceLight.xyz - (gl_Vertex.xyz * objSpaceLight.w)); \n"
1286 " float diffuseFactor = max( dot( vec3(0.0,1.0,0.0), light), 0.0); \n"
1287 " color = (lightAmbient + diffuseFactor * lightDiffuse) * gl_Color; \n";
1288 }
1289 else
1290 {
1291 vertexProgSource +=
1292 " color.xyz = gl_Color.xyz; \n";
1293 }
1294
1296 {
1297 vertexProgSource +=
1298 //Fade out in the distance
1299 " color.w = 2.0 - (2.0 * dist / fadeRange); \n";
1300 }
1301 else
1302 {
1303 vertexProgSource +=
1304 " color.w = 1.0; \n";
1305 }
1306
1308 {
1309 vertexProgSource +=
1310 //Face the camera
1311 " vec3 dirVec = position.xyz - camPos.xyz; \n"
1312 " vec3 p = normalize(cross(vec3(0.0,1.0,0.0), dirVec)); \n"
1313 " position += vec4(p.x * gl_Normal.x, gl_Normal.y, p.z * gl_Normal.x, 0.0); \n";
1314 }
1315
1316 if (animate)
1317 {
1318 vertexProgSource +=
1319 " if (gl_MultiTexCoord0.y == 0.0) \n"
1320 " { \n"
1321 //Wave grass in breeze
1322 " position += direction * sin(time + gl_Vertex.x * frequency); \n"
1323 " } \n";
1324 }
1325
1326 if (blend && animate)
1327 {
1328 vertexProgSource +=
1329 " else \n"
1330 " { \n";
1331 }
1332 else if (blend)
1333 {
1334 vertexProgSource +=
1335 " if (gl_MultiTexCoord0.y != 0.0) \n"
1336 " { \n";
1337 }
1338
1339 if (blend)
1340 {
1341 vertexProgSource +=
1342 //Blend the base of nearby grass into the terrain
1343 " color.w = clamp(color.w, 0.0, 1.0) * 4.0 * ((dist / fadeRange) - 0.1); \n"
1344 " } \n";
1345 }
1346
1348 {
1349 vertexProgSource +=
1350 " position.y -= grassHeight * clamp((2.0 * dist / fadeRange) - 1.0, 0.0, 1.0); \n";
1351 }
1352
1353 vertexProgSource +=
1354 " gl_Position = worldViewProj * position; \n"
1355 " gl_FrontColor = color; \n"
1356 " gl_TexCoord[0] = gl_MultiTexCoord0; \n"
1357 " gl_FogFragCoord = gl_Position.z; \n"
1358 "}";
1359 }
1360
1361 vertexShader = HighLevelGpuProgramManager::getSingleton().createProgram(
1362 vsName,
1363 ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
1364 shaderLanguage, GPT_VERTEX_PROGRAM);
1365
1366 vertexShader->setSource(vertexProgSource);
1367
1368 if (shaderLanguage == "hlsl")
1369 {
1370 vertexShader->setParameter("target", "vs_1_1");
1371 vertexShader->setParameter("entry_point", "main");
1372 }
1373 else if(shaderLanguage == "cg")
1374 {
1375 vertexShader->setParameter("profiles", "vs_1_1 arbvp1");
1376 vertexShader->setParameter("entry_point", "main");
1377 }
1378 // GLSL can only have one entry point "main".
1379
1380 vertexShader->load();
1381 } else
1382 {
1383 shaderLanguage = vertexShader->getLanguage();
1384 }
1385
1386 //Now the vertex shader (vertexShader) has either been found or just generated
1387 //(depending on whether or not it was already generated).
1388
1389 //Apply the shader to the material
1390 Pass *pass = tmpMat->getTechnique(0)->getPass(0);
1391 pass->setVertexProgram(vsName);
1392 GpuProgramParametersSharedPtr params = pass->getVertexProgramParameters();
1393 params->setNamedAutoConstant("worldViewProj", GpuProgramParameters::ACT_WORLDVIEWPROJ_MATRIX);
1394 params->setNamedAutoConstant("camPos", GpuProgramParameters::ACT_CAMERA_POSITION_OBJECT_SPACE);
1395 params->setNamedAutoConstant("fadeRange", GpuProgramParameters::ACT_CUSTOM, 1);
1396
1397 if (animate){
1398 params->setNamedAutoConstant("time", GpuProgramParameters::ACT_CUSTOM, 1);
1399 params->setNamedAutoConstant("frequency", GpuProgramParameters::ACT_CUSTOM, 1);
1400 params->setNamedAutoConstant("direction", GpuProgramParameters::ACT_CUSTOM, 4);
1401 }
1402
1403 if (lighting){
1404 params->setNamedAutoConstant("objSpaceLight", GpuProgramParameters::ACT_LIGHT_POSITION_OBJECT_SPACE);
1405 params->setNamedAutoConstant("lightDiffuse", GpuProgramParameters::ACT_DERIVED_LIGHT_DIFFUSE_COLOUR);
1406 params->setNamedAutoConstant("lightAmbient", GpuProgramParameters::ACT_DERIVED_AMBIENT_LIGHT_COLOUR);
1407 }
1408
1410 params->setNamedAutoConstant("grassHeight", GpuProgramParameters::ACT_CUSTOM, 1);
1411 params->setNamedConstant("grassHeight", maxHeight * 1.05f);
1412 }
1413
1414 pass->getVertexProgramParameters()->setNamedConstant("fadeRange", fadeRange);
1415 }
1416 //Now the material (tmpMat) has either been found or just created (depending on whether or not it was already
1417 //created). The appropriate vertex shader should be applied and the material is ready for use.
1418
1419 //Apply the new material
1420 material = tmpMat;
1421 }
1422 }
1423}
1424
1425
1426unsigned long GrassPage::GUID = 0;
1427
1428void GrassPage::init(PagedGeometry *geom, const Ogre::Any &data)
1429{
1430 sceneMgr = geom->getSceneManager();
1431 rootNode = geom->getSceneNode();
1432}
1433
1438
1439void GrassPage::addEntity(Entity *entity, const Vector3 &position, const Quaternion &rotation, const Vector3 &scale, const Ogre::ColourValue &color)
1440{
1441 SceneNode *node = rootNode->createChildSceneNode();
1442 node->setPosition(position);
1443 nodeList.push_back(node);
1444
1445 entity->setCastShadows(false);
1446 node->attachObject(entity);
1447}
1448
1450{
1451 std::list<SceneNode*>::iterator i;
1452 for (i = nodeList.begin(); i != nodeList.end(); ++i)
1453 {
1454 SceneNode *node = *i;
1455 int numObjs = node->numAttachedObjects();
1456 for(int j = 0; j < numObjs; j++)
1457 {
1458 Entity *ent = static_cast<Entity*>(node->getAttachedObject(j));
1459 if(!ent) continue;
1460 // remove the mesh
1461 MeshManager::getSingleton().remove(ent->getMesh()->getName());
1462 // then the entity
1463 sceneMgr->destroyEntity(ent);
1464 // and finally the scene node
1465 sceneMgr->destroySceneNode(node);
1466 }
1467 }
1468 nodeList.clear();
1469}
1470
1471void GrassPage::setVisible(bool visible)
1472{
1473 std::list<SceneNode*>::iterator i;
1474 for (i = nodeList.begin(); i != nodeList.end(); ++i){
1475 SceneNode *node = *i;
1476 node->setVisible(visible);
1477 }
1478}
1479
1480
1481}
#define ROR_ASSERT(_EXPR)
Definition Application.h:40
static ColorMap * load(const Ogre::String &fileName, MapChannel channel=CHANNEL_COLOR)
void setFilter(MapFilter filter)
Sets the filtering mode used for this color map.
Ogre::uint32 getColorAt(Ogre::Real x, Ogre::Real z, const Ogre::TRect< Ogre::Real > &mapBounds)
Gets the color value at the specified position.
Ogre::Real _getDensityAt_Bilinear(Ogre::Real x, Ogre::Real z, const Ogre::TRect< Ogre::Real > &mapBounds)
MapFilter getFilter()
Returns the filtering mode being used for this density map.
static DensityMap * load(const Ogre::String &fileName, MapChannel channel=CHANNEL_COLOR)
void setFilter(MapFilter filter)
Sets the filtering mode used for this density map.
Ogre::Real _getDensityAt_Unfiltered(Ogre::Real x, Ogre::Real z, const Ogre::TRect< Ogre::Real > &mapBounds)
A data structure giving you full control over grass properties.
void setColorMapFilter(MapFilter filter)
Sets the filtering mode used for color maps.
GrassTechnique renderTechnique
void setMaximumSize(float width, float height)
Sets the maximum size that grass quads/billboards will be.
Ogre::MaterialPtr material
void setDensityMapFilter(MapFilter filter)
Sets the filtering mode used for density maps.
MapFilter colorMapFilter
void setColorMap(const Ogre::String &mapFile, MapChannel channel=CHANNEL_COLOR)
Sets the color map used for this grass layer.
GrassLayer(PagedGeometry *geom, GrassLoader *ldr)
Do not create a GrassLayer directly - use GrassLoader->addLayer()
MapFilter densityMapFilter
Ogre::Real getMaxSlope() const
Get the maximum slope a grass blade can be placed on (as set by setMaxSlope()).
void setLightingEnabled(bool enabled)
Enables/disables lighting on this layer.
void setRenderTechnique(GrassTechnique style, bool blendBase=false)
Sets the technique used to render this grass layer.
unsigned int _populateGrassList_BilinearDM(PageInfo page, float *posBuff, unsigned int grassCount)
GrassLoader * parent
unsigned int _populateGrassList_UnfilteredDM(PageInfo page, float *posBuff, unsigned int grassCount)
void setMinimumSize(float width, float height)
Sets the minimum size that grass quads/billboards will be.
~GrassLayer()
Do not delete a GrassLayer yourself - the GrassLoader will do this automatically when it's deleted.
void setFadeTechnique(FadeTechnique style)
Sets the technique used when fading out distant grass.
DensityMap * densityMap
PagedGeometry * geom
FadeTechnique fadeTechnique
void setAnimationEnabled(bool enabled)
Enables/disables animation on this layer.
void setDensityMap(const Ogre::String &mapFile, MapChannel channel=CHANNEL_COLOR)
Sets the density map used for this grass layer.
unsigned int _populateGrassList_Uniform(PageInfo page, float *posBuff, unsigned int grassCount)
void setMaterialName(const Ogre::String &matName)
Sets the material that is applied to all grass billboards/quads.
A PageLoader-derived object you can use with PagedGeometry to produce realistic grass.
Definition GrassLoader.h:50
Ogre::Timer windTimer
static unsigned long GUID
Ogre::uint8 renderQueue
Ogre::Vector3 windDir
void deleteLayer(GrassLayer *layer)
Removes and deletes a grass layer from the scene.
static Ogre::String getUniqueID()
Ogre::Real(* heightFunction)(Ogre::Real x, Ogre::Real z, void *userData)
GrassLoader(PagedGeometry *geom)
Creates a new GrassLoader object.
void frameUpdate()
INTERNAL FUNCTION - DO NOT USE.
Ogre::Mesh * generateGrass_QUAD(PageInfo &page, GrassLayer *layer, const float *grassPositions, unsigned int grassCount)
Ogre::Mesh * generateGrass_CROSSQUADS(PageInfo &page, GrassLayer *layer, const float *grassPositions, unsigned int grassCount)
Ogre::Mesh * generateGrass_SPRITE(PageInfo &page, GrassLayer *layer, const float *grassPositions, unsigned int grassCount)
void unloadPage(PageInfo &page)
INTERNAL FUNCTION - DO NOT USE.
void loadPage(PageInfo &page)
INTERNAL FUNCTION - DO NOT USE.
GrassLayer * addLayer(const Ogre::String &material)
Adds a grass layer to the scene.
std::list< GrassLayer * > layerList
RandomTable * rTable
PagedGeometry * geom
unsigned long lastTime
friend class GrassLayer
Ogre::SceneNode * rootNode
void addEntity(Ogre::Entity *ent, const Ogre::Vector3 &position, const Ogre::Quaternion &rotation, const Ogre::Vector3 &scale, const Ogre::ColourValue &color)
Add an entity to the page, at the specified position, rotation, and scale.
std::list< Ogre::SceneNode * > nodeList
Ogre::SceneManager * sceneMgr
void removeEntities()
Remove all geometry/entities from the page completely.
void init(PagedGeometry *geom, const Ogre::Any &data)
Prepare a geometry page for use.
void setVisible(bool visible)
Toggle the entire page's visibility.
static unsigned long GUID
void addEntity(Ogre::Entity *ent, const Ogre::Vector3 &position, const Ogre::Quaternion &rotation, const Ogre::Vector3 &scale=Ogre::Vector3::UNIT_SCALE, const Ogre::ColourValue &color=Ogre::ColourValue::White)
Call this from loadPage() to add an entity to the page being loaded.
A class providing highly optimized methods for rendering massive amounts of geometry.
Ogre::uint8 getRenderQueue() const
Returns the rendering queue that paged geometry was constructed with.
const std::list< GeometryPageManager * > & getDetailLevels()
Returns a reference to a list of all added detail levels.
Ogre::SceneNode * getSceneNode() const
Gets the scene node to which all PagedGeometry geometry is attached.
Ogre::SceneManager * getSceneManager() const
Gets the scene manager which is being used to display the geometry.
Ogre::Camera * getCamera() const
Gets the camera which is used to calculate levels of detail.
float getUnitRandom()
Definition RandomTable.h:64
float getRangeRandom(float start, float end)
Definition RandomTable.h:72
void resetRandomIndex()
Definition RandomTable.h:59
MapFilter
Specifies the filtering method used to interpret property maps.
@ MAPFILTER_NONE
Use no filtering - fast, but may appear blocky.
@ MAPFILTER_BILINEAR
Use bilinear filtering - slower, but will appear less blocky.
GrassTechnique
A technique used to render grass.
@ GRASSTECH_QUAD
Grass constructed of randomly placed and rotated quads.
@ GRASSTECH_CROSSQUADS
Grass constructed of two quads forming a "X" cross shape.
@ GRASSTECH_SPRITE
Grass constructed of camera-facing billboard quads.
FadeTechnique
A technique used to fade grass into the distance.
@ FADETECH_ALPHAGROW
Grass that fades in by slowly becoming opaque while it "grows" up out of the ground....
@ FADETECH_ALPHA
Grass that fades into the distance with transparency. Fairly effective in most cases.
@ FADETECH_GROW
Grass that fades in by "growing" up out of the ground. Very effective when grass fades in against the...
MapChannel
Specifies which color channel(s) to extract from an image.
Useful page information supplied to a pageloader.
Ogre::Vector3 centerPoint
The center of the page (simply the middle of the bounds).
TBounds bounds
The page boundaries in which all entities should be placed.
std::vector< Ogre::Mesh * > meshList