Index: ps/trunk/source/renderer/SHCoeffs.h =================================================================== --- ps/trunk/source/renderer/SHCoeffs.h (revision 3507) +++ ps/trunk/source/renderer/SHCoeffs.h (nonexistent) @@ -1,36 +0,0 @@ -//---------------------------------------------------------------- -// -// Name: SHCoeffs.h -// Last Update: 25/11/03 -// Author: Rich Cross -// Contact: rich@0ad.wildfiregames.com -// -// Description: implementation of 9 component spherical harmonic -// lighting -//---------------------------------------------------------------- - -#ifndef __SHCOEFFS_H -#define __SHCOEFFS_H - -#include "Color.h" - -class CSHCoeffs -{ -public: - CSHCoeffs(); - - void Clear(); - - void AddAmbientLight(const RGBColor& color); - void AddDirectionalLight(const CVector3D& lightDir,const RGBColor& lightColor); - - void Evaluate(const CVector3D& normal, RGBColor& color, RGBColor mod=RGBColor(1,1,1)) const; - - const RGBColor* GetCoefficients() const { return _data; } - -private: - RGBColor _data[9]; -}; - - -#endif Property changes on: ps/trunk/source/renderer/SHCoeffs.h ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:executable ## -1 +0,0 ## -* \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -author date id revision \ No newline at end of property Index: ps/trunk/source/renderer/SHCoeffs.cpp =================================================================== --- ps/trunk/source/renderer/SHCoeffs.cpp (revision 3507) +++ ps/trunk/source/renderer/SHCoeffs.cpp (nonexistent) @@ -1,85 +0,0 @@ -//---------------------------------------------------------------- -// -// Name: SHCoeffs.h -// Last Update: 25/11/03 -// Author: Rich Cross -// Contact: rich@0ad.wildfiregames.com -// -// Description: implementation of 9 component spherical harmonic -// lighting -//---------------------------------------------------------------- - -#include "precompiled.h" - -#include "SHCoeffs.h" -#include "MathUtil.h" - -CSHCoeffs::CSHCoeffs() -{ - Clear(); -} - -void CSHCoeffs::Clear() -{ - for (int i=0;i<9;i++) { - _data[i].Clear(); - } -} - -void CSHCoeffs::AddAmbientLight(const RGBColor& color) -{ - _data[0]+=color; -} - -void CSHCoeffs::AddDirectionalLight(const CVector3D& lightDir,const RGBColor& lightColor) -{ - CVector3D dirToLight(-lightDir.X,-lightDir.Y,-lightDir.Z); - - const float normalisation = PI*16/17; - const float c1 = SQR(0.282095f) * normalisation * 1.0f; - const float c2 = SQR(0.488603f) * normalisation * (2.0f/3.0f); - const float c3 = SQR(1.092548f) * normalisation * (1.0f/4.0f); - const float c4 = SQR(0.315392f) * normalisation * (1.0f/4.0f); - const float c5 = SQR(0.546274f) * normalisation * (1.0f/4.0f); - - _data[0]+=lightColor*c1; - _data[1]+=lightColor*c2*dirToLight.X; - _data[2]+=lightColor*c2*dirToLight.Y; - _data[3]+=lightColor*c2*dirToLight.Z; - _data[4]+=lightColor*c3*dirToLight.X*dirToLight.Z; - _data[5]+=lightColor*c3*dirToLight.Z*dirToLight.Y; - _data[6]+=lightColor*c3*dirToLight.Y*dirToLight.X; - _data[7]+=lightColor*c4*(3.0f*SQR(dirToLight.Z)-1.0f); - _data[8]+=lightColor*c5*(SQR(dirToLight.X)-SQR(dirToLight.Y)); -} - -void CSHCoeffs::Evaluate(const CVector3D& normal,RGBColor& color,RGBColor mod) const -{ -#if 1 - float c4=normal.X*normal.Z; - float c5=normal.Z*normal.Y; - float c6=normal.Y*normal.X; - float c7=(3*SQR(normal.Z)-1.0f); - float c8=(SQR(normal.X)-SQR(normal.Y)); - -#define DO(C) \ - color.C = mod.C * (\ - _data[0].C \ - + _data[1].C*normal.X \ - + _data[2].C*normal.Y \ - + _data[3].C*normal.Z \ - + _data[4].C*c4 \ - + _data[5].C*c5 \ - + _data[6].C*c6 \ - + _data[7].C*c7 \ - + _data[8].C*c8) - DO(X); - DO(Y); - DO(Z); -#undef DO - -#else - // debug aid: output quantised normal - color=RGBColor((normal.X+1)*0.5,(normal.Y+1)*0.5,(normal.Z+1)*0.5); -#endif -} Property changes on: ps/trunk/source/renderer/SHCoeffs.cpp ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:executable ## -1 +0,0 ## -* \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -author date id revision \ No newline at end of property Index: ps/trunk/binaries/data/mods/official/shaders/shlight.vs =================================================================== --- ps/trunk/binaries/data/mods/official/shaders/shlight.vs (revision 3507) +++ ps/trunk/binaries/data/mods/official/shaders/shlight.vs (nonexistent) @@ -1,16 +0,0 @@ -uniform vec3 SHCoefficients[9]; - -vec3 lighting(vec3 normal) -{ - vec3 color = SHCoefficients[0]; - vec3 normalsq = normal*normal; - color += SHCoefficients[1]*normal.x; - color += SHCoefficients[2]*normal.y; - color += SHCoefficients[3]*normal.z; - color += SHCoefficients[4]*(normal.x*normal.z); - color += SHCoefficients[5]*(normal.z*normal.y); - color += SHCoefficients[6]*(normal.y*normal.x); - color += SHCoefficients[7]*(3.0*normalsq.z-1.0); - color += SHCoefficients[8]*(normalsq.x-normalsq.y); - return color; -} Property changes on: ps/trunk/binaries/data/mods/official/shaders/shlight.vs ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Index: ps/trunk/source/graphics/Terrain.cpp =================================================================== --- ps/trunk/source/graphics/Terrain.cpp (revision 3507) +++ ps/trunk/source/graphics/Terrain.cpp (revision 3508) @@ -1,432 +1,432 @@ /////////////////////////////////////////////////////////////////////////////// // // Name: Terrain.cpp // Author: Rich Cross // Contact: rich@wildfiregames.com // /////////////////////////////////////////////////////////////////////////////// #include "precompiled.h" #include "lib/res/graphics/ogl_tex.h" #include "lib/res/mem.h" #include "renderer/Renderer.h" #include "renderer/WaterManager.h" #include #include "Terrain.h" #include "MathUtil.h" /////////////////////////////////////////////////////////////////////////////// // CTerrain constructor CTerrain::CTerrain() : m_Heightmap(0), m_Patches(0), m_MapSize(0), m_MapSizePatches(0) { } /////////////////////////////////////////////////////////////////////////////// // CTerrain constructor CTerrain::~CTerrain() { ReleaseData(); } /////////////////////////////////////////////////////////////////////////////// // ReleaseData: delete any data allocated by this terrain void CTerrain::ReleaseData() { delete[] m_Heightmap; delete[] m_Patches; } /////////////////////////////////////////////////////////////////////////////// // Initialise: initialise this terrain to the given size (in patches per side); // using given heightmap to setup elevation data bool CTerrain::Initialize(u32 size,const u16* data) { // clean up any previous terrain ReleaseData(); // store terrain size m_MapSize=(size*PATCH_SIZE)+1; m_MapSizePatches=size; - WaterManager *WaterMgr = g_Renderer.GetWaterManager(); - WaterMgr->InitWave(); + //WaterManager *WaterMgr = g_Renderer.GetWaterManager(); + //WaterMgr->InitWave(); // allocate data for new terrain m_Heightmap=new u16[m_MapSize*m_MapSize]; m_Patches=new CPatch[m_MapSizePatches*m_MapSizePatches]; // given a heightmap? if (data) { // yes; keep a copy of it memcpy2(m_Heightmap,data,m_MapSize*m_MapSize*sizeof(u16)); } else { // build a flat terrain memset(m_Heightmap,0,m_MapSize*m_MapSize*sizeof(u16)); } // setup patch parents, indices etc InitialisePatches(); return true; } /////////////////////////////////////////////////////////////////////////////// // CalcPosition: calculate the world space position of the vertex at (i,j) void CTerrain::CalcPosition(i32 i, i32 j, CVector3D& pos) const { u16 height; if ((u32)i < m_MapSize && (u32)j < m_MapSize) // will reject negative coordinates height = m_Heightmap[j*m_MapSize + i]; else height = 0; pos.X = float(i)*CELL_SIZE; pos.Y = float(height)*HEIGHT_SCALE; pos.Z = float(j)*CELL_SIZE; } /////////////////////////////////////////////////////////////////////////////// // CalcNormal: calculate the world space normal of the vertex at (i,j) void CTerrain::CalcNormal(u32 i, u32 j, CVector3D& normal) const { CVector3D left, right, up, down; - + left.Clear(); right.Clear(); up.Clear(); down.Clear(); - + // get position of vertex where normal is being evaluated CVector3D basepos; CalcPosition(i,j,basepos); CVector3D tmp; if (i>0) { CalcPosition(i-1,j,tmp); left=tmp-basepos; } if (i0) { CalcPosition(i,j-1,tmp); up=tmp-basepos; } if (j0.00001f) normal*=1.0f/nlen; } /////////////////////////////////////////////////////////////////////////////// -// GetPatch: return the patch at (x,z) in patch space, or null if the patch is +// GetPatch: return the patch at (x,z) in patch space, or null if the patch is // out of bounds CPatch* CTerrain::GetPatch(i32 x, i32 z) const { if (x<0 || x>=i32(m_MapSizePatches)) return 0; if (z<0 || z>=i32(m_MapSizePatches)) return 0; - return &m_Patches[(z*m_MapSizePatches)+x]; + return &m_Patches[(z*m_MapSizePatches)+x]; } /////////////////////////////////////////////////////////////////////////////// -// GetPatch: return the tile at (x,z) in tile space, or null if the tile is out +// GetPatch: return the tile at (x,z) in tile space, or null if the tile is out // of bounds CMiniPatch* CTerrain::GetTile(i32 x, i32 z) const { if (x<0 || x>=i32(m_MapSize)-1) return 0; if (z<0 || z>=i32(m_MapSize)-1) return 0; CPatch* patch=GetPatch(x/PATCH_SIZE,z/PATCH_SIZE); return &patch->m_MiniPatches[z%PATCH_SIZE][x%PATCH_SIZE]; } float CTerrain::getVertexGroundLevel(int x, int z) const -{ +{ if (x < 0) { x = 0; } else if (x >= (int) m_MapSize) { x = m_MapSize - 1; } if (z < 0) { z = 0; } else if (z >= (int) m_MapSize) { z = m_MapSize - 1; } return HEIGHT_SCALE * m_Heightmap[z*m_MapSize + x]; } float CTerrain::getExactGroundLevel(float x, float y) const { x /= (float)CELL_SIZE; y /= (float)CELL_SIZE; int xi = (int)floor(x); int yi = (int)floor(y); float xf = x - (float)xi; float yf = y - (float)yi; if (xi < 0) { xi = 0; xf = 0.0f; } else if (xi >= (int)m_MapSize-1) { xi = m_MapSize - 2; xf = 1.0f; } if (yi < 0) { yi = 0; yf = 0.0f; } else if (yi >= (int)m_MapSize-1) { yi = m_MapSize - 2; yf = 1.0f; } /* debug_assert( isOnMap( x, y ) ); if( !isOnMap( x, y ) ) return 0.0f; */ float h00 = m_Heightmap[yi*m_MapSize + xi]; float h01 = m_Heightmap[yi*m_MapSize + xi + m_MapSize]; float h10 = m_Heightmap[yi*m_MapSize + xi + 1]; float h11 = m_Heightmap[yi*m_MapSize + xi + m_MapSize + 1]; return (HEIGHT_SCALE * ( (1 - yf) * ((1 - xf) * h00 + xf * h10) + yf * ((1 - xf) * h01 + xf * h11))); } /////////////////////////////////////////////////////////////////////////////// // Resize: resize this terrain to the given size (in patches per side) void CTerrain::Resize(u32 size) { if (size==m_MapSizePatches) { // inexplicable request to resize terrain to the same size .. ignore it return; } if (!m_Heightmap) { // not yet created a terrain; build a default terrain of the given size now Initialize(size,0); return; } // allocate data for new terrain u32 newMapSize=(size*PATCH_SIZE)+1; u16* newHeightmap=new u16[newMapSize*newMapSize]; CPatch* newPatches=new CPatch[size*size]; if (size>m_MapSizePatches) { // new map is bigger than old one - zero the heightmap so we don't get uninitialised // height data along the expanded edges memset(newHeightmap,0,newMapSize*newMapSize); } // now copy over rows of data u32 j; u16* src=m_Heightmap; u16* dst=newHeightmap; u32 copysize=newMapSize>m_MapSize ? m_MapSize : newMapSize; for (j=0;jm_MapSize) { // entend the last height to the end of the row for (u32 i=0;im_MapSize) { // copy over heights of the last row to any remaining rows src=newHeightmap+((m_MapSize-1)*newMapSize); dst=src+newMapSize; for (u32 i=0;im_MapSizePatches) { // copy over the last tile from each column for (u32 n=0;nm_MapSizePatches) { // copy over the last tile from each column CPatch* srcpatch=&newPatches[(m_MapSizePatches-1)*size]; CPatch* dstpatch=srcpatch+size; for (u32 p=0;pm_MiniPatches[15][k]; CMiniPatch& dst=dstpatch->m_MiniPatches[m][k]; dst.Tex1=src.Tex1; dst.Tex1Priority=src.Tex1Priority; } } srcpatch++; dstpatch++; } } } // release all the original data ReleaseData(); // store new data m_Heightmap=newHeightmap; m_Patches=newPatches; m_MapSize=newMapSize; m_MapSizePatches=size; // initialise all the new patches InitialisePatches(); } /////////////////////////////////////////////////////////////////////////////// // InitialisePatches: initialise patch data void CTerrain::InitialisePatches() { for (u32 j=0;jInitialize(this,i,j); } } } /////////////////////////////////////////////////////////////////////////////// -// SetHeightMap: set up a new heightmap from 16-bit source data; +// SetHeightMap: set up a new heightmap from 16-bit source data; // assumes heightmap matches current terrain size void CTerrain::SetHeightMap(u16* heightmap) { // keep a copy of the given heightmap memcpy2(m_Heightmap,heightmap,m_MapSize*m_MapSize*sizeof(u16)); // recalculate patch bounds, invalidate vertices for (u32 j=0;jInvalidateBounds(); patch->SetDirty(RENDERDATA_UPDATE_VERTICES); } } } /////////////////////////////////////////////////////////////////////////////// -// FlattenArea: flatten out an area of terrain (specified in world space +// FlattenArea: flatten out an area of terrain (specified in world space // coords); return the average height of the flattened area float CTerrain::FlattenArea(float x0,float x1,float z0,float z1) { - u32 tx0=u32(clamp(int(float(x0/CELL_SIZE)),0,int(m_MapSize))); - u32 tx1=u32(clamp(int(float(x1/CELL_SIZE)+1.0f),0,int(m_MapSize))); - u32 tz0=u32(clamp(int(float(z0/CELL_SIZE)),0,int(m_MapSize))); - u32 tz1=u32(clamp(int(float(z1/CELL_SIZE)+1.0f),0,int(m_MapSize))); - + u32 tx0=u32(clamp(int(float(x0/CELL_SIZE)),0,int(m_MapSize))); + u32 tx1=u32(clamp(int(float(x1/CELL_SIZE)+1.0f),0,int(m_MapSize))); + u32 tz0=u32(clamp(int(float(z0/CELL_SIZE)),0,int(m_MapSize))); + u32 tz1=u32(clamp(int(float(z1/CELL_SIZE)+1.0f),0,int(m_MapSize))); + u32 count=0; u32 y=0; for (u32 x=tx0;x<=tx1;x++) { for (u32 z=tz0;z<=tz1;z++) { y+=m_Heightmap[z*m_MapSize + x]; count++; } } y/=count; for (u32 x=tx0;x<=tx1;x++) { for (u32 z=tz0;z<=tz1;z++) { m_Heightmap[z*m_MapSize + x]=(u16)y; CPatch* patch=GetPatch(x/PATCH_SIZE,z/PATCH_SIZE); patch->SetDirty(RENDERDATA_UPDATE_VERTICES); } } return y*HEIGHT_SCALE; } /////////////////////////////////////////////////////////////////////////////// void CTerrain::MakeDirty(int x0, int z0, int x1, int z1, int dirtyFlags) { // flag vertex data as dirty for affected patches, and rebuild bounds of these patches int px0 = clamp((x0/PATCH_SIZE)-1, 0, (int)m_MapSizePatches); int px1 = clamp((x1/PATCH_SIZE)+1, 0, (int)m_MapSizePatches); int pz0 = clamp((z0/PATCH_SIZE)-1, 0, (int)m_MapSizePatches); int pz1 = clamp((z1/PATCH_SIZE)+1, 0, (int)m_MapSizePatches); for (int j = pz0; j < pz1; j++) { for (int i = px0; i < px1; i++) { CPatch* patch = GetPatch(i,j); if (dirtyFlags & RENDERDATA_UPDATE_VERTICES) patch->CalcBounds(); patch->SetDirty(dirtyFlags); } } } void CTerrain::MakeDirty(int dirtyFlags) { for (u32 j = 0; j < m_MapSizePatches; j++) { for (u32 i = 0; i < m_MapSizePatches; i++) { CPatch* patch = GetPatch(i,j); if (dirtyFlags & RENDERDATA_UPDATE_VERTICES) patch->CalcBounds(); patch->SetDirty(dirtyFlags); } } } Index: ps/trunk/source/graphics/Color.h =================================================================== --- ps/trunk/source/graphics/Color.h (revision 3507) +++ ps/trunk/source/graphics/Color.h (revision 3508) @@ -1,41 +1,44 @@ /////////////////////////////////////////////////////////////////////////////// // // Name: Color.h // Author: Rich Cross // Contact: rich@wildfiregames.com // /////////////////////////////////////////////////////////////////////////////// #ifndef _COLOR_H #define _COLOR_H #include "Vector3D.h" #include "Vector4D.h" #include "lib/types.h" -// simple defines for 3 and 4 component floating point colors - just map to +// simple defines for 3 and 4 component floating point colors - just map to // corresponding vector types typedef CVector3D RGBColor; typedef CVector4D RGBAColor; // SColor3ub: structure for packed RGB colors struct SColor3ub { u8 R; u8 G; u8 B; }; // SColor4ub: structure for packed RGBA colors struct SColor4ub { u8 R; u8 G; u8 B; u8 A; + + SColor4ub() { } + SColor4ub(u8 _r, u8 _g, u8 _b, u8 _a) : R(_r), G(_g), B(_b), A(_a) { } }; extern u32 (*ConvertRGBColorTo4ub)(const RGBColor& src); #endif Index: ps/trunk/source/graphics/LightEnv.h =================================================================== --- ps/trunk/source/graphics/LightEnv.h (revision 3507) +++ ps/trunk/source/graphics/LightEnv.h (revision 3508) @@ -1,80 +1,132 @@ /////////////////////////////////////////////////////////////////////////////// // // Name: LightEnv.h // Author: Rich Cross // Contact: rich@wildfiregames.com // -// Description: class describing current lighting environment - -// at the minute, this is only sunlight and ambient light +// Description: class describing current lighting environment - +// at the minute, this is only sunlight and ambient light // parameters; will be extended to handle dynamic lights at some // later date // /////////////////////////////////////////////////////////////////////////////// #ifndef __LIGHTENV_H #define __LIGHTENV_H #include "Color.h" #include "Vector3D.h" class CMapWriter; class CMapReader; class CEditorData; class CMainFrame; class CLightSettingsDlg; /////////////////////////////////////////////////////////////////////////////// -// CLightEnv: description of a lighting environment - contains all the +// CLightEnv: description of a lighting environment - contains all the // necessary parameters for representation of the lighting within a scenario class CLightEnv { friend class CMapWriter; friend class CMapReader; friend class CXMLReader; friend class CEditorData; friend class CMainFrame; friend class CLightSettingsDlg; // weird accessor order to preserve memory layout of the class public: RGBColor m_SunColor; private: float m_Elevation; float m_Rotation; public: RGBColor m_TerrainAmbientColor; RGBColor m_UnitsAmbientColor; CVector3D m_SunDir; // get sun direction from a rotation and elevation; defined such that: // 0 rotation = (0,0,1) // PI/2 rotation = (-1,0,0) // 0 elevation = (0,0,0) // PI/2 elevation = (0,-1,0) float GetElevation() { return m_Elevation; } float GetRotation() { return m_Rotation; } void SetElevation(float f) { m_Elevation = f; CalculateSunDirection(); } void SetRotation(float f) { m_Rotation = f; CalculateSunDirection(); } + /** + * EvaluateTerrain: Calculate brightness of a point of the terrain with the given normal + * vector. + * The resulting color contains both ambient and diffuse light. + * + * @param normal normal vector (must have length 1) + * @param color resulting color + */ + void EvaluateTerrain(const CVector3D& normal, RGBColor& color) const + { + float dot = -normal.Dot(m_SunDir); + + color = m_TerrainAmbientColor; + if (dot > 0) + color += m_SunColor * dot; + } + + /** + * EvaluateUnit: Calculate brightness of a point of a unit with the given normal + * vector. + * The resulting color contains both ambient and diffuse light. + * + * @param normal normal vector (must have length 1) + * @param color resulting color + */ + void EvaluateUnit(const CVector3D& normal, RGBColor& color) const + { + float dot = -normal.Dot(m_SunDir); + + color = m_UnitsAmbientColor; + if (dot > 0) + color += m_SunColor * dot; + } + + /** + * EvaluateDirect: Like EvaluateTerrain and EvaluateUnit, but return only the direct + * sunlight term without ambient. + * + * @param normal normal vector (must have length 1) + * @param color resulting color + */ + void EvaluateDirect(const CVector3D& normal, RGBColor& color) const + { + float dot = -normal.Dot(m_SunDir); + + if (dot > 0) + color = m_SunColor * dot; + else + color = CVector3D(0,0,0); + } + +private: void CalculateSunDirection() { m_SunDir.Y=-float(sin(m_Elevation)); float scale=1+m_SunDir.Y; m_SunDir.X=scale*float(sin(m_Rotation)); m_SunDir.Z=scale*float(cos(m_Rotation)); m_SunDir.Normalize(); } }; #endif Index: ps/trunk/source/ps/GameSetup/GameSetup.cpp =================================================================== --- ps/trunk/source/ps/GameSetup/GameSetup.cpp (revision 3507) +++ ps/trunk/source/ps/GameSetup/GameSetup.cpp (revision 3508) @@ -1,1053 +1,1053 @@ #include "precompiled.h" #include "lib.h" #include "lib/sdl.h" #include "lib/ogl.h" #include "lib/detect.h" #include "lib/timer.h" #include "lib/input.h" #if CPU_IA32 # include "lib/sysdep/ia32.h" #endif #include "lib/res/res.h" #include "lib/res/file/vfs_optimizer.h" #include "lib/res/sound/snd.h" #include "lib/res/graphics/tex.h" #include "lib/res/graphics/cursor.h" #include "ps/Profile.h" #include "ps/ProfileViewer.h" #include "ps/Loader.h" #include "ps/Font.h" #include "ps/CConsole.h" #include "ps/Game.h" #include "ps/Interact.h" #include "ps/Hotkey.h" #include "ps/ConfigDB.h" #include "ps/CLogger.h" #include "ps/i18n.h" #include "ps/Overlay.h" #include "ps/StringConvert.h" #include "ps/Globals.h" #include "ps/Util.h" #include "graphics/ParticleEngine.h" #include "graphics/MapReader.h" #include "graphics/Terrain.h" #include "graphics/TextureManager.h" #include "graphics/ObjectManager.h" #include "graphics/SkeletonAnimManager.h" #include "graphics/LightEnv.h" #include "graphics/Model.h" #include "graphics/UnitManager.h" #include "graphics/MaterialManager.h" #include "graphics/MeshManager.h" #include "renderer/Renderer.h" #include "renderer/VertexBufferManager.h" #include "maths/MathUtil.h" #include "simulation/BaseEntityCollection.h" #include "simulation/Entity.h" #include "simulation/EntityHandles.h" #include "simulation/EntityManager.h" #include "simulation/PathfindEngine.h" #include "simulation/Scheduler.h" #include "simulation/Projectile.h" #include "scripting/ScriptingHost.h" #include "scripting/GameEvents.h" #include "scripting/JSInterface_Entity.h" #include "scripting/JSInterface_BaseEntity.h" #include "scripting/JSInterface_Vector3D.h" #include "scripting/JSInterface_Camera.h" #include "scripting/JSInterface_Selection.h" #include "scripting/JSInterface_Console.h" #include "scripting/JSCollection.h" #include "scripting/DOMEvent.h" #ifndef NO_GUI # include "gui/scripting/JSInterface_IGUIObject.h" # include "gui/scripting/JSInterface_GUITypes.h" # include "gui/GUI.h" #endif #include "sound/CMusicPlayer.h" #include "sound/JSI_Sound.h" #include "Network/SessionManager.h" #include "Network/Server.h" #include "Network/Client.h" #include "GameSetup/Config.h" #include "Atlas.h" #include "GameSetup.h" #include "Config.h" ERROR_GROUP(System); ERROR_TYPE(System, SDLInitFailed); ERROR_TYPE(System, VmodeFailed); ERROR_TYPE(System, RequiredExtensionsMissing); #define LOG_CATEGORY "gamesetup" static int SetVideoMode(int w, int h, int bpp, bool fullscreen) { SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); ulong flags = SDL_OPENGL; if(fullscreen) flags |= SDL_FULLSCREEN; if(!SDL_SetVideoMode(w, h, bpp, flags)) return -1; // Work around a bug in the proprietary Linux ATI driver (at least versions 8.16.20 and 8.14.13). // The driver appears to register its own atexit hook on context creation. // If this atexit hook is called before SDL_Quit destroys the OpenGL context, // some kind of double-free problem causes a crash and lockup in the driver. // Calling SDL_Quit twice appears to be harmless, though, and avoids the problem // by destroying the context *before* the driver's atexit hook is called. // (Note that atexit hooks are guarantueed to be called in reverse order of their registration.) atexit(SDL_Quit); // End work around. - + glViewport(0, 0, w, h); #ifndef NO_GUI g_GUI.UpdateResolution(); #endif oglInit(); // required after each mode change if(SDL_SetGamma(g_Gamma, g_Gamma, g_Gamma) < 0) debug_warn("SDL_SetGamma failed"); return 0; } static const uint SANE_TEX_QUALITY_DEFAULT = 5; // keep in sync with code static void SetTextureQuality(uint quality) { uint q_flags; GLint filter; retry: // keep this in sync with SANE_TEX_QUALITY_DEFAULT switch(quality) { // worst quality case 0: q_flags = OGL_TEX_HALF_RES|OGL_TEX_HALF_BPP; filter = GL_NEAREST; break; // [perf] add bilinear filtering case 1: q_flags = OGL_TEX_HALF_RES|OGL_TEX_HALF_BPP; filter = GL_LINEAR; break; // [vmem] no longer reduce resolution case 2: q_flags = OGL_TEX_HALF_BPP; filter = GL_LINEAR; break; // [vmem] add mipmaps case 3: q_flags = OGL_TEX_HALF_BPP; filter = GL_NEAREST_MIPMAP_LINEAR; break; // [perf] better filtering case 4: q_flags = OGL_TEX_HALF_BPP; filter = GL_LINEAR_MIPMAP_LINEAR; break; // [vmem] no longer reduce bpp case SANE_TEX_QUALITY_DEFAULT: q_flags = OGL_TEX_FULL_QUALITY; filter = GL_LINEAR_MIPMAP_LINEAR; break; // [perf] add anisotropy case 6: // TODO: add anisotropic filtering q_flags = OGL_TEX_FULL_QUALITY; filter = GL_LINEAR_MIPMAP_LINEAR; break; // invalid default: debug_warn("SetTextureQuality: invalid quality"); quality = SANE_TEX_QUALITY_DEFAULT; // careful: recursion doesn't work and we don't want to duplicate // the "sane" default values. goto retry; } ogl_tex_set_defaults(q_flags, filter); } //---------------------------------------------------------------------------- // GUI integration //---------------------------------------------------------------------------- void GUI_Init() { #ifndef NO_GUI {TIMER("ps_gui_init"); g_GUI.Initialize();} {TIMER("ps_gui_setup_xml"); g_GUI.LoadXMLFile("gui/test/setup.xml");} {TIMER("ps_gui_styles_xml"); g_GUI.LoadXMLFile("gui/test/styles.xml");} {TIMER("ps_gui_sprite1_xml"); g_GUI.LoadXMLFile("gui/test/sprite1.xml");} // Atlas is running, we won't need these GUI pages (for now! // what if Atlas switches to in-game mode?!) // TODO: temporary hack until revised GUI structure is completed. // if(ATLAS_IsRunning()) // return; {TIMER("ps_gui_1"); g_GUI.LoadXMLFile("gui/test/1_init.xml");} {TIMER("ps_gui_2"); g_GUI.LoadXMLFile("gui/test/2_mainmenu.xml");} {TIMER("ps_gui_3"); g_GUI.LoadXMLFile("gui/test/3_loading.xml");} {TIMER("ps_gui_4"); g_GUI.LoadXMLFile("gui/test/4_session.xml");} {TIMER("ps_gui_6"); g_GUI.LoadXMLFile("gui/test/6_subwindows.xml");} {TIMER("ps_gui_6_1"); g_GUI.LoadXMLFile("gui/test/6_1_manual.xml");} {TIMER("ps_gui_6_2"); g_GUI.LoadXMLFile("gui/test/6_2_jukebox.xml");} {TIMER("ps_gui_7"); g_GUI.LoadXMLFile("gui/test/7_atlas.xml");} {TIMER("ps_gui_9"); g_GUI.LoadXMLFile("gui/test/9_global.xml");} #endif } void GUI_Shutdown() { #ifndef NO_GUI g_GUI.Destroy(); delete &g_GUI; #endif } void GUI_ShowMainMenu() { } // display progress / description in loading screen void GUI_DisplayLoadProgress(int percent, const wchar_t* pending_task) { #ifndef NO_GUI CStrW i18n_description = I18n::translate(pending_task); JSString* js_desc = StringConvert::wstring_to_jsstring(g_ScriptingHost.getContext(), i18n_description); - g_ScriptingHost.SetGlobal("g_Progress", INT_TO_JSVAL(percent)); + g_ScriptingHost.SetGlobal("g_Progress", INT_TO_JSVAL(percent)); g_ScriptingHost.SetGlobal("g_LoadDescription", STRING_TO_JSVAL(js_desc)); g_GUI.SendEventToAll("progress"); #endif } ///////////////////////////////////////////////////////////////////////////////////////////// // RenderNoCull: render absolutely everything to a blank frame to force renderer // to load required assets static void RenderNoCull() { g_Renderer.BeginFrame(); if (g_Game) g_Game->GetView()->RenderNoCull(); g_Renderer.FlushFrame(); glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); g_Renderer.EndFrame(); } void Render() { MICROLOG(L"begin frame"); oglCheck(); #ifndef NO_GUI // HACK: because colour-parsing requires the GUI CStr skystring = "61 193 255"; CFG_GET_USER_VAL("skycolor", String, skystring); CColor skycol; GUI::ParseString(skystring, skycol); g_Renderer.SetClearColor(skycol.Int()); #endif // start new frame g_Renderer.BeginFrame(); oglCheck(); if (g_Game && g_Game->IsGameStarted()) { g_Game->GetView()->Render(); oglCheck(); MICROLOG(L"flush frame"); PROFILE_START( "flush frame" ); g_Renderer.FlushFrame(); PROFILE_END( "flush frame" ); glPushAttrib( GL_ENABLE_BIT ); glDisable( GL_LIGHTING ); glDisable( GL_TEXTURE_2D ); glDisable( GL_DEPTH_TEST ); if( g_EntGraph ) { PROFILE( "render entity overlays" ); glColor3f( 1.0f, 0.0f, 1.0f ); MICROLOG(L"render entities"); g_EntityManager.renderAll(); // <-- collision outlines, pathing routes } PROFILE_START( "render selection" ); glEnable( GL_DEPTH_TEST ); g_Mouseover.renderSelectionOutlines(); g_Selection.renderSelectionOutlines(); glDisable( GL_DEPTH_TEST ); PROFILE_END( "render selection" ); PROFILE_START( "render building placement cursor" ); - if( g_BuildingPlacer.m_active ) + if( g_BuildingPlacer.m_active ) { //glEnable( GL_DEPTH_TEST ); g_BuildingPlacer.render(); //glDisable( GL_DEPTH_TEST ); } PROFILE_END( "render building placement cursor" ); - + PROFILE_START( "render health bars" ); glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); glOrtho(0.f, (float)g_xres, 0.f, (float)g_yres, -1.f, 1000.f); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); g_Mouseover.renderHealthBars(); g_Selection.renderHealthBars(); g_Mouseover.renderStaminaBars(); g_Selection.renderStaminaBars(); glPopMatrix(); glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); PROFILE_END( "render health bars" ); glPopAttrib(); } else { PROFILE_START( "flush frame" ); g_Renderer.FlushFrame(); PROFILE_END( "flush frame" ); } oglCheck(); PROFILE_START( "render fonts" ); MICROLOG(L"render fonts"); // overlay mode glPushAttrib(GL_ENABLE_BIT); glEnable(GL_TEXTURE_2D); glDisable(GL_CULL_FACE); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_BLEND); glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); glOrtho(0.f, (float)g_xres, 0.f, (float)g_yres, -1.f, 1000.f); glMatrixMode(GL_MODELVIEW); glPushMatrix(); PROFILE_END( "render fonts" ); oglCheck(); #ifndef NO_GUI // Temp GUI message GeeTODO glLoadIdentity(); MICROLOG(L"render GUI"); PROFILE_START( "render gui" ); g_GUI.Draw(); PROFILE_END( "render gui" ); #endif oglCheck(); // Particle Engine Updating CParticleEngine::GetInstance()->updateEmitters(); // Text: // Use the GL_ALPHA texture as the alpha channel with a flat colouring glDisable(GL_ALPHA_TEST); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); // Added -- glEnable(GL_TEXTURE_2D); // -- GL oglCheck(); { PROFILE( "render console" ); glLoadIdentity(); MICROLOG(L"render console"); CFont font("console"); font.Bind(); g_Console->Render(); } oglCheck(); // Profile information PROFILE_START( "render profiling" ); g_ProfileViewer.RenderProfile(); PROFILE_END( "render profiling" ); oglCheck(); if (g_Game && g_Game->IsGameStarted()) { PROFILE( "render selection overlays" ); g_Mouseover.renderOverlays(); g_Selection.renderOverlays(); } oglCheck(); // Draw the cursor (or set the Windows cursor, on Windows) CStr8 cursorName = g_BuildingPlacer.m_active ? "action-build" : g_CursorName; cursor_draw(cursorName, g_mouse_x, g_mouse_y); // restore glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); glPopMatrix(); glPopAttrib(); MICROLOG(L"end frame"); g_Renderer.EndFrame(); oglCheck(); } static void InitScripting() { TIMER("InitScripting"); // Create the scripting host. This needs to be done before the GUI is created. new ScriptingHost; // It would be nice for onLoad code to be able to access the setTimeout() calls. new CScheduler; // Register the JavaScript interfaces with the runtime CEntity::ScriptingInit(); CBaseEntity::ScriptingInit(); JSI_Sound::ScriptingInit(); CProfileNode::ScriptingInit(); #ifndef NO_GUI JSI_IGUIObject::init(); JSI_GUITypes::init(); #endif JSI_Vector3D::init(); EntityCollection::Init( "EntityCollection" ); SColour::ScriptingInit(); CPlayer::ScriptingInit(); PlayerCollection::Init( "PlayerCollection" ); CJSComplexPropertyAccessor::ScriptingInit(); // <-- Doesn't really matter which we use, but we know CJSPropertyAccessor is already being compiled for T = CEntity. CScriptEvent::ScriptingInit(); CJSProgressTimer::ScriptingInit(); CProjectile::ScriptingInit(); - + g_ScriptingHost.DefineConstant( "NOTIFY_GOTO", CEntityListener::NOTIFY_GOTO ); g_ScriptingHost.DefineConstant( "NOTIFY_RUN", CEntityListener::NOTIFY_RUN ); g_ScriptingHost.DefineConstant( "NOTIFY_FOLLOW", CEntityListener::NOTIFY_FOLLOW ); g_ScriptingHost.DefineConstant( "NOTIFY_ATTACK", CEntityListener::NOTIFY_ATTACK ); g_ScriptingHost.DefineConstant( "NOTIFY_DAMAGE", CEntityListener::NOTIFY_DAMAGE ); g_ScriptingHost.DefineConstant( "NOTIFY_COMBAT", CEntityListener::NOTIFY_COMBAT ); g_ScriptingHost.DefineConstant( "NOTIFY_ESCORT", CEntityListener::NOTIFY_ESCORT ); g_ScriptingHost.DefineConstant( "NOTIFY_HEAL", CEntityListener::NOTIFY_HEAL ); g_ScriptingHost.DefineConstant( "NOTIFY_GATHER", CEntityListener::NOTIFY_GATHER ); g_ScriptingHost.DefineConstant( "ORDER_NONE", -1 ); g_ScriptingHost.DefineConstant( "ORDER_GOTO", CEntityOrder::ORDER_GOTO ); g_ScriptingHost.DefineConstant( "ORDER_RUN", CEntityOrder::ORDER_RUN ); g_ScriptingHost.DefineConstant( "ORDER_PATROL", CEntityOrder::ORDER_PATROL ); g_ScriptingHost.DefineConstant( "ORDER_GENERIC", CEntityOrder::ORDER_GENERIC ); #define REG_JS_CONSTANT(_name) g_ScriptingHost.DefineConstant(#_name, _name) REG_JS_CONSTANT(SDL_BUTTON_LEFT); REG_JS_CONSTANT(SDL_BUTTON_MIDDLE); REG_JS_CONSTANT(SDL_BUTTON_RIGHT); REG_JS_CONSTANT(SDL_BUTTON_WHEELUP); REG_JS_CONSTANT(SDL_BUTTON_WHEELDOWN); #undef REG_JS_CONSTANT CNetMessage::ScriptingInit(); JSI_Camera::init(); JSI_Console::init(); new CGameEvents; } static void InitVfs(const char* argv0) { TIMER("InitVfs"); (void)file_init(); // set root directory to "$game_dir/data". all relative file paths // passed to file.cpp will be based from this dir. // (we don't set current directory because other libraries may // hijack it). // // "../data" is relative to the executable (in "$game_dir/system"). // // rationale for data/ being root: untrusted scripts must not be // allowed to overwrite critical game (or worse, OS) files. // the VFS prevents any accesses to files above this directory. (void)file_set_root_dir(argv0, "../data"); vfs_init(); vfs_mount("", "mods/official", VFS_MOUNT_RECURSIVE|VFS_MOUNT_ARCHIVES|VFS_MOUNT_WATCH); vfs_mount("screenshots/", "screenshots"); vfs_mount("profiles/", "profiles", VFS_MOUNT_RECURSIVE); // don't try vfs_display yet: SDL_Init hasn't yet redirected stdout } static void InitPs(bool setup_gui) { // console { TIMER("ps_console"); g_Console->UpdateScreenSize(g_xres, g_yres); // Calculate and store the line spacing CFont font("console"); g_Console->m_iFontHeight = font.GetLineSpacing(); // Offset by an arbitrary amount, to make it fit more nicely g_Console->m_iFontOffset = 9; } // language and hotkeys { TIMER("ps_lang_hotkeys"); std::string lang = "english"; CFG_GET_SYS_VAL("language", String, lang); I18n::LoadLanguage(lang.c_str()); loadHotkeys(); } // GUI uses VFS, so this must come after VFS init. if (setup_gui) GUI_Init(); } static void InitInput() { SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL); // register input handlers // This stack is constructed so the first added, will be the last // one called. This is important, because each of the handlers // has the potential to block events to go further down // in the chain. I.e. the last one in the list added, is the // only handler that can block all messages before they are // processed. in_add_handler(game_view_handler); in_add_handler(interactInputHandler); in_add_handler(conInputHandler); in_add_handler(CProfileViewer::InputThunk); - in_add_handler(hotkeyInputHandler); + in_add_handler(hotkeyInputHandler); in_add_handler(GlobalsInputHandler); } static void ShutdownPs() { GUI_Shutdown(); delete g_Console; // disable the special Windows cursor, or free textures for OGL cursors cursor_draw(0, g_mouse_x, g_mouse_y); // close down Xerces if it was loaded CXeromyces::Terminate(); // Unload the real language (since it depends on the scripting engine, // which is going to be killed later) and use the English fallback messages I18n::LoadLanguage(NULL); } static void InitRenderer() { TIMER("InitRenderer"); // create renderer new CRenderer; // set renderer options from command line options - NOVBO must be set before opening the renderer g_Renderer.SetOptionBool(CRenderer::OPT_NOVBO,g_NoGLVBO); g_Renderer.SetOptionBool(CRenderer::OPT_SHADOWS,g_Shadows); g_Renderer.SetOptionBool(CRenderer::OPT_NOPBUFFER,g_NoPBuffer); g_Renderer.SetRenderPath(CRenderer::GetRenderPathByName(g_RenderPath)); g_Renderer.SetOptionFloat(CRenderer::OPT_LODBIAS, g_LodBias); // create terrain related stuff new CTextureManager; // create the material manager new CMaterialManager; new CMeshManager; // create actor related stuff new CSkeletonAnimManager; new CObjectManager; new CUnitManager; // Particle Engine stuff CParticleEngine::GetInstance()->initParticleSystem(); MICROLOG(L"init renderer"); g_Renderer.Open(g_xres,g_yres,g_bpp); // Setup default lighting environment. Since the Renderer accesses the // lighting environment through a pointer, this has to be done before // the first Frame. g_LightEnv.m_SunColor=RGBColor(1,1,1); g_LightEnv.SetRotation(DEGTORAD(270)); g_LightEnv.SetElevation(DEGTORAD(45)); - g_LightEnv.m_TerrainAmbientColor=RGBColor(0,0,0); + g_LightEnv.m_TerrainAmbientColor=RGBColor(0.4f,0.4f,0.4f); g_LightEnv.m_UnitsAmbientColor=RGBColor(0.4f,0.4f,0.4f); g_Renderer.SetLightEnv(&g_LightEnv); // I haven't seen the camera affecting GUI rendering and such, but the // viewport has to be updated according to the video mode SViewPort vp; vp.m_X=0; vp.m_Y=0; vp.m_Width=g_xres; vp.m_Height=g_yres; g_Renderer.SetViewport(vp); } static void InitSDL() { MICROLOG(L"init sdl"); if(SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER|SDL_INIT_NOPARACHUTE) < 0) { LOG(ERROR, LOG_CATEGORY, "SDL library initialization failed: %s", SDL_GetError()); throw PSERROR_System_SDLInitFailed(); } atexit(SDL_Quit); SDL_EnableUNICODE(1); } void EndGame() { if (g_NetServer) { delete g_NetServer; g_NetServer=NULL; } else if (g_NetClient) { delete g_NetClient; g_NetClient=NULL; } delete g_Game; g_Game=NULL; } void Shutdown() { MICROLOG(L"Shutdown"); ShutdownPs(); // Must delete g_GUI before g_ScriptingHost if (g_Game) EndGame(); TIMER_BEGIN("shutdown Scheduler"); delete &g_Scheduler; TIMER_END("shutdown Scheduler"); TIMER_BEGIN("shutdown SessionManager"); delete &g_SessionManager; TIMER_END("shutdown SessionManager"); TIMER_BEGIN("shutdown mouse stuff"); delete &g_Mouseover; delete &g_Selection; delete &g_BuildingPlacer; TIMER_END("shutdown mouse stuff"); TIMER_BEGIN("shutdown Pathfinder"); delete &g_Pathfinder; TIMER_END("shutdown Pathfinder"); // Managed by CWorld // delete &g_EntityManager; TIMER_BEGIN("shutdown game scripting stuff"); delete &g_GameAttributes; delete &g_JSGameEvents; delete &g_EntityTemplateCollection; TIMER_END("shutdown game scripting stuff"); // destroy actor related stuff TIMER_BEGIN("shutdown actor stuff"); delete &g_UnitMan; delete &g_ObjMan; delete &g_SkelAnimMan; delete &g_MaterialManager; delete &g_MeshManager; TIMER_END("shutdown actor stuff"); // destroy terrain related stuff TIMER_BEGIN("shutdown TexMan"); delete &g_TexMan; TIMER_END("shutdown TexMan"); // destroy renderer TIMER_BEGIN("shutdown Renderer"); delete &g_Renderer; g_VBMan.Shutdown(); TIMER_END("shutdown Renderer"); TIMER_BEGIN("shutdown ScriptingHost"); delete &g_ScriptingHost; TIMER_END("shutdown ScriptingHost"); TIMER_BEGIN("shutdown ConfigDB"); delete &g_ConfigDB; TIMER_END("shutdown ConfigDB"); // Shut down the network loop TIMER_BEGIN("shutdown CSocketBase"); CSocketBase::Shutdown(); TIMER_END("shutdown CSocketBase"); // Really shut down the i18n system. Any future calls // to translate() will crash. TIMER_BEGIN("shutdown I18N"); I18n::Shutdown(); TIMER_END("shutdown I18N"); // resource // first shut down all resource owners, and then the handle manager. TIMER_BEGIN("resource modules"); snd_shutdown(); (void)trace_write_to_file("../logs/trace.txt"); vfs_shutdown(); // must come before h_mgr_shutdown - it frees IO buffers, // which we don't want showing up as leaks. file_shutdown(); // this forcibly frees all open handles (thus preventing real leaks), // and makes further access to h_mgr impossible. h_mgr_shutdown(); // must come after h_mgr_shutdown - it causes memory // to be freed, which requires this module to still be active. mem_shutdown(); TIMER_END("resource modules"); TIMER_BEGIN("shutdown misc"); timer_display_client_totals(); // should be last, since the above use them debug_shutdown(); delete &g_Logger; delete &g_Profiler; delete &g_ProfileViewer; TIMER_END("shutdown misc"); } // workaround for VC7 EBP-trashing bug, which confuses the stack trace code. #if MSC_VERSION # pragma optimize("", off) #endif void Init(int argc, char* argv[], uint flags) { const bool setup_vmode = (flags & INIT_HAVE_VMODE) == 0; debug_printf("INIT &argc=%p &argv=%p\n", &argc, &argv); MICROLOG(L"Init"); debug_set_thread_name("main"); // add all debug_printf "tags" that we are interested in: debug_filter_add("TIMER"); // If you ever want to catch a particular allocation: //_CrtSetBreakAlloc(187); // Query CPU capabilities, possibly set some CPU-dependent flags cpu_init(); - + // Do this as soon as possible, because it chdirs // and will mess up the error reporting if anything // crashes before the working directory is set. MICROLOG(L"init vfs"); const char* argv0 = argc? argv[0] : NULL; // ScEd doesn't have a main(argc, argv), and so it has no argv. In that // case, just pass NULL to InitVfs, which will work out the current // directory for itself. InitVfs(argv0); // This must come after VFS init, which sets the current directory // (required for finding our output log files). new CLogger; // Call LoadLanguage(NULL) to initialise the I18n system, but // without loading an actual language file - translate() will // just show the English key text, which is better than crashing // from a null pointer when attempting to translate e.g. error messages. // Real languages can only be loaded when the scripting system has // been initialised. // // this uses LOG and must therefore come after CLogger init. MICROLOG(L"init i18n"); I18n::LoadLanguage(NULL); // Set up the console early, so that debugging // messages can be logged to it. (The console's size // and fonts are set later in InitPs()) g_Console = new CConsole(); if(setup_vmode) InitSDL(); // preferred video mode = current desktop settings // (command line params may override these) get_cur_vmode(&g_xres, &g_yres, &g_bpp, &g_freq); new CProfileViewer; new CProfileManager; // before any script code MICROLOG(L"init scripting"); InitScripting(); // before GUI // g_ConfigDB, command line args, globals CONFIG_Init(argc, argv); // setup_gui must be set after CONFIG_Init, so command-line parameters can disable it const bool setup_gui = ((flags & INIT_NO_GUI) == 0 && g_AutostartMap.Length() == 0); // GUI is notified in SetVideoMode, so this must come before that. #ifndef NO_GUI new CGUI; #endif bool windowed = false; CFG_GET_SYS_VAL("windowed", Bool, windowed); if(setup_vmode) { MICROLOG(L"SetVideoMode"); if(SetVideoMode(g_xres, g_yres, 32, !windowed) < 0) { LOG(ERROR, LOG_CATEGORY, "Could not set %dx%d graphics mode: %s", g_xres, g_yres, SDL_GetError()); throw PSERROR_System_VmodeFailed(); } SDL_WM_SetCaption("0 A.D.", "0 A.D."); } uint quality = SANE_TEX_QUALITY_DEFAULT; // TODO: set value from config file SetTextureQuality(quality); // required by ogl_tex to detect broken gfx card/driver combos get_gfx_info(); oglCheck(); if(!g_Quickstart) { WriteSystemInfo(); vfs_display(); // disabled to decrease startup time } else { // speed up startup by disabling all sound // (OpenAL init will be skipped). // must be called before first snd_open. snd_disable(true); } // (must come after SetVideoMode, since it calls oglInit) const char* missing = oglHaveExtensions(0, "GL_ARB_multitexture", "GL_EXT_draw_range_elements", "GL_ARB_texture_env_combine", "GL_ARB_texture_env_dot3", "GL_ARB_texture_env_crossbar", 0); if(missing) { wchar_t buf[500]; const wchar_t* fmt = L"The %hs extension doesn't appear to be available on your computer. " L"The game may still work, though - you are welcome to try at your own risk. " L"If not or it doesn't look right, upgrade your graphics card."; swprintf(buf, ARRAY_SIZE(buf), fmt, missing); DISPLAY_ERROR(buf); // TODO: i18n } // enable/disable VSync // note: "GL_EXT_SWAP_CONTROL" is "historical" according to dox. #if OS_WIN if(oglHaveExtension("WGL_EXT_swap_control")) pwglSwapIntervalEXT(g_VSync? 1 : 0); #endif MICROLOG(L"init ps"); InitPs(setup_gui); oglCheck(); InitRenderer(); { TIMER("Init_entitiessection"); // This needs to be done after the renderer has loaded all its actors... new CBaseEntityCollection; // CEntityManager is managed by CWorld //new CEntityManager; new CSelectedEntities; new CMouseoverEntities; } { TIMER("Init_miscgamesection"); new CPathfindEngine; new CBuildingPlacer; new CSessionManager; new CGameAttributes; } { TIMER("Init_misc"); // Register a few Game/Network JS globals g_ScriptingHost.SetGlobal("g_GameAttributes", OBJECT_TO_JSVAL(g_GameAttributes.GetScript())); // Check for heap corruption after every allocation. Very, very slowly. // (And it highlights the allocation just after the one you care about, // so you need to run it again and tell it to break on the one before.) //debug_heap_enable(DEBUG_HEAP_ALL); InitInput(); oglCheck(); } #ifndef NO_GUI { TIMER("Init_guiload"); g_GUI.SendEventToAll("load"); } #endif #ifndef SCED // HACK: ScEd's init order makes this break { TIMER("Init_renderblank"); MICROLOG(L"render blank"); // render everything to a blank frame to force renderer to load everything RenderNoCull(); } #endif if (g_FixedFrameTiming) { CCamera &g_Camera=*g_Game->GetView()->GetCamera(); #if 0 // TOPDOWN g_Camera.SetProjection(1.0f,10000.0f,DEGTORAD(90)); g_Camera.m_Orientation.SetIdentity(); g_Camera.m_Orientation.RotateX(DEGTORAD(90)); g_Camera.m_Orientation.Translate(CELL_SIZE*250*0.5, 250, CELL_SIZE*250*0.5); #else // std view g_Camera.SetProjection(1.0f,10000.0f,DEGTORAD(20)); g_Camera.m_Orientation.SetXRotation(DEGTORAD(30)); g_Camera.m_Orientation.RotateY(DEGTORAD(-45)); g_Camera.m_Orientation.Translate(350, 350, -275); #endif g_Camera.UpdateFrustum(); } if (g_AutostartMap.Length()) { // Code copied mostly from atlas/GameInterface/Handlers/Map.cpp - // maybe should be refactored to avoid duplication g_GameAttributes.m_MapFile = g_AutostartMap+".pmp"; - for (int i=1; i<8; ++i) + for (int i=1; i<8; ++i) g_GameAttributes.GetSlot(i)->AssignLocal(); g_GameAttributes.m_LOSSetting = 2; g_Game = new CGame(); PSRETURN ret = g_Game->StartGame(&g_GameAttributes); debug_assert(ret == PSRETURN_OK); LDR_NonprogressiveLoad(); ret = g_Game->ReallyStartGame(); debug_assert(ret == PSRETURN_OK); } } #if MSC_VERSION # pragma optimize("", on) // restore; see above. #endif Index: ps/trunk/source/renderer/Renderer.h =================================================================== --- ps/trunk/source/renderer/Renderer.h (revision 3507) +++ ps/trunk/source/renderer/Renderer.h (revision 3508) @@ -1,456 +1,461 @@ /////////////////////////////////////////////////////////////////////////////// // // Name: Renderer.h // Author: Rich Cross // Contact: rich@wildfiregames.com // // Description: OpenGL renderer class; a higher level interface // on top of OpenGL to handle rendering the basic visual games // types - terrain, models, sprites, particles etc // /////////////////////////////////////////////////////////////////////////////// #ifndef RENDERER_H #define RENDERER_H #include #include "ogl.h" #include "Camera.h" #include "Frustum.h" -#include "SHCoeffs.h" #include "Terrain.h" #include "Singleton.h" #include "Overlay.h" #include "scripting/ScriptableObject.h" #include "renderer/ModelRenderer.h" // necessary declarations class CCamera; class CPatch; class CSprite; class CParticleSys; class COverlay; class CMaterial; class CModel; class CLightEnv; class CTexture; class CTerrain; class RenderPathVertexShader; class WaterManager; // rendering modes enum ERenderMode { WIREFRAME, SOLID, EDGED_FACES }; // stream flags #define STREAM_POS 0x01 #define STREAM_NORMAL 0x02 #define STREAM_COLOR 0x04 #define STREAM_UV0 0x08 #define STREAM_UV1 0x10 #define STREAM_UV2 0x20 #define STREAM_UV3 0x40 #define STREAM_POSTOUV0 0x80 ////////////////////////////////////////////////////////////////////////////////////////// // SVertex3D: simple 3D vertex declaration struct SVertex3D { float m_Position[3]; float m_TexCoords[2]; unsigned int m_Color; }; ////////////////////////////////////////////////////////////////////////////////////////// // SVertex2D: simple 2D vertex declaration struct SVertex2D { float m_Position[2]; float m_TexCoords[2]; unsigned int m_Color; }; // access to sole renderer object #define g_Renderer CRenderer::GetSingleton() /////////////////////////////////////////////////////////////////////////////////////////// // CRenderer: base renderer class - primary interface to the rendering engine struct CRendererInternals; class CRenderer : public Singleton, public CJSObject { public: // various enumerations and renderer related constants enum { NumAlphaMaps=14 }; enum { MaxTextureUnits=16 }; enum Option { OPT_NOVBO, OPT_NOPBUFFER, OPT_SHADOWS, OPT_SHADOWCOLOR, OPT_LODBIAS }; enum RenderPath { // If no rendering path is configured explicitly, the renderer // will choose the path when Open() is called. RP_DEFAULT, // Classic fixed function. RP_FIXED, // Use (GL 2.0) vertex shaders for T&L when possible. RP_VERTEXSHADER }; // stats class - per frame counts of number of draw calls, poly counts etc struct Stats { // set all stats to zero void Reset() { memset(this,0,sizeof(*this)); } // add given stats to this stats Stats& operator+=(const Stats& rhs) { m_Counter++; m_DrawCalls+=rhs.m_DrawCalls; m_TerrainTris+=rhs.m_TerrainTris; m_ModelTris+=rhs.m_ModelTris; m_BlendSplats+=rhs.m_BlendSplats; return *this; } // count of the number of stats added together size_t m_Counter; // number of draw calls per frame - total DrawElements + Begin/End immediate mode loops size_t m_DrawCalls; // number of terrain triangles drawn size_t m_TerrainTris; // number of (non-transparent) model triangles drawn size_t m_ModelTris; // number of splat passes for alphamapping size_t m_BlendSplats; }; // renderer options struct Options { bool m_NoVBO; bool m_Shadows; RGBAColor m_ShadowColor; float m_LodBias; RenderPath m_RenderPath; } m_Options; struct Caps { bool m_VBO; bool m_TextureBorderClamp; bool m_GenerateMipmaps; bool m_VertexShader; bool m_DepthTextureShadows; }; public: // constructor, destructor CRenderer(); ~CRenderer(); // open up the renderer: performs any necessary initialisation bool Open(int width,int height,int depth); // resize renderer view void Resize(int width,int height); // set/get boolean renderer option void SetOptionBool(enum Option opt,bool value); bool GetOptionBool(enum Option opt) const; // set/get RGBA color renderer option void SetOptionColor(enum Option opt,const RGBAColor& value); void SetOptionFloat(enum Option opt, float val); const RGBAColor& GetOptionColor(enum Option opt) const; void SetRenderPath(RenderPath rp); RenderPath GetRenderPath() const { return m_Options.m_RenderPath; } static CStr GetRenderPathName(RenderPath rp); static RenderPath GetRenderPathByName(CStr name); // return view width int GetWidth() const { return m_Width; } // return view height int GetHeight() const { return m_Height; } // return view aspect ratio float GetAspect() const { return float(m_Width)/float(m_Height); } // signal frame start void BeginFrame(); // render any batched objects void FlushFrame(); // signal frame end void EndFrame(); // set color used to clear screen in BeginFrame() void SetClearColor(u32 color); // return current frame counter int GetFrameCounter() const { return m_FrameCounter; } /** * SetCamera: Set up the camera used for subsequent rendering operations; this includes * setting OpenGL state like viewport, projection and modelview matrices. * * @param viewCamera this camera determines the eye position for rendering * @param culLCamera this camera determines the frustum for culling in the renderer and * for shadow calculations */ void SetCamera(const CCamera& viewCamera, const CCamera& cullCamera); // set the viewport void SetViewport(const SViewPort &); // submission of objects for rendering; the passed matrix indicating the transform must be scoped such that it is valid beyond // the call to frame end, as must the object itself void Submit(CPatch* patch); void Submit(CModel* model); void Submit(CSprite* sprite); void Submit(CParticleSys* psys); void Submit(COverlay* overlay); // basic primitive rendering operations in 2 and 3D; handy for debugging stuff, but also useful in // editor tools (eg for highlighting specific terrain patches) // note: // * all 3D vertices specified in world space // * primitive operations rendered immediatedly, never batched // * primitives rendered in current material (set via SetMaterial) // void RenderLine(const SVertex2D* vertices); // void RenderLineLoop(int len,const SVertex2D* vertices); // void RenderTri(const SVertex2D* vertices); // void RenderQuad(const SVertex2D* vertices); // void RenderLine(const SVertex3D* vertices); // void RenderLineLoop(int len,const SVertex3D* vertices); // void RenderTri(const SVertex3D* vertices); // void RenderQuad(const SVertex3D* vertices); // set the current lighting environment; (note: the passed pointer is just copied to a variable within the renderer, // so the lightenv passed must be scoped such that it is not destructed until after the renderer is no longer rendering) void SetLightEnv(CLightEnv* lightenv) { m_LightEnv=lightenv; } // set the mode to render subsequent terrain patches void SetTerrainRenderMode(ERenderMode mode) { m_TerrainRenderMode=mode; } // get the mode to render subsequent terrain patches ERenderMode GetTerrainRenderMode() const { return m_TerrainRenderMode; } // set the mode to render subsequent models void SetModelRenderMode(ERenderMode mode) { m_ModelRenderMode=mode; } // get the mode to render subsequent models ERenderMode GetModelRenderMode() const { return m_ModelRenderMode; } // try and load the given texture bool LoadTexture(CTexture* texture,u32 wrapflags); // set the given unit to reference the given texture; pass a null texture to disable texturing on any unit; // active texture unit always set to given unit on exit void SetTexture(int unit,CTexture* texture); // bind a GL texture object to active unit void BindTexture(int unit,GLuint tex); // query transparency of given texture bool IsTextureTransparent(CTexture* texture); // load the default set of alphamaps. // return a negative error code if anything along the way fails. // called via delay-load mechanism. int LoadAlphaMaps(); void UnloadAlphaMaps(); // return stats accumulated for current frame const Stats& GetStats() { return m_Stats; } // return the current light environment const CLightEnv &GetLightEnv() { return *m_LightEnv; } // return the current view camera const CCamera& GetViewCamera() const { return m_ViewCamera; } // return the current cull camera const CCamera& GetCullCamera() const { return m_CullCamera; } /** * GetWaterManager: Return the renderer's water manager. * * @return the WaterManager object used by the renderer */ WaterManager* GetWaterManager() { return m_WaterManager; } /** * SetFastPlayerColor: Tell the renderer which path to take for * player colored models. Both paths should provide the same visual * quality, however the slow path runs on older hardware using multi-pass. * * @param fast true if the fast path should be used from now on. If fast * is true but the OpenGL implementation does not support it, a warning * is printed and the slow path is used instead. */ void SetFastPlayerColor(bool fast); /** * GetCapabilities: Return which OpenGL capabilities are available and enabled. * * @return capabilities structure */ const Caps& GetCapabilities() const { return m_Caps; } protected: friend struct CRendererInternals; friend class CVertexBuffer; friend class CPatchRData; friend class FixedFunctionModelRenderer; friend class ModelRenderer; friend class PolygonSortModelRenderer; friend class SortModelRenderer; friend class RenderPathVertexShader; friend class HWLightingModelRenderer; friend class InstancingModelRenderer; friend class TerrainRenderer; // scripting jsval JSI_GetFastPlayerColor(JSContext*); void JSI_SetFastPlayerColor(JSContext* ctx, jsval newval); jsval JSI_GetRenderPath(JSContext*); void JSI_SetRenderPath(JSContext* ctx, jsval newval); jsval JSI_GetUseDepthTexture(JSContext*); void JSI_SetUseDepthTexture(JSContext* ctx, jsval newval); static void ScriptingInit(); // patch rendering stuff void RenderPatches(); // model rendering stuff void RenderModels(); void RenderTransparentModels(); // shadow rendering stuff void RenderShadowMap(); // debugging void DisplayFrustum(); // RENDERER DATA: /// Private data that is not needed by inline functions CRendererInternals* m; // view width int m_Width; // view height int m_Height; // view depth (bpp) int m_Depth; // frame counter int m_FrameCounter; // current terrain rendering mode ERenderMode m_TerrainRenderMode; // current model rendering mode ERenderMode m_ModelRenderMode; /** * m_ViewCamera: determines the eye position for rendering * * @see CGameView::m_ViewCamera */ CCamera m_ViewCamera; /** * m_CullCamera: determines the frustum for culling and shadowmap calculations * * @see CGameView::m_ViewCamera */ CCamera m_CullCamera; // color used to clear screen in BeginFrame float m_ClearColor[4]; // submitted object lists for batching std::vector m_Sprites; std::vector m_ParticleSyses; std::vector m_Overlays; // current lighting setup CLightEnv* m_LightEnv; - // current spherical harmonic coefficients (for unit lighting), derived from lightenv - CSHCoeffs m_SHCoeffsUnits; - // current spherical harmonic coefficients (for terrain lighting), derived from lightenv - CSHCoeffs m_SHCoeffsTerrain; // ogl_tex handle of composite alpha map (all the alpha maps packed into one texture) Handle m_hCompositeAlphaMap; // coordinates of each (untransformed) alpha map within the packed texture struct { float u0,u1,v0,v1; } m_AlphaMapCoords[NumAlphaMaps]; // card capabilities Caps m_Caps; // build card cap bits void EnumCaps(); // per-frame renderer stats Stats m_Stats; // active textures on each unit GLuint m_ActiveTextures[MaxTextureUnits]; // Additional state that is only available when the vertex shader // render path is used (according to m_Options.m_RenderPath) RenderPathVertexShader* m_VertexShader; /// If false, use a multipass fallback for player colors. bool m_FastPlayerColor; /** * m_WaterManager: the WaterManager object used for water textures and settings * (e.g. water color, water height) */ WaterManager* m_WaterManager; /** * m_SortAllTransparent: If true, all transparent models are * rendered using the TransparencyRenderer which performs sorting. * * Otherwise, transparent models are rendered using the faster * batching renderer when possible. */ bool m_SortAllTransparent; /** * m_FastNormals: Use faster normal transformation in the * software transform by multiplying with the bone matrix itself * instead of the transpose of the inverse. */ bool m_FastNormals; /** * m_DisplayFrustum: Render the cull frustum and other data that may be interesting * to evaluate culling and shadow map calculations * * Can be controlled from JS via renderer.displayFrustum */ bool m_DisplayFrustum; // Various model renderers struct Models { ModelRenderer* NormalFF; ModelRenderer* PlayerFF; ModelRenderer* NormalHWLit; ModelRenderer* PlayerHWLit; ModelRenderer* NormalInstancing; ModelRenderer* PlayerInstancing; ModelRenderer* TranspFF; ModelRenderer* TranspHWLit; ModelRenderer* TranspSortAll; ModelVertexRendererPtr VertexFF; ModelVertexRendererPtr VertexHWLit; ModelVertexRendererPtr VertexInstancing; ModelVertexRendererPtr VertexPolygonSort; RenderModifierPtr ModWireframe; RenderModifierPtr ModPlain; RenderModifierPtr ModPlayer; RenderModifierPtr ModSolidColor; RenderModifierPtr ModTransparent; RenderModifierPtr ModTransparentShadow; RenderModifierPtr ModTransparentDepthShadow; } m_Models; + +public: + /** + * m_ShadowZBias: Z bias used when rendering shadows into a depth texture. + * This can be used to control shadowing artifacts. + * + * Can be accessed via JS as renderer.shadowZBias + * ShadowMap uses this for matrix calculation. + */ + float m_ShadowZBias; }; #endif Index: ps/trunk/source/renderer/PatchRData.h =================================================================== --- ps/trunk/source/renderer/PatchRData.h (revision 3507) +++ ps/trunk/source/renderer/PatchRData.h (revision 3508) @@ -1,114 +1,111 @@ #ifndef _PATCHRDATA_H #define _PATCHRDATA_H #include #include "Color.h" #include "Vector3D.h" #include "RenderableObject.h" #include "VertexBufferManager.h" class CPatch; ////////////////////////////////////////////////////////////////////////////////////////////////// // CPatchRData: class encapsulating logic for rendering terrain patches; holds per // patch data, plus some supporting static functions for batching, etc class CPatchRData : public CRenderData { public: CPatchRData(CPatch* patch); ~CPatchRData(); void Update(); - void RenderBase(); + void RenderBase(bool losColor); void RenderBlends(); void RenderOutline(); - void RenderStreams(u32 streamflags); + void RenderStreams(u32 streamflags, bool losColor); private: struct SSplat { - SSplat() : m_Texture(0), m_IndexCount(0) {} + SSplat() : m_Texture(0), m_IndexCount(0) {} // handle of texture to apply during splat Handle m_Texture; // offset into the index array for this patch where splat starts u32 m_IndexStart; // number of indices used by splat u32 m_IndexCount; }; struct SBaseVertex { // vertex position CVector3D m_Position; - // vertex color - SColor4ub m_Color; + // diffuse color from sunlight + SColor4ub m_DiffuseColor; // vertex uvs for base texture float m_UVs[2]; - }; + // color modulation from LOS + SColor4ub m_LOSColor; + }; struct SBlendVertex { // vertex position CVector3D m_Position; - // vertex color - SColor4ub m_Color; + // color modulation from LOS + SColor4ub m_LOSColor; // vertex uvs for base texture float m_UVs[2]; // vertex uvs for alpha texture float m_AlphaUVs[2]; - }; + }; struct STex { bool operator==(const STex& rhs) const { return m_Handle==rhs.m_Handle; } bool operator<(const STex& rhs) const { return m_Priority m_Indices; // indices into base vertices for the shadow map pass std::vector m_ShadowMapIndices; // list of base splats to apply to this patch std::vector m_Splats; // vertices to use for blending transition texture passes std::vector m_BlendVertices; // remembers the index in the m_Vertices array of each blend vertex, so that we can // properly update its color for fog of war and shroud of darkness std::vector m_BlendVertexIndices; // indices into blend vertices for the blend splats std::vector m_BlendIndices; // splats used in blend pass std::vector m_BlendSplats; }; #endif Index: ps/trunk/source/renderer/RenderPathVertexShader.cpp =================================================================== --- ps/trunk/source/renderer/RenderPathVertexShader.cpp (revision 3507) +++ ps/trunk/source/renderer/RenderPathVertexShader.cpp (revision 3508) @@ -1,93 +1,103 @@ #include "precompiled.h" #include "ogl.h" #include "lib/res/graphics/ogl_shader.h" #include "ps/CLogger.h" #include "renderer/Renderer.h" #include "renderer/RenderPathVertexShader.h" #define LOG_CATEGORY "graphics" RenderPathVertexShader::RenderPathVertexShader() { m_ModelLight = 0; - m_ModelLight_SHCoefficients = -1; + m_ModelLight_Ambient = -1; + m_ModelLight_SunDir = -1; + m_ModelLight_SunColor = -1; m_InstancingLight = 0; - m_InstancingLight_SHCoefficients = -1; + m_InstancingLight_Ambient = -1; + m_InstancingLight_SunDir = -1; + m_InstancingLight_SunColor = -1; m_InstancingLight_Instancing1 = -1; m_InstancingLight_Instancing2 = -1; m_InstancingLight_Instancing3 = -1; m_Instancing = 0; m_Instancing_Instancing1 = -1; m_Instancing_Instancing2 = -1; m_Instancing_Instancing3 = -1; } RenderPathVertexShader::~RenderPathVertexShader() { if (m_ModelLight) ogl_program_free(m_ModelLight); if (m_InstancingLight) ogl_program_free(m_InstancingLight); if (m_Instancing) ogl_program_free(m_Instancing); } // Initialize this render path. // Use delayed initialization so that we can fallback to a different render path // if anything went wrong and use the destructor to clean things up. bool RenderPathVertexShader::Init() { if (!g_Renderer.m_Caps.m_VertexShader) return false; m_ModelLight = ogl_program_load("shaders/model_light.xml"); if (m_ModelLight < 0) { LOG(WARNING, LOG_CATEGORY, "Failed to load shaders/model_light.xml: %i\n", (int)m_ModelLight); return false; } - + m_InstancingLight = ogl_program_load("shaders/instancing_light.xml"); if (m_InstancingLight < 0) { LOG(WARNING, LOG_CATEGORY, "Failed to load shaders/instancing_light.xml: %i\n", (int)m_InstancingLight); return false; } - + m_Instancing = ogl_program_load("shaders/instancing.xml"); if (m_Instancing < 0) { LOG(WARNING, LOG_CATEGORY, "Failed to load shaders/instancing.xml: %i\n", (int)m_Instancing); return false; } - + return true; } // This is quite the hack, but due to shader reloads, // the uniform locations might have changed under us. void RenderPathVertexShader::BeginFrame() { - m_ModelLight_SHCoefficients = ogl_program_get_uniform_location(m_ModelLight, "SHCoefficients"); - - m_InstancingLight_SHCoefficients = ogl_program_get_uniform_location( - m_InstancingLight, "SHCoefficients"); + m_ModelLight_Ambient = ogl_program_get_uniform_location(m_ModelLight, "ambient"); + m_ModelLight_SunDir = ogl_program_get_uniform_location(m_ModelLight, "sunDir"); + m_ModelLight_SunColor = ogl_program_get_uniform_location(m_ModelLight, "sunColor"); + + m_InstancingLight_Ambient = ogl_program_get_uniform_location( + m_InstancingLight, "ambient"); + m_InstancingLight_SunDir = ogl_program_get_uniform_location( + m_InstancingLight, "sunDir"); + m_InstancingLight_SunColor = ogl_program_get_uniform_location( + m_InstancingLight, "sunColor"); m_InstancingLight_Instancing1 = ogl_program_get_attrib_location( m_InstancingLight, "Instancing1"); m_InstancingLight_Instancing2 = ogl_program_get_attrib_location( m_InstancingLight, "Instancing2"); m_InstancingLight_Instancing3 = ogl_program_get_attrib_location( m_InstancingLight, "Instancing3"); m_Instancing_Instancing1 = ogl_program_get_attrib_location( m_Instancing, "Instancing1"); m_Instancing_Instancing2 = ogl_program_get_attrib_location( m_Instancing, "Instancing2"); m_Instancing_Instancing3 = ogl_program_get_attrib_location( m_Instancing, "Instancing3"); } Index: ps/trunk/source/renderer/RenderModifiers.h =================================================================== --- ps/trunk/source/renderer/RenderModifiers.h (revision 3507) +++ ps/trunk/source/renderer/RenderModifiers.h (revision 3508) @@ -1,151 +1,150 @@ /** * ========================================================================= * File : RenderModifiers.h * Project : Pyrogenesis * Description : RenderModifiers can affect the fragment stage behaviour * : of some ModelRenderers. This file defines some common * : RenderModifiers in addition to the base class. * * @author Nicolai Hähnle * ========================================================================= */ #ifndef RENDERMODIFIERS_H #define RENDERMODIFIERS_H #include "ModelRenderer.h" class CModel; class CTexture; /** * Class RenderModifier: Some ModelRenderer implementations provide vertex * management behaviour but allow fragment stages to be modified by a plugged in * RenderModifier. * * You should use RenderModifierPtr when referencing RenderModifiers. */ class RenderModifier { public: RenderModifier() { } virtual ~RenderModifier() { } /** * BeginPass: Setup OpenGL for the given rendering pass. * * Must be implemented by derived classes. * * @param pass The current pass number (pass == 0 is the first pass) * * @return The streamflags that indicate which vertex components * are required by the fragment stages (see STREAM_XYZ constants). */ virtual u32 BeginPass(uint pass) = 0; - + /** * EndPass: Cleanup OpenGL state after the given pass. This function * may indicate that additional passes are needed. * * Must be implemented by derived classes. * * @param pass The current pass number (pass == 0 is the first pass) * * @return true if rendering is complete, false if an additional pass * is needed. If false is returned, BeginPass is then called with an * increased pass number. */ virtual bool EndPass(uint pass) = 0; - + /** * PrepareTexture: Called before rendering models that use the given * texture. * * Must be implemented by derived classes. * * @param pass The current pass number (pass == 0 is the first pass) * @param texture The texture used by subsequent models */ virtual void PrepareTexture(uint pass, CTexture* texture) = 0; - + /** * PrepareModel: Called before rendering the given model. - * + * * Default behaviour does nothing. * * @param pass The current pass number (pass == 0 is the first pass) * @param model The model that is about to be rendered. */ virtual void PrepareModel(uint pass, CModel* model); }; /** * Class RenderModifierRenderer: Interface to a model renderer that can render * its models via a RenderModifier that sets up fragment stages. */ class RenderModifierRenderer : public ModelRenderer { public: RenderModifierRenderer() { } virtual ~RenderModifierRenderer(); - }; /** * Class PlainRenderModifier: RenderModifier that simply uses opaque textures * modulated by primary color. It is used for normal, no-frills models. */ class PlainRenderModifier : public RenderModifier { public: PlainRenderModifier(); ~PlainRenderModifier(); // Implementation u32 BeginPass(uint pass); bool EndPass(uint pass); void PrepareTexture(uint pass, CTexture* texture); void PrepareModel(uint pass, CModel* model); }; /** * Class WireframeRenderModifier: RenderModifier that renders wireframe models. */ class WireframeRenderModifier : public RenderModifier { public: WireframeRenderModifier(); ~WireframeRenderModifier(); // Implementation u32 BeginPass(uint pass); bool EndPass(uint pass); void PrepareTexture(uint pass, CTexture* texture); void PrepareModel(uint pass, CModel* model); }; /** * Class SolidColorRenderModifier: Render all models using the same * solid color without lighting. * * You have to specify the color using a glColor*() calls before rendering. */ class SolidColorRenderModifier : public RenderModifier { public: SolidColorRenderModifier(); ~SolidColorRenderModifier(); // Implementation u32 BeginPass(uint pass); bool EndPass(uint pass); void PrepareTexture(uint pass, CTexture* texture); void PrepareModel(uint pass, CModel* model); }; #endif // RENDERMODIFIERS_H Index: ps/trunk/source/renderer/ModelRenderer.cpp =================================================================== --- ps/trunk/source/renderer/ModelRenderer.cpp (revision 3507) +++ ps/trunk/source/renderer/ModelRenderer.cpp (revision 3508) @@ -1,492 +1,494 @@ /** * ========================================================================= * File : ModelRenderer.cpp * Project : Pyrogenesis * Description : Implementation of ModelRenderer and BatchModelRenderer * * @author Nicolai Hähnle * ========================================================================= */ #include "precompiled.h" #include "ogl.h" #include "Vector3D.h" #include "Vector4D.h" #include "ps/CLogger.h" #include "ps/Profile.h" #include "graphics/Color.h" +#include "graphics/LightEnv.h" #include "graphics/Model.h" #include "graphics/ModelDef.h" #include "renderer/ModelRenderer.h" #include "renderer/ModelVertexRenderer.h" #include "renderer/Renderer.h" #include "renderer/RenderModifiers.h" -#include "renderer/SHCoeffs.h" #define LOG_CATEGORY "graphics" ///////////////////////////////////////////////////////////////////////////////////////////////////////////// // SkinPoint: skin the vertex position using it's blend data and given bone matrices static void SkinPoint(const SModelVertex& vertex,const CMatrix3D* matrices,CVector3D& result) { CVector3D tmp; const SVertexBlend& blend=vertex.m_Blend; // must have at least one valid bone if we're using SkinPoint debug_assert(blend.m_Bone[0]!=0xff); const CMatrix3D& m=matrices[blend.m_Bone[0]]; m.Transform(vertex.m_Coords,result); result*=blend.m_Weight[0]; for (u32 i=1; i Position, VertexArrayIterator Normal) { size_t numVertices = mdef->GetNumVertices(); SModelVertex* vertices = mdef->GetVertices(); - + for(size_t j = 0; j < numVertices; ++j) { Position[j] = vertices[j].m_Coords; Normal[j] = vertices[j].m_Norm; } } // Helper function to transform position and normal vectors into world-space. void ModelRenderer::BuildPositionAndNormals( CModel* model, VertexArrayIterator Position, VertexArrayIterator Normal) { CModelDefPtr mdef = model->GetModelDef(); size_t numVertices = mdef->GetNumVertices(); SModelVertex* vertices=mdef->GetVertices(); - + const CMatrix3D* bonematrices = model->GetBoneMatrices(); if (bonematrices) { // boned model - calculate skinned vertex positions/normals PROFILE( "skinning bones" ); const CMatrix3D* invtranspbonematrices; - + // Analytic geometry tells us that normal vectors need to be // multiplied by the inverse of the transpose. However, calculating // the inverse is slow, and analytic geometry also tells us that // for orthogonal matrices, the inverse is equal to the transpose, // so the inverse of the transpose is, in fact, the original matrix. // // The "fast normals" code assumes that bone transformation contain // no "weird" transformations like shears or non-uniform scaling // (actually, the entire code assumes no scaling) and thus gets // around the slow calculation of the inverse. if (g_Renderer.m_FastNormals) invtranspbonematrices = bonematrices; else invtranspbonematrices = model->GetInvTranspBoneMatrices(); - + for (size_t j=0; jGetTransform(); const CMatrix3D& invtransform = model->GetInvTransform(); for (uint j=0; j Normal, VertexArrayIterator Color) { PROFILE( "lighting vertices" ); - + CModelDefPtr mdef = model->GetModelDef(); size_t numVertices = mdef->GetNumVertices(); - CSHCoeffs& shcoeffs = g_Renderer.m_SHCoeffsUnits; - CColor sc = model->GetShadingColor(); - RGBColor shadingcolor(sc.r, sc.g, sc.b); + const CLightEnv& lightEnv = g_Renderer.GetLightEnv(); + CColor shadingColor = model->GetShadingColor(); RGBColor tempcolor; - + for (uint j=0; j UV) { size_t numVertices = mdef->GetNumVertices(); SModelVertex* vertices = mdef->GetVertices(); - + for (uint j=0; j < numVertices; ++j, ++UV) { (*UV)[0] = vertices[j].m_U; (*UV)[1] = 1.0-vertices[j].m_V; } } // Build default indices array. void ModelRenderer::BuildIndices( CModelDefPtr mdef, u16* Indices) { u32 idxidx = 0; SModelFace* faces = mdef->GetFaces(); - + for (size_t j = 0; j < mdef->GetNumFaces(); ++j) { SModelFace& face=faces[j]; Indices[idxidx++]=face.m_Verts[0]; Indices[idxidx++]=face.m_Verts[1]; Indices[idxidx++]=face.m_Verts[2]; } } /////////////////////////////////////////////////////////////////////////////////////////////// // BatchModelRenderer implementation /// See BatchModelRendererInternals::phase enum BMRPhase { /// Currently allow calls to Submit and PrepareModels BMRSubmit, - + /// Allow calls to rendering and EndFrame BMRRender }; /** * Struct BMRModelData: Per-CModel render data used by the BatchModelRenderer. */ struct BMRModelData : public CModelRData { BMRModelData(BatchModelRendererInternals* bmri, CModel* model) : CModelRData(bmri, model), m_BMRI(bmri), m_Data(0), m_Next(0) { } virtual ~BMRModelData(); - + /// Back-link to "our" modelrenderer BatchModelRendererInternals* m_BMRI; - + /// Private data created by derived class' CreateModelData void* m_Data; - + /// Next model in the per-ModelDefTracker-slot linked list. BMRModelData* m_Next; }; /** * Class BMRModelDefTracker: Per-CModelDef data used by the BatchModelRenderer. - * + * * Note that classes that derive from BatchModelRenderer should use * their own per-CModelDef data if necessary. */ struct BMRModelDefTracker : public CModelDefRPrivate { BMRModelDefTracker(CModelDefPtr mdef) : m_ModelDef(mdef), m_Next(0), m_Slots(0) { } /// Back-link to the CModelDef object boost::weak_ptr m_ModelDef; /// Pointer to the next ModelDefTracker that has submitted models. BMRModelDefTracker* m_Next; /// Number of slots used in m_ModelSlots uint m_Slots; - + /// Each slot contains a linked list of model data objects, up to m_Slots-1 // At the end of the frame, m_Slots is reset to 0, but m_ModelSlots stays // the same size (we assume the same number of slots is going to be used // next frame) std::vector m_ModelSlots; }; /** * Struct BatchModelRendererInternals: Internal data of the BatchModelRenderer - * - * Separated into the source file to increase implementation hiding (and to + * + * Separated into the source file to increase implementation hiding (and to * avoid some causes of recompiles). */ struct BatchModelRendererInternals { BatchModelRendererInternals(BatchModelRenderer* r) : m_Renderer(r) { } - + /// Back-link to "our" renderer BatchModelRenderer* m_Renderer; - + /// ModelVertexRenderer used for vertex transformations ModelVertexRendererPtr vertexRenderer; - + /// Track the current "phase" of the frame (only for debugging purposes) BMRPhase phase; - + /// Linked list of ModelDefTrackers that have submitted models BMRModelDefTracker* submissions; - + /// Helper functions void ThunkDestroyModelData(CModel* model, void* data) { vertexRenderer->DestroyModelData(model, data); } - + void RenderAllModels(RenderModifierPtr modifier, u32 filterflags, uint pass, uint streamflags); }; BMRModelData::~BMRModelData() { m_BMRI->ThunkDestroyModelData(GetModel(), m_Data); } // Construction/Destruction BatchModelRenderer::BatchModelRenderer(ModelVertexRendererPtr vertexrenderer) { m = new BatchModelRendererInternals(this); m->vertexRenderer = vertexrenderer; m->phase = BMRSubmit; m->submissions = 0; } BatchModelRenderer::~BatchModelRenderer() { delete m; } // Submit one model. void BatchModelRenderer::Submit(CModel* model) { debug_assert(m->phase == BMRSubmit); - + CModelDefPtr mdef = model->GetModelDef(); BMRModelDefTracker* mdeftracker = (BMRModelDefTracker*)mdef->GetRenderData(m); CModelRData* rdata = (CModelRData*)model->GetRenderData(); BMRModelData* bmrdata = 0; - + // Ensure model def data and model data exist if (!mdeftracker) { mdeftracker = new BMRModelDefTracker(mdef); mdef->SetRenderData(m, mdeftracker); } - + if (rdata && rdata->GetKey() == m) { bmrdata = (BMRModelData*)rdata; } else { bmrdata = new BMRModelData(m, model); bmrdata->m_Data = m->vertexRenderer->CreateModelData(model); rdata = bmrdata; model->SetRenderData(bmrdata); model->SetDirty(~0u); g_Renderer.LoadTexture(model->GetTexture(), GL_CLAMP_TO_EDGE); } // Add the model def tracker to the submission list if necessary if (!mdeftracker->m_Slots) { mdeftracker->m_Next = m->submissions; m->submissions = mdeftracker; } // Add the bmrdata to the modeldef list Handle htex = model->GetTexture()->GetHandle(); uint idx; - + for(idx = 0; idx < mdeftracker->m_Slots; ++idx) { BMRModelData* in = mdeftracker->m_ModelSlots[idx]; - + if (in->GetModel()->GetTexture()->GetHandle() == htex) break; } - + if (idx >= mdeftracker->m_Slots) { ++mdeftracker->m_Slots; if (mdeftracker->m_Slots > mdeftracker->m_ModelSlots.size()) { mdeftracker->m_ModelSlots.push_back(0); debug_assert(mdeftracker->m_ModelSlots.size() == mdeftracker->m_Slots); } mdeftracker->m_ModelSlots[idx] = 0; } bmrdata->m_Next = mdeftracker->m_ModelSlots[idx]; mdeftracker->m_ModelSlots[idx] = bmrdata; } // Call update for all submitted models and enter the rendering phase void BatchModelRenderer::PrepareModels() { debug_assert(m->phase == BMRSubmit); - + for(BMRModelDefTracker* mdeftracker = m->submissions; mdeftracker; mdeftracker = mdeftracker->m_Next) { for(uint idx = 0; idx < mdeftracker->m_Slots; ++idx) { for(BMRModelData* bmrdata = mdeftracker->m_ModelSlots[idx]; bmrdata; bmrdata = bmrdata->m_Next) { CModel* model = bmrdata->GetModel(); - + debug_assert(model->GetRenderData() == bmrdata); - + m->vertexRenderer->UpdateModelData( model, bmrdata->m_Data, bmrdata->m_UpdateFlags); bmrdata->m_UpdateFlags = 0; } } } - + m->phase = BMRRender; } // Clear the submissions list void BatchModelRenderer::EndFrame() { static uint mostslots = 1; - + for(BMRModelDefTracker* mdeftracker = m->submissions; mdeftracker; mdeftracker = mdeftracker->m_Next) { if (mdeftracker->m_Slots > mostslots) { mostslots = mdeftracker->m_Slots; debug_printf("BatchModelRenderer: SubmissionSlots maximum: %u\n", mostslots); } mdeftracker->m_Slots = 0; } m->submissions = 0; - + m->phase = BMRSubmit; } // Return whether we models have been submitted this frame bool BatchModelRenderer::HaveSubmissions() { return m->submissions != 0; } // Render models, outer loop for multi-passing void BatchModelRenderer::Render(RenderModifierPtr modifier, u32 flags) { debug_assert(m->phase == BMRRender); - + if (!HaveSubmissions()) return; - + uint pass = 0; - + do { uint streamflags = modifier->BeginPass(pass); - + m->vertexRenderer->BeginPass(streamflags); - + m->RenderAllModels(modifier, flags, pass, streamflags); m->vertexRenderer->EndPass(streamflags); } while(!modifier->EndPass(pass++)); } // Render one frame worth of models void BatchModelRendererInternals::RenderAllModels( RenderModifierPtr modifier, u32 filterflags, uint pass, uint streamflags) { for(BMRModelDefTracker* mdeftracker = submissions; mdeftracker; mdeftracker = mdeftracker->m_Next) { vertexRenderer->PrepareModelDef(streamflags, mdeftracker->m_ModelDef.lock()); - + for(uint idx = 0; idx < mdeftracker->m_Slots; ++idx) { BMRModelData* bmrdata = mdeftracker->m_ModelSlots[idx]; modifier->PrepareTexture(pass, bmrdata->GetModel()->GetTexture()); for(; bmrdata; bmrdata = bmrdata->m_Next) { CModel* model = bmrdata->GetModel(); - + debug_assert(bmrdata->GetKey() == this); - + if (filterflags && !(model->GetFlags()&filterflags)) continue; - + modifier->PrepareModel(pass, model); vertexRenderer->RenderModel(streamflags, model, bmrdata->m_Data); } } } } Index: ps/trunk/source/renderer/TerrainRenderer.h =================================================================== --- ps/trunk/source/renderer/TerrainRenderer.h (revision 3507) +++ ps/trunk/source/renderer/TerrainRenderer.h (revision 3508) @@ -1,108 +1,108 @@ /** * ========================================================================= * File : TerrainRenderer.h * Project : Pyrogenesis * Description : Terrain rendering (everything related to patches and * : water) is encapsulated in TerrainRenderer * * @author Nicolai Hähnle * ========================================================================= */ #ifndef TERRAINRENDERER_H #define TERRAINRENDERER_H class CPatch; class ShadowMap; class WaterManager; struct TerrainRendererInternals; /** * Class TerrainRenderer: Render everything related to the terrain, * especially patches and water. */ class TerrainRenderer { public: TerrainRenderer(); ~TerrainRenderer(); /** * Submit: Add a patch for rendering in this frame. * * preconditions : PrepareForRendering must not have been called * for this frame yet. * The patch must not have been submitted in this frame yet (i.e. you * can only submit a frame once). * * @param patch the patch */ void Submit(CPatch* patch); /** * PrepareForRendering: Prepare internal data structures like vertex * buffers for rendering. * * All patches must have been submitted before the call to * PrepareForRendering. * PrepareForRendering must be called before any rendering calls. */ void PrepareForRendering(); /** * EndFrame: Remove all patches from the list of submitted patches. */ void EndFrame(); /** * HaveSubmissions: Query whether any patches have been submitted * for this frame. * * @return @c true if a patch has been submitted for this frame, * @c false otherwise. */ bool HaveSubmissions(); /** * RenderTerrain: Render textured terrain (including blends between * different terrain types). * * preconditions : PrepareForRendering must have been called this * frame before calling RenderTerrain. * * @param shadow a prepared shadow map, in case rendering with shadows is enabled * @param shadowColor color of shadows */ - void RenderTerrain(ShadowMap* shadow, const RGBAColor& shadowColor); + void RenderTerrain(ShadowMap* shadow); /** * RenderPatches: Render all patches un-textured as polygons. * * preconditions : PrepareForRendering must have been called this * frame before calling RenderPatches. */ void RenderPatches(); /** * RenderOutlines: Render the outline of patches as lines. * * preconditions : PrepareForRendering must have been called this * frame before calling RenderOutlines. */ void RenderOutlines(); /** * RenderWater: Render water for all patches that have been submitted * this frame. * * preconditions : PrepareForRendering must have been called this * frame before calling RenderWater. */ void RenderWater(); private: TerrainRendererInternals* m; }; #endif // TERRAINRENDERER_H Index: ps/trunk/source/renderer/InstancingModelRenderer.cpp =================================================================== --- ps/trunk/source/renderer/InstancingModelRenderer.cpp (revision 3507) +++ ps/trunk/source/renderer/InstancingModelRenderer.cpp (revision 3508) @@ -1,240 +1,244 @@ /** * ========================================================================= * File : InstancingModelRenderer.cpp * Project : Pyrogenesis * Description : Implementation of InstancingModelRenderer * * @author Nicolai Hähnle * ========================================================================= */ #include "precompiled.h" #include "ogl.h" #include "lib/res/graphics/ogl_shader.h" #include "Vector3D.h" #include "Vector4D.h" #include "ps/CLogger.h" #include "graphics/Color.h" +#include "graphics/LightEnv.h" #include "graphics/Model.h" #include "graphics/ModelDef.h" #include "renderer/InstancingModelRenderer.h" #include "renderer/Renderer.h" #include "renderer/RenderModifiers.h" #include "renderer/RenderPathVertexShader.h" -#include "renderer/SHCoeffs.h" #include "renderer/VertexArray.h" #define LOG_CATEGORY "graphics" /////////////////////////////////////////////////////////////////////////////////////////////// // InstancingModelRenderer implementation struct IModelDef : public CModelDefRPrivate { /// Static per-CModel vertex array VertexArray m_Array; - + /// Position, normals and UV are all static VertexArray::Attribute m_Position; VertexArray::Attribute m_Normal; VertexArray::Attribute m_UV; - + /// Indices are the same for all models, so share them u16* m_Indices; - + IModelDef(CModelDefPtr mdef); ~IModelDef() { delete[] m_Indices; } }; IModelDef::IModelDef(CModelDefPtr mdef) : m_Array(false) { size_t numVertices = mdef->GetNumVertices(); - + m_Position.type = GL_FLOAT; m_Position.elems = 3; m_Array.AddAttribute(&m_Position); - + m_Normal.type = GL_FLOAT; m_Normal.elems = 3; m_Array.AddAttribute(&m_Normal); - + m_UV.type = GL_FLOAT; m_UV.elems = 2; m_Array.AddAttribute(&m_UV); - + m_Array.SetNumVertices(numVertices); m_Array.Layout(); VertexArrayIterator Position = m_Position.GetIterator(); VertexArrayIterator Normal = m_Normal.GetIterator(); VertexArrayIterator UVit = m_UV.GetIterator(); - + ModelRenderer::CopyPositionAndNormals(mdef, Position, Normal); ModelRenderer::BuildUV(mdef, UVit); - + m_Array.Upload(); m_Array.FreeBackingStore(); - + m_Indices = new u16[mdef->GetNumFaces()*3]; ModelRenderer::BuildIndices(mdef, m_Indices); } struct InstancingModelRendererInternals { /// Currently used RenderModifier RenderModifierPtr modifier; - + /// Previously prepared modeldef IModelDef* imodeldef; }; // Construction and Destruction InstancingModelRenderer::InstancingModelRenderer() { m = new InstancingModelRendererInternals; m->imodeldef = 0; } InstancingModelRenderer::~InstancingModelRenderer() { delete m; } // Check hardware support bool InstancingModelRenderer::IsAvailable() { return g_Renderer.m_VertexShader != 0; } // Build modeldef data if necessary - we have no per-CModel data void* InstancingModelRenderer::CreateModelData(CModel* model) { CModelDefPtr mdef = model->GetModelDef(); IModelDef* imodeldef = (IModelDef*)mdef->GetRenderData(m); debug_assert(!model->GetBoneMatrices()); - + if (!imodeldef) { imodeldef = new IModelDef(mdef); mdef->SetRenderData(m, imodeldef); } - + return NULL; } void InstancingModelRenderer::UpdateModelData(CModel* UNUSED(model), void* UNUSED(data), u32 UNUSED(updateflags)) { // We have no per-CModel data } void InstancingModelRenderer::DestroyModelData(CModel* UNUSED(model), void* UNUSED(data)) { // We have no per-CModel data, and per-CModelDef data is deleted by the CModelDef } // Setup one rendering pass. void InstancingModelRenderer::BeginPass(uint streamflags) { glEnableClientState(GL_VERTEX_ARRAY); - + if (streamflags & STREAM_UV0) glEnableClientState(GL_TEXTURE_COORD_ARRAY); if (streamflags & STREAM_COLOR) { - const RGBColor* coeffs = g_Renderer.m_SHCoeffsUnits.GetCoefficients(); + const CLightEnv& lightEnv = g_Renderer.GetLightEnv(); int idx; - + ogl_program_use(g_Renderer.m_VertexShader->m_InstancingLight); - idx = g_Renderer.m_VertexShader->m_InstancingLight_SHCoefficients; - pglUniform3fvARB(idx, 9, (float*)coeffs); + idx = g_Renderer.m_VertexShader->m_InstancingLight_Ambient; + pglUniform3fvARB(idx, 1, &lightEnv.m_UnitsAmbientColor.X); + idx = g_Renderer.m_VertexShader->m_InstancingLight_SunDir; + pglUniform3fvARB(idx, 1, &lightEnv.m_SunDir.X); + idx = g_Renderer.m_VertexShader->m_InstancingLight_SunColor; + pglUniform3fvARB(idx, 1, &lightEnv.m_SunColor.X); glEnableClientState(GL_NORMAL_ARRAY); } else { ogl_program_use(g_Renderer.m_VertexShader->m_Instancing); } } // Cleanup rendering pass. void InstancingModelRenderer::EndPass(uint streamflags) { if (streamflags & STREAM_UV0) glDisableClientState(GL_TEXTURE_COORD_ARRAY); if (streamflags & STREAM_COLOR) glDisableClientState(GL_NORMAL_ARRAY); pglUseProgramObjectARB(0); glDisableClientState(GL_VERTEX_ARRAY); } // Prepare UV coordinates for this modeldef void InstancingModelRenderer::PrepareModelDef(uint streamflags, CModelDefPtr def) { m->imodeldef = (IModelDef*)def->GetRenderData(m); - + debug_assert(m->imodeldef); u8* base = m->imodeldef->m_Array.Bind(); GLsizei stride = (GLsizei)m->imodeldef->m_Array.GetStride(); - + glVertexPointer(3, GL_FLOAT, stride, base + m->imodeldef->m_Position.offset); if (streamflags & STREAM_COLOR) { glNormalPointer(GL_FLOAT, stride, base + m->imodeldef->m_Normal.offset); } if (streamflags & STREAM_UV0) { glTexCoordPointer(2, GL_FLOAT, stride, base + m->imodeldef->m_UV.offset); } } // Render one model void InstancingModelRenderer::RenderModel(uint streamflags, CModel* model, void* UNUSED(data)) { CModelDefPtr mdldef = model->GetModelDef(); const CMatrix3D& mat = model->GetTransform(); RenderPathVertexShader* rpvs = g_Renderer.m_VertexShader; - + if (streamflags & STREAM_COLOR) { CColor sc = model->GetShadingColor(); glColor3f(sc.r, sc.g, sc.b); - + pglVertexAttrib4fARB(rpvs->m_InstancingLight_Instancing1, mat._11, mat._12, mat._13, mat._14); pglVertexAttrib4fARB(rpvs->m_InstancingLight_Instancing2, mat._21, mat._22, mat._23, mat._24); pglVertexAttrib4fARB(rpvs->m_InstancingLight_Instancing3, mat._31, mat._32, mat._33, mat._34); } else { pglVertexAttrib4fARB(rpvs->m_Instancing_Instancing1, mat._11, mat._12, mat._13, mat._14); pglVertexAttrib4fARB(rpvs->m_Instancing_Instancing2, mat._21, mat._22, mat._23, mat._24); pglVertexAttrib4fARB(rpvs->m_Instancing_Instancing3, mat._31, mat._32, mat._33, mat._34); } // render the lot size_t numFaces = mdldef->GetNumFaces(); pglDrawRangeElementsEXT(GL_TRIANGLES, 0, (GLuint)mdldef->GetNumVertices(), (GLsizei)numFaces*3, GL_UNSIGNED_SHORT, m->imodeldef->m_Indices); // bump stats g_Renderer.m_Stats.m_DrawCalls++; g_Renderer.m_Stats.m_ModelTris += numFaces; } Index: ps/trunk/source/renderer/Renderer.cpp =================================================================== --- ps/trunk/source/renderer/Renderer.cpp (revision 3507) +++ ps/trunk/source/renderer/Renderer.cpp (revision 3508) @@ -1,1296 +1,1299 @@ /////////////////////////////////////////////////////////////////////////////// // // Name: Renderer.cpp // Author: Rich Cross // Contact: rich@wildfiregames.com // // Description: OpenGL renderer class; a higher level interface // on top of OpenGL to handle rendering the basic visual games // types - terrain, models, sprites, particles etc // /////////////////////////////////////////////////////////////////////////////// #include "precompiled.h" #include #include #include #include "Renderer.h" #include "Terrain.h" #include "Matrix3D.h" #include "MathUtil.h" #include "Camera.h" #include "Texture.h" #include "LightEnv.h" #include "Terrain.h" #include "CLogger.h" #include "ps/Game.h" #include "Profile.h" #include "Game.h" #include "World.h" #include "Player.h" #include "LOSManager.h" #include "Model.h" #include "ModelDef.h" #include "ogl.h" #include "lib/res/res.h" #include "lib/res/file/file.h" #include "lib/res/graphics/tex.h" #include "lib/res/graphics/ogl_tex.h" #include "ps/Loader.h" #include "ps/ProfileViewer.h" #include "graphics/ParticleEngine.h" #include "graphics/DefaultEmitter.h" #include "renderer/FixedFunctionModelRenderer.h" #include "renderer/HWLightingModelRenderer.h" #include "renderer/InstancingModelRenderer.h" #include "renderer/ModelRenderer.h" #include "renderer/PlayerRenderer.h" #include "renderer/RenderModifiers.h" #include "renderer/RenderPathVertexShader.h" #include "renderer/ShadowMap.h" #include "renderer/TerrainRenderer.h" #include "renderer/TransparencyRenderer.h" #include "renderer/WaterManager.h" #define LOG_CATEGORY "graphics" /////////////////////////////////////////////////////////////////////////////////// // CRendererStatsTable - Profile display of rendering stats /** * Class CRendererStatsTable: Implementation of AbstractProfileTable to * display the renderer stats in-game. * * Accesses CRenderer::m_Stats by keeping the reference passed to the * constructor. */ class CRendererStatsTable : public AbstractProfileTable { public: CRendererStatsTable(const CRenderer::Stats& st); // Implementation of AbstractProfileTable interface CStr GetName(); CStr GetTitle(); uint GetNumberRows(); const std::vector& GetColumns(); CStr GetCellText(uint row, uint col); AbstractProfileTable* GetChild(uint row); private: /// Reference to the renderer singleton's stats const CRenderer::Stats& Stats; /// Column descriptions std::vector columnDescriptions; enum { Row_Counter = 0, Row_DrawCalls, Row_TerrainTris, Row_ModelTris, Row_BlendSplats, // Must be last to count number of rows NumberRows }; // no copy ctor because some members are const CRendererStatsTable& operator=(const CRendererStatsTable&); }; // Construction CRendererStatsTable::CRendererStatsTable(const CRenderer::Stats& st) : Stats(st) { columnDescriptions.push_back(ProfileColumn("Name", 230)); columnDescriptions.push_back(ProfileColumn("Value", 100)); } // Implementation of AbstractProfileTable interface CStr CRendererStatsTable::GetName() { return "renderer"; } CStr CRendererStatsTable::GetTitle() { return "Renderer statistics"; } uint CRendererStatsTable::GetNumberRows() { return NumberRows; } const std::vector& CRendererStatsTable::GetColumns() { return columnDescriptions; } CStr CRendererStatsTable::GetCellText(uint row, uint col) { char buf[256]; switch(row) { case Row_Counter: if (col == 0) return "counter"; snprintf(buf, sizeof(buf), "%d", Stats.m_Counter); return buf; case Row_DrawCalls: if (col == 0) return "# draw calls"; snprintf(buf, sizeof(buf), "%d", Stats.m_DrawCalls); return buf; case Row_TerrainTris: if (col == 0) return "# terrain tris"; snprintf(buf, sizeof(buf), "%d", Stats.m_TerrainTris); return buf; case Row_ModelTris: if (col == 0) return "# model tris"; snprintf(buf, sizeof(buf), "%d", Stats.m_ModelTris); return buf; case Row_BlendSplats: if (col == 0) return "# blend splats"; snprintf(buf, sizeof(buf), "%d", Stats.m_BlendSplats); return buf; default: return "???"; } } AbstractProfileTable* CRendererStatsTable::GetChild(uint UNUSED(row)) { return 0; } /////////////////////////////////////////////////////////////////////////////////// // CRenderer implementation /** * Struct CRendererInternals: Truly hide data that is supposed to be hidden * in this structure so it won't even appear in header files. */ struct CRendererInternals { /// Table to display renderer stats in-game via profile system CRendererStatsTable profileTable; /// Water manager WaterManager waterManager; /// Terrain renderer TerrainRenderer* terrainRenderer; /// Shadow map ShadowMap* shadow; CRendererInternals() : profileTable(g_Renderer.m_Stats) { terrainRenderer = new TerrainRenderer(); shadow = new ShadowMap(); } ~CRendererInternals() { delete shadow; delete terrainRenderer; } }; /////////////////////////////////////////////////////////////////////////////////// // CRenderer destructor CRenderer::CRenderer() { m = new CRendererInternals; m_WaterManager = &m->waterManager; g_ProfileViewer.AddRootTable(&m->profileTable); m_Width=0; m_Height=0; m_Depth=0; m_FrameCounter=0; m_TerrainRenderMode=SOLID; m_ModelRenderMode=SOLID; m_ClearColor[0]=m_ClearColor[1]=m_ClearColor[2]=m_ClearColor[3]=0; m_SortAllTransparent = false; m_FastNormals = true; m_DisplayFrustum = false; m_VertexShader = 0; m_Options.m_NoVBO=false; m_Options.m_Shadows=true; m_Options.m_ShadowColor=RGBAColor(0.4f,0.4f,0.4f,1.0f); m_Options.m_RenderPath = RP_DEFAULT; for (uint i=0;ishadow->SetUseDepthTexture(true); m_VertexShader = new RenderPathVertexShader; if (!m_VertexShader->Init()) { delete m_VertexShader; m_VertexShader = 0; } // model rendering m_Models.VertexFF = ModelVertexRendererPtr(new FixedFunctionModelRenderer); if (HWLightingModelRenderer::IsAvailable()) m_Models.VertexHWLit = ModelVertexRendererPtr(new HWLightingModelRenderer); if (InstancingModelRenderer::IsAvailable()) m_Models.VertexInstancing = ModelVertexRendererPtr(new InstancingModelRenderer); m_Models.VertexPolygonSort = ModelVertexRendererPtr(new PolygonSortModelRenderer); m_Models.NormalFF = new BatchModelRenderer(m_Models.VertexFF); m_Models.PlayerFF = new BatchModelRenderer(m_Models.VertexFF); m_Models.TranspFF = new SortModelRenderer(m_Models.VertexFF); if (m_Models.VertexHWLit) { m_Models.NormalHWLit = new BatchModelRenderer(m_Models.VertexHWLit); m_Models.PlayerHWLit = new BatchModelRenderer(m_Models.VertexHWLit); m_Models.TranspHWLit = new SortModelRenderer(m_Models.VertexHWLit); } else { m_Models.NormalHWLit = NULL; m_Models.PlayerHWLit = NULL; m_Models.TranspHWLit = NULL; } if (m_Models.VertexInstancing) { m_Models.NormalInstancing = new BatchModelRenderer(m_Models.VertexInstancing); m_Models.PlayerInstancing = new BatchModelRenderer(m_Models.VertexInstancing); } else { m_Models.NormalInstancing = NULL; m_Models.PlayerInstancing = NULL; } m_Models.TranspSortAll = new SortModelRenderer(m_Models.VertexPolygonSort); m_Models.ModWireframe = RenderModifierPtr(new WireframeRenderModifier); m_Models.ModPlain = RenderModifierPtr(new PlainRenderModifier); SetFastPlayerColor(true); m_Models.ModSolidColor = RenderModifierPtr(new SolidColorRenderModifier); m_Models.ModTransparent = RenderModifierPtr(new TransparentRenderModifier); m_Models.ModTransparentShadow = RenderModifierPtr(new TransparentShadowRenderModifier); m_Models.ModTransparentDepthShadow = RenderModifierPtr(new TransparentDepthShadowModifier); + m_ShadowZBias = 0.001; + CEmitter *pEmitter = new CDefaultEmitter(1000, -1); CParticleEngine::GetInstance()->addEmitter(pEmitter); ONCE( ScriptingInit(); ); } /////////////////////////////////////////////////////////////////////////////////// // CRenderer destructor CRenderer::~CRenderer() { // model rendering delete m_Models.NormalFF; delete m_Models.PlayerFF; delete m_Models.TranspFF; delete m_Models.NormalHWLit; delete m_Models.PlayerHWLit; delete m_Models.TranspHWLit; delete m_Models.NormalInstancing; delete m_Models.PlayerInstancing; delete m_Models.TranspSortAll; // general delete m_VertexShader; m_VertexShader = 0; // we no longer UnloadAlphaMaps / UnloadWaterTextures here - // that is the responsibility of the module that asked for // them to be loaded (i.e. CGameView). delete m; } /////////////////////////////////////////////////////////////////////////////////// // EnumCaps: build card cap bits void CRenderer::EnumCaps() { // assume support for nothing m_Caps.m_VBO=false; m_Caps.m_TextureBorderClamp=false; m_Caps.m_GenerateMipmaps=false; m_Caps.m_VertexShader=false; m_Caps.m_DepthTextureShadows = false; // now start querying extensions if (!m_Options.m_NoVBO) { if (oglHaveExtension("GL_ARB_vertex_buffer_object")) { m_Caps.m_VBO=true; } } if (oglHaveExtension("GL_ARB_texture_border_clamp")) { m_Caps.m_TextureBorderClamp=true; } if (oglHaveExtension("GL_SGIS_generate_mipmap")) { m_Caps.m_GenerateMipmaps=true; } if (0 == oglHaveExtensions(0, "GL_ARB_shader_objects", "GL_ARB_shading_language_100", 0)) { if (oglHaveExtension("GL_ARB_vertex_shader")) m_Caps.m_VertexShader=true; } if (0 == oglHaveExtensions(0, "GL_ARB_shadow", "GL_ARB_depth_texture", 0)) { m_Caps.m_DepthTextureShadows = true; } } bool CRenderer::Open(int width, int height, int depth) { m_Width = width; m_Height = height; m_Depth = depth; // set packing parameters glPixelStorei(GL_PACK_ALIGNMENT,1); glPixelStorei(GL_UNPACK_ALIGNMENT,1); // setup default state glDepthFunc(GL_LEQUAL); glEnable(GL_DEPTH_TEST); glCullFace(GL_BACK); glFrontFace(GL_CCW); glEnable(GL_CULL_FACE); GLint bits; glGetIntegerv(GL_DEPTH_BITS,&bits); LOG(NORMAL, LOG_CATEGORY, "CRenderer::Open: depth bits %d",bits); glGetIntegerv(GL_STENCIL_BITS,&bits); LOG(NORMAL, LOG_CATEGORY, "CRenderer::Open: stencil bits %d",bits); glGetIntegerv(GL_ALPHA_BITS,&bits); LOG(NORMAL, LOG_CATEGORY, "CRenderer::Open: alpha bits %d",bits); if (m_Options.m_RenderPath == RP_DEFAULT) SetRenderPath(m_Options.m_RenderPath); return true; } // resize renderer view void CRenderer::Resize(int width,int height) { // need to recreate the shadow map object to resize the shadow texture delete m->shadow; m->shadow = new ShadowMap; m_Width = width; m_Height = height; } ////////////////////////////////////////////////////////////////////////////////////////// // SetOptionBool: set boolean renderer option void CRenderer::SetOptionBool(enum Option opt,bool value) { switch (opt) { case OPT_NOVBO: m_Options.m_NoVBO=value; break; case OPT_SHADOWS: m_Options.m_Shadows=value; break; case OPT_NOPBUFFER: // NOT IMPLEMENTED break; default: debug_warn("CRenderer::SetOptionBool: unknown option"); break; } } ////////////////////////////////////////////////////////////////////////////////////////// // GetOptionBool: get boolean renderer option bool CRenderer::GetOptionBool(enum Option opt) const { switch (opt) { case OPT_NOVBO: return m_Options.m_NoVBO; case OPT_SHADOWS: return m_Options.m_Shadows; default: debug_warn("CRenderer::GetOptionBool: unknown option"); break; } return false; } ////////////////////////////////////////////////////////////////////////////////////////// // SetOptionColor: set color renderer option void CRenderer::SetOptionColor(enum Option opt,const RGBAColor& value) { switch (opt) { case OPT_SHADOWCOLOR: m_Options.m_ShadowColor=value; break; default: debug_warn("CRenderer::SetOptionColor: unknown option"); break; } } void CRenderer::SetOptionFloat(enum Option opt, float val) { switch(opt) { case OPT_LODBIAS: m_Options.m_LodBias = val; break; default: debug_warn("CRenderer::SetOptionFloat: unknown option"); break; } } ////////////////////////////////////////////////////////////////////////////////////////// // GetOptionColor: get color renderer option const RGBAColor& CRenderer::GetOptionColor(enum Option opt) const { static const RGBAColor defaultColor(1.0f,1.0f,1.0f,1.0f); switch (opt) { case OPT_SHADOWCOLOR: return m_Options.m_ShadowColor; default: debug_warn("CRenderer::GetOptionColor: unknown option"); break; } return defaultColor; } ////////////////////////////////////////////////////////////////////////////////////////// // SetRenderPath: Select the preferred render path. // This may only be called before Open(), because the layout of vertex arrays and other // data may depend on the chosen render path. void CRenderer::SetRenderPath(RenderPath rp) { if (rp == RP_DEFAULT) { if (m_Models.NormalHWLit && m_Models.PlayerHWLit) rp = RP_VERTEXSHADER; else rp = RP_FIXED; } if (rp == RP_VERTEXSHADER) { if (!m_Models.NormalHWLit || !m_Models.PlayerHWLit) { LOG(WARNING, LOG_CATEGORY, "Falling back to fixed function\n"); rp = RP_FIXED; } } m_Options.m_RenderPath = rp; } CStr CRenderer::GetRenderPathName(RenderPath rp) { switch(rp) { case RP_DEFAULT: return "default"; case RP_FIXED: return "fixed"; case RP_VERTEXSHADER: return "vertexshader"; default: return "(invalid)"; } } CRenderer::RenderPath CRenderer::GetRenderPathByName(CStr name) { if (name == "fixed") return RP_FIXED; if (name == "vertexshader") return RP_VERTEXSHADER; if (name == "default") return RP_DEFAULT; LOG(WARNING, LOG_CATEGORY, "Unknown render path name '%hs', assuming 'default'", name.c_str()); return RP_DEFAULT; } ////////////////////////////////////////////////////////////////////////////////////////// // SetFastPlayerColor void CRenderer::SetFastPlayerColor(bool fast) { m_FastPlayerColor = fast; if (m_FastPlayerColor) { if (!FastPlayerColorRender::IsAvailable()) { LOG(WARNING, LOG_CATEGORY, "Falling back to slower player color rendering."); m_FastPlayerColor = false; } } if (m_FastPlayerColor) m_Models.ModPlayer = RenderModifierPtr(new FastPlayerColorRender); else m_Models.ModPlayer = RenderModifierPtr(new SlowPlayerColorRender); } ////////////////////////////////////////////////////////////////////////////////////////// // BeginFrame: signal frame start void CRenderer::BeginFrame() { #ifndef SCED if(!g_Game || !g_Game->IsGameStarted()) return; #endif // bump frame counter m_FrameCounter++; if (m_VertexShader) m_VertexShader->BeginFrame(); // zero out all the per-frame stats m_Stats.Reset(); - // calculate coefficients for terrain and unit lighting - m_SHCoeffsUnits.Clear(); - m_SHCoeffsTerrain.Clear(); - - if (m_LightEnv) { - m_SHCoeffsUnits.AddDirectionalLight(m_LightEnv->m_SunDir, m_LightEnv->m_SunColor); - m_SHCoeffsTerrain.AddDirectionalLight(m_LightEnv->m_SunDir, m_LightEnv->m_SunColor); - - m_SHCoeffsUnits.AddAmbientLight(m_LightEnv->m_UnitsAmbientColor); - m_SHCoeffsTerrain.AddAmbientLight(m_LightEnv->m_TerrainAmbientColor); - } - // init per frame stuff m->shadow->SetupFrame(m_CullCamera, m_LightEnv->m_SunDir); } ////////////////////////////////////////////////////////////////////////////////////////// // SetClearColor: set color used to clear screen in BeginFrame() void CRenderer::SetClearColor(u32 color) { m_ClearColor[0]=float(color & 0xff)/255.0f; m_ClearColor[1]=float((color>>8) & 0xff)/255.0f; m_ClearColor[2]=float((color>>16) & 0xff)/255.0f; m_ClearColor[3]=float((color>>24) & 0xff)/255.0f; } void CRenderer::RenderShadowMap() { PROFILE( "render shadow map" ); m->shadow->BeginRender(); glColor4fv(m_Options.m_ShadowColor); + // Figure out transparent rendering strategy + RenderModifierPtr transparentShadows = m_Models.ModTransparentShadow; + + if (m->shadow->GetUseDepthTexture()) + transparentShadows = m_Models.ModTransparentDepthShadow; + + // Render all closed models (i.e. models where rendering back faces will produce + // the correct result) + glCullFace(GL_FRONT); + + if (m->shadow->GetUseDepthTexture()) + m->terrainRenderer->RenderPatches(); + + glCullFace(GL_BACK); + + // Render models that aren't closed glDisable(GL_CULL_FACE); m_Models.NormalFF->Render(m_Models.ModSolidColor, MODELFLAG_CASTSHADOWS); m_Models.PlayerFF->Render(m_Models.ModSolidColor, MODELFLAG_CASTSHADOWS); if (m_Models.NormalHWLit) m_Models.NormalHWLit->Render(m_Models.ModSolidColor, MODELFLAG_CASTSHADOWS); if (m_Models.PlayerHWLit) m_Models.PlayerHWLit->Render(m_Models.ModSolidColor, MODELFLAG_CASTSHADOWS); if (m_Models.NormalInstancing) m_Models.NormalInstancing->Render(m_Models.ModSolidColor, MODELFLAG_CASTSHADOWS); if (m_Models.PlayerInstancing) m_Models.PlayerInstancing->Render(m_Models.ModSolidColor, MODELFLAG_CASTSHADOWS); - RenderModifierPtr transparentShadows = m_Models.ModTransparentShadow; - - if (m->shadow->GetUseDepthTexture()) - transparentShadows = m_Models.ModTransparentDepthShadow; m_Models.TranspFF->Render(transparentShadows, MODELFLAG_CASTSHADOWS); if (m_Models.TranspHWLit) m_Models.TranspHWLit->Render(transparentShadows, MODELFLAG_CASTSHADOWS); m_Models.TranspSortAll->Render(transparentShadows, MODELFLAG_CASTSHADOWS); glEnable(GL_CULL_FACE); glColor3f(1.0, 1.0, 1.0); m->shadow->EndRender(); } void CRenderer::RenderPatches() { PROFILE(" render patches "); // switch on wireframe if we need it if (m_TerrainRenderMode==WIREFRAME) { MICROLOG(L"wireframe on"); glPolygonMode(GL_FRONT_AND_BACK,GL_LINE); } // render all the patches, including blend pass MICROLOG(L"render patch submissions"); - m->terrainRenderer->RenderTerrain(m_Options.m_Shadows ? m->shadow : 0, m_Options.m_ShadowColor); + m->terrainRenderer->RenderTerrain(m_Options.m_Shadows ? m->shadow : 0); if (m_TerrainRenderMode==WIREFRAME) { // switch wireframe off again MICROLOG(L"wireframe off"); glPolygonMode(GL_FRONT_AND_BACK,GL_FILL); } else if (m_TerrainRenderMode==EDGED_FACES) { // edged faces: need to make a second pass over the data: // first switch on wireframe glPolygonMode(GL_FRONT_AND_BACK,GL_LINE); // setup some renderstate .. glDepthMask(0); SetTexture(0,0); glColor4f(1,1,1,0.35f); glLineWidth(2.0f); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); // render tiles edges m->terrainRenderer->RenderPatches(); // set color for outline glColor3f(0,0,1); glLineWidth(4.0f); // render outline of each patch m->terrainRenderer->RenderOutlines(); // .. and restore the renderstates glDisable(GL_BLEND); glDepthMask(1); // restore fill mode, and we're done glLineWidth(1.0f); glPolygonMode(GL_FRONT_AND_BACK,GL_FILL); } } void CRenderer::RenderModels() { PROFILE( "render models "); // switch on wireframe if we need it if (m_ModelRenderMode==WIREFRAME) { glPolygonMode(GL_FRONT_AND_BACK,GL_LINE); } m_Models.NormalFF->Render(m_Models.ModPlain, 0); m_Models.PlayerFF->Render(m_Models.ModPlayer, 0); if (m_Models.NormalHWLit) m_Models.NormalHWLit->Render(m_Models.ModPlain, 0); if (m_Models.PlayerHWLit) m_Models.PlayerHWLit->Render(m_Models.ModPlayer, 0); if (m_Models.NormalInstancing) m_Models.NormalInstancing->Render(m_Models.ModPlain, 0); if (m_Models.PlayerInstancing) m_Models.PlayerInstancing->Render(m_Models.ModPlayer, 0); if (m_ModelRenderMode==WIREFRAME) { // switch wireframe off again glPolygonMode(GL_FRONT_AND_BACK,GL_FILL); } else if (m_ModelRenderMode==EDGED_FACES) { m_Models.NormalFF->Render(m_Models.ModWireframe, 0); m_Models.PlayerFF->Render(m_Models.ModWireframe, 0); if (m_Models.NormalHWLit) m_Models.NormalHWLit->Render(m_Models.ModWireframe, 0); if (m_Models.PlayerHWLit) m_Models.PlayerHWLit->Render(m_Models.ModWireframe, 0); if (m_Models.NormalInstancing) m_Models.NormalInstancing->Render(m_Models.ModWireframe, 0); if (m_Models.PlayerInstancing) m_Models.PlayerInstancing->Render(m_Models.ModWireframe, 0); } } void CRenderer::RenderTransparentModels() { PROFILE( "render transparent models "); // switch on wireframe if we need it if (m_ModelRenderMode==WIREFRAME) { glPolygonMode(GL_FRONT_AND_BACK,GL_LINE); } m_Models.TranspFF->Render(m_Models.ModTransparent, 0); if (m_Models.TranspHWLit) m_Models.TranspHWLit->Render(m_Models.ModTransparent, 0); m_Models.TranspSortAll->Render(m_Models.ModTransparent, 0); if (m_ModelRenderMode==WIREFRAME) { // switch wireframe off again glPolygonMode(GL_FRONT_AND_BACK,GL_FILL); } else if (m_ModelRenderMode==EDGED_FACES) { m_Models.TranspFF->Render(m_Models.ModWireframe, 0); if (m_Models.TranspHWLit) m_Models.TranspHWLit->Render(m_Models.ModWireframe, 0); m_Models.TranspSortAll->Render(m_Models.ModWireframe, 0); } } /////////////////////////////////////////////////////////////////////////////////////////////////// // FlushFrame: force rendering of any batched objects void CRenderer::FlushFrame() { #ifndef SCED if(!g_Game || !g_Game->IsGameStarted()) return; #endif oglCheck(); // Prepare model renderers PROFILE_START("prepare models"); m_Models.NormalFF->PrepareModels(); m_Models.PlayerFF->PrepareModels(); m_Models.TranspFF->PrepareModels(); if (m_Models.NormalHWLit) m_Models.NormalHWLit->PrepareModels(); if (m_Models.PlayerHWLit) m_Models.PlayerHWLit->PrepareModels(); if (m_Models.TranspHWLit) m_Models.TranspHWLit->PrepareModels(); if (m_Models.NormalInstancing) m_Models.NormalInstancing->PrepareModels(); if (m_Models.PlayerInstancing) m_Models.PlayerInstancing->PrepareModels(); m_Models.TranspSortAll->PrepareModels(); PROFILE_END("prepare models"); PROFILE_START("prepare terrain"); m->terrainRenderer->PrepareForRendering(); PROFILE_END("prepare terrain"); if (m_Options.m_Shadows) { MICROLOG(L"render shadows"); RenderShadowMap(); } // clear buffers glClearColor(m_ClearColor[0],m_ClearColor[1],m_ClearColor[2],m_ClearColor[3]); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); oglCheck(); // render submitted patches and models MICROLOG(L"render patches"); RenderPatches(); oglCheck(); MICROLOG(L"render models"); RenderModels(); oglCheck(); // call on the transparency renderer to render all the transparent stuff MICROLOG(L"render transparent"); RenderTransparentModels(); oglCheck(); // render water (note: we're assuming there's no transparent stuff over water... // we could also do this above render transparent if we assume there's no transparent // stuff underwater) if (m_WaterManager->m_RenderWater) { MICROLOG(L"render water"); m->terrainRenderer->RenderWater(); oglCheck(); } //// Particle Engine Rendering. MICROLOG(L"render particles"); CParticleEngine::GetInstance()->renderParticles(); oglCheck(); // render debug lines if (m_DisplayFrustum) { MICROLOG(L"display frustum"); DisplayFrustum(); m->shadow->RenderDebugDisplay(); oglCheck(); } // empty lists MICROLOG(L"empty lists"); m->terrainRenderer->EndFrame(); // Finish model renderers m_Models.NormalFF->EndFrame(); m_Models.PlayerFF->EndFrame(); m_Models.TranspFF->EndFrame(); if (m_Models.NormalHWLit) m_Models.NormalHWLit->EndFrame(); if (m_Models.PlayerHWLit) m_Models.PlayerHWLit->EndFrame(); if (m_Models.TranspHWLit) m_Models.TranspHWLit->EndFrame(); if (m_Models.NormalInstancing) m_Models.NormalInstancing->EndFrame(); if (m_Models.PlayerInstancing) m_Models.PlayerInstancing->EndFrame(); m_Models.TranspSortAll->EndFrame(); } /////////////////////////////////////////////////////////////////////////////////////////////////// // EndFrame: signal frame end void CRenderer::EndFrame() { #ifndef SCED if(!g_Game || !g_Game->IsGameStarted()) return; #endif g_Renderer.SetTexture(0,0); static bool once=false; if (!once && glGetError()) { LOG(ERROR, LOG_CATEGORY, "CRenderer::EndFrame: GL errors occurred"); once=true; } } /////////////////////////////////////////////////////////////////////////////////////////////////// // DisplayFrustum: debug displays // - white: cull camera frustum // - red: bounds of shadow casting objects void CRenderer::DisplayFrustum() { glDepthMask(0); glDisable(GL_CULL_FACE); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glColor4ub(255,255,255,64); m_CullCamera.Render(2); glDisable(GL_BLEND); glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); glColor3ub(255,255,255); m_CullCamera.Render(2); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); glEnable(GL_CULL_FACE); glDepthMask(1); } /////////////////////////////////////////////////////////////////////////////////////////////////// // SetCamera: setup projection and transform of camera and adjust viewport to current view void CRenderer::SetCamera(const CCamera& viewCamera, const CCamera& cullCamera) { m_ViewCamera = viewCamera; m_CullCamera = cullCamera; CMatrix3D view; m_ViewCamera.m_Orientation.GetInverse(view); const CMatrix3D& proj = m_ViewCamera.GetProjection(); glMatrixMode(GL_PROJECTION); glLoadMatrixf(&proj._11); glMatrixMode(GL_MODELVIEW); glLoadMatrixf(&view._11); SetViewport(m_ViewCamera.GetViewPort()); } void CRenderer::SetViewport(const SViewPort &vp) { glViewport(vp.m_X,vp.m_Y,vp.m_Width,vp.m_Height); } void CRenderer::Submit(CPatch* patch) { m->terrainRenderer->Submit(patch); } void CRenderer::Submit(CModel* model) { if (model->GetFlags() & MODELFLAG_CASTSHADOWS) { PROFILE( "updating shadow bounds" ); m->shadow->AddShadowedBound(model->GetBounds()); } // Tricky: The call to GetBounds() above can invalidate the position model->ValidatePosition(); bool canUseInstancing = false; if (model->GetModelDef()->GetNumBones() == 0) canUseInstancing = true; if (model->GetMaterial().IsPlayer()) { if (m_Options.m_RenderPath == RP_VERTEXSHADER) { if (canUseInstancing && m_Models.PlayerInstancing) m_Models.PlayerInstancing->Submit(model); else m_Models.PlayerHWLit->Submit(model); } else m_Models.PlayerFF->Submit(model); } else if (model->GetMaterial().UsesAlpha()) { if (m_SortAllTransparent) m_Models.TranspSortAll->Submit(model); else if (m_Options.m_RenderPath == RP_VERTEXSHADER) m_Models.TranspHWLit->Submit(model); else m_Models.TranspFF->Submit(model); } else { if (m_Options.m_RenderPath == RP_VERTEXSHADER) { if (canUseInstancing && m_Models.NormalInstancing) m_Models.NormalInstancing->Submit(model); else m_Models.NormalHWLit->Submit(model); } else m_Models.NormalFF->Submit(model); } } void CRenderer::Submit(CSprite* UNUSED(sprite)) { } void CRenderer::Submit(CParticleSys* UNUSED(psys)) { } void CRenderer::Submit(COverlay* UNUSED(overlay)) { } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // LoadTexture: try and load the given texture; set clamp/repeat flags on texture object if necessary bool CRenderer::LoadTexture(CTexture* texture,u32 wrapflags) { const Handle errorhandle = -1; Handle h=texture->GetHandle(); // already tried to load this texture if (h) { // nothing to do here - just return success according to // whether this is a valid handle or not return h==errorhandle ? true : false; } h=ogl_tex_load(texture->GetName()); if (h <= 0) { LOG(ERROR, LOG_CATEGORY, "LoadTexture failed on \"%s\"",(const char*) texture->GetName()); texture->SetHandle(errorhandle); return false; } if(!wrapflags) wrapflags = GL_CLAMP_TO_EDGE; (void)ogl_tex_set_wrap(h, wrapflags); (void)ogl_tex_set_filter(h, GL_LINEAR_MIPMAP_LINEAR); // (this also verifies that the texture is a power-of-two) if(ogl_tex_upload(h) < 0) { LOG(ERROR, LOG_CATEGORY, "LoadTexture failed on \"%s\" : upload failed",(const char*) texture->GetName()); ogl_tex_free(h); texture->SetHandle(errorhandle); return false; } texture->SetHandle(h); return true; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // BindTexture: bind a GL texture object to current active unit void CRenderer::BindTexture(int unit,GLuint tex) { pglActiveTextureARB(GL_TEXTURE0+unit); glBindTexture(GL_TEXTURE_2D,tex); if (tex) { glEnable(GL_TEXTURE_2D); } else { glDisable(GL_TEXTURE_2D); } m_ActiveTextures[unit]=tex; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // SetTexture: set the given unit to reference the given texture; pass a null texture to disable texturing on any unit void CRenderer::SetTexture(int unit,CTexture* texture) { Handle h = texture? texture->GetHandle() : 0; ogl_tex_bind(h, unit); } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // IsTextureTransparent: return true if given texture is transparent, else false - note texture must be loaded // beforehand bool CRenderer::IsTextureTransparent(CTexture* texture) { if (!texture) return false; Handle h=texture->GetHandle(); uint flags = 0; // assume no alpha on failure (void)ogl_tex_get_format(h, &flags, 0); return (flags & TEX_ALPHA) != 0; } static inline void CopyTriple(unsigned char* dst,const unsigned char* src) { dst[0]=src[0]; dst[1]=src[1]; dst[2]=src[2]; } /////////////////////////////////////////////////////////////////////////////////////////////////// // LoadAlphaMaps: load the 14 default alpha maps, pack them into one composite texture and // calculate the coordinate of each alphamap within this packed texture int CRenderer::LoadAlphaMaps() { const char* const key = "(alpha map composite)"; Handle ht = ogl_tex_find(key); // alpha map texture had already been created and is still in memory: // reuse it, do not load again. if(ht > 0) { m_hCompositeAlphaMap = ht; return 0; } // // load all textures and store Handle in array // Handle textures[NumAlphaMaps] = {0}; PathPackage pp; (void)pp_set_dir(&pp, "art/textures/terrain/alphamaps/special"); const char* fnames[NumAlphaMaps] = { "blendcircle.dds", "blendlshape.dds", "blendedge.dds", "blendedgecorner.dds", "blendedgetwocorners.dds", "blendfourcorners.dds", "blendtwooppositecorners.dds", "blendlshapecorner.dds", "blendtwocorners.dds", "blendcorner.dds", "blendtwoedges.dds", "blendthreecorners.dds", "blendushape.dds", "blendbad.dds" }; uint base = 0; // texture width/height (see below) // for convenience, we require all alpha maps to be of the same BPP // (avoids another ogl_tex_get_size call, and doesn't hurt) uint bpp = 0; for(uint i=0;ishadow->GetUseDepthTexture()); } void CRenderer::JSI_SetUseDepthTexture(JSContext* ctx, jsval newval) { bool depthTexture; if (!ToPrimitive(ctx, newval, depthTexture)) return; m->shadow->SetUseDepthTexture(depthTexture); } void CRenderer::ScriptingInit() { AddProperty(L"fastPlayerColor", &CRenderer::JSI_GetFastPlayerColor, &CRenderer::JSI_SetFastPlayerColor); AddProperty(L"renderpath", &CRenderer::JSI_GetRenderPath, &CRenderer::JSI_SetRenderPath); AddProperty(L"useDepthTexture", &CRenderer::JSI_GetUseDepthTexture, &CRenderer::JSI_SetUseDepthTexture); AddProperty(L"sortAllTransparent", &CRenderer::m_SortAllTransparent); AddProperty(L"fastNormals", &CRenderer::m_FastNormals); AddProperty(L"displayFrustum", &CRenderer::m_DisplayFrustum); + AddProperty(L"shadowZBias", &CRenderer::m_ShadowZBias); CJSObject::ScriptingInit("Renderer"); } Index: ps/trunk/source/renderer/PatchRData.cpp =================================================================== --- ps/trunk/source/renderer/PatchRData.cpp (revision 3507) +++ ps/trunk/source/renderer/PatchRData.cpp (revision 3508) @@ -1,590 +1,595 @@ #include "precompiled.h" #include #include #include "Pyrogenesis.h" #include "lib/res/graphics/ogl_tex.h" +#include "graphics/LightEnv.h" #include "Renderer.h" #include "renderer/PatchRData.h" #include "AlphaMapCalculator.h" #include "ps/CLogger.h" #include "ps/Profile.h" #include "MathUtil.h" #include "LOSManager.h" const int BlendOffsets[8][2] = { { 0, -1 }, { -1, -1 }, { -1, 0 }, { -1, 1 }, { 0, 1 }, { 1, 1 }, { 1, 0 }, { 1, -1 } }; /////////////////////////////////////////////////////////////////// // CPatchRData constructor -CPatchRData::CPatchRData(CPatch* patch) : m_Patch(patch), m_VBBase(0), m_VBBlends(0), m_Vertices(0), m_LightingColors(0) +CPatchRData::CPatchRData(CPatch* patch) : m_Patch(patch), m_VBBase(0), m_VBBlends(0), m_Vertices(0) { debug_assert(patch); Build(); } /////////////////////////////////////////////////////////////////// // CPatchRData destructor -CPatchRData::~CPatchRData() +CPatchRData::~CPatchRData() { // delete copy of vertex data delete[] m_Vertices; - delete[] m_LightingColors; // release vertex buffer chunks if (m_VBBase) g_VBMan.Release(m_VBBase); if (m_VBBlends) g_VBMan.Release(m_VBBlends); } static Handle GetTerrainTileTexture(CTerrain* terrain,int gx,int gz) { CMiniPatch* mp=terrain->GetTile(gx,gz); return mp ? mp->Tex1 : 0; } bool QueryAdjacency(int x,int y,Handle h,Handle* texgrid) { for (int j=y-1;j<=y+1;j++) { for (int i=x-1;i<=x+1;i++) { if (i<0 || i>PATCH_SIZE+1 || j<0 || j>PATCH_SIZE+1) { continue; } if (texgrid[j*(PATCH_SIZE+2)+i]==h) { return true; } } } return false; } struct STmpSplat { Handle m_Texture; u16 m_Indices[4]; }; void CPatchRData::BuildBlends() { m_BlendIndices.clear(); m_BlendSplats.clear(); m_BlendVertices.clear(); m_BlendVertexIndices.clear(); // get index of this patch (unused) //int px=m_Patch->m_X; //int pz=m_Patch->m_Z; CTerrain* terrain=m_Patch->m_Parent; // temporary list of splats std::vector splats; // set of textures used for splats std::set splatTextures; // for each tile in patch .. for (int j=0;jm_MiniPatches[j][i]; mp->GetTileIndex(gx,gz); // build list of textures of higher priority than current tile that are used by neighbouring tiles std::vector neighbourTextures; - for (int m=-1;m<=1;m++) { + for (int m=-1;m<=1;m++) { for (int k=-1;k<=1;k++) { CMiniPatch* nmp=terrain->GetTile(gx+k,gz+m); if (nmp && nmp->Tex1 != mp->Tex1) { if (nmp->Tex1Priority>mp->Tex1Priority || (nmp->Tex1Priority==mp->Tex1Priority && nmp->Tex1>mp->Tex1)) { STex tex; tex.m_Handle=nmp->Tex1; tex.m_Priority=nmp->Tex1Priority; if (std::find(neighbourTextures.begin(),neighbourTextures.end(),tex)==neighbourTextures.end()) { neighbourTextures.push_back(tex); } } } } } if (neighbourTextures.size()>0) { // sort textures from lowest to highest priority std::sort(neighbourTextures.begin(),neighbourTextures.end()); // for each of the neighbouring textures .. uint count=(uint)neighbourTextures.size(); for (uint k=0;km_Owner->UpdateChunkVertices(m_VBBlends,&m_BlendVertices[0]); - + // now build outgoing splats m_BlendSplats.resize(splatTextures.size()); int splatCount=0; - + debug_assert(m_VBBlends->m_Index < 65536); unsigned short base = (unsigned short)m_VBBlends->m_Index; std::set::iterator iter=splatTextures.begin(); for (;iter!=splatTextures.end();++iter) { Handle tex=*iter; - + SSplat& splat=m_BlendSplats[splatCount]; splat.m_IndexStart=(u32)m_BlendIndices.size(); splat.m_Texture=tex; - + for (uint k=0;k<(uint)splats.size();k++) { if (splats[k].m_Texture==tex) { m_BlendIndices.push_back(splats[k].m_Indices[0]+base); m_BlendIndices.push_back(splats[k].m_Indices[1]+base); m_BlendIndices.push_back(splats[k].m_Indices[2]+base); m_BlendIndices.push_back(splats[k].m_Indices[3]+base); splat.m_IndexCount+=4; } } splatCount++; } } } void CPatchRData::BuildIndices() { // must have allocated some vertices before trying to build corresponding indices debug_assert(m_VBBase); // number of vertices in each direction in each patch int vsize=PATCH_SIZE+1; // release existing indices and bins m_Indices.clear(); m_ShadowMapIndices.clear(); m_Splats.clear(); // build grid of textures on this patch and boundaries of adjacent patches std::vector textures; Handle texgrid[PATCH_SIZE][PATCH_SIZE]; for (int j=0;jm_MiniPatches[j][i].Tex1; texgrid[j][i]=h; if (std::find(textures.begin(),textures.end(),h)==textures.end()) { textures.push_back(h); } } } // now build base splats from interior textures m_Splats.resize(textures.size()); - // build indices for base splats + // build indices for base splats u32 base=(u32)m_VBBase->m_Index; for (uint i=0;i<(uint)m_Splats.size();i++) { Handle h=textures[i]; SSplat& splat=m_Splats[i]; splat.m_Texture=h; splat.m_IndexStart=(u32)m_Indices.size(); - + for (int j=0;jm_X; u32 pz=m_Patch->m_Z; - + CTerrain* terrain=m_Patch->m_Parent; + const CLightEnv& lightEnv = g_Renderer.GetLightEnv(); // build vertices for (int j=0;jCalcPosition(ix,iz,vertices[v].m_Position); - *(uint32_t*)&vertices[v].m_Color = 0; // will be set to the proper value in Update() + terrain->CalcPosition(ix,iz,vertices[v].m_Position); + *(uint32_t*)&vertices[v].m_LOSColor = 0; // will be set to the proper value in Update() vertices[v].m_UVs[0]=i*0.125f; vertices[v].m_UVs[1]=1 - j*0.125f; - - // calculate lighting into the separate m_LightingColors array, which will - // be used to set the vertex colors in Update() + + // Calculate diffuse lighting for this vertex + // Ambient is added by the lighting pass (since ambient is the same + // for all vertices, it need not be stored in the vertex structure) terrain->CalcNormal(ix,iz,normal); - g_Renderer.m_SHCoeffsTerrain.Evaluate(normal, m_LightingColors[v]); + + RGBColor diffuse; + lightEnv.EvaluateDirect(normal, diffuse); + *(u32*)&vertices[v].m_DiffuseColor = ConvertRGBColorTo4ub(diffuse); } } - + // upload to vertex buffer if (!m_VBBase) { m_VBBase=g_VBMan.Allocate(sizeof(SBaseVertex),vsize*vsize,true); } m_VBBase->m_Owner->UpdateChunkVertices(m_VBBase,m_Vertices); } void CPatchRData::Build() { BuildVertices(); BuildIndices(); BuildBlends(); } void CPatchRData::Update() { if (m_UpdateFlags!=0) { // TODO,RC 11/04/04 - need to only rebuild necessary bits of renderdata rather // than everything; it's complicated slightly because the blends are dependent // on both vertex and index data BuildVertices(); BuildIndices(); BuildBlends(); m_UpdateFlags=0; } // Update vertex colors, which are affected by LOS - + u32 px=m_Patch->m_X; u32 pz=m_Patch->m_Z; CTerrain* terrain=m_Patch->m_Parent; int mapSize=terrain->GetVerticesPerSide(); CLOSManager* losMgr = g_Game->GetWorld()->GetLOSManager(); int vsize=PATCH_SIZE+1; // this is very similar to BuildVertices(), but just for color for (int j=0;j= 0 && tz >= 0 && tx <= mapSize-2 && tz <= mapSize-2) { ELOSStatus s = losMgr->GetStatus(tx, tz, g_Game->GetLocalPlayer()); - if(s==LOS_EXPLORED && losMod > 0.7f) - losMod = 0.7f; - else if(s==LOS_UNEXPLORED && losMod > 0.0f) - losMod = 0.0f; + if(s==LOS_EXPLORED && losMod.R > 178) + losMod = SColor4ub(178, 178, 178, 255); + else if(s==LOS_UNEXPLORED && losMod.R > 0) + losMod = SColor4ub(0, 0, 0, 255); } } - RGBColor c = m_LightingColors[v]; - c *= losMod; - - *(uint32_t*)&m_Vertices[v].m_Color = ConvertRGBColorTo4ub(c); + m_Vertices[v].m_LOSColor = losMod; } } // upload base vertices into their vertex buffer m_VBBase->m_Owner->UpdateChunkVertices(m_VBBase,m_Vertices); // update blend colors by copying them from vertex colors - for(uint i=0; im_Owner->UpdateChunkVertices(m_VBBlends,&m_BlendVertices[0]); } } -void CPatchRData::RenderBase() +void CPatchRData::RenderBase(bool losColor) { debug_assert(m_UpdateFlags==0); SBaseVertex *base=(SBaseVertex *)m_VBBase->m_Owner->Bind(); // setup data pointers u32 stride=sizeof(SBaseVertex); glVertexPointer(3,GL_FLOAT,stride,&base->m_Position[0]); - glColorPointer(4,GL_UNSIGNED_BYTE,stride,&base->m_Color); + glColorPointer(4,GL_UNSIGNED_BYTE,stride,losColor ? &base->m_LOSColor : &base->m_DiffuseColor); glTexCoordPointer(2,GL_FLOAT,stride,&base->m_UVs[0]); - + // render each splat for (uint i=0;i<(uint)m_Splats.size();i++) { SSplat& splat=m_Splats[i]; ogl_tex_bind(splat.m_Texture); glDrawElements(GL_QUADS,splat.m_IndexCount,GL_UNSIGNED_SHORT,&m_Indices[splat.m_IndexStart]); // bump stats g_Renderer.m_Stats.m_DrawCalls++; g_Renderer.m_Stats.m_TerrainTris+=splat.m_IndexCount/2; } } -void CPatchRData::RenderStreams(u32 streamflags) +void CPatchRData::RenderStreams(u32 streamflags, bool losColor) { debug_assert(m_UpdateFlags==0); SBaseVertex* base=(SBaseVertex *)m_VBBase->m_Owner->Bind(); // setup data pointers u32 stride=sizeof(SBaseVertex); glVertexPointer(3, GL_FLOAT, stride, &base->m_Position); if (streamflags & STREAM_UV0) { glTexCoordPointer(2, GL_FLOAT, stride, &base->m_UVs); } else if (streamflags & STREAM_POSTOUV0) { glTexCoordPointer(3, GL_FLOAT, stride, &base->m_Position); } - + if (streamflags & STREAM_COLOR) + { + glColorPointer(4,GL_UNSIGNED_BYTE,stride,losColor ? &base->m_LOSColor : &base->m_DiffuseColor); + } + // render all base splats at once glDrawElements(GL_QUADS,(GLsizei)m_Indices.size(),GL_UNSIGNED_SHORT,&m_Indices[0]); // bump stats g_Renderer.m_Stats.m_DrawCalls++; g_Renderer.m_Stats.m_TerrainTris+=(u32)m_Indices.size()/2; } void CPatchRData::RenderBlends() { debug_assert(m_UpdateFlags==0); if (m_BlendVertices.size()==0) return; u8* base=m_VBBlends->m_Owner->Bind(); // setup data pointers u32 stride=sizeof(SBlendVertex); glVertexPointer(3,GL_FLOAT,stride,base+offsetof(SBlendVertex,m_Position)); - glColorPointer(4,GL_UNSIGNED_BYTE,stride,base+offsetof(SBlendVertex,m_Color)); - + glColorPointer(4,GL_UNSIGNED_BYTE,stride,base+offsetof(SBlendVertex,m_LOSColor)); + pglClientActiveTextureARB(GL_TEXTURE0); glTexCoordPointer(2,GL_FLOAT,stride,base+offsetof(SBlendVertex,m_UVs[0])); pglClientActiveTextureARB(GL_TEXTURE1); glTexCoordPointer(2,GL_FLOAT,stride,base+offsetof(SBlendVertex,m_AlphaUVs[0])); for (uint i=0;i<(uint)m_BlendSplats.size();i++) { SSplat& splat=m_BlendSplats[i]; ogl_tex_bind(splat.m_Texture); glDrawElements(GL_QUADS,splat.m_IndexCount,GL_UNSIGNED_SHORT,&m_BlendIndices[splat.m_IndexStart]); // bump stats g_Renderer.m_Stats.m_DrawCalls++; g_Renderer.m_Stats.m_BlendSplats++; g_Renderer.m_Stats.m_TerrainTris+=splat.m_IndexCount/2; } } void CPatchRData::RenderOutline() { int i; uint vsize=PATCH_SIZE+1; u8* base=m_VBBase->m_Owner->Bind(); //TODO: this makes no sense, get rid of it UNUSED2(base); glBegin(GL_LINES); for (i=0;i * ========================================================================= */ #include "precompiled.h" #include "ogl.h" #include "lib/res/graphics/ogl_shader.h" #include "Vector3D.h" #include "Vector4D.h" #include "ps/CLogger.h" #include "graphics/Color.h" +#include "graphics/LightEnv.h" #include "graphics/Model.h" #include "graphics/ModelDef.h" #include "renderer/HWLightingModelRenderer.h" #include "renderer/Renderer.h" #include "renderer/RenderModifiers.h" #include "renderer/RenderPathVertexShader.h" -#include "renderer/SHCoeffs.h" #include "renderer/VertexArray.h" #define LOG_CATEGORY "graphics" /////////////////////////////////////////////////////////////////////////////////////////////// // HWLightingModelRenderer implementation struct HWLModelDef : public CModelDefRPrivate { /// Indices are the same for all models, so share them u16* m_Indices; - + HWLModelDef(CModelDefPtr mdef); ~HWLModelDef() { delete[] m_Indices; } }; HWLModelDef::HWLModelDef(CModelDefPtr mdef) { m_Indices = new u16[mdef->GetNumFaces()*3]; ModelRenderer::BuildIndices(mdef, m_Indices); } struct HWLModel { /// Dynamic per-CModel vertex array VertexArray m_Array; - + /// Position and normals are recalculated on CPU every frame VertexArray::Attribute m_Position; VertexArray::Attribute m_Normal; - + /// UV is stored per-CModel in order to avoid space wastage due to alignment VertexArray::Attribute m_UV; - + HWLModel() : m_Array(true) { } }; struct HWLightingModelRendererInternals { /// Currently used RenderModifier RenderModifierPtr modifier; - + /// Previously prepared modeldef HWLModelDef* hwlmodeldef; }; // Construction and Destruction HWLightingModelRenderer::HWLightingModelRenderer() { m = new HWLightingModelRendererInternals; m->hwlmodeldef = 0; } HWLightingModelRenderer::~HWLightingModelRenderer() { delete m; } // Check hardware support bool HWLightingModelRenderer::IsAvailable() { return g_Renderer.m_VertexShader != 0; } // Build model data (and modeldef data if necessary) void* HWLightingModelRenderer::CreateModelData(CModel* model) { CModelDefPtr mdef = model->GetModelDef(); HWLModelDef* hwlmodeldef = (HWLModelDef*)mdef->GetRenderData(m); if (!hwlmodeldef) { hwlmodeldef = new HWLModelDef(mdef); mdef->SetRenderData(m, hwlmodeldef); } // Build the per-model data HWLModel* hwlmodel = new HWLModel; hwlmodel->m_Position.type = GL_FLOAT; hwlmodel->m_Position.elems = 3; hwlmodel->m_Array.AddAttribute(&hwlmodel->m_Position); hwlmodel->m_UV.type = GL_FLOAT; hwlmodel->m_UV.elems = 2; hwlmodel->m_Array.AddAttribute(&hwlmodel->m_UV); hwlmodel->m_Normal.type = GL_FLOAT; hwlmodel->m_Normal.elems = 3; hwlmodel->m_Array.AddAttribute(&hwlmodel->m_Normal); - + hwlmodel->m_Array.SetNumVertices(mdef->GetNumVertices()); hwlmodel->m_Array.Layout(); // Fill in static UV coordinates VertexArrayIterator UVit = hwlmodel->m_UV.GetIterator(); - + ModelRenderer::BuildUV(mdef, UVit); return hwlmodel; } // Fill in and upload dynamic vertex array void HWLightingModelRenderer::UpdateModelData(CModel* model, void* data, u32 updateflags) { HWLModel* hwlmodel = (HWLModel*)data; - + if (updateflags & RENDERDATA_UPDATE_VERTICES) { CModelDefPtr mdef = model->GetModelDef(); - + // build vertices VertexArrayIterator Position = hwlmodel->m_Position.GetIterator(); VertexArrayIterator Normal = hwlmodel->m_Normal.GetIterator(); - + ModelRenderer::BuildPositionAndNormals(model, Position, Normal); - + // upload everything to vertex buffer hwlmodel->m_Array.Upload(); } } // Cleanup per-model data. // Note that per-CModelDef data is deleted by the CModelDef itself. void HWLightingModelRenderer::DestroyModelData(CModel* UNUSED(model), void* data) { HWLModel* hwlmodel = (HWLModel*)data; - + delete hwlmodel; } // Setup one rendering pass void HWLightingModelRenderer::BeginPass(uint streamflags) { glEnableClientState(GL_VERTEX_ARRAY); - + if (streamflags & STREAM_UV0) glEnableClientState(GL_TEXTURE_COORD_ARRAY); if (streamflags & STREAM_COLOR) { - const RGBColor* coeffs = g_Renderer.m_SHCoeffsUnits.GetCoefficients(); + const CLightEnv& lightEnv = g_Renderer.GetLightEnv(); int idx; - + ogl_program_use(g_Renderer.m_VertexShader->m_ModelLight); - idx = g_Renderer.m_VertexShader->m_ModelLight_SHCoefficients; - pglUniform3fvARB(idx, 9, (float*)coeffs); + idx = g_Renderer.m_VertexShader->m_ModelLight_Ambient; + pglUniform3fvARB(idx, 1, &lightEnv.m_UnitsAmbientColor.X); + idx = g_Renderer.m_VertexShader->m_ModelLight_SunDir; + pglUniform3fvARB(idx, 1, &lightEnv.m_SunDir.X); + idx = g_Renderer.m_VertexShader->m_ModelLight_SunColor; + pglUniform3fvARB(idx, 1, &lightEnv.m_SunColor.X); glEnableClientState(GL_NORMAL_ARRAY); } } // Cleanup one rendering pass void HWLightingModelRenderer::EndPass(uint streamflags) { if (streamflags & STREAM_UV0) glDisableClientState(GL_TEXTURE_COORD_ARRAY); if (streamflags & STREAM_COLOR) { pglUseProgramObjectARB(0); glDisableClientState(GL_NORMAL_ARRAY); } - + glDisableClientState(GL_VERTEX_ARRAY); } // Prepare UV coordinates for this modeldef void HWLightingModelRenderer::PrepareModelDef(uint UNUSED(streamflags), CModelDefPtr def) { m->hwlmodeldef = (HWLModelDef*)def->GetRenderData(m); - + debug_assert(m->hwlmodeldef); } // Render one model void HWLightingModelRenderer::RenderModel(uint streamflags, CModel* model, void* data) { CModelDefPtr mdldef = model->GetModelDef(); HWLModel* hwlmodel = (HWLModel*)data; - + u8* base = hwlmodel->m_Array.Bind(); GLsizei stride = (GLsizei)hwlmodel->m_Array.GetStride(); - + glVertexPointer(3, GL_FLOAT, stride, base + hwlmodel->m_Position.offset); if (streamflags & STREAM_COLOR) { CColor sc = model->GetShadingColor(); glColor3f(sc.r, sc.g, sc.b); - + glNormalPointer(GL_FLOAT, stride, base + hwlmodel->m_Normal.offset); } if (streamflags & STREAM_UV0) { glTexCoordPointer(2, GL_FLOAT, stride, base + hwlmodel->m_UV.offset); } // render the lot size_t numFaces = mdldef->GetNumFaces(); pglDrawRangeElementsEXT(GL_TRIANGLES, 0, (GLuint)mdldef->GetNumVertices(), (GLsizei)numFaces*3, GL_UNSIGNED_SHORT, m->hwlmodeldef->m_Indices); // bump stats g_Renderer.m_Stats.m_DrawCalls++; g_Renderer.m_Stats.m_ModelTris += numFaces; } Index: ps/trunk/source/renderer/ShadowMap.cpp =================================================================== --- ps/trunk/source/renderer/ShadowMap.cpp (revision 3507) +++ ps/trunk/source/renderer/ShadowMap.cpp (revision 3508) @@ -1,456 +1,461 @@ /** * ========================================================================= * File : ShadowMap.cpp * Project : Pyrogenesis * Description : Shadow mapping related texture and matrix management * * @author Nicolai Hähnle * ========================================================================= */ #include "precompiled.h" #include "ogl.h" #include "CLogger.h" #include "graphics/LightEnv.h" #include "maths/Bound.h" #include "maths/MathUtil.h" #include "maths/Matrix3D.h" #include "renderer/Renderer.h" #include "renderer/ShadowMap.h" #define LOG_CATEGORY "graphics" /////////////////////////////////////////////////////////////////////////////////////////////////// // ShadowMap implementation /** * Struct ShadowMapInternals: Internal data for the ShadowMap implementation */ struct ShadowMapInternals { // whether we're using depth texture or luminance map bool UseDepthTexture; // handle of shadow map GLuint Texture; // width, height of shadow map u32 Width, Height; // transform light space into projected light space // in projected light space, the shadowbound box occupies the [-1..1] cube // calculated on BeginRender, after the final shadow bounds are known CMatrix3D LightProjection; // Transform world space into light space; calculated on SetupFrame CMatrix3D LightTransform; // Transform world space into texture space of the shadow map; // calculated on BeginRender, after the final shadow bounds are known CMatrix3D TextureMatrix; // transform light space into world space CMatrix3D InvLightTransform; // bounding box of shadowed objects in light space CBound ShadowBound; // Helper functions void CalcShadowMatrices(); void CreateTexture(); }; /////////////////////////////////////////////////////////////////////////////////////////////////// // Construction/Destruction ShadowMap::ShadowMap() { m = new ShadowMapInternals; m->Texture = 0; m->Width = 0; m->Height = 0; m->UseDepthTexture = false; } ShadowMap::~ShadowMap() { if (m->Texture) glDeleteTextures(1, &m->Texture); delete m; } ////////////////////////////////////////////////////////////////////////////// // SetCameraAndLight: camera and light direction for this frame void ShadowMap::SetupFrame(const CCamera& camera, const CVector3D& lightdir) { if (!m->Texture) m->CreateTexture(); CVector3D z = lightdir; CVector3D y; CVector3D x = camera.m_Orientation.GetIn(); CVector3D eyepos = camera.m_Orientation.GetTranslation(); z.Normalize(); x -= z * z.Dot(x); if (x.GetLength() < 0.001) { // this is invoked if the camera and light directions almost coincide // assumption: light direction has a significant Z component x = CVector3D(1.0, 0.0, 0.0); x -= z * z.Dot(x); } x.Normalize(); y = z.Cross(x); // X axis perpendicular to light direction, flowing along with view direction m->LightTransform._11 = x.X; m->LightTransform._12 = x.Y; m->LightTransform._13 = x.Z; // Y axis perpendicular to light and view direction m->LightTransform._21 = y.X; m->LightTransform._22 = y.Y; m->LightTransform._23 = y.Z; // Z axis is in direction of light m->LightTransform._31 = z.X; m->LightTransform._32 = z.Y; m->LightTransform._33 = z.Z; // eye is at the origin of the coordinate system m->LightTransform._14 = -x.Dot(eyepos); m->LightTransform._24 = -y.Dot(eyepos); m->LightTransform._34 = -z.Dot(eyepos); m->LightTransform._41 = 0.0; m->LightTransform._42 = 0.0; m->LightTransform._43 = 0.0; m->LightTransform._44 = 1.0; m->LightTransform.GetInverse(m->InvLightTransform); m->ShadowBound.SetEmpty(); } ////////////////////////////////////////////////////////////////////////////// // AddShadowedBound: add a world-space bounding box to the bounds of shadowed // objects void ShadowMap::AddShadowedBound(const CBound& bounds) { CBound lightspacebounds; bounds.Transform(m->LightTransform, lightspacebounds); m->ShadowBound += lightspacebounds; } /////////////////////////////////////////////////////////////////////////////////////////////////// // CalcShadowMatrices: calculate required matrices for shadow map generation - the light's // projection and transformation matrices void ShadowMapInternals::CalcShadowMatrices() { CRenderer& renderer = g_Renderer; // Setup orthogonal projection (lightspace -> clip space) for shadowmap rendering CVector3D scale = ShadowBound[1] - ShadowBound[0]; CVector3D shift = (ShadowBound[1] + ShadowBound[0]) * -0.5; if (scale.X < 1.0) scale.X = 1.0; if (scale.Y < 1.0) scale.Y = 1.0; if (scale.Z < 1.0) scale.Z = 1.0; scale.X = 2.0 / scale.X; scale.Y = 2.0 / scale.Y; scale.Z = 2.0 / scale.Z; LightProjection.SetZero(); LightProjection._11 = scale.X; LightProjection._14 = shift.X * scale.X; LightProjection._22 = scale.Y; LightProjection._24 = shift.Y * scale.Y; LightProjection._33 = scale.Z; - LightProjection._34 = shift.Z * scale.Z; + LightProjection._34 = shift.Z * scale.Z + renderer.m_ShadowZBias; LightProjection._44 = 1.0; // Calculate texture matrix by creating the clip space to texture coordinate matrix // and then concatenating all matrices that have been calculated so far - CMatrix3D clipToTex; - float texscalex = 0.5 * (float)renderer.GetWidth() / (float)Width; - float texscaley = 0.5 * (float)renderer.GetHeight() / (float)Height; - - clipToTex.SetZero(); - clipToTex._11 = texscalex; - clipToTex._14 = texscalex; - clipToTex._22 = texscaley; - clipToTex._24 = texscaley; - clipToTex._33 = 0.5; // translate -1..1 clip space Z values to tex Z values - clipToTex._34 = 0.5; - clipToTex._44 = 1.0; + CMatrix3D lightToTex; + float texscalex = (float)renderer.GetWidth() / (float)Width; + float texscaley = (float)renderer.GetHeight() / (float)Height; + float texscalez = 1.0; + + texscalex = texscalex / (ShadowBound[1].X - ShadowBound[0].X); + texscaley = texscaley / (ShadowBound[1].Y - ShadowBound[0].Y); + texscalez = texscalez / (ShadowBound[1].Z - ShadowBound[0].Z); + + lightToTex.SetZero(); + lightToTex._11 = texscalex; + lightToTex._14 = -ShadowBound[0].X * texscalex; + lightToTex._22 = texscaley; + lightToTex._24 = -ShadowBound[0].Y * texscaley; + lightToTex._33 = texscalez; + lightToTex._34 = -ShadowBound[0].Z * texscalez; + lightToTex._44 = 1.0; - TextureMatrix = clipToTex * LightProjection * LightTransform; + TextureMatrix = lightToTex * LightTransform; } /////////////////////////////////////////////////////////////////////////////////////////////////// // Create the shadow map void ShadowMapInternals::CreateTexture() { if (Texture) glDeleteTextures(1, &Texture); // get shadow map size as next power of two up from view width and height Width = g_Renderer.GetWidth(); Width = RoundUpToPowerOf2(Width); Height = g_Renderer.GetHeight(); Height = RoundUpToPowerOf2(Height); LOG(NORMAL, LOG_CATEGORY, "Creating shadow texture (size %ix%i) (format = %s)", Width, Height, UseDepthTexture ? "DEPTH_COMPONENT" : "LUMINANCE"); // create texture object glGenTextures(1, &Texture); g_Renderer.BindTexture(0, Texture); u32 size = Width*Height; if (UseDepthTexture) { float* buf = new float[size]; for(uint i = 0; i < size; i++) buf[i] = 1.0; - glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, Width, Height, 0, + glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT16, Width, Height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, buf); delete[] buf; } else { u32* buf=new u32[size]; for (uint i=0;iCalcShadowMatrices(); // clear buffers if (m->UseDepthTexture) { glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); glColorMask(0,0,0,0); } else { glClearColor(1,1,1,0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); } // setup viewport glViewport(0, 0, renderWidth, renderHeight); glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadMatrixf(&m->LightProjection._11); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadMatrixf(&m->LightTransform._11); glEnable(GL_SCISSOR_TEST); glScissor(1,1, renderWidth-2, renderHeight-2); } /////////////////////////////////////////////////////////////////////////////////////////////////// // Finish rendering into shadow map texture void ShadowMap::EndRender() { glDisable(GL_SCISSOR_TEST); // copy result into shadow map texture g_Renderer.BindTexture(0, m->Texture); glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, g_Renderer.GetWidth(), g_Renderer.GetHeight()); if (m->UseDepthTexture) { glColorMask(1,1,1,1); } // restore matrix stack glPopMatrix(); glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); } /////////////////////////////////////////////////////////////////////////////////////////////////// // Retrieve the texture handle and texture matrix for shadowing GLuint ShadowMap::GetTexture() { return m->Texture; } const CMatrix3D& ShadowMap::GetTextureMatrix() { return m->TextureMatrix; } /////////////////////////////////////////////////////////////////////////////////////////////////// // Using depth textures vs. a simple luminance map bool ShadowMap::GetUseDepthTexture() { return m->UseDepthTexture; } void ShadowMap::SetUseDepthTexture(bool depthTexture) { if (depthTexture) { if (!g_Renderer.GetCapabilities().m_DepthTextureShadows) { LOG(WARNING, LOG_CATEGORY, "Depth textures are not supported by your graphics card/driver. Fallback to luminance map (no self-shadowing)!"); depthTexture = false; } } if (depthTexture != m->UseDepthTexture) { if (m->Texture) { glDeleteTextures(1, &m->Texture); m->Texture = 0; } m->Width = m->Height = 0; m->UseDepthTexture = depthTexture; } } ////////////////////////////////////////////////////////////////////////////// // RenderDebugDisplay: debug visualizations // - blue: objects in shadow void ShadowMap::RenderDebugDisplay() { glDepthMask(0); glDisable(GL_CULL_FACE); // Render shadow bound glMatrixMode(GL_MODELVIEW); glPushMatrix(); glMultMatrixf(&m->InvLightTransform._11); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glColor4ub(0,0,255,64); m->ShadowBound.Render(); glDisable(GL_BLEND); glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); glColor3ub(0,0,255); m->ShadowBound.Render(); glBegin(GL_LINES); glVertex3f(0.0, 0.0, 0.0); glVertex3f(0.0, 0.0, 50.0); glEnd(); glBegin(GL_POLYGON); glVertex3f(0.0, 0.0, 50.0); glVertex3f(50.0, 0.0, 50.0); glVertex3f(0.0, 50.0, 50.0); glEnd(); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); glPopMatrix(); #if 0 CMatrix3D InvTexTransform; m->TextureMatrix.GetInverse(InvTexTransform); // Render representative texture rectangle glPushMatrix(); glMultMatrixf(&InvTexTransform._11); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glColor4ub(255,0,0,64); glBegin(GL_QUADS); glVertex3f(0.0, 0.0, 0.0); glVertex3f(1.0, 0.0, 0.0); glVertex3f(1.0, 1.0, 0.0); glVertex3f(0.0, 1.0, 0.0); glEnd(); glDisable(GL_BLEND); glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); glColor3ub(255,0,0); glBegin(GL_QUADS); glVertex3f(0.0, 0.0, 0.0); glVertex3f(1.0, 0.0, 0.0); glVertex3f(1.0, 1.0, 0.0); glVertex3f(0.0, 1.0, 0.0); glEnd(); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); glPopMatrix(); #endif // Render the shadow map glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); glOrtho(0.0, 1.0, 1.0, 0.0, -1.0, 1.0); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); glDisable(GL_DEPTH_TEST); g_Renderer.BindTexture(0, m->Texture); if (m->UseDepthTexture) { glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE, GL_LUMINANCE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_NONE); } glColor3f(1.0, 1.0, 1.0); glBegin(GL_QUADS); glTexCoord2f(0.0, 0.0); glVertex2f(0.0, 0.0); glTexCoord2f(1.0, 0.0); glVertex2f(0.2, 0.0); glTexCoord2f(1.0, 1.0); glVertex2f(0.2, 0.2); glTexCoord2f(0.0, 1.0); glVertex2f(0.0, 0.2); glEnd(); glEnable(GL_CULL_FACE); glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); glPopMatrix(); glEnable(GL_DEPTH_TEST); glDepthMask(1); } Index: ps/trunk/source/renderer/RenderPathVertexShader.h =================================================================== --- ps/trunk/source/renderer/RenderPathVertexShader.h (revision 3507) +++ ps/trunk/source/renderer/RenderPathVertexShader.h (revision 3508) @@ -1,32 +1,36 @@ #ifndef __RENDERPATHVERTEXSHADER_H__ #define __RENDERPATHVERTEXSHADER_H__ class RenderPathVertexShader { public: RenderPathVertexShader(); ~RenderPathVertexShader(); - + // Initialize this render path. bool Init(); // Call once per frame to update program stuff void BeginFrame(); public: Handle m_ModelLight; - GLint m_ModelLight_SHCoefficients; - + GLint m_ModelLight_Ambient; + GLint m_ModelLight_SunDir; + GLint m_ModelLight_SunColor; + Handle m_InstancingLight; - GLint m_InstancingLight_SHCoefficients; + GLint m_InstancingLight_Ambient; + GLint m_InstancingLight_SunDir; + GLint m_InstancingLight_SunColor; GLint m_InstancingLight_Instancing1; // matrix rows GLint m_InstancingLight_Instancing2; GLint m_InstancingLight_Instancing3; Handle m_Instancing; GLint m_Instancing_Instancing1; // matrix rows GLint m_Instancing_Instancing2; GLint m_Instancing_Instancing3; }; #endif // __RENDERPATHVERTEXSHADER_H__ Index: ps/trunk/source/renderer/TerrainRenderer.cpp =================================================================== --- ps/trunk/source/renderer/TerrainRenderer.cpp (revision 3507) +++ ps/trunk/source/renderer/TerrainRenderer.cpp (revision 3508) @@ -1,448 +1,436 @@ /** * ========================================================================= * File : TerrainRenderer.cpp * Project : Pyrogenesis * Description : Terrain rendering (everything related to patches and * : water) is encapsulated in TerrainRenderer * * @author Nicolai Hähnle * ========================================================================= */ #include "precompiled.h" #include "graphics/Camera.h" +#include "graphics/LightEnv.h" #include "graphics/Patch.h" #include "graphics/Terrain.h" #include "maths/MathUtil.h" #include "ps/Game.h" #include "ps/Profile.h" #include "simulation/LOSManager.h" #include "renderer/PatchRData.h" #include "renderer/Renderer.h" #include "renderer/ShadowMap.h" #include "renderer/TerrainRenderer.h" #include "renderer/WaterManager.h" /////////////////////////////////////////////////////////////////////////////////////////////// // TerrainRenderer implementation /** * TerrainRenderer keeps track of which phase it is in, to detect * when Submit, PrepareForRendering etc. are called in the wrong order. */ enum Phase { Phase_Submit, Phase_Render }; /** * Struct TerrainRendererInternals: Internal variables used by the TerrainRenderer class. */ struct TerrainRendererInternals { /// Which phase (submitting or rendering patches) are we in right now? Phase phase; /** * VisiblePatches: Patches that were submitted for this frame * * @todo Merge this list with CPatchRData list */ std::vector visiblePatches; - - // Helper functions - void ApplyShadowMap(ShadowMap* shadow, const RGBAColor& shadowColor); }; /////////////////////////////////////////////////////////////////// // Construction/Destruction TerrainRenderer::TerrainRenderer() { m = new TerrainRendererInternals(); m->phase = Phase_Submit; } TerrainRenderer::~TerrainRenderer() { delete m; } /////////////////////////////////////////////////////////////////// // Submit a patch for rendering void TerrainRenderer::Submit(CPatch* patch) { debug_assert(m->phase == Phase_Submit); CPatchRData* data=(CPatchRData*) patch->GetRenderData(); if (data == 0) { // no renderdata for patch, create it now data = new CPatchRData(patch); patch->SetRenderData(data); } data->Update(); m->visiblePatches.push_back(patch); } /////////////////////////////////////////////////////////////////// // Prepare for rendering void TerrainRenderer::PrepareForRendering() { debug_assert(m->phase == Phase_Submit); m->phase = Phase_Render; } /////////////////////////////////////////////////////////////////// // Clear submissions lists void TerrainRenderer::EndFrame() { debug_assert(m->phase == Phase_Render); m->visiblePatches.clear(); m->phase = Phase_Submit; } /////////////////////////////////////////////////////////////////// // Query if patches have been submitted this frame bool TerrainRenderer::HaveSubmissions() { return m->visiblePatches.size() > 0; } /////////////////////////////////////////////////////////////////// // Full-featured terrain rendering with blending and everything -void TerrainRenderer::RenderTerrain(ShadowMap* shadow, const RGBAColor& shadowColor) +void TerrainRenderer::RenderTerrain(ShadowMap* shadow) { debug_assert(m->phase == Phase_Render); // switch on required client states glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_COLOR_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); - // render everything + // render everything fullbright // set up texture environment for base pass MICROLOG(L"base splat textures"); pglActiveTextureARB(GL_TEXTURE0); pglClientActiveTextureARB(GL_TEXTURE0); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PRIMARY_COLOR); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR); // Set alpha to 1.0 glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_CONSTANT); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA); float one[4] = { 1.f, 1.f, 1.f, 1.f }; glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, one); for(uint i = 0; i < m->visiblePatches.size(); ++i) { CPatchRData* patchdata = (CPatchRData*)m->visiblePatches[i]->GetRenderData(); - patchdata->RenderBase(); + patchdata->RenderBase(true); // with LOS color } // render blends // switch on the composite alpha map texture (void)ogl_tex_bind(g_Renderer.m_hCompositeAlphaMap, 1); // switch on second uv set pglClientActiveTextureARB(GL_TEXTURE1); glEnableClientState(GL_TEXTURE_COORD_ARRAY); // setup additional texenv required by blend pass pglActiveTextureARB(GL_TEXTURE1); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_REPLACE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_ONE_MINUS_SRC_ALPHA); // switch on blending glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); // no need to write to the depth buffer a second time glDepthMask(0); // render blend passes for each patch for(uint i = 0; i < m->visiblePatches.size(); ++i) { CPatchRData* patchdata = (CPatchRData*)m->visiblePatches[i]->GetRenderData(); patchdata->RenderBlends(); } - // restore OpenGL state - glDepthMask(1); - glDisable(GL_BLEND); - + // Disable second texcoord array pglClientActiveTextureARB(GL_TEXTURE1); glDisableClientState(GL_TEXTURE_COORD_ARRAY); + + // Now apply lighting + // Light color is Ambient + ShadowTerm * Diffuse + glBlendFunc(GL_DST_COLOR, GL_ZERO); + + pglActiveTextureARB(GL_TEXTURE0); // Diffuse * Shadow + if (shadow) + glBindTexture(GL_TEXTURE_2D, shadow->GetTexture()); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PREVIOUS); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA); + + pglActiveTextureARB(GL_TEXTURE1); // + Ambient + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_ADD); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_CONSTANT); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PREVIOUS); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA); + + const CLightEnv& lightEnv = g_Renderer.GetLightEnv(); + glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, &lightEnv.m_TerrainAmbientColor.X); + + pglActiveTextureARB(GL_TEXTURE0); + + if (shadow) + { + const CMatrix3D& texturematrix = shadow->GetTextureMatrix(); + + glMatrixMode(GL_TEXTURE); + glLoadMatrixf(&texturematrix._11); + glMatrixMode(GL_MODELVIEW); + + if (shadow->GetUseDepthTexture()) + { + glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE, GL_LUMINANCE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL); + } + } + else + { + glBindTexture(GL_TEXTURE_2D, 0); + } + + pglClientActiveTextureARB(GL_TEXTURE0); + + for (uint i = 0; i < m->visiblePatches.size(); ++i) + { + CPatchRData* patchdata = (CPatchRData*)m->visiblePatches[i]->GetRenderData(); + patchdata->RenderStreams(STREAM_POS|STREAM_COLOR|STREAM_POSTOUV0, false); + } + + glMatrixMode(GL_TEXTURE); + glLoadIdentity(); + glMatrixMode(GL_MODELVIEW); + + // restore OpenGL state g_Renderer.BindTexture(1,0); pglClientActiveTextureARB(GL_TEXTURE0); pglActiveTextureARB(GL_TEXTURE0); - // switch off all client states - glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDepthMask(1); + glDisable(GL_BLEND); + glDisableClientState(GL_COLOR_ARRAY); glDisableClientState(GL_VERTEX_ARRAY); - - // Now render shadows as a second pass - if (shadow) - { - m->ApplyShadowMap(shadow, shadowColor); - } + glDisableClientState(GL_TEXTURE_COORD_ARRAY); } /////////////////////////////////////////////////////////////////// // Render un-textured patches as polygons void TerrainRenderer::RenderPatches() { debug_assert(m->phase == Phase_Render); glEnableClientState(GL_VERTEX_ARRAY); for(uint i = 0; i < m->visiblePatches.size(); ++i) { CPatchRData* patchdata = (CPatchRData*)m->visiblePatches[i]->GetRenderData(); - patchdata->RenderStreams(STREAM_POS); + patchdata->RenderStreams(STREAM_POS, true); } glDisableClientState(GL_VERTEX_ARRAY); } /////////////////////////////////////////////////////////////////// // Render outlines of submitted patches as lines void TerrainRenderer::RenderOutlines() { glEnableClientState(GL_VERTEX_ARRAY); for(uint i = 0; i < m->visiblePatches.size(); ++i) { CPatchRData* patchdata = (CPatchRData*)m->visiblePatches[i]->GetRenderData(); patchdata->RenderOutline(); } glDisableClientState(GL_VERTEX_ARRAY); } /////////////////////////////////////////////////////////////////// // Render water that is part of the terrain void TerrainRenderer::RenderWater() { PROFILE(" render water "); //Fresnel effect CCamera* Camera=g_Game->GetView()->GetCamera(); CVector3D CamFace=Camera->m_Orientation.GetIn(); CamFace.Normalize(); float FresnelScalar = CamFace.Dot( CVector3D(0.0f, -1.0f, 0.0f) ); //Invert and set boundaries FresnelScalar = (1 - FresnelScalar) * 0.4f + 0.6f; const int DX[] = {1,1,0,0}; const int DZ[] = {0,1,1,0}; CTerrain* terrain = g_Game->GetWorld()->GetTerrain(); int mapSize = terrain->GetVerticesPerSide(); CLOSManager* losMgr = g_Game->GetWorld()->GetLOSManager(); WaterManager* WaterMgr = g_Renderer.GetWaterManager(); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); glDepthMask(GL_FALSE); double time = WaterMgr->m_WaterTexTimer; double period = 1.6; int curTex = (int)(time*60/period) % 60; ogl_tex_bind(WaterMgr->m_WaterTexture[curTex], 0); glMatrixMode(GL_TEXTURE); glLoadIdentity(); float tx = -fmod(time, 20.0)/20.0; float ty = fmod(time, 35.0)/35.0; glTranslatef(tx, ty, 0); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PRIMARY_COLOR_ARB); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PRIMARY_COLOR_ARB); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA); // Set the proper LOD bias glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, g_Renderer.m_Options.m_LodBias); glBegin(GL_QUADS); for(size_t i=0; ivisiblePatches.size(); i++) { CPatch* patch = m->visiblePatches[i]; for(int dx=0; dxm_X*PATCH_SIZE + dx); int z = (patch->m_Z*PATCH_SIZE + dz); // is any corner of the tile below the water height? if not, no point rendering it bool shouldRender = false; for(int j=0; j<4; j++) { float terrainHeight = terrain->getVertexGroundLevel(x + DX[j], z + DZ[j]); if(terrainHeight < WaterMgr->m_WaterHeight) { shouldRender = true; break; } } if(!shouldRender) { continue; } for(int j=0; j<4; j++) { int ix = x + DX[j]; int iz = z + DZ[j]; float vertX = ix * CELL_SIZE; float vertZ = iz * CELL_SIZE; float terrainHeight = terrain->getVertexGroundLevel(ix, iz); float alpha = clamp( (WaterMgr->m_WaterHeight - terrainHeight) / WaterMgr->m_WaterFullDepth + WaterMgr->m_WaterAlphaOffset, -100.0f, WaterMgr->m_WaterMaxAlpha); float losMod = 1.0f; for(int k=0; k<4; k++) { int tx = ix - DX[k]; int tz = iz - DZ[k]; if(tx >= 0 && tz >= 0 && tx <= mapSize-2 && tz <= mapSize-2) { ELOSStatus s = losMgr->GetStatus(tx, tz, g_Game->GetLocalPlayer()); if(s == LOS_EXPLORED && losMod > 0.7f) losMod = 0.7f; else if(s==LOS_UNEXPLORED && losMod > 0.0f) losMod = 0.0f; } } glColor4f(WaterMgr->m_WaterColor.r*losMod, WaterMgr->m_WaterColor.g*losMod, WaterMgr->m_WaterColor.b*losMod, alpha * FresnelScalar); pglMultiTexCoord2fARB(GL_TEXTURE0, vertX/16.0f, vertZ/16.0f); glVertex3f(vertX, WaterMgr->m_WaterHeight, vertZ); } } //end of x loop } //end of z loop } glEnd(); glLoadIdentity(); glMatrixMode(GL_MODELVIEW); glDepthMask(GL_TRUE); glDisable(GL_BLEND); glDisable(GL_TEXTURE_2D); } - -/////////////////////////////////////////////////////////////////// -// ApplyShadowMap: add luminance map shadows after rendering the -// actual terrain -void TerrainRendererInternals::ApplyShadowMap(ShadowMap* shadow, const RGBAColor& shadowColor) -{ - debug_assert(phase == Phase_Render); - - const CMatrix3D& texturematrix = shadow->GetTextureMatrix(); - GLuint shadowmap = shadow->GetTexture(); - - glMatrixMode(GL_TEXTURE); - glLoadMatrixf(&texturematrix._11); - glMatrixMode(GL_MODELVIEW); - - g_Renderer.BindTexture(0, shadowmap); - if (shadow->GetUseDepthTexture()) - { - glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE, GL_LUMINANCE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_GREATER); - - glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); - glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_ONE_MINUS_SRC_COLOR); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_TEXTURE); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR); - glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA); - - glColor4fv(shadowColor); - glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR); - } - else - { - glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); - glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_REPLACE); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR); - glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA); - - glColor3f(1,1,1); - glBlendFunc(GL_DST_COLOR,GL_ZERO); - } - - glEnable(GL_BLEND); - glDepthMask(0); - - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - - for (uint i = 0; i < visiblePatches.size(); ++i) - { - CPatchRData* patchdata = (CPatchRData*)visiblePatches[i]->GetRenderData(); - patchdata->RenderStreams(STREAM_POS|STREAM_POSTOUV0); - } - - glDepthMask(1); - glDisable(GL_BLEND); - glDisableClientState(GL_VERTEX_ARRAY); - glDisableClientState(GL_TEXTURE_COORD_ARRAY); - - glMatrixMode(GL_TEXTURE); - glLoadIdentity(); - glMatrixMode(GL_MODELVIEW); -} Index: ps/trunk/source/renderer/ModelRenderer.h =================================================================== --- ps/trunk/source/renderer/ModelRenderer.h (revision 3507) +++ ps/trunk/source/renderer/ModelRenderer.h (revision 3508) @@ -1,262 +1,263 @@ /** * ========================================================================= * File : ModelRenderer.h * Project : Pyrogenesis * Description : Home to the ModelRenderer class, an abstract base class * : that manages a per-frame list of submitted models, * : as well as simple helper classes. * * @author Nicolai Hähnle * ========================================================================= */ #ifndef MODELRENDERER_H #define MODELRENDERER_H #include "boost/shared_ptr.hpp" +#include "graphics/Color.h" #include "graphics/MeshManager.h" #include "graphics/RenderableObject.h" #include "renderer/VertexArray.h" class RenderModifier; typedef boost::shared_ptr RenderModifierPtr; class ModelVertexRenderer; typedef boost::shared_ptr ModelVertexRendererPtr; class CModel; /** - * Class CModelRData: Render data that is maintained per CModel. + * Class CModelRData: Render data that is maintained per CModel. * ModelRenderer implementations may derive from this class to store * per-CModel data. - * + * * The main purpose of this class over CRenderData is to track which * ModelRenderer the render data belongs to (via the key that is passed * to the constructor). When a model changes the renderer it uses * (e.g. via run-time modification of the renderpath configuration), * the old ModelRenderer's render data is supposed to be replaced by * the new data. */ class CModelRData : public CRenderData { public: CModelRData(const void* key, CModel* model) : m_Key(key), m_Model(model) { } /** * GetKey: Retrieve the key that can be used to identify the * ModelRenderer that created this data. * * @return The opaque key that was passed to the constructor. */ const void* GetKey() const { return m_Key; } /** * GetModel: Retrieve the model that this render data object * belongs to. * * @return The model pointer that was passed to the constructor. */ CModel* GetModel() const { return m_Model; } - + private: /// The key for model renderer identification const void* m_Key; - + /// The model this object was created for CModel* m_Model; }; /** * Class ModelRenderer: Abstract base class for all model renders. - * + * * A ModelRenderer manages a per-frame list of models. - * + * * It is supposed to be derived in order to create new ways in which * the per-frame list of models can be managed (for batching, for * transparent rendering, etc.) or potentially for rarely used special * effects. - * + * * A typical ModelRenderer will delegate vertex transformation/setup * to a ModelVertexRenderer. * It will delegate fragment stage setup to a RenderModifier. - * + * * For most purposes, you should use a BatchModelRenderer with * specialized ModelVertexRenderer and RenderModifier implementations. - * + * * It is suggested that a derived class implement the provided generic * Render function, however in some cases it may be necessary to supply * a Render function with a different prototype. - * + * * ModelRenderer also contains a number of static helper functions * for building vertex arrays. */ class ModelRenderer { public: ModelRenderer() { } virtual ~ModelRenderer() { } - + /** * Submit: Submit a model for rendering this frame. * * preconditions : The model must not have been submitted to any * ModelRenderer in this frame. Submit may only be called * after EndFrame and before PrepareModels. * * @param model The model that will be added to the list of models * submitted this frame. */ virtual void Submit(CModel* model) = 0; - + /** * PrepareModels: Calculate renderer data for all previously * submitted models. * * Must be called before any rendering calls and after all models * for this frame have been submitted. */ virtual void PrepareModels() = 0; - + /** * EndFrame: Remove all models from the list of submitted * models. */ virtual void EndFrame() = 0; - + /** * HaveSubmissions: Return whether any models have been submitted this frame. * * @return true if models have been submitted, false otherwise. */ virtual bool HaveSubmissions() = 0; - + /** * Render: Render submitted models, using the given RenderModifier to setup * the fragment stage. * - * @note It is suggested that derived model renderers implement and use + * @note It is suggested that derived model renderers implement and use * this Render functions. However, a highly specialized model renderer * may need to "disable" this function and provide its own Render function * with a different prototype. * * preconditions : PrepareModels must be called after all models have been * submitted and before calling Render. * * @param modifier The RenderModifier that specifies the fragment stage. * @param flags If flags is 0, all submitted models are rendered. * If flags is non-zero, only models that contain flags in their * CModel::GetFlags() are rendered. */ virtual void Render(RenderModifierPtr modifier, u32 flags) = 0; - + /** * CopyPositionAndNormals: Copy unanimated object-space vertices and * normals into the given vertex array. * * @param mdef The underlying CModelDef that contains mesh data. * @param Position Points to the array that will receive * position vectors. The array behind the iterator * must be large enough to hold model->GetModelDef()->GetNumVertices() * vertices. * @param Normal Points to the array that will receive normal vectors. * The array behind the iterator must be as large as the Position array. */ static void CopyPositionAndNormals( CModelDefPtr mdef, VertexArrayIterator Position, VertexArrayIterator Normal); - + /** - * BuildPositionAndNormals: Build animated vertices and normals, + * BuildPositionAndNormals: Build animated vertices and normals, * transformed into world space. * * @param model The model that is to be transformed. * @param Position Points to the array that will receive * transformed position vectors. The array behind the iterator * must be large enough to hold model->GetModelDef()->GetNumVertices() * vertices. * @param Normal Points to the array that will receive transformed * normal vectors. The array behind the iterator must be as large as * the Position array. */ static void BuildPositionAndNormals( CModel* model, VertexArrayIterator Position, VertexArrayIterator Normal); - + /** * BuildColor4ub: Build lighting colors for the given model, * based on previously calculated world space normals. * * @param model The model that is to be lit. * @param Normal Array of the model's normal vectors, animated and * transformed into world space. * @param Color Points to the array that will receive the lit vertex color. * The array behind the iterator must large enough to hold * model->GetModelDef()->GetNumVertices() vertices. */ static void BuildColor4ub( CModel* model, VertexArrayIterator Normal, VertexArrayIterator Color); - + /** * BuildUV: Copy UV coordinates into the given vertex array. * * @param mdef The model def. * @param UV Points to the array that will receive UV coordinates. * The array behind the iterator must large enough to hold * mdef->GetNumVertices() vertices. */ static void BuildUV( CModelDefPtr mdef, VertexArrayIterator UV); /** * BuildIndices: Create the indices array for the given CModelDef. * * @param mdef The model definition object. - * @param Indices The index array, must be able to hold + * @param Indices The index array, must be able to hold * mdef->GetNumFaces()*3 elements. */ static void BuildIndices( CModelDefPtr mdef, u16* Indices); }; struct BatchModelRendererInternals; /** * Class BatchModelRenderer: Model renderer that sorts submitted models * by CModelDef and texture for batching, and uses a ModelVertexRenderer * (e.g. FixedFunctionModelRenderer) to manage model vertices. - * + * * @note Deriving from this class is highly discouraged. Specialize * using ModelVertexRendererPtr and RenderModifier instead. */ class BatchModelRenderer : public ModelRenderer { friend struct BatchModelRendererInternals; - + public: BatchModelRenderer(ModelVertexRendererPtr vertexrender); virtual ~BatchModelRenderer(); // Batching implementations virtual void Submit(CModel* model); virtual void PrepareModels(); virtual void EndFrame(); virtual bool HaveSubmissions(); virtual void Render(RenderModifierPtr modifier, u32 flags); private: BatchModelRendererInternals* m; }; #endif // MODELRENDERER_H Index: ps/trunk/binaries/data/mods/official/maps/scenarios/cantabrian_generated.xml =================================================================== --- ps/trunk/binaries/data/mods/official/maps/scenarios/cantabrian_generated.xml (revision 3507) +++ ps/trunk/binaries/data/mods/official/maps/scenarios/cantabrian_generated.xml (revision 3508) @@ -1,35089 +1,35089 @@ - + - - + + 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 2 2 2 2 0 0 0 0 0 0 0 0 0 0 0 0 3 3 3 3 0 0 0 0 0 0 0 0 0 0 0 0 4 4 4 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_large.xml geology/stone_granite_large.xml geology/stone_granite_med.xml geology/stone_granite_large.xml geology/stone_granite_med.xml geology/stone_granite_large.xml geology/stone_granite_large.xml geology/stone_granite_med.xml geology/stone_granite_large.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_large.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_large.xml geology/stone_granite_large.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_large.xml geology/stone_granite_large.xml geology/stone_granite_med.xml geology/stone_granite_large.xml geology/stone_granite_large.xml geology/stone_granite_med.xml geology/stone_granite_large.xml geology/stone_granite_large.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_large.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_large.xml geology/stone_granite_large.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_large.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_large.xml geology/stone_granite_large.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_large.xml geology/stone_granite_med.xml geology/stone_granite_large.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_large.xml geology/stone_granite_med.xml geology/stone_granite_large.xml geology/stone_granite_large.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_large.xml geology/stone_granite_large.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_large.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_med.xml geology/stone_granite_large.xml geology/stone_granite_large.xml geology/stone_granite_med.xml geology/stone_granite_large.xml geology/stone_granite_large.xml geology/stone_granite_med.xml geology/stone_granite_large.xml geology/stone_granite_large.xml geology/stone_granite_med.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_tall.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/grass_field_lush_short.xml props/flora/bush_tempe_sm.xml props/flora/bush_tempe_sm.xml props/flora/bush_tempe_sm.xml props/flora/bush_tempe_sm.xml props/flora/bush_tempe_sm.xml props/flora/bush_tempe_sm.xml props/flora/bush_tempe_sm.xml props/flora/bush_tempe_sm.xml props/flora/bush_tempe_sm.xml props/flora/bush_tempe_sm.xml props/flora/bush_tempe_sm.xml props/flora/bush_tempe_sm.xml props/flora/bush_tempe_sm.xml props/flora/bush_tempe_sm.xml props/flora/bush_tempe_sm.xml props/flora/bush_tempe_sm.xml props/flora/bush_tempe_sm.xml props/flora/bush_tempe_sm.xml props/flora/bush_tempe_sm.xml props/flora/bush_tempe_sm.xml props/flora/bush_tempe_sm.xml props/flora/bush_tempe_sm.xml props/flora/bush_tempe_sm.xml props/flora/bush_tempe_sm.xml props/flora/bush_tempe_sm.xml props/flora/bush_tempe_sm.xml props/flora/bush_tempe_sm.xml props/flora/bush_tempe_sm.xml props/flora/bush_tempe_sm.xml props/flora/bush_tempe_sm.xml props/flora/bush_tempe_sm.xml props/flora/bush_tempe_sm.xml props/flora/bush_tempe_sm.xml props/flora/bush_tempe_sm.xml props/flora/bush_tempe_sm.xml props/flora/bush_tempe_sm.xml props/flora/bush_tempe_sm.xml props/flora/bush_tempe_sm.xml props/flora/bush_tempe_sm.xml props/flora/bush_tempe_sm.xml props/flora/bush_tempe_sm.xml props/flora/bush_tempe_sm.xml props/flora/bush_tempe_sm.xml props/flora/bush_tempe_sm.xml props/flora/bush_tempe_sm.xml props/flora/bush_tempe_sm.xml props/flora/bush_tempe_sm.xml props/flora/bush_tempe_sm.xml props/flora/bush_tempe_sm.xml props/flora/bush_tempe_sm.xml props/flora/bush_tempe_sm.xml props/flora/bush_tempe_sm.xml props/flora/bush_tempe_sm.xml props/flora/bush_tempe_sm.xml props/flora/bush_tempe_sm.xml props/flora/bush_tempe_sm.xml props/flora/bush_tempe_sm.xml props/flora/bush_tempe_sm.xml props/flora/bush_tempe_sm.xml props/flora/bush_tempe_sm.xml props/flora/bush_tempe_sm.xml props/flora/bush_tempe_sm.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml props/flora/reeds_pond_dry.xml Index: ps/trunk/binaries/data/mods/official/shaders/globallight.vs =================================================================== --- ps/trunk/binaries/data/mods/official/shaders/globallight.vs (nonexistent) +++ ps/trunk/binaries/data/mods/official/shaders/globallight.vs (revision 3508) @@ -0,0 +1,11 @@ +uniform vec3 ambient; +uniform vec3 sunDir; +uniform vec3 sunColor; + +vec3 lighting(vec3 normal) +{ + vec3 color = ambient; + float d = max(0.0, -dot(normal, sunDir)); + color += d * sunColor; + return color; +} Index: ps/trunk/binaries/data/mods/official/shaders/instancing_light.xml =================================================================== --- ps/trunk/binaries/data/mods/official/shaders/instancing_light.xml (revision 3507) +++ ps/trunk/binaries/data/mods/official/shaders/instancing_light.xml (revision 3508) @@ -1,9 +1,9 @@ shaders/instancing_light.vs shaders/instancing_base.vs - shaders/shlight.vs + shaders/globallight.vs Index: ps/trunk/binaries/data/mods/official/shaders/model_light.xml =================================================================== --- ps/trunk/binaries/data/mods/official/shaders/model_light.xml (revision 3507) +++ ps/trunk/binaries/data/mods/official/shaders/model_light.xml (revision 3508) @@ -1,8 +1,8 @@ shaders/model_light.vs - shaders/shlight.vs + shaders/globallight.vs