1
/*-------------------------------------------------------------------------------------
2
Copyright (c) 2006 John Judnich
4
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.
5
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:
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
-------------------------------------------------------------------------------------*/
11
#ifndef __GrassLoader_H__
12
#define __GrassLoader_H__
14
#include "PagedGeometry.h"
15
#include "PropertyMaps.h"
16
#include "RandomTable.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>
56
/** \brief A PageLoader-derived object you can use with PagedGeometry to produce realistic grass.
58
Using a GrassLoader is simple - simply create an instance, attach it to your PagedGeometry object
59
with PagedGeometry::setPageLoader(), and add your grass. Important: For best performance, it is
60
recommended that you use GrassPage (included in GrassLoader.h) to display geometry loaded by GrassLoader.
61
This page type is designed for best performance with this grass system. BatchPage
62
will work, although performance will be reduced slightly, and ImpostorPage will run extremely slow.
64
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:
66
::Forests::GrassLoader<GrassLayer> loader = new ::Forests::GrassLoader<GrassLayer>(grass);
68
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.
71
To add grass, just call addLayer(). addLayer() returns a GrassLayer object pointer, which you should
72
use to further configure your newly added grass. Properties like size, density, color, animation, etc.
73
can be controlled through the GrassLayer class.
75
\note By default, the GrassLoader doesn't know what shape your terrain so all grass will be placed at
76
0 height. To inform GrassLoader of the shape of your terrain, you must specify a height function
77
that returns the height (y coordinate) of your terrain at the given x and z coordinates. See
78
the TreeLoader2D::setHeightFunction() documentation for more information.
80
\warning If you attempt to use Ogre's scene queries to get the terrain height,
81
keep in mind that calculating the height of Ogre's built-in terrain this way can
82
be VERY slow if not done properly, and may cause stuttering due to long paging delays.
84
template <typename TGrassLayer>
85
class GrassLoader: public PageLoader
88
/** \brief Creates a new GrassLoader object.
89
\param geom The PagedGeometry object that this GrassLoader will be assigned to.*/
90
inline GrassLoader(PagedGeometry *geom);
93
/** \brief Adds a grass layer to the scene.
94
\param material The initial grass texture to use (this can be changed later).
96
Since all grass is potentially infinite, it is not added like normal entities which
97
have a specific position. Instead you add a grass "layer" to the scene. A grass layer
98
is a "carpet" of a single type of grass that gets applied everywhere in your world.
99
If you want multiple types of grass with different appearances, you'll have to add
100
a multiple grass layers for each style.
102
Of course, a grass layer is not completely uniform. The GrassLayer class contains
103
functions to vary grass size and density levels as desired.
105
\see GrassLayer class for more information. */
106
TGrassLayer *addLayer(const Ogre::String &material);
108
/** \brief Removes and deletes a grass layer from the scene
110
This function simply deletes a GrassLayer previously created with addLayer(). */
111
void deleteLayer(TGrassLayer *layer);
113
/** \brief Returns a list of added grass layers.
115
This function returns a std::list<GrassLayer*> reference, which contains all grass
116
layers which have been added to this GrassLoader. */
117
inline std::list<TGrassLayer*> &getLayerList() { return layerList; }
119
/** \brief Sets the global wind direction for this GrassLoader.
121
GrassLayer animation properties are used to configure the most of the animation
122
behavior (sway length, speed, etc.), but wind direction is not included in GrassLayer
123
since this is really a global property. Using this function, you can set the "global"
124
wind direction which affects all animated grass associated with this PageLoader.
126
Default value is Vector3::UNIT_X.
128
\note This only affects grass layers which have breeze animations enabled.
130
inline void setWindDirection(Ogre::Vector3 &dir) { windDir = dir; }
132
inline void setBuildEdgesEnabled(bool value) { autoEdgeBuildEnabled=value; }
133
inline bool getBuildEdgesEnabled() { return autoEdgeBuildEnabled; }
135
/** \brief Returns the global wind direction for this GrassLoader.
137
\see setWindDirection() for more information about the wind direction. */
138
inline Ogre::Vector3 &getWindDirection() { return windDir; }
140
/** \brief Sets the global density factor for this GrassLoader.
142
This function can be used to up-scale or down-scale the density of all grass
143
associated with this GrassLoader. This is typically used to provide the user
144
the option to reduce grass density for better performance on slower machines.
146
Final density values are calculated by multiplying the layer density by this
147
density factor. For example, a layer with .4 density and a density factor of .5
148
will result in a final density of .2 (.5 * .4)
150
By default, the density factor is set to 1.0 so the layer density is not modified.
152
inline void setDensityFactor(float density) { densityFactor = density; }
154
/** \brief Returns the global density factor for this GrassLoader.
156
\see setDensityFactor() for more information about the density factor. */
157
inline float getDensityFactor() { return densityFactor; }
159
/** \brief Sets the render queue group the grass will be rendered through
160
\param queueID Enumerated value of the queue group to use
162
Like Ogre's MovableObject::setRenderQueueGroup(), this allows you to customize
163
the rendering order of your scene. Since grass is transparent, it's likely that
164
you may encounter alpha-sorting issues between grass and your particle effects,
165
for example. In this case you can use this function to adjust the rendering order
166
of the grass to fix the problem.
168
If you don't call this function, the RENDER_QUEUE_6 queue will be used.
170
\note Once grass is loaded and being rendered, this won't have any effect on it.
171
Be sure to call this function before the scene begins rendering, otherwise you will
172
have to call PagedGeometry::reloadGeometry() to force a reload in order for the changes
174
inline void setRenderQueueGroup(Ogre::uint8 queueID) { renderQueue = queueID; }
176
/** \brief Sets the height function used to calculate grass Y coordinates
177
\param heightFunction A pointer to a height function
179
Unless you want all your grass placed at 0 height, you need to specify a height function
180
so GrassLoader will be able to calculate the Y coordinate. The height function given
181
to setHeightFunction() should use the following prototype (although you can name the
182
function anything you want):
185
Real getHeightAt(Real x, Real z, void *userData);
188
\note If you're not using the default coordinate system (where x = right, z = back), the
189
x/z parameters will actually be representing the appropriate equivalents.
191
The userData parameter allows you to include any additional data you want when your height
192
function is called, and is completely optional (although you can't actually omit it from the
193
declaration, you can ignore it). Any userData value you choose to supply to setHeightFunction()
194
will be passed on to your height function every time it is called.
196
After you've defined a height function, using setHeightFunction is easy:
199
pageLoader2D->setHeightFunction(&getHeightAt);
200
//Or (if you want to pass additional data on to your height function)...
201
pageLoader2D->setHeightFunction(&getHeightAt, myUserData);
204
In most cases, you may not even need to use the extra "userData" parameter, but it's there in
205
the event that your height function needs extra contextual data.
207
void setHeightFunction(Ogre::Real (*heightFunction)(Ogre::Real x, Ogre::Real z, void *userData), void *userData = NULL) {
208
this->heightFunction = heightFunction;
209
heightFunctionUserData = userData;
212
bool preparePage(PageInfo &page);
214
/** INTERNAL FUNCTION - DO NOT USE */
215
void loadPage(PageInfo &page);
216
/** INTERNAL FUNCTION - DO NOT USE */
217
void unloadPage(PageInfo &page);
218
/** INTERNAL FUNCTION - DO NOT USE */
221
static float getRangeRandom(float start, float end);
225
friend class GrassLayer;
228
Ogre::Mesh *generateGrass_QUAD(PageInfo &page, TGrassLayer *layer, float *grassPositions, unsigned int grassCount);
229
Ogre::Mesh *generateGrass_CROSSQUADS(PageInfo &page, TGrassLayer *layer, float *grassPositions, unsigned int grassCount);
230
Ogre::Mesh *generateGrass_SPRITE(PageInfo &page, TGrassLayer *layer, float *grassPositions, unsigned int grassCount);
232
//List of grass types
233
std::list<TGrassLayer*> layerList;
236
Ogre::Real (*heightFunction)(Ogre::Real x, Ogre::Real z, void *userData); //Pointer to height function
237
void *heightFunctionUserData;
241
Ogre::uint8 renderQueue;
248
Ogre::Timer windTimer;
249
Ogre::Vector3 windDir;
250
unsigned long lastTime;
252
bool autoEdgeBuildEnabled;
254
static unsigned long GUID;
255
static inline Ogre::String getUniqueID()
257
return "GrassLDR" + Ogre::StringConverter::toString(++GUID);
262
/** \brief A technique used to render grass. Passed to GrassLayer::setRenderTechnique(). */
265
/// Grass constructed of randomly placed and rotated quads
267
/// Grass constructed of two quads forming a "X" cross shape
268
GRASSTECH_CROSSQUADS,
269
/// Grass constructed of camera-facing billboard quads
273
/** \brief A technique used to fade grass into the distance. Passed to GrassLayer::setFadeTechnique(). */
276
/// Grass that fades into the distance with transparency. Fairly effective in most cases.
278
/// 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.
280
/// 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.
287
/** \brief Sets the material that is applied to all grass billboards/quads */
288
void setMaterialName(const Ogre::String &matName);
290
/** \brief Sets the minimum size that grass quads/billboards will be */
291
void setMinimumSize(float width, float height);
293
/** \brief Sets the maximum size that grass quads/billboards will be */
294
void setMaximumSize(float width, float height);
296
/** \brief Sets the technique used to render this grass layer
297
\param style The GrassTechnique style used to display grass.
298
\param blendBase Whether or not grass base blending is enabled.
300
The "style" setting allows you to choose from various construction methods, such as
301
sprite-style grass quads, plain 3D quads, etc. See the GrassTechnique documentation
302
for more information about this option. GRASSTECH_QUAD is used by default.
304
Setting "blendBase" to true will enable grass base blending, a technique which helps
305
reduce the unnatural flat appearance of grass quads near the camera. Since the flatness
306
is most obvious where the grass intersects the terrain, this technique attempts to
307
smoothly blend the base of near-by grass into the terrain.
309
\note Base blending does not work well with alpha-rejected textures.
311
void setRenderTechnique(GrassTechnique style, bool blendBase = false);
313
/** \brief Sets the technique used when fading out distant grass
314
\param style The FadeTechnique style used to fade grass.
316
This "style" setting allows you to choose from various fade techniques. Depending on
317
your scene, certain techniques may look better than others. The most compatible method
318
is FADETECH_ALPHA (used by default), although better results can usually be achieved
319
with other methods. See the FadeTechnique documentation for more information.
321
void setFadeTechnique(FadeTechnique style);
323
/** \brief Enables/disables animation on this layer
325
Always use this function to disable animation, rather than setting SwayLength or SwaySpeed
326
to 0. This function will use a different vertex shader which means improved performance
327
when animation is disabled.
329
void setAnimationEnabled(bool enabled);
331
/** \brief Enables/disables lighting on this layer
333
void setLightingEnabled(bool enabled);
335
/** \brief Whether vertex colours are enabled on this layer.
337
* If you're using a shader to provide dynamic lightning you probably want to disable vertex colours.
339
virtual bool isColoursEnabled() const;
341
/** \brief Whether normals are enabled on this layer.
343
virtual bool isNormalsEnabled() const;
345
/** \brief Whether tangents are enabled on this layer.
348
virtual bool isTangentsEnabled() const;
350
/** \brief Whether shadow casting should be enabled for the mesh generated.
352
virtual bool isCastShadowsEnabled() const;
354
/** \brief Sets how far grass should sway back and forth
356
\note Since this is measured in world units, you may have to adjust this depending on
357
the size of your grass as set by setMinimumSize() and setMaximumSize().*/
358
void setSwayLength(float mag) { animMag = mag; }
360
/** \brief Sets the sway speed of the grass (measured in "sways-per-second") */
361
void setSwaySpeed(float speed) { animSpeed = speed; }
363
/** \brief Sets the smooth distribution (positional phase shift) of the grass swaying animation
365
If you set this to 0, grass animation will look very unnatural, since all the grass sway motions
366
will be in perfect synchronization (everything sways to the right, then everything sways to the
367
left, etc.) This sets the "positional phase shift", which gives the grass a "wave" like phase
368
distribution. The higher this value is, the more "chaotic" the wind will appear. Lower values give
369
a smoother breeze appearance, but values too high can look unrealistic.
371
void setSwayDistribution(float freq) { animFreq = freq; }
373
/** \brief Sets the boundaries of the density/color maps
374
\param bounds The map boundary
376
By default, the GrassLayer has no knowledge of your terrain/world boundaries, so you must
377
use this function to specify a rectangular/square area of your world, otherwise density/color maps
378
won't work properly. The boundary given to this function defines the area where density/color
379
maps take effect. Normally this is set to your terrain's bounds so the density/color map is aligned
380
to your heightmap, but you could apply it anywhere you want.
382
\note The grass system is infinite, so there's no need to worry about using too expansive
383
boundaries. This setting simply configures the behavior of density and color maps. */
384
virtual void setMapBounds(const Ogre::TRect<Ogre::Real> &bounds)
391
* @brief Calculates the max number of grass instances for this layer.
392
* @param page The page to create grass for.
393
* @param densityFactor The density factor set on the grass loader
394
* @param volume The volume, in world units, to fill
395
* @param isAvailable This must be set to true if the grass is currently available.
396
* @return The max number of grass instances to create.
398
virtual unsigned int prepareGrass(const PageInfo& page, float densityFactor, float volume, bool& isAvailable) = 0;
400
/** \brief Set the maximum slope a grass of blade can be placed on.
401
\param maxSlopeRatio The maximum slope (h/w ratio) a grass blade is allowed to be placed on.
403
This function can be used to set the maximum slope you want your grass to be placed on
404
(although it doesn't work for sprite grass). By default grass is allowed on any slope.
406
This version of setMaxSlope() accepts a slope ratio value, where ATan(maxSlopeRatio) =
407
maxSlopeAngle. If you wish to provide a maximum slope as an angle, either use the other
408
overload of this function, or convert your angle to a slope ratio first with Tan().*/
409
void setMaxSlope(const float maxSlopeRatio) { maxSlope = maxSlopeRatio; }
411
void setMaxSlope(Ogre::Radian maxSlopeAngle) {
412
if (maxSlopeAngle > Ogre::Degree(89.99f))
413
maxSlopeAngle = Ogre::Degree(89.99f);
414
if (maxSlopeAngle < Ogre::Degree(0))
415
maxSlopeAngle = Ogre::Degree(0);
417
maxSlope = Ogre::Math::Tan(maxSlopeAngle);
420
/** \brief Get the maximum slope a grass blade can be placed on (as set by setMaxSlope()).
421
\returns The currently set maximum slope ratio value (not an angle).
423
This returns the currently set maximum slope which is used to determine what ground is too steep
424
for grass to be placed on. Note that this returns the slope as a slope ratio, not an angle. If you
425
need an angle value, convert with ATan() (maxSlopeAngle = ATan(maxSlopeRatio)).*/
426
float getMaxSlope() const { return maxSlope; }
430
//Used by GrassLoader::loadPage() - populates an array with grass.
431
//Returns the final number of grasses, which will always be <= grassCount
432
virtual unsigned int _populateGrassList(PageInfo page, float *posBuff, unsigned int grassCount) = 0;
434
//Updates the vertex shader used by this layer based on the animate enable status
435
void _updateShaders();
437
//Grass material/shape properties
438
Ogre::MaterialPtr material;
439
float minWidth, maxWidth;
440
float minHeight, maxHeight;
443
FadeTechnique fadeTechnique;
444
GrassTechnique renderTechnique;
447
Ogre::TRect<Ogre::Real> mapBounds;
449
//Grass shader properties
450
bool animate, blend, lighting, shaderNeedsUpdate;
451
float animMag, animSpeed, animFreq;
453
//Current frame of animation for this layer
461
/** \brief A data structure giving you full control over grass properties.
463
Grass is added to the scene through GrassLoader::addLayer(). Through this class you
464
can configure your grass layer any way you like - size, density, render technique,
465
animation, etc. Simply call the appropriate "set" member function to set the desired property.
467
Remember that you cannot create or delete layers directly. Layers can only be created
468
with GrassLoader::addLayer(), and may not be deleted manually (they will be deleted when
469
the associated GrassLoader is deleted).
471
class GrassLayer : public GrassLayerBase
475
/** \brief Sets the maximum density (measured in grass quads/billboards per square unit) of grass */
476
void setDensity(float density) { this->density = density; }
478
/** \brief Sets a minimum / maximum height where grass may appear
479
\param minHeight Sets the minimum height grass may have. 0 = no minimum
480
\param maxHeight Sets the maximum height grass may have. 0 = no maximum
482
By default grass appears at all altitudes. You can use this function to restrict grass to a
483
certain height range. For example, if sea level is at 100 units Y, you might restrict this
484
layer to display only above 100 units (so your grass doesn't grow under water).
486
It's possible to use density maps (see setDensityMap()) to achieve similar results, but if
487
your density map is extremely low resolution, this function may be the only practical option
488
to prevent grass from growing under water (when used in combination with your density map).
490
Setting minHeight to 0 means grass has no minimum height - it can grow as low as necessary.
491
Similarly, setting maxHeight to 0 means grass has no maximum height - it can grow as high
493
void setHeightRange(float minHeight, float maxHeight = 0) { minY = minHeight; maxY = maxHeight; }
495
/** \brief Sets the density map used for this grass layer
496
\param mapFile The density map image
497
\param channel The color channel(s) to from the image to interpret as density
499
A density map is simply a greyscale image, similar to a heightmap, that specifies the grass
500
density on your map. Full pixel intensity indicates that grass should be fully dense at that
501
point (the maximum density is specified by GrassLayer::setDensity()), while no pixel intensity
502
indicates that no grass should appear at that location.
504
The channel parameter allows you to extract the density information from the image's
505
red, green, blue, alpha, or color values. For example, you may store density values in the
506
alpha channel, in which case you would use CHANNEL_ALPHA. By default, CHANNEL_COLOR is used,
507
which means the image color is converted to greyscale internally and used as a density map.
509
Note that GrassLayer by default has no idea of your terrain/world boundaries, so you
510
must specify a rectangular/square area of your world that is affected by density/color maps.
511
To do this, use the setMapBounds() function. Normally this is set to your terrain's bounds
512
so the density/color map is aligned to your heightmap, but you could apply it anywhere you
514
void setDensityMap(const Ogre::String &mapFile, MapChannel channel = CHANNEL_COLOR);
516
/** \brief Sets the density map used for this grass layer
518
Overloaded to accept a Texture object. See the original setDensityMap() documentation above
519
for more detailed information on density maps.
521
\note The texture data you provide is copied into the GrassLayer's own memory space, so you
522
can delete the texture after calling this function without risk of crashing. */
523
void setDensityMap(Ogre::TexturePtr map, MapChannel channel = CHANNEL_COLOR);
525
/** \brief Sets the filtering mode used for density maps
527
This function can be used to set the filtering mode used for your density map when generating
528
grass. By default, bilinear filtering is used (MAPFILTER_BILINEAR). If you disable filtering
529
by using MAPFILTER_NONE, the resulting layout of your grass may look square and blocky,
530
depending on the resolution of your density map.
532
MAPFILTER_NONE is slightly faster than MAPFILTER_BILINEAR, so use it if you don't notice any
533
considerable blockiness.
535
void setDensityMapFilter(MapFilter filter);
537
/** \brief Sets the color map used for this grass layer
538
\param mapFile The color map image
539
\param channel The color channel(s) to from the image to use
541
A color map is simply a texture that allows you to vary the color and shading of grass
542
across your world for a more realistic look. For example, adding a dark spot to the center
543
of your color map will make grass near the center of your terrain look darker, as long as
544
you have the color map aligned to your terrain (see setMapBounds()).
546
The channel parameter allows you to extract the color information from the image's
547
red, green, blue, alpha, or color values. For example, you may store the desired shade of your
548
grass in the red channel of an image, in which case you would use CHANNEL_RED (when you choose
549
a single channel, it is converted to a greyscale color). By default, CHANNEL_COLOR is used,
550
which uses the full color information available in the image.
552
Remember that GrassLayer by default has no idea of your terrain/world boundaries, so you
553
must specify a rectangular/square area of your world that is affected by density/color maps.
554
To do this, use the setMapBounds() function. Normally this is set to your terrain's bounds
555
so the density/color map is aligned to your heightmap, but you could apply it anywhere you
557
void setColorMap(const Ogre::String &mapFile, MapChannel channel = CHANNEL_COLOR);
559
/** \brief Sets the color map used for this grass layer
561
Overloaded to accept a Texture object. See the original setColorMap() documentation above
562
for more detailed information on color maps.
564
\note The texture data you provide is copied into RAM, so you can delete the texture after
565
calling this function without risk of crashing. */
566
void setColorMap(Ogre::TexturePtr map, MapChannel channel = CHANNEL_COLOR);
568
/** \brief Sets the filtering mode used for color maps
570
This function can be used to set the filtering mode used for your color map when generating
571
grass. By default, bilinear filtering is used (MAPFILTER_BILINEAR). If you disable filtering
572
by using MAPFILTER_NONE, the resulting grass coloration may appear slightly pixelated,
573
depending on the resolution of your color map.
575
MAPFILTER_NONE is slightly faster than MAPFILTER_BILINEAR, so use it if you don't notice any
576
considerable pixelation.
578
void setColorMapFilter(MapFilter filter);
580
/** \brief Gets a pointer to the density map being used
582
You can use this function to access the internal density map object used by the GrassLoader.
583
Through this object you can directly manipulate the pixels of the density map, among other
586
Note that although you can edit the density map in real-time through this class, the changes
587
won't be uploaded to your video card until you call PagedGeometry::reloadGeometry(). If you
588
don't, the grass you see will remain unchanged. */
589
DensityMap *getDensityMap() { return densityMap; }
591
/** \brief Gets a pointer to the color map being used
593
You can use this function to access the internal color map object used by the GrassLoader.
594
Through this object you can directly manipulate the pixels of the color map, among other
597
Note that although you can edit the color map in real-time through this class, the changes
598
won't be uploaded to your video card until you call PagedGeometry::reloadGeometry(). If you
599
don't, the grass you see will remain unchanged. */
600
ColorMap *getColorMap() { return colorMap; }
602
virtual unsigned int prepareGrass(const PageInfo& page, float densityFactor, float volume, bool& isAvailable);
606
* If there's a colormap registered use that for lookup, else return fullbright.
611
inline Ogre::uint32 getColorAt(float x, float z)
614
return colorMap->getColorAt(x, z, mapBounds);
620
friend class GrassLoader<GrassLayer>;
622
/** \brief Do not create a GrassLayer directly - use GrassLoader->addLayer() */
623
GrassLayer(PagedGeometry *geom, GrassLoader<GrassLayer> *ldr);
625
/** \brief Do not delete a GrassLayer yourself - the GrassLoader will do this automatically when it's deleted */
628
//Used by GrassLoader::loadPage() - populates an array with grass.
629
//Returns the final number of grasses, which will always be <= grassCount
630
virtual unsigned int _populateGrassList(PageInfo page, float *posBuff, unsigned int grassCount);
632
//Used by GrassLoader::loadPage() - populates an array with a uniform distribution of grass
633
//Returns the final number of grasses, which will always be <= grassCount
634
unsigned int _populateGrassList_Uniform(PageInfo page, float *posBuff, unsigned int grassCount);
636
//Used by GrassLoader::loadPage() - populates an array of grass positions based on the density map
637
//Returns the final number of grasses, which will always be <= grassCount
638
unsigned int _populateGrassList_UnfilteredDM(PageInfo page, float *posBuff, unsigned int grassCount);
640
//Variation of _populateGrassList(), using bilinear filtering on the density map lookups
641
//Returns the final number of grasses, which will always be <= grassCount
642
unsigned int _populateGrassList_BilinearDM(PageInfo page, float *posBuff, unsigned int grassCount);
645
GrassLoader<GrassLayer> *parent;
647
//Grass material/shape properties
652
DensityMap *densityMap;
653
MapFilter densityMapFilter;
656
MapFilter colorMapFilter;
661
/** \brief A custom page type designed specifically for use with GrassLoader.
663
You can use this in your own project if you want, but remember that no optimizations
664
are performed. The given entity is simply cloned and attached to a new scene node as
665
quickly and simply as possible (this means there's no batching overhead as in BatchPage,
666
but it also means potentially poor performance if you don't know what you're doing).
668
class GrassPage: public GeometryPage
671
void init(PagedGeometry *geom, const Ogre::Any &data);
674
void addEntity(Ogre::Entity *ent, const Ogre::Vector3 &position, const Ogre::Quaternion &rotation, const Ogre::Vector3 &scale, const Ogre::ColourValue &color);
675
void removeEntities();
676
void setFade(bool enabled, Ogre::Real visibleDist, Ogre::Real invisibleDist) {}
677
void setVisible(bool visible);
680
Ogre::SceneManager *sceneMgr;
681
Ogre::SceneNode *rootNode;
683
std::list<Ogre::SceneNode*> nodeList;
685
static unsigned long GUID;
686
static inline Ogre::String getUniqueID()
688
return "GrassPage" + Ogre::StringConverter::toString(++GUID);
692
template <class TGrassLayer>
693
GrassLoader<TGrassLayer>::GrassLoader(PagedGeometry *geom)
695
GrassLoader<TGrassLayer>::geom = geom;
697
// generate some random numbers
698
rTable = new RandomTable();
700
heightFunction = NULL;
701
heightFunctionUserData = NULL;
703
windDir = Ogre::Vector3::UNIT_X;
704
densityFactor = 1.0f;
705
renderQueue = geom->getRenderQueue();
711
template <class TGrassLayer>
712
GrassLoader<TGrassLayer>::~GrassLoader()
714
typename std::list<TGrassLayer*>::iterator it;
715
for (it = layerList.begin(); it != layerList.end(); ++it){
728
template <class TGrassLayer>
729
TGrassLayer *GrassLoader<TGrassLayer>::addLayer(const Ogre::String &material)
731
TGrassLayer *layer = new TGrassLayer(geom, this);
732
layer->setMaterialName(material);
733
layerList.push_back(layer);
738
template <class TGrassLayer>
739
void GrassLoader<TGrassLayer>::deleteLayer(TGrassLayer *layer)
741
layerList.remove(layer);
745
template <class TGrassLayer>
746
void GrassLoader<TGrassLayer>::frameUpdate()
748
unsigned long currentTime = windTimer.getMilliseconds();
749
unsigned long ellapsedTime = currentTime - lastTime;
750
lastTime = currentTime;
752
float ellapsed = ellapsedTime / 1000.0f;
754
//Update the vertex shader parameters
755
typename std::list<TGrassLayer*>::iterator it;
756
for (it = layerList.begin(); it != layerList.end(); ++it){
757
TGrassLayer *layer = *it;
759
layer->_updateShaders();
761
Ogre::Technique* tech = layer->material->getBestTechnique();
762
if (tech && tech->getNumPasses()) {
763
Ogre::Pass* pass = tech->getPass(0);
764
if (pass->hasVertexProgram()) {
765
Ogre::GpuProgramParametersSharedPtr params = pass->getVertexProgramParameters();
766
if (!params.isNull() && layer->animate){
767
//Increment animation frame
768
layer->waveCount += ellapsed * (layer->animSpeed * Ogre::Math::PI);
769
if (layer->waveCount > Ogre::Math::PI*2) layer->waveCount -= Ogre::Math::PI*2;
771
//Set vertex shader parameters
772
params->setNamedConstant("time", layer->waveCount);
773
params->setNamedConstant("frequency", layer->animFreq);
775
Ogre::Vector3 direction = windDir * layer->animMag;
776
params->setNamedConstant("direction", Ogre::Vector4(direction.x, direction.y, direction.z, 0));
783
template <class TGrassLayer>
784
bool GrassLoader<TGrassLayer>::preparePage(PageInfo &page)
787
//Calculate how much grass needs to be added
788
float volume = page.bounds.width() * page.bounds.height();
789
typename std::list<TGrassLayer*>::iterator it;
790
for (it = layerList.begin(); it != layerList.end(); ++it){
791
TGrassLayer *layer = *it;
792
bool isAvailable = false;
793
layer->prepareGrass(page, densityFactor, volume, isAvailable);
794
result = result && isAvailable;
800
template <class TGrassLayer>
801
void GrassLoader<TGrassLayer>::loadPage(PageInfo &page)
804
typename std::list<TGrassLayer*>::iterator it;
805
for (it = layerList.begin(); it != layerList.end(); ++it){
806
TGrassLayer *layer = *it;
807
layer->material->prepare();
809
// Continue to the next layer if the current page is outside of the layers map boundaries.
810
if(layer->mapBounds.right < page.bounds.left || layer->mapBounds.left > page.bounds.right ||
811
layer->mapBounds.bottom < page.bounds.top || layer->mapBounds.top > page.bounds.bottom)
816
//Calculate how much grass needs to be added
817
float volume = page.bounds.width() * page.bounds.height();
818
bool isAvailable = false;
819
unsigned int grassCount = layer->prepareGrass(page, densityFactor, volume, isAvailable);
821
if (isAvailable && grassCount) {
822
//The vertex buffer can't be allocated until the exact number of polygons is known,
823
//so the locations of all grasses in this page must be precalculated.
825
//Precompute grass locations into an array of floats. A plain array is used for speed;
826
//there's no need to use a dynamic sized array since a maximum size is known.
827
float *position = new float[grassCount*4];
828
grassCount = layer->_populateGrassList(page, position, grassCount);
830
//Don't build a mesh unless it contains something
831
if (grassCount != 0){
833
switch (layer->renderTechnique){
835
mesh = generateGrass_QUAD(page, layer, position, grassCount);
837
case GRASSTECH_CROSSQUADS:
838
mesh = generateGrass_CROSSQUADS(page, layer, position, grassCount);
840
case GRASSTECH_SPRITE:
841
mesh = generateGrass_SPRITE(page, layer, position, grassCount);
846
//Add the mesh to PagedGeometry
847
Entity *entity = geom->getCamera()->getSceneManager()->createEntity(getUniqueID(), mesh->getName());
848
entity->setRenderQueueGroup(renderQueue);
849
entity->setCastShadows(layer->isCastShadowsEnabled());
850
addEntity(entity, page.centerPoint, Quaternion::IDENTITY, Vector3::UNIT_SCALE);
852
//Store the mesh pointer
853
page.meshList.push_back(mesh);
856
//Delete the position list
863
template <class TGrassLayer>
864
void GrassLoader<TGrassLayer>::unloadPage(PageInfo &page)
866
// we unload the page in the page's destructor
868
template <class TGrassLayer>
869
Mesh *GrassLoader<TGrassLayer>::generateGrass_QUAD(PageInfo &page, TGrassLayer *layer, float *grassPositions, unsigned int grassCount)
871
//Calculate the number of quads to be added
872
unsigned int quadCount;
873
quadCount = grassCount;
875
// check for overflows of the uint16's
876
unsigned int maxUInt16 = std::numeric_limits<uint16>::max();
877
if(grassCount > maxUInt16)
879
LogManager::getSingleton().logMessage("grass count overflow: you tried to use more than " + StringConverter::toString(maxUInt16) + " (thats the maximum) grass meshes for one page");
882
if(quadCount > maxUInt16)
884
LogManager::getSingleton().logMessage("quad count overflow: you tried to use more than " + StringConverter::toString(maxUInt16) + " (thats the maximum) grass meshes for one page");
888
//Create manual mesh to store grass quads
889
MeshPtr mesh = MeshManager::getSingleton().createManual(getUniqueID(), ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
890
SubMesh *subMesh = mesh->createSubMesh();
891
subMesh->useSharedVertices = false;
893
//Setup vertex format information
894
subMesh->vertexData = new VertexData;
895
subMesh->vertexData->vertexStart = 0;
896
subMesh->vertexData->vertexCount = 4 * quadCount;
898
VertexDeclaration* dcl = subMesh->vertexData->vertexDeclaration;
900
bool colours = layer->isColoursEnabled();
901
bool normals = layer->isNormalsEnabled();
902
dcl->addElement(0, offset, VET_FLOAT3, VES_POSITION);
903
offset += VertexElement::getTypeSize(VET_FLOAT3);
905
dcl->addElement(0, offset, VET_COLOUR, VES_DIFFUSE);
906
offset += VertexElement::getTypeSize(VET_COLOUR);
908
dcl->addElement(0, offset, VET_FLOAT2, VES_TEXTURE_COORDINATES);
909
offset += VertexElement::getTypeSize(VET_FLOAT2);
911
dcl->addElement(0, offset, VET_FLOAT4, VES_NORMAL);
912
offset += VertexElement::getTypeSize(VET_FLOAT4);
915
//Populate a new vertex buffer with grass
916
HardwareVertexBufferSharedPtr vbuf = HardwareBufferManager::getSingleton()
917
.createVertexBuffer(offset, subMesh->vertexData->vertexCount, HardwareBuffer::HBU_STATIC_WRITE_ONLY, false);
918
float* pReal = static_cast<float*>(vbuf->lock(HardwareBuffer::HBL_DISCARD));
920
//Calculate size variance
921
float rndWidth = layer->maxWidth - layer->minWidth;
922
float rndHeight = layer->maxHeight - layer->minHeight;
923
Vector3 normal(0.0f, 1.0f, 0.0f); //We'll use a normal pointing straight up, to best simulate grass and sunlight.
925
float minY = Math::POS_INFINITY, maxY = Math::NEG_INFINITY;
926
float *posPtr = grassPositions; //Position array "iterator"
927
for (uint16 i = 0; i < grassCount; ++i)
929
//Get the x and z positions from the position array
933
//Get the color at the grass position
934
Ogre::uint32 color(0);
937
color = layer->getColorAt(x, z);
940
float rnd = *posPtr++; //The same rnd value is used for width and height to maintain aspect ratio
941
float halfScaleX = (layer->minWidth + rndWidth * rnd) * 0.5f;
942
float scaleY = (layer->minHeight + rndHeight * rnd);
945
float angle = *posPtr++;
946
float xTrans = Math::Cos(angle) * halfScaleX;
947
float zTrans = Math::Sin(angle) * halfScaleX;
949
//Calculate heights and edge positions
950
float x1 = x - xTrans, z1 = z - zTrans;
951
float x2 = x + xTrans, z2 = z + zTrans;
955
y1 = heightFunction(x1, z1, heightFunctionUserData);
956
y2 = heightFunction(x2, z2, heightFunctionUserData);
958
if (layer->getMaxSlope() < (Math::Abs(y1 - y2) / (halfScaleX * 2))) {
959
//Degenerate the face
971
*pReal++ = (x1 - page.centerPoint.x); *pReal++ = (y1 + scaleY); *pReal++ = (z1 - page.centerPoint.z); //pos
973
*((uint32*)pReal++) = color; //color
975
*pReal++ = 0; *pReal++ = 0; //uv
977
*pReal++ = normal.x; *pReal++ = normal.y; *pReal++ = normal.z; *pReal++ = 0.0f;
980
*pReal++ = (x2 - page.centerPoint.x); *pReal++ = (y2 + scaleY); *pReal++ = (z2 - page.centerPoint.z); //pos
982
*((uint32*)pReal++) = color; //color
984
*pReal++ = 1; *pReal++ = 0; //uv
986
*pReal++ = normal.x; *pReal++ = normal.y; *pReal++ = normal.z; *pReal++ = 0.0f;
989
*pReal++ = (x1 - page.centerPoint.x); *pReal++ = (y1); *pReal++ = (z1 - page.centerPoint.z); //pos
991
*((uint32*)pReal++) = color; //color
993
*pReal++ = 0; *pReal++ = 1; //uv
995
*pReal++ = normal.x; *pReal++ = normal.y; *pReal++ = normal.z; *pReal++ = 0.0f;
998
*pReal++ = (x2 - page.centerPoint.x); *pReal++ = (y2); *pReal++ = (z2 - page.centerPoint.z); //pos
1000
*((uint32*)pReal++) = color; //color
1002
*pReal++ = 1; *pReal++ = 1; //uv
1004
*pReal++ = normal.x; *pReal++ = normal.y; *pReal++ = normal.z; *pReal++ = 0.0f;
1008
if (y1 < minY) minY = y1;
1009
if (y2 < minY) minY = y2;
1010
if (y1 + scaleY > maxY) maxY = y1 + scaleY;
1011
if (y2 + scaleY > maxY) maxY = y2 + scaleY;
1015
subMesh->vertexData->vertexBufferBinding->setBinding(0, vbuf);
1017
//Populate index buffer
1018
subMesh->indexData->indexStart = 0;
1019
subMesh->indexData->indexCount = 6 * quadCount;
1020
subMesh->indexData->indexBuffer = HardwareBufferManager::getSingleton()
1021
.createIndexBuffer(HardwareIndexBuffer::IT_16BIT, subMesh->indexData->indexCount, HardwareBuffer::HBU_STATIC_WRITE_ONLY);
1022
uint16* pI = static_cast<uint16*>(subMesh->indexData->indexBuffer->lock(HardwareBuffer::HBL_DISCARD));
1023
for (uint16 i = 0; i < quadCount; ++i)
1025
uint16 offset = i * 4;
1036
subMesh->indexData->indexBuffer->unlock();
1037
//subMesh->setBuildEdgesEnabled(autoEdgeBuildEnabled);
1040
AxisAlignedBox bounds(page.bounds.left - page.centerPoint.x, minY, page.bounds.top - page.centerPoint.z,
1041
page.bounds.right - page.centerPoint.x, maxY, page.bounds.bottom - page.centerPoint.z);
1042
mesh->_setBounds(bounds);
1043
Vector3 temp = bounds.getMaximum() - bounds.getMinimum();
1044
mesh->_setBoundingSphereRadius(temp.length() * 0.5f);
1046
LogManager::getSingleton().setLogDetail(static_cast<LoggingLevel>(0));
1047
mesh->setAutoBuildEdgeLists(autoEdgeBuildEnabled);
1049
LogManager::getSingleton().setLogDetail(LL_NORMAL);
1051
//Apply grass material to mesh
1052
subMesh->setMaterialName(layer->material->getName());
1054
if (layer->isTangentsEnabled()) {
1055
mesh->buildTangentVectors();
1059
return mesh.getPointer();
1062
template <class TGrassLayer>
1063
Mesh *GrassLoader<TGrassLayer>::generateGrass_CROSSQUADS(PageInfo &page, TGrassLayer *layer, float *grassPositions, unsigned int grassCount)
1065
//Calculate the number of quads to be added
1066
unsigned int quadCount;
1067
quadCount = grassCount * 2;
1069
// check for overflows of the uint16's
1070
unsigned int maxUInt16 = std::numeric_limits<uint16>::max();
1071
if(grassCount > maxUInt16)
1073
LogManager::getSingleton().logMessage("grass count overflow: you tried to use more than " + StringConverter::toString(maxUInt16) + " (thats the maximum) grass meshes for one page");
1076
if(quadCount > maxUInt16)
1078
LogManager::getSingleton().logMessage("quad count overflow: you tried to use more than " + StringConverter::toString(maxUInt16) + " (thats the maximum) grass meshes for one page");
1082
//Create manual mesh to store grass quads
1083
MeshPtr mesh = MeshManager::getSingleton().createManual(getUniqueID(), ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
1084
SubMesh *subMesh = mesh->createSubMesh();
1085
subMesh->useSharedVertices = false;
1087
//Setup vertex format information
1088
subMesh->vertexData = new VertexData;
1089
subMesh->vertexData->vertexStart = 0;
1090
subMesh->vertexData->vertexCount = 4 * quadCount;
1092
VertexDeclaration* dcl = subMesh->vertexData->vertexDeclaration;
1094
bool colours = layer->isColoursEnabled();
1095
bool normals = layer->isNormalsEnabled();
1096
dcl->addElement(0, offset, VET_FLOAT3, VES_POSITION);
1097
offset += VertexElement::getTypeSize(VET_FLOAT3);
1099
dcl->addElement(0, offset, VET_COLOUR, VES_DIFFUSE);
1100
offset += VertexElement::getTypeSize(VET_COLOUR);
1102
dcl->addElement(0, offset, VET_FLOAT2, VES_TEXTURE_COORDINATES);
1103
offset += VertexElement::getTypeSize(VET_FLOAT2);
1105
dcl->addElement(0, offset, VET_FLOAT4, VES_NORMAL);
1106
offset += VertexElement::getTypeSize(VET_FLOAT4);
1109
//Populate a new vertex buffer with grass
1110
HardwareVertexBufferSharedPtr vbuf = HardwareBufferManager::getSingleton()
1111
.createVertexBuffer(offset, subMesh->vertexData->vertexCount, HardwareBuffer::HBU_STATIC_WRITE_ONLY, false);
1112
float* pReal = static_cast<float*>(vbuf->lock(HardwareBuffer::HBL_DISCARD));
1114
//Calculate size variance
1115
float rndWidth = layer->maxWidth - layer->minWidth;
1116
float rndHeight = layer->maxHeight - layer->minHeight;
1118
float minY = Math::POS_INFINITY, maxY = Math::NEG_INFINITY;
1119
float *posPtr = grassPositions; //Position array "iterator"
1120
Vector3 normal(0.0f, 1.0f, 0.0f); //We'll use a normal pointing straight up, to best simulate grass and sunlight.
1121
for (uint16 i = 0; i < grassCount; ++i)
1123
//Get the x and z positions from the position array
1124
float x = *posPtr++;
1125
float z = *posPtr++;
1127
Ogre::uint32 color(0);
1130
//Get the color at the grass position
1131
color = layer->getColorAt(x, z);
1135
float rnd = *posPtr++; //The same rnd value is used for width and height to maintain aspect ratio
1136
float halfScaleX = (layer->minWidth + rndWidth * rnd) * 0.5f;
1137
float scaleY = (layer->minHeight + rndHeight * rnd);
1139
//Calculate rotation
1140
float angle = *posPtr++;
1141
float xTrans = Math::Cos(angle) * halfScaleX;
1142
float zTrans = Math::Sin(angle) * halfScaleX;
1144
//Calculate heights and edge positions
1145
float x1 = x - xTrans, z1 = z - zTrans;
1146
float x2 = x + xTrans, z2 = z + zTrans;
1149
if (heightFunction){
1150
y1 = heightFunction(x1, z1, heightFunctionUserData);
1151
y2 = heightFunction(x2, z2, heightFunctionUserData);
1153
if (layer->getMaxSlope() < (Math::Abs(y1 - y2) / (halfScaleX * 2))) {
1154
//Degenerate the face
1165
*pReal++ = (x1 - page.centerPoint.x); *pReal++ = (y1 + scaleY); *pReal++ = (z1 - page.centerPoint.z); //pos
1167
*((uint32*)pReal++) = color; //color
1169
*pReal++ = 0; *pReal++ = 0; //uv
1171
*pReal++ = normal.x; *pReal++ = normal.y; *pReal++ = normal.z; *pReal++ = 0.0f;
1174
*pReal++ = (x2 - page.centerPoint.x); *pReal++ = (y2 + scaleY); *pReal++ = (z2 - page.centerPoint.z); //pos
1176
*((uint32*)pReal++) = color; //color
1178
*pReal++ = 1; *pReal++ = 0; //uv
1180
*pReal++ = normal.x; *pReal++ = normal.y; *pReal++ = normal.z; *pReal++ = 0.0f;
1183
*pReal++ = (x1 - page.centerPoint.x); *pReal++ = (y1); *pReal++ = (z1 - page.centerPoint.z); //pos
1185
*((uint32*)pReal++) = color; //color
1187
*pReal++ = 0; *pReal++ = 1; //uv
1189
*pReal++ = normal.x; *pReal++ = normal.y; *pReal++ = normal.z; *pReal++ = 0.0f;
1192
*pReal++ = (x2 - page.centerPoint.x); *pReal++ = (y2); *pReal++ = (z2 - page.centerPoint.z); //pos
1194
*((uint32*)pReal++) = color; //color
1196
*pReal++ = 1; *pReal++ = 1; //uv
1198
*pReal++ = normal.x; *pReal++ = normal.y; *pReal++ = normal.z; *pReal++ = 0.0f;
1202
if (y1 < minY) minY = y1;
1203
if (y2 < minY) minY = y2;
1204
if (y1 + scaleY > maxY) maxY = y1 + scaleY;
1205
if (y2 + scaleY > maxY) maxY = y2 + scaleY;
1207
//Calculate heights and edge positions
1208
float x3 = x + zTrans, z3 = z - xTrans;
1209
float x4 = x - zTrans, z4 = z + xTrans;
1212
if (heightFunction){
1213
y3 = heightFunction(x3, z3, heightFunctionUserData);
1214
y4 = heightFunction(x4, z4, heightFunctionUserData);
1215
if (layer->getMaxSlope() < (Math::Abs(y3 - y4) / (halfScaleX * 2))) {
1216
//Degenerate the face
1227
*pReal++ = (x3 - page.centerPoint.x); *pReal++ = (y3 + scaleY); *pReal++ = (z3 - page.centerPoint.z); //pos
1229
*((uint32*)pReal++) = color; //color
1231
*pReal++ = 0; *pReal++ = 0; //uv
1233
*pReal++ = normal.x; *pReal++ = normal.y; *pReal++ = normal.z; *pReal++ = 0.0f;
1236
*pReal++ = (x4 - page.centerPoint.x); *pReal++ = (y4 + scaleY); *pReal++ = (z4 - page.centerPoint.z); //pos
1238
*((uint32*)pReal++) = color; //color
1240
*pReal++ = 1; *pReal++ = 0; //uv
1242
*pReal++ = normal.x; *pReal++ = normal.y; *pReal++ = normal.z; *pReal++ = 0.0f;
1245
*pReal++ = (x3 - page.centerPoint.x); *pReal++ = (y3); *pReal++ = (z3 - page.centerPoint.z); //pos
1247
*((uint32*)pReal++) = color; //color
1249
*pReal++ = 0; *pReal++ = 1; //uv
1251
*pReal++ = normal.x; *pReal++ = normal.y; *pReal++ = normal.z; *pReal++ = 0.0f;
1254
*pReal++ = (x4 - page.centerPoint.x); *pReal++ = (y4); *pReal++ = (z4 - page.centerPoint.z); //pos
1256
*((uint32*)pReal++) = color; //color
1258
*pReal++ = 1; *pReal++ = 1; //uv
1260
*pReal++ = normal.x; *pReal++ = normal.y; *pReal++ = normal.z; *pReal++ = 0.0f;
1264
if (y3 < minY) minY = y1;
1265
if (y4 < minY) minY = y2;
1266
if (y3 + scaleY > maxY) maxY = y3 + scaleY;
1267
if (y4 + scaleY > maxY) maxY = y4 + scaleY;
1271
subMesh->vertexData->vertexBufferBinding->setBinding(0, vbuf);
1273
//Populate index buffer
1274
subMesh->indexData->indexStart = 0;
1275
subMesh->indexData->indexCount = 6 * quadCount;
1276
subMesh->indexData->indexBuffer = HardwareBufferManager::getSingleton()
1277
.createIndexBuffer(HardwareIndexBuffer::IT_16BIT, subMesh->indexData->indexCount, HardwareBuffer::HBU_STATIC_WRITE_ONLY);
1278
uint16* pI = static_cast<uint16*>(subMesh->indexData->indexBuffer->lock(HardwareBuffer::HBL_DISCARD));
1279
for (uint16 i = 0; i < quadCount; ++i)
1281
uint16 offset = i * 4;
1292
subMesh->indexData->indexBuffer->unlock();
1293
//subMesh->setBuildEdgesEnabled(autoEdgeBuildEnabled);
1297
AxisAlignedBox bounds(page.bounds.left - page.centerPoint.x, minY, page.bounds.top - page.centerPoint.z,
1298
page.bounds.right - page.centerPoint.x, maxY, page.bounds.bottom - page.centerPoint.z);
1299
mesh->_setBounds(bounds);
1300
Vector3 temp = bounds.getMaximum() - bounds.getMinimum();
1301
mesh->_setBoundingSphereRadius(temp.length() * 0.5f);
1303
LogManager::getSingleton().setLogDetail(static_cast<LoggingLevel>(0));
1304
mesh->setAutoBuildEdgeLists(autoEdgeBuildEnabled);
1306
LogManager::getSingleton().setLogDetail(LL_NORMAL);
1308
//Apply grass material to mesh
1309
subMesh->setMaterialName(layer->material->getName());
1311
if (layer->isTangentsEnabled()) {
1312
mesh->buildTangentVectors();
1316
return mesh.getPointer();
1319
template <class TGrassLayer>
1320
Mesh *GrassLoader<TGrassLayer>::generateGrass_SPRITE(PageInfo &page, TGrassLayer *layer, float *grassPositions, unsigned int grassCount)
1322
//Calculate the number of quads to be added
1323
unsigned int quadCount;
1324
quadCount = grassCount;
1326
// check for overflows of the uint16's
1327
unsigned int maxUInt16 = std::numeric_limits<uint16>::max();
1328
if(grassCount > maxUInt16)
1330
LogManager::getSingleton().logMessage("grass count overflow: you tried to use more than " + StringConverter::toString(maxUInt16) + " (thats the maximum) grass meshes for one page");
1333
if(quadCount > maxUInt16)
1335
LogManager::getSingleton().logMessage("quad count overflow: you tried to use more than " + StringConverter::toString(maxUInt16) + " (thats the maximum) grass meshes for one page");
1339
//Create manual mesh to store grass quads
1340
MeshPtr mesh = MeshManager::getSingleton().createManual(getUniqueID(), ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
1341
SubMesh *subMesh = mesh->createSubMesh();
1342
subMesh->useSharedVertices = false;
1344
//Setup vertex format information
1345
subMesh->vertexData = new VertexData;
1346
subMesh->vertexData->vertexStart = 0;
1347
subMesh->vertexData->vertexCount = 4 * quadCount;
1349
VertexDeclaration* dcl = subMesh->vertexData->vertexDeclaration;
1351
bool colours = layer->isColoursEnabled();
1352
dcl->addElement(0, offset, VET_FLOAT3, VES_POSITION);
1353
offset += VertexElement::getTypeSize(VET_FLOAT3);
1354
dcl->addElement(0, offset, VET_FLOAT4, VES_NORMAL);
1355
offset += VertexElement::getTypeSize(VET_FLOAT4);
1357
dcl->addElement(0, offset, VET_COLOUR, VES_DIFFUSE);
1358
offset += VertexElement::getTypeSize(VET_COLOUR);
1360
dcl->addElement(0, offset, VET_FLOAT2, VES_TEXTURE_COORDINATES);
1361
offset += VertexElement::getTypeSize(VET_FLOAT2);
1363
//Populate a new vertex buffer with grass
1364
HardwareVertexBufferSharedPtr vbuf = HardwareBufferManager::getSingleton()
1365
.createVertexBuffer(offset, subMesh->vertexData->vertexCount, HardwareBuffer::HBU_STATIC_WRITE_ONLY, false);
1366
float* pReal = static_cast<float*>(vbuf->lock(HardwareBuffer::HBL_DISCARD));
1368
//Calculate size variance
1369
float rndWidth = layer->maxWidth - layer->minWidth;
1370
float rndHeight = layer->maxHeight - layer->minHeight;
1372
float minY = Math::POS_INFINITY, maxY = Math::NEG_INFINITY;
1373
float *posPtr = grassPositions; //Position array "iterator"
1374
for (uint16 i = 0; i < grassCount; ++i)
1376
//Get the x and z positions from the position array
1377
float x = *posPtr++;
1378
float z = *posPtr++;
1382
if (heightFunction){
1383
y = heightFunction(x, z, heightFunctionUserData);
1388
float x1 = (x - page.centerPoint.x);
1389
float z1 = (z - page.centerPoint.z);
1391
Ogre::uint32 color(0);
1394
//Get the color at the grass position
1395
color = layer->getColorAt(x, z);
1399
float rnd = *posPtr++; //The same rnd value is used for width and height to maintain aspect ratio
1400
float halfXScale = (layer->minWidth + rndWidth * rnd) * 0.5f;
1401
float scaleY = (layer->minHeight + rndHeight * rnd);
1403
//Randomly mirror grass textures
1404
float uvLeft, uvRight;
1405
if (*posPtr++ > 0.5f){
1414
*pReal++ = x1; *pReal++ = y; *pReal++ = z1; //center position
1415
*pReal++ = -halfXScale; *pReal++ = scaleY; *pReal++ = 0; *pReal++ = 0; //normal (used to store relative corner positions)
1417
*((uint32*)pReal++) = color; //color
1419
*pReal++ = uvLeft; *pReal++ = 0; //uv
1421
*pReal++ = x1; *pReal++ = y; *pReal++ = z1; //center position
1422
*pReal++ = +halfXScale; *pReal++ = scaleY; *pReal++ = 0; *pReal++ = 0; //normal (used to store relative corner positions)
1424
*((uint32*)pReal++) = color; //color
1426
*pReal++ = uvRight; *pReal++ = 0; //uv
1428
*pReal++ = x1; *pReal++ = y; *pReal++ = z1; //center position
1429
*pReal++ = -halfXScale; *pReal++ = 0.0f; *pReal++ = 0; *pReal++ = 0; //normal (used to store relative corner positions)
1431
*((uint32*)pReal++) = color; //color
1433
*pReal++ = uvLeft; *pReal++ = 1; //uv
1435
*pReal++ = x1; *pReal++ = y; *pReal++ = z1; //center position
1436
*pReal++ = +halfXScale; *pReal++ = 0.0f; *pReal++ = 0; *pReal++ = 0; //normal (used to store relative corner positions)
1438
*((uint32*)pReal++) = color; //color
1440
*pReal++ = uvRight; *pReal++ = 1; //uv
1443
if (y < minY) minY = y;
1444
if (y + scaleY > maxY) maxY = y + scaleY;
1448
subMesh->vertexData->vertexBufferBinding->setBinding(0, vbuf);
1450
//Populate index buffer
1451
subMesh->indexData->indexStart = 0;
1452
subMesh->indexData->indexCount = 6 * quadCount;
1453
subMesh->indexData->indexBuffer = HardwareBufferManager::getSingleton()
1454
.createIndexBuffer(HardwareIndexBuffer::IT_16BIT, subMesh->indexData->indexCount, HardwareBuffer::HBU_STATIC_WRITE_ONLY);
1455
uint16* pI = static_cast<uint16*>(subMesh->indexData->indexBuffer->lock(HardwareBuffer::HBL_DISCARD));
1456
for (uint16 i = 0; i < quadCount; ++i)
1458
uint16 offset = i * 4;
1469
subMesh->indexData->indexBuffer->unlock();
1470
//subMesh->setBuildEdgesEnabled(autoEdgeBuildEnabled);
1474
AxisAlignedBox bounds(page.bounds.left - page.centerPoint.x, minY, page.bounds.top - page.centerPoint.z,
1475
page.bounds.right - page.centerPoint.x, maxY, page.bounds.bottom - page.centerPoint.z);
1476
mesh->_setBounds(bounds);
1477
Vector3 temp = bounds.getMaximum() - bounds.getMinimum();
1478
mesh->_setBoundingSphereRadius(temp.length() * 0.5f);
1480
LogManager::getSingleton().setLogDetail(static_cast<LoggingLevel>(0));
1481
mesh->setAutoBuildEdgeLists(autoEdgeBuildEnabled);
1483
LogManager::getSingleton().setLogDetail(LL_NORMAL);
1485
//Apply grass material to mesh
1486
subMesh->setMaterialName(layer->material->getName());
1489
return mesh.getPointer();
1492
template <class TGrassLayer>
1493
unsigned long GrassLoader<TGrassLayer>::GUID = 0;