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
#ifndef __GrassLoader_H__
13
#define __GrassLoader_H__
15
#include "PagedGeometry.h"
16
#include "PropertyMaps.h"
18
#include <OgrePrerequisites.h>
19
#include <OgreMaterial.h>
20
#include <OgrePixelFormat.h>
21
#include <OgreStringConverter.h>
22
#include <OgreMeshManager.h>
26
#include <OgreTimer.h>
27
#include <OgreCamera.h>
28
#include <OgreVector3.h>
29
#include <OgreQuaternion.h>
30
#include <OgreEntity.h>
31
#include <OgreString.h>
32
#include <OgreStringConverter.h>
33
#include <OgreMaterialManager.h>
34
#include <OgreMaterial.h>
35
#include <OgreHardwareBufferManager.h>
36
#include <OgreHardwareBuffer.h>
37
#include <OgreMeshManager.h>
39
#include <OgreSubMesh.h>
40
#include <OgreLogManager.h>
41
#include <OgreTextureManager.h>
42
#include <OgreHardwarePixelBuffer.h>
43
#include <OgreRenderSystem.h>
44
#include <OgreRenderSystemCapabilities.h>
45
#include <OgreHighLevelGpuProgram.h>
46
#include <OgreHighLevelGpuProgramManager.h>
48
namespace PagedGeometry {
53
/** \brief A PageLoader-derived object you can use with PagedGeometry to produce realistic grass.
55
Using a GrassLoader is simple - simply create an instance, attach it to your PagedGeometry object
56
with PagedGeometry::setPageLoader(), and add your grass. Important: For best performance, it is
57
recommended that you use GrassPage (included in GrassLoader.h) to display geometry loaded by GrassLoader.
59
This page type is designed for best performance with this grass system. BatchPage
60
will work, although performance will be reduced slightly, and ImpostorPage will run extremely slow.
62
When creating a GrassLoader you must specify the class which will be used for creating layers. By default a GrassLayer class is provided, which can be used as this:
64
::PagedGeometry::GrassLoader<GrassLayer> loader = new ::PagedGeometry::GrassLoader<GrassLayer>(grass);
66
It's also possible for you to provide your own GrassLayer implementation by overriding the class GrassLayerBase. This will allow you to have much more control over how the grass is placed.
69
To add grass, just call addLayer(). addLayer() returns a GrassLayer object pointer, which you should
70
use to further configure your newly added grass. Properties like size, density, color, animation, etc.
71
can be controlled through the GrassLayer class.
73
\note By default, the GrassLoader doesn't know what shape your terrain so all grass will be placed at
74
0 height. To inform GrassLoader of the shape of your terrain, you must specify a height function
75
that returns the height (y coordinate) of your terrain at the given x and z coordinates. See
76
the TreeLoader2D::setHeightFunction() documentation for more information.
78
\warning If you attempt to use Ogre's scene queries to get the terrain height,
79
keep in mind that calculating the height of Ogre's built-in terrain this way can
80
be VERY slow if not done properly, and may cause stuttering due to long paging delays.
82
template <typename TGrassLayer>
83
class GrassLoader: public PageLoader
86
/** \brief Creates a new GrassLoader object.
87
\param geom The PagedGeometry object that this GrassLoader will be assigned to.*/
88
inline GrassLoader(PagedGeometry *geom);
91
/** \brief Adds a grass layer to the scene.
92
\param material The initial grass texture to use (this can be changed later).
94
Since all grass is potentially infinite, it is not added like normal entities which
95
have a specific position. Instead you add a grass "layer" to the scene. A grass layer
96
is a "carpet" of a single type of grass that gets applied everywhere in your world.
97
If you want multiple types of grass with different appearances, you'll have to add
98
a multiple grass layers for each style.
100
Of course, a grass layer is not completely uniform. The GrassLayer class contains
101
functions to vary grass size and density levels as desired.
103
\see GrassLayer class for more information. */
104
TGrassLayer *addLayer(const Ogre::String &material);
106
/** \brief Removes and deletes a grass layer from the scene
108
This function simply deletes a GrassLayer previously created with addLayer(). */
109
void deleteLayer(TGrassLayer *layer);
111
/** \brief Returns a list of added grass layers.
113
This function returns a std::list<GrassLayer*> reference, which contains all grass
114
layers which have been added to this GrassLoader. */
115
inline std::list<TGrassLayer*> &getLayerList() { return layerList; }
117
/** \brief Sets the global wind direction for this GrassLoader.
119
GrassLayer animation properties are used to configure the most of the animation
120
behavour (sway length, speed, etc.), but wind direction is not included in GrassLayer
121
since this is really a global property. Using this function, you can set the "global"
122
wind direction which affects all animated grass associated with this PageLoader.
124
Default value is Vector3::UNIT_X.
126
\note This only affects grass layers which have breeze animations enabled.
128
inline void setWindDirection(Ogre::Vector3 &dir) { windDir = dir; }
130
/** \brief Returns the global wind direction for this GrassLoader.
132
\see setWindDirection() for more information about the wind direction. */
133
inline Ogre::Vector3 &getWindDirection() { return windDir; }
135
/** \brief Sets the global density factor for this GrassLoader.
137
This function can be used to up-scale or down-scale the density of all grass
138
associated with this GrassLoader. This is typically used to provide the user
139
the option to reduce grass density for better performance on slower machines.
141
Final density values are calculated by multiplying the layer density by this
142
density factor. For example, a layer with .4 density and a density factor of .5
143
will result in a final density of .2 (.5 * .4)
145
By default, the density factor is set to 1.0 so the layer density is not modified.
147
inline void setDensityFactor(float density) { densityFactor = density; }
149
/** \brief Returns the global density factor for this GrassLoader.
151
\see setDensityFactor() for more information about the density factor. */
152
inline float getDensityFactor() { return densityFactor; }
154
/** \brief Sets the render queue group the grass will be rendered through
155
\param queueID Enumerated value of the queue group to use
157
Like Ogre's MovableObject::setRenderQueueGroup(), this allows you to customize
158
the rendering order of your scene. Since grass is transparent, it's likely that
159
you may encounter alpha-sorting issues between grass and your particle effects,
160
for example. In this case you can use this function to adjust the rendering order
161
of the grass to fix the problem.
163
If you don't call this function, the RENDER_QUEUE_6 queue will be used.
165
\note Once grass is loaded and being rendered, this won't have any effect on it.
166
Be sure to call this function before the scene begins rendering, otherwise you will
167
have to call PagedGeometry::reloadGeometry() to force a reload in order for the changes
169
inline void setRenderQueueGroup(Ogre::uint8 queueID) { renderQueue = queueID; }
171
/** \brief Sets the height function used to calculate grass Y coordinates
172
\param heightFunction A pointer to a height function
174
Unless you want all your grass placed at 0 height, you need to specify a height function
175
so GrassLoader will be able to calculate the Y coordinate. The height function given
176
to setHeightFunction() should use the following prototype (although you can name the
177
function anything you want):
180
Real getHeightAt(Real x, Real z, void *userData);
183
\note If you're not using the default coordinate system (where x = right, z = back), the
184
x/z parameters will actually be representing the appropriate equivalents.
186
The userData parameter allows you to include any additional data you want when your height
187
function is called, and is completely optional (although you can't actually omit it from the
188
declaration, you can ignore it). Any userData value you choose to supply to setHeightFunction()
189
will be passed on to your height function every time it is called.
191
After you've defined a height function, using setHeightFunction is easy:
194
pageLoader2D->setHeightFunction(&getHeightAt);
195
//Or (if you want to pass additional data on to your height function)...
196
pageLoader2D->setHeightFunction(&getHeightAt, myUserData);
199
In most cases, you may not even need to use the extra "userData" parameter, but it's there in
200
the event that your height function needs extra contextual data.
202
void setHeightFunction(Ogre::Real (*heightFunction)(Ogre::Real x, Ogre::Real z, void *userData), void *userData = NULL) {
203
this->heightFunction = heightFunction;
204
heightFunctionUserData = userData;
208
/** INTERNAL FUNCTION - DO NOT USE */
209
void loadPage(PageInfo &page);
210
/** INTERNAL FUNCTION - DO NOT USE */
211
void unloadPage(const PageInfo &page);
212
/** INTERNAL FUNCTION - DO NOT USE */
216
friend class GrassLayer;
219
Ogre::Mesh *generateGrass_QUAD(PageInfo &page, TGrassLayer *layer, float *grassPositions, unsigned int grassCount);
220
Ogre::Mesh *generateGrass_CROSSQUADS(PageInfo &page, TGrassLayer *layer, float *grassPositions, unsigned int grassCount);
221
Ogre::Mesh *generateGrass_SPRITE(PageInfo &page, TGrassLayer *layer, float *grassPositions, unsigned int grassCount);
223
//List of grass types
224
std::list<TGrassLayer*> layerList;
227
Ogre::Real (*heightFunction)(Ogre::Real x, Ogre::Real z, void *userData); //Pointer to height function
228
void *heightFunctionUserData;
232
Ogre::uint8 renderQueue;
236
Ogre::Timer windTimer;
237
Ogre::Vector3 windDir;
238
unsigned long lastTime;
240
static unsigned long GUID;
241
static inline Ogre::String getUniqueID()
243
return "GrassLDR" + Ogre::StringConverter::toString(++GUID);
251
/** \brief A technique used to render grass. Passed to GrassLayer::setRenderTechnique(). */
254
/// Grass constructed of randomly placed and rotated quads
256
/// Grass constructed of two quads forming a "X" cross shape
257
GRASSTECH_CROSSQUADS,
258
/// Grass constructed of camera-facing billboard quads
262
/** \brief A technique used to fade grass into the distance. Passed to GrassLayer::setFadeTechnique(). */
265
/// Grass that fades into the distance with transparency. Fairly effective in most cases.
267
/// Grass that fades in by "growing" up out of the ground. Very effective when grass fades in against the sky, or with alpha-rejected grass.
269
/// Grass that fades in by slowly becoming opaque while it "grows" up out of the ground. Effective with alpha grass fading in against the sky.
276
/** \brief Sets the material that is applied to all grass billboards/quads */
277
void setMaterialName(const Ogre::String &matName);
279
/** \brief Sets the minimum size that grass quads/billboards will be */
280
void setMinimumSize(float width, float height);
282
/** \brief Sets the maximum size that grass quads/billboards will be */
283
void setMaximumSize(float width, float height);
285
/** \brief Sets the technique used to render this grass layer
286
\param style The GrassTechnique style used to display grass.
287
\param blendBase Whether or not grass base blending is enabled.
289
The "style" setting allows you to choose from various construction methods, such as
290
sprite-style grass quads, plain 3D quads, etc. See the GrassTechnique documentation
291
for more information about this option. GRASSTECH_QUAD is used by default.
293
Setting "blendBase" to true will enable grass base blending, a technique which helps
294
reduce the unnatural flat appearance of grass quads near the camera. Since the flatness
295
is most obvious where the grass intersects the terrain, this technique attempts to
296
smoothly blend the base of near-by grass into the terrain.
298
\note Base blending does not work well with alpha-rejected textures.
300
void setRenderTechnique(GrassTechnique style, bool blendBase = false);
302
/** \brief Sets the technique used when fading out distant grass
303
\param style The FadeTechnique style used to fade grass.
305
This "style" setting allows you to choose from various fade techniques. Depending on
306
your scene, certain techniques may look better than others. The most compatible method
307
is FADETECH_ALPHA (used by default), although better results can usually be achieved
308
with other methods. See the FadeTechnique documentation for more information.
310
void setFadeTechnique(FadeTechnique style);
312
/** \brief Enables/disables animation on this layer
314
Always use this function to disable animation, rather than setting SwayLength or SwaySpeed
315
to 0. This function will use a different vertex shader which means improved performance
316
when animation is disabled.
318
void setAnimationEnabled(bool enabled);
320
/** \brief Sets how far grass should sway back and forth
322
\note Since this is measured in world units, you may have to adjust this depending on
323
the size of your grass as set by setMinimumSize() and setMaximumSize().*/
324
void setSwayLength(float mag) { animMag = mag; }
326
/** \brief Sets the sway speed of the grass (measured in "sways-per-second") */
327
void setSwaySpeed(float speed) { animSpeed = speed; }
329
/** \brief Sets the smooth distribution (positional phase shift) of the grass swaying animation
331
If you set this to 0, grass animation will look very unnatural, since all the grass sway motions
332
will be in perfect synchronization (everything sways to the right, then everything sways to the
333
left, etc.) This sets the "positional phase shift", which gives the grass a "wave" like phase
334
distribution. The higher this value is, the more "chaotic" the wind will appear. Lower values give
335
a smoother breeze appearance, but values too high can look unrealistic.
337
void setSwayDistribution(float freq) { animFreq = freq; }
339
/** \brief Sets the boundaries of the density/color maps
340
\param bounds The map boundary
342
By default, the GrassLayer has no knowledge of your terrain/world boundaries, so you must
343
use this function to specify a rectangular/square area of your world, otherwise density/color maps
344
won't work properly. The boundary given to this function defines the area where density/color
345
maps take effect. Normally this is set to your terrain's bounds so the density/color map is aligned
346
to your heightmap, but you could apply it anywhere you want.
348
\note The grass system is infinite, so there's no need to worry about using too expansive
349
boundaries. This setting simply configures the behavior of density and color maps. */
350
virtual void setMapBounds(const Ogre::TRect<Ogre::Real> &bounds)
357
* Calculates the max number of grass instances for this layer.
358
* @param densityFactor The density factor set on the grass loader
359
* @param volume The volume, in world units, to fill
360
* @return The max number of grass instances to create.
362
virtual unsigned int calculateMaxGrassCount(float densityFactor, float volume) = 0;
366
//Used by GrassLoader::loadPage() - populates an array with grass.
367
//Returns the final number of grasses, which will always be <= grassCount
368
virtual unsigned int _populateGrassList(PageInfo page, float *posBuff, unsigned int grassCount) = 0;
370
//Updates the vertex shader used by this layer based on the animate enable status
371
void _updateShaders();
373
//Grass material/shape properties
374
Ogre::MaterialPtr material;
375
float minWidth, maxWidth;
376
float minHeight, maxHeight;
378
FadeTechnique fadeTechnique;
379
GrassTechnique renderTechnique;
382
Ogre::TRect<Ogre::Real> mapBounds;
384
//Grass shader properties
385
bool animate, blend, shaderNeedsUpdate;
386
float animMag, animSpeed, animFreq;
388
//Current frame of animation for this layer
396
/** \brief A data structure giving you full control over grass properties.
398
Grass is added to the scene through GrassLoader::addLayer(). Through this class you
399
can configure your grass layer any way you like - size, density, render technique,
400
animation, etc. Simply call the appropriate "set" member function to set the desired property.
402
Remember that you cannot create or delete layers directly. Layers can only be created
403
with GrassLoader::addLayer(), and may not be deleted manually (they will be deleted when
404
the associated GrassLoader is deleted).
406
class GrassLayer : public GrassLayerBase
410
/** \brief Sets the maximum density (measured in grass quads/billboards per square unit) of grass */
411
void setDensity(float density) { this->density = density; }
413
/** \brief Sets a minimum / maximum height where grass may appear
414
\param minHeight Sets the minimum height grass may have. 0 = no minimum
415
\param maxHeight Sets the maximum height grass may have. 0 = no maximum
417
By default grass appears at all altitudes. You can use this function to restrict grass to a
418
certain height range. For example, if sea level is at 100 units Y, you might restrict this
419
layer to display only above 100 units (so your grass doesn't grow under water).
421
It's possible to use density maps (see setDensityMap()) to achieve similar results, but if
422
your density map is extremely low resolution, this function may be the only practical option
423
to prevent grass from growing under water (when used in combination with your density map).
425
Setting minHeight to 0 means grass has no minimum height - it can grow as low as necessary.
426
Similarly, setting maxHeight to 0 means grass has no maximum height - it can grow as high
428
void setHeightRange(float minHeight, float maxHeight = 0) { minY = minHeight; maxY = maxHeight; }
430
/** \brief Sets the density map used for this grass layer
431
\param mapFile The density map image
432
\param channel The color channel(s) to from the image to interpret as density
434
A density map is simply a greyscale image, similar to a heightmap, that specifies the grass
435
density on your map. Full pixel intensity indicates that grass should be fully dense at that
436
point (the maximum density is specified by GrassLayer::setDensity()), while no pixel intensity
437
indicates that no grass should appear at that location.
439
The channel parameter allows you to extract the density information from the image's
440
red, green, blue, alpha, or color values. For example, you may store density values in the
441
alpha channel, in which case you would use CHANNEL_ALPHA. By default, CHANNEL_COLOR is used,
442
which means the image color is converted to greyscale internally and used as a density map.
444
Note that GrassLayer by default has no idea of your terrain/world boundaries, so you
445
must specify a rectangular/square area of your world that is affected by density/color maps.
446
To do this, use the setMapBounds() function. Normally this is set to your terrain's bounds
447
so the density/color map is aligned to your heightmap, but you could apply it anywhere you
449
void setDensityMap(const Ogre::String &mapFile, MapChannel channel = CHANNEL_COLOR);
451
/** \brief Sets the density map used for this grass layer
453
Overloaded to accept a Texture object. See the original setDensityMap() documentation above
454
for more detailed information on density maps.
456
\note The texture data you provide is copied into the GrassLayer's own memory space, so you
457
can delete the texture after calling this function without risk of crashing. */
458
void setDensityMap(Ogre::Texture *map, MapChannel channel = CHANNEL_COLOR);
460
/** \brief Sets the filtering mode used for density maps
462
This function can be used to set the filtering mode used for your density map when generating
463
grass. By default, bilinear filtering is used (MAPFILTER_BILINEAR). If you disable filtering
464
by using MAPFILTER_NONE, the resulting layout of your grass may look square and blocky,
465
depending on the resolution of your density map.
467
MAPFILTER_NONE is slightly faster than MAPFILTER_BILINEAR, so use it if you don't notice any
468
considerable blockyness.
470
void setDensityMapFilter(MapFilter filter);
472
/** \brief Sets the color map used for this grass layer
473
\param mapFile The color map image
474
\param channel The color channel(s) to from the image to use
476
A color map is simply a texture that allows you to vary the color and shading of grass
477
across your world for a more realistic look. For example, adding a dark spot to the center
478
of your color map will make grass near the center of your terrain look darker, as long as
479
you have the color map aligned to your terrain (see setMapBounds()).
481
The channel parameter allows you to extract the color information from the image's
482
red, green, blue, alpha, or color values. For example, you may store the desired shade of your
483
grass in the red channel of an image, in which case you would use CHANNEL_RED (when you choose
484
a single channel, it is converted to a greyscale color). By default, CHANNEL_COLOR is used,
485
which uses the full color information available in the image.
487
Remember that GrassLayer by default has no idea of your terrain/world boundaries, so you
488
must specify a rectangular/square area of your world that is affected by density/color maps.
489
To do this, use the setMapBounds() function. Normally this is set to your terrain's bounds
490
so the density/color map is aligned to your heightmap, but you could apply it anywhere you
492
void setColorMap(const Ogre::String &mapFile, MapChannel channel = CHANNEL_COLOR);
494
/** \brief Sets the color map used for this grass layer
496
Overloaded to accept a Texture object. See the original setColorMap() documentation above
497
for more detailed information on color maps.
499
\note The texture data you provide is copied into RAM, so you can delete the texture after
500
calling this function without risk of crashing. */
501
void setColorMap(Ogre::Texture *map, MapChannel channel = CHANNEL_COLOR);
503
/** \brief Sets the filtering mode used for color maps
505
This function can be used to set the filtering mode used for your color map when generating
506
grass. By default, bilinear filtering is used (MAPFILTER_BILINEAR). If you disable filtering
507
by using MAPFILTER_NONE, the resulting grass coloration may appear slightly pixelated,
508
depending on the resolution of your color map.
510
MAPFILTER_NONE is slightly faster than MAPFILTER_BILINEAR, so use it if you don't notice any
511
considerable pixelation.
513
void setColorMapFilter(MapFilter filter);
515
/** \brief Gets a pointer to the density map being used
517
You can use this function to access the internal density map object used by the GrassLoader.
518
Through this object you can directly manipulate the pixels of the density map, among other
521
Note that although you can edit the density map in real-time through this class, the changes
522
won't be uploaded to your video card until you call PagedGeometry::reloadGeometry(). If you
523
don't, the grass you see will remain unchanged. */
524
DensityMap *getDensityMap() { return densityMap; }
526
/** \brief Gets a pointer to the color map being used
528
You can use this function to access the internal color map object used by the GrassLoader.
529
Through this object you can directly manipulate the pixels of the color map, among other
532
Note that although you can edit the color map in real-time through this class, the changes
533
won't be uploaded to your video card until you call PagedGeometry::reloadGeometry(). If you
534
don't, the grass you see will remain unchanged. */
535
ColorMap *getColorMap() { return colorMap; }
538
/** \brief Sets the boundaries of the density/color maps
539
\param bounds The map boundary
541
By default, the GrassLayer has no knowledge of your terrain/world boundaries, so you must
542
use this function to specify a rectangular/square area of your world, otherwise density/color maps
543
won't work properly. The boundary given to this function defines the area where density/color
544
maps take effect. Normally this is set to your terrain's bounds so the density/color map is aligned
545
to your heightmap, but you could apply it anywhere you want.
547
\note The grass system is infinite, so there's no need to worry about using too expansive
548
boundaries. This setting simply configures the behavior of density and color maps. */
549
virtual void setMapBounds(const Ogre::TRect<Ogre::Real> &bounds)
551
GrassLayerBase::setMapBounds(bounds);
553
densityMap->setMapBounds(mapBounds);
555
colorMap->setMapBounds(mapBounds);
559
* Calculates the max number of grass instances for this layer.
560
* @param densityFactor The density factor set on the grass loader
561
* @param volume The volume, in world units, to fill
562
* @return The max number of grass instances to create.
564
virtual unsigned int calculateMaxGrassCount(float densityFactor, float volume);
568
* If there's a colormap registered use that for lookup, else return fullbright.
573
inline Ogre::uint32 getColorAt(float x, float z)
576
return colorMap->getColorAt(x, z);
582
friend class GrassLoader<GrassLayer>;
584
/** \brief Do not create a GrassLayer directly - use GrassLoader->addLayer() */
585
GrassLayer(PagedGeometry *geom, GrassLoader<GrassLayer> *ldr);
587
/** \brief Do not delete a GrassLayer yourself - the GrassLoader will do this automatically when it's deleted */
590
//Used by GrassLoader::loadPage() - populates an array with grass.
591
//Returns the final number of grasses, which will always be <= grassCount
592
virtual unsigned int _populateGrassList(PageInfo page, float *posBuff, unsigned int grassCount);
594
//Used by GrassLoader::loadPage() - populates an array with a uniform distribution of grass
595
//Returns the final number of grasses, which will always be <= grassCount
596
unsigned int _populateGrassList_Uniform(PageInfo page, float *posBuff, unsigned int grassCount);
598
//Used by GrassLoader::loadPage() - populates an array of grass positions based on the density map
599
//Returns the final number of grasses, which will always be <= grassCount
600
unsigned int _populateGrassList_UnfilteredDM(PageInfo page, float *posBuff, unsigned int grassCount);
602
//Variation of _populateGrassList(), using bilinear filtering on the density map lookups
603
//Returns the final number of grasses, which will always be <= grassCount
604
unsigned int _populateGrassList_BilinearDM(PageInfo page, float *posBuff, unsigned int grassCount);
607
GrassLoader<GrassLayer> *parent;
609
//Grass material/shape properties
615
DensityMap *densityMap;
616
MapFilter densityMapFilter;
619
MapFilter colorMapFilter;
624
/** \brief A custom page type designed specifically for use with GrassLoader.
626
You can use this in your own project if you want, but remember that no optimizations
627
are performed. The given entity is simply cloned and attached to a new scene node as
628
quickly and simply as possible (this means there's no batching overhead as in BatchPage,
629
but it also means potentially poor performance if you don't know what you're doing).
631
class GrassPage: public GeometryPage
634
void init(PagedGeometry *geom);
637
void addEntity(Ogre::Entity *ent, const Ogre::Vector3 &position, const Ogre::Quaternion &rotation, const Ogre::Vector3 &scale, const Ogre::ColourValue &color);
638
void removeEntities();
639
void setFade(bool enabled, Ogre::Real visibleDist, Ogre::Real invisibleDist) {}
640
void setVisible(bool visible);
643
Ogre::SceneManager *sceneMgr;
644
Ogre::SceneNode *rootNode;
646
std::list<Ogre::SceneNode*> nodeList;
648
static unsigned long GUID;
649
static inline Ogre::String getUniqueID()
651
return "GrassPage" + Ogre::StringConverter::toString(++GUID);
655
template <class TGrassLayer>
656
GrassLoader<TGrassLayer>::GrassLoader(PagedGeometry *geom)
658
GrassLoader<TGrassLayer>::geom = geom;
660
heightFunction = NULL;
661
heightFunctionUserData = NULL;
663
windDir = Ogre::Vector3::UNIT_X;
664
densityFactor = 1.0f;
665
renderQueue = Ogre::RENDER_QUEUE_6;
671
template <class TGrassLayer>
672
GrassLoader<TGrassLayer>::~GrassLoader()
674
typename std::list<TGrassLayer*>::iterator it;
675
for (it = layerList.begin(); it != layerList.end(); ++it){
681
template <class TGrassLayer>
682
TGrassLayer *GrassLoader<TGrassLayer>::addLayer(const Ogre::String &material)
684
TGrassLayer *layer = new TGrassLayer(geom, this);
685
layer->setMaterialName(material);
686
layerList.push_back(layer);
691
template <class TGrassLayer>
692
void GrassLoader<TGrassLayer>::deleteLayer(TGrassLayer *layer)
694
layerList.remove(layer);
698
template <class TGrassLayer>
699
void GrassLoader<TGrassLayer>::frameUpdate()
701
unsigned long currentTime = windTimer.getMilliseconds();
702
unsigned long ellapsedTime = currentTime - lastTime;
703
lastTime = currentTime;
705
float ellapsed = ellapsedTime / 1000.0f;
707
//Update the vertex shader parameters
708
typename std::list<TGrassLayer*>::iterator it;
709
for (it = layerList.begin(); it != layerList.end(); ++it){
710
TGrassLayer *layer = *it;
712
layer->_updateShaders();
714
Ogre::GpuProgramParametersSharedPtr params = layer->material->getTechnique(0)->getPass(0)->getVertexProgramParameters();
716
//Increment animation frame
717
layer->waveCount += ellapsed * (layer->animSpeed * Ogre::Math::PI);
718
if (layer->waveCount > Ogre::Math::PI*2) layer->waveCount -= Ogre::Math::PI*2;
720
//Set vertex shader parameters
721
params->setNamedConstant("time", layer->waveCount);
722
params->setNamedConstant("frequency", layer->animFreq);
724
Ogre::Vector3 direction = windDir * layer->animMag;
725
params->setNamedConstant("direction", Ogre::Vector4(direction.x, direction.y, direction.z, 0));
731
template <class TGrassLayer>
732
void GrassLoader<TGrassLayer>::loadPage(PageInfo &page)
734
//Seed random number generator based on page indexes
735
Ogre::uint16 xSeed = static_cast<Ogre::uint16>(page.xIndex % 0xFFFF);
736
Ogre::uint16 zSeed = static_cast<Ogre::uint16>(page.zIndex % 0xFFFF);
737
Ogre::uint32 seed = (xSeed << 16) | zSeed;
740
typename std::list<TGrassLayer*>::iterator it;
741
for (it = layerList.begin(); it != layerList.end(); ++it){
742
TGrassLayer *layer = *it;
744
//Calculate how much grass needs to be added
745
float volume = page.bounds.width() * page.bounds.height();
746
unsigned int grassCount = layer->calculateMaxGrassCount(densityFactor, volume);
748
//The vertex buffer can't be allocated until the exact number of polygons is known,
749
//so the locations of all grasses in this page must be precalculated.
751
//Precompute grass locations into an array of floats. A plain array is used for speed;
752
//there's no need to use a dynamic sized array since a maximum size is known.
753
float *position = new float[grassCount*2];
754
grassCount = layer->_populateGrassList(page, position, grassCount);
756
//Don't build a mesh unless it contains something
757
if (grassCount != 0){
758
Ogre::Mesh *mesh = NULL;
759
switch (layer->renderTechnique){
761
mesh = generateGrass_QUAD(page, layer, position, grassCount);
763
case GRASSTECH_CROSSQUADS:
764
mesh = generateGrass_CROSSQUADS(page, layer, position, grassCount);
766
case GRASSTECH_SPRITE:
767
mesh = generateGrass_SPRITE(page, layer, position, grassCount);
772
//Add the mesh to PagedGeometry
773
Ogre::Entity *entity = geom->getCamera()->getSceneManager()->createEntity(getUniqueID(), mesh->getName());
774
entity->setRenderQueueGroup(renderQueue);
775
entity->setCastShadows(false);
776
addEntity(entity, page.centerPoint, Ogre::Quaternion::IDENTITY, Ogre::Vector3::UNIT_SCALE);
777
geom->getSceneManager()->destroyEntity(entity);
779
//Store the mesh pointer
780
page.userData = mesh;
783
page.userData = NULL;
786
//Delete the position list
791
template <class TGrassLayer>
792
void GrassLoader<TGrassLayer>::unloadPage(const PageInfo &page)
795
Ogre::Mesh *mesh = (Ogre::Mesh*)page.userData;
797
Ogre::MeshManager::getSingleton().remove(mesh->getName());
799
template <class TGrassLayer>
800
Ogre::Mesh *GrassLoader<TGrassLayer>::generateGrass_QUAD(PageInfo &page, TGrassLayer *layer, float *grassPositions, unsigned int grassCount)
802
//Calculate the number of quads to be added
803
unsigned int quadCount;
804
quadCount = grassCount;
806
//Create manual mesh to store grass quads
807
Ogre::MeshPtr mesh = Ogre::MeshManager::getSingleton().createManual(getUniqueID(), Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
808
Ogre::SubMesh *subMesh = mesh->createSubMesh();
809
subMesh->useSharedVertices = false;
811
//Setup vertex format information
812
subMesh->vertexData = new Ogre::VertexData;
813
subMesh->vertexData->vertexStart = 0;
814
subMesh->vertexData->vertexCount = 4 * quadCount;
816
Ogre::VertexDeclaration* dcl = subMesh->vertexData->vertexDeclaration;
818
dcl->addElement(0, offset, Ogre::VET_FLOAT3, Ogre::VES_POSITION);
819
offset += Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT3);
820
dcl->addElement(0, offset, Ogre::VET_COLOUR, Ogre::VES_DIFFUSE);
821
offset += Ogre::VertexElement::getTypeSize(Ogre::VET_COLOUR);
822
dcl->addElement(0, offset, Ogre::VET_FLOAT2, Ogre::VES_TEXTURE_COORDINATES);
823
offset += Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT2);
825
//Populate a new vertex buffer with grass
826
Ogre::HardwareVertexBufferSharedPtr vbuf = Ogre::HardwareBufferManager::getSingleton()
827
.createVertexBuffer(offset, subMesh->vertexData->vertexCount, Ogre::HardwareBuffer::HBU_STATIC_WRITE_ONLY, false);
828
float* pReal = static_cast<float*>(vbuf->lock(Ogre::HardwareBuffer::HBL_DISCARD));
830
//Calculate size variance
831
float rndWidth = layer->maxWidth - layer->minWidth;
832
float rndHeight = layer->maxHeight - layer->minHeight;
834
float minY = Ogre::Math::POS_INFINITY, maxY = Ogre::Math::NEG_INFINITY;
835
float *posPtr = grassPositions; //Position array "iterator"
836
for (Ogre::uint16 i = 0; i < grassCount; ++i)
838
//Get the x and z positions from the position array
842
//Get the color at the grass position
843
Ogre::uint32 color(layer->getColorAt(x, z));
846
float rnd = Ogre::Math::UnitRandom(); //The same rnd value is used for width and height to maintain aspect ratio
847
float halfScaleX = (layer->minWidth + rndWidth * rnd) * 0.5f;
848
float scaleY = (layer->minHeight + rndHeight * rnd);
851
float angle = Ogre::Math::RangeRandom(0, Ogre::Math::TWO_PI);
852
float xTrans = Ogre::Math::Cos(angle) * halfScaleX;
853
float zTrans = Ogre::Math::Sin(angle) * halfScaleX;
855
//Calculate heights and edge positions
856
float x1 = x - xTrans, z1 = z - zTrans;
857
float x2 = x + xTrans, z2 = z + zTrans;
861
y1 = heightFunction(x1, z1, heightFunctionUserData);
862
y2 = heightFunction(x2, z2, heightFunctionUserData);
869
*pReal++ = (x1 - page.centerPoint.x); *pReal++ = (y1 + scaleY); *pReal++ = (z1 - page.centerPoint.z); //pos
870
*((Ogre::uint32*)pReal++) = color; //color
871
*pReal++ = 0; *pReal++ = 0; //uv
873
*pReal++ = (x2 - page.centerPoint.x); *pReal++ = (y2 + scaleY); *pReal++ = (z2 - page.centerPoint.z); //pos
874
*((Ogre::uint32*)pReal++) = color; //color
875
*pReal++ = 1; *pReal++ = 0; //uv
877
*pReal++ = (x1 - page.centerPoint.x); *pReal++ = (y1); *pReal++ = (z1 - page.centerPoint.z); //pos
878
*((Ogre::uint32*)pReal++) = color; //color
879
*pReal++ = 0; *pReal++ = 1; //uv
881
*pReal++ = (x2 - page.centerPoint.x); *pReal++ = (y2); *pReal++ = (z2 - page.centerPoint.z); //pos
882
*((Ogre::uint32*)pReal++) = color; //color
883
*pReal++ = 1; *pReal++ = 1; //uv
886
if (y1 < minY) minY = y1;
887
if (y2 < minY) minY = y2;
888
if (y1 + scaleY > maxY) maxY = y1 + scaleY;
889
if (y2 + scaleY > maxY) maxY = y2 + scaleY;
893
subMesh->vertexData->vertexBufferBinding->setBinding(0, vbuf);
895
//Populate index buffer
896
subMesh->indexData->indexStart = 0;
897
subMesh->indexData->indexCount = 6 * quadCount;
898
subMesh->indexData->indexBuffer = Ogre::HardwareBufferManager::getSingleton()
899
.createIndexBuffer(Ogre::HardwareIndexBuffer::IT_16BIT, subMesh->indexData->indexCount, Ogre::HardwareBuffer::HBU_STATIC_WRITE_ONLY);
900
Ogre::uint16* pI = static_cast<Ogre::uint16*>(subMesh->indexData->indexBuffer->lock(Ogre::HardwareBuffer::HBL_DISCARD));
901
for (Ogre::uint16 i = 0; i < quadCount; ++i)
903
Ogre::uint16 offset = i * 4;
914
subMesh->indexData->indexBuffer->unlock();
917
Ogre::AxisAlignedBox bounds(page.bounds.left - page.centerPoint.x, minY, page.bounds.top - page.centerPoint.z,
918
page.bounds.right - page.centerPoint.x, maxY, page.bounds.bottom - page.centerPoint.z);
919
mesh->_setBounds(bounds);
920
Ogre::Vector3 temp = bounds.getMaximum() - bounds.getMinimum();
921
mesh->_setBoundingSphereRadius(temp.length() * 0.5f);
923
Ogre::LogManager::getSingleton().setLogDetail(static_cast<Ogre::LoggingLevel>(0));
925
Ogre::LogManager::getSingleton().setLogDetail(Ogre::LL_NORMAL);
927
//Apply grass material to mesh
928
subMesh->setMaterialName(layer->material->getName());
931
return mesh.getPointer();
934
template <class TGrassLayer>
935
Ogre::Mesh *GrassLoader<TGrassLayer>::generateGrass_CROSSQUADS(PageInfo &page, TGrassLayer *layer, float *grassPositions, unsigned int grassCount)
937
//Calculate the number of quads to be added
938
unsigned int quadCount;
939
quadCount = grassCount * 2;
941
//Create manual mesh to store grass quads
942
Ogre::MeshPtr mesh = Ogre::MeshManager::getSingleton().createManual(getUniqueID(), Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
943
Ogre::SubMesh *subMesh = mesh->createSubMesh();
944
subMesh->useSharedVertices = false;
946
//Setup vertex format information
947
subMesh->vertexData = new Ogre::VertexData;
948
subMesh->vertexData->vertexStart = 0;
949
subMesh->vertexData->vertexCount = 4 * quadCount;
951
Ogre::VertexDeclaration* dcl = subMesh->vertexData->vertexDeclaration;
953
dcl->addElement(0, offset, Ogre::VET_FLOAT3, Ogre::VES_POSITION);
954
offset += Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT3);
955
dcl->addElement(0, offset, Ogre::VET_COLOUR, Ogre::VES_DIFFUSE);
956
offset += Ogre::VertexElement::getTypeSize(Ogre::VET_COLOUR);
957
dcl->addElement(0, offset, Ogre::VET_FLOAT2, Ogre::VES_TEXTURE_COORDINATES);
958
offset += Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT2);
960
//Populate a new vertex buffer with grass
961
Ogre::HardwareVertexBufferSharedPtr vbuf = Ogre::HardwareBufferManager::getSingleton()
962
.createVertexBuffer(offset, subMesh->vertexData->vertexCount, Ogre::HardwareBuffer::HBU_STATIC_WRITE_ONLY, false);
963
float* pReal = static_cast<float*>(vbuf->lock(Ogre::HardwareBuffer::HBL_DISCARD));
965
//Calculate size variance
966
float rndWidth = layer->maxWidth - layer->minWidth;
967
float rndHeight = layer->maxHeight - layer->minHeight;
969
float minY = Ogre::Math::POS_INFINITY, maxY = Ogre::Math::NEG_INFINITY;
970
float *posPtr = grassPositions; //Position array "iterator"
971
for (Ogre::uint16 i = 0; i < grassCount; ++i)
973
//Get the x and z positions from the position array
977
//Get the color at the grass position
978
Ogre::uint32 color(layer->getColorAt(x, z));
980
// if (layer->colorMap)
981
// color = layer->colorMap->getColorAt(x, z);
983
// color = 0xFFFFFFFF;
986
float rnd = Ogre::Math::UnitRandom(); //The same rnd value is used for width and height to maintain aspect ratio
987
float halfScaleX = (layer->minWidth + rndWidth * rnd) * 0.5f;
988
float scaleY = (layer->minHeight + rndHeight * rnd);
991
float angle = Ogre::Math::RangeRandom(0, Ogre::Math::TWO_PI);
992
float xTrans = Ogre::Math::Cos(angle) * halfScaleX;
993
float zTrans = Ogre::Math::Sin(angle) * halfScaleX;
995
//Calculate heights and edge positions
996
float x1 = x - xTrans, z1 = z - zTrans;
997
float x2 = x + xTrans, z2 = z + zTrans;
1000
if (heightFunction){
1001
y1 = heightFunction(x1, z1, heightFunctionUserData);
1002
y2 = heightFunction(x2, z2, heightFunctionUserData);
1009
*pReal++ = (x1 - page.centerPoint.x); *pReal++ = (y1 + scaleY); *pReal++ = (z1 - page.centerPoint.z); //pos
1010
*((Ogre::uint32*)pReal++) = color; //color
1011
*pReal++ = 0; *pReal++ = 0; //uv
1013
*pReal++ = (x2 - page.centerPoint.x); *pReal++ = (y2 + scaleY); *pReal++ = (z2 - page.centerPoint.z); //pos
1014
*((Ogre::uint32*)pReal++) = color; //color
1015
*pReal++ = 1; *pReal++ = 0; //uv
1017
*pReal++ = (x1 - page.centerPoint.x); *pReal++ = (y1); *pReal++ = (z1 - page.centerPoint.z); //pos
1018
*((Ogre::uint32*)pReal++) = color; //color
1019
*pReal++ = 0; *pReal++ = 1; //uv
1021
*pReal++ = (x2 - page.centerPoint.x); *pReal++ = (y2); *pReal++ = (z2 - page.centerPoint.z); //pos
1022
*((Ogre::uint32*)pReal++) = color; //color
1023
*pReal++ = 1; *pReal++ = 1; //uv
1026
if (y1 < minY) minY = y1;
1027
if (y2 < minY) minY = y2;
1028
if (y1 + scaleY > maxY) maxY = y1 + scaleY;
1029
if (y2 + scaleY > maxY) maxY = y2 + scaleY;
1031
//Calculate heights and edge positions
1032
float x3 = x + zTrans, z3 = z - xTrans;
1033
float x4 = x - zTrans, z4 = z + xTrans;
1036
if (heightFunction){
1037
y3 = heightFunction(x3, z3, heightFunctionUserData);
1038
y4 = heightFunction(x4, z4, heightFunctionUserData);
1045
*pReal++ = (x3 - page.centerPoint.x); *pReal++ = (y3 + scaleY); *pReal++ = (z3 - page.centerPoint.z); //pos
1046
*((Ogre::uint32*)pReal++) = color; //color
1047
*pReal++ = 0; *pReal++ = 0; //uv
1049
*pReal++ = (x4 - page.centerPoint.x); *pReal++ = (y4 + scaleY); *pReal++ = (z4 - page.centerPoint.z); //pos
1050
*((Ogre::uint32*)pReal++) = color; //color
1051
*pReal++ = 1; *pReal++ = 0; //uv
1053
*pReal++ = (x3 - page.centerPoint.x); *pReal++ = (y3); *pReal++ = (z3 - page.centerPoint.z); //pos
1054
*((Ogre::uint32*)pReal++) = color; //color
1055
*pReal++ = 0; *pReal++ = 1; //uv
1057
*pReal++ = (x4 - page.centerPoint.x); *pReal++ = (y4); *pReal++ = (z4 - page.centerPoint.z); //pos
1058
*((Ogre::uint32*)pReal++) = color; //color
1059
*pReal++ = 1; *pReal++ = 1; //uv
1062
if (y3 < minY) minY = y1;
1063
if (y4 < minY) minY = y2;
1064
if (y3 + scaleY > maxY) maxY = y3 + scaleY;
1065
if (y4 + scaleY > maxY) maxY = y4 + scaleY;
1069
subMesh->vertexData->vertexBufferBinding->setBinding(0, vbuf);
1071
//Populate index buffer
1072
subMesh->indexData->indexStart = 0;
1073
subMesh->indexData->indexCount = 6 * quadCount;
1074
subMesh->indexData->indexBuffer = Ogre::HardwareBufferManager::getSingleton()
1075
.createIndexBuffer(Ogre::HardwareIndexBuffer::IT_16BIT, subMesh->indexData->indexCount, Ogre::HardwareBuffer::HBU_STATIC_WRITE_ONLY);
1076
Ogre::uint16* pI = static_cast<Ogre::uint16*>(subMesh->indexData->indexBuffer->lock(Ogre::HardwareBuffer::HBL_DISCARD));
1077
for (Ogre::uint16 i = 0; i < quadCount; ++i)
1079
Ogre::uint16 offset = i * 4;
1090
subMesh->indexData->indexBuffer->unlock();
1093
Ogre::AxisAlignedBox bounds(page.bounds.left - page.centerPoint.x, minY, page.bounds.top - page.centerPoint.z,
1094
page.bounds.right - page.centerPoint.x, maxY, page.bounds.bottom - page.centerPoint.z);
1095
mesh->_setBounds(bounds);
1096
Ogre::Vector3 temp = bounds.getMaximum() - bounds.getMinimum();
1097
mesh->_setBoundingSphereRadius(temp.length() * 0.5f);
1099
Ogre::LogManager::getSingleton().setLogDetail(static_cast<Ogre::LoggingLevel>(0));
1101
Ogre::LogManager::getSingleton().setLogDetail(Ogre::LL_NORMAL);
1103
//Apply grass material to mesh
1104
subMesh->setMaterialName(layer->material->getName());
1107
return mesh.getPointer();
1110
template <class TGrassLayer>
1111
Ogre::Mesh *GrassLoader<TGrassLayer>::generateGrass_SPRITE(PageInfo &page, TGrassLayer *layer, float *grassPositions, unsigned int grassCount)
1113
//Calculate the number of quads to be added
1114
unsigned int quadCount;
1115
quadCount = grassCount;
1117
//Create manual mesh to store grass quads
1118
Ogre::MeshPtr mesh = Ogre::MeshManager::getSingleton().createManual(getUniqueID(), Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
1119
Ogre::SubMesh *subMesh = mesh->createSubMesh();
1120
subMesh->useSharedVertices = false;
1122
//Setup vertex format information
1123
subMesh->vertexData = new Ogre::VertexData;
1124
subMesh->vertexData->vertexStart = 0;
1125
subMesh->vertexData->vertexCount = 4 * quadCount;
1127
Ogre::VertexDeclaration* dcl = subMesh->vertexData->vertexDeclaration;
1129
dcl->addElement(0, offset, Ogre::VET_FLOAT3, Ogre::VES_POSITION);
1130
offset += Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT3);
1131
dcl->addElement(0, offset, Ogre::VET_FLOAT4, Ogre::VES_NORMAL);
1132
offset += Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT4);
1133
dcl->addElement(0, offset, Ogre::VET_COLOUR, Ogre::VES_DIFFUSE);
1134
offset += Ogre::VertexElement::getTypeSize(Ogre::VET_COLOUR);
1135
dcl->addElement(0, offset, Ogre::VET_FLOAT2, Ogre::VES_TEXTURE_COORDINATES);
1136
offset += Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT2);
1138
//Populate a new vertex buffer with grass
1139
Ogre::HardwareVertexBufferSharedPtr vbuf = Ogre::HardwareBufferManager::getSingleton()
1140
.createVertexBuffer(offset, subMesh->vertexData->vertexCount, Ogre::HardwareBuffer::HBU_STATIC_WRITE_ONLY, false);
1141
float* pReal = static_cast<float*>(vbuf->lock(Ogre::HardwareBuffer::HBL_DISCARD));
1143
//Calculate size variance
1144
float rndWidth = layer->maxWidth - layer->minWidth;
1145
float rndHeight = layer->maxHeight - layer->minHeight;
1147
float minY = Ogre::Math::POS_INFINITY, maxY = Ogre::Math::NEG_INFINITY;
1148
float *posPtr = grassPositions; //Position array "iterator"
1149
for (Ogre::uint16 i = 0; i < grassCount; ++i)
1151
//Get the x and z positions from the position array
1152
float x = *posPtr++;
1153
float z = *posPtr++;
1157
if (heightFunction){
1158
y = heightFunction(x, z, heightFunctionUserData);
1163
float x1 = (x - page.centerPoint.x);
1164
float z1 = (z - page.centerPoint.z);
1166
//Get the color at the grass position
1167
Ogre::uint32 color(layer->getColorAt(x, z));
1170
float rnd = Ogre::Math::UnitRandom(); //The same rnd value is used for width and height to maintain aspect ratio
1171
float halfXScale = (layer->minWidth + rndWidth * rnd) * 0.5f;
1172
float scaleY = (layer->minHeight + rndHeight * rnd);
1174
//Randomly mirror grass textures
1175
float uvLeft, uvRight;
1176
if (Ogre::Math::UnitRandom() > 0.5f){
1185
*pReal++ = x1; *pReal++ = y; *pReal++ = z1; //center position
1186
*pReal++ = -halfXScale; *pReal++ = scaleY; *pReal++ = 0; *pReal++ = 0; //normal (used to store relative corner positions)
1187
*((Ogre::uint32*)pReal++) = color; //color
1188
*pReal++ = uvLeft; *pReal++ = 0; //uv
1190
*pReal++ = x1; *pReal++ = y; *pReal++ = z1; //center position
1191
*pReal++ = +halfXScale; *pReal++ = scaleY; *pReal++ = 0; *pReal++ = 0; //normal (used to store relative corner positions)
1192
*((Ogre::uint32*)pReal++) = color; //color
1193
*pReal++ = uvRight; *pReal++ = 0; //uv
1195
*pReal++ = x1; *pReal++ = y; *pReal++ = z1; //center position
1196
*pReal++ = -halfXScale; *pReal++ = 0.0f; *pReal++ = 0; *pReal++ = 0; //normal (used to store relative corner positions)
1197
*((Ogre::uint32*)pReal++) = color; //color
1198
*pReal++ = uvLeft; *pReal++ = 1; //uv
1200
*pReal++ = x1; *pReal++ = y; *pReal++ = z1; //center position
1201
*pReal++ = +halfXScale; *pReal++ = 0.0f; *pReal++ = 0; *pReal++ = 0; //normal (used to store relative corner positions)
1202
*((Ogre::uint32*)pReal++) = color; //color
1203
*pReal++ = uvRight; *pReal++ = 1; //uv
1206
if (y < minY) minY = y;
1207
if (y + scaleY > maxY) maxY = y + scaleY;
1211
subMesh->vertexData->vertexBufferBinding->setBinding(0, vbuf);
1213
//Populate index buffer
1214
subMesh->indexData->indexStart = 0;
1215
subMesh->indexData->indexCount = 6 * quadCount;
1216
subMesh->indexData->indexBuffer = Ogre::HardwareBufferManager::getSingleton()
1217
.createIndexBuffer(Ogre::HardwareIndexBuffer::IT_16BIT, subMesh->indexData->indexCount, Ogre::HardwareBuffer::HBU_STATIC_WRITE_ONLY);
1218
Ogre::uint16* pI = static_cast<Ogre::uint16*>(subMesh->indexData->indexBuffer->lock(Ogre::HardwareBuffer::HBL_DISCARD));
1219
for (Ogre::uint16 i = 0; i < quadCount; ++i)
1221
Ogre::uint16 offset = i * 4;
1232
subMesh->indexData->indexBuffer->unlock();
1235
Ogre::AxisAlignedBox bounds(page.bounds.left - page.centerPoint.x, minY, page.bounds.top - page.centerPoint.z,
1236
page.bounds.right - page.centerPoint.x, maxY, page.bounds.bottom - page.centerPoint.z);
1237
mesh->_setBounds(bounds);
1238
Ogre::Vector3 temp = bounds.getMaximum() - bounds.getMinimum();
1239
mesh->_setBoundingSphereRadius(temp.length() * 0.5f);
1241
Ogre::LogManager::getSingleton().setLogDetail(static_cast<Ogre::LoggingLevel>(0));
1243
Ogre::LogManager::getSingleton().setLogDetail(Ogre::LL_NORMAL);
1245
//Apply grass material to mesh
1246
subMesh->setMaterialName(layer->material->getName());
1249
return mesh.getPointer();
1252
template <class TGrassLayer>
1253
unsigned long GrassLoader<TGrassLayer>::GUID = 0;