Example #1
0
///////////////////////////////////////////////////////////////////
// Scissor rectangle of water patches
CBoundingBoxAligned TerrainRenderer::ScissorWater(const CMatrix3D &viewproj)
{
	CBoundingBoxAligned scissor;
	for (size_t i = 0; i < m->visiblePatches.size(); ++i)
	{
		CPatchRData* data = m->visiblePatches[i];
		const CBoundingBoxAligned& waterBounds = data->GetWaterBounds();
		if (waterBounds.IsEmpty())
			continue;

		CVector4D v1 = viewproj.Transform(CVector4D(waterBounds[0].X, waterBounds[1].Y, waterBounds[0].Z, 1.0f));
		CVector4D v2 = viewproj.Transform(CVector4D(waterBounds[1].X, waterBounds[1].Y, waterBounds[0].Z, 1.0f));
		CVector4D v3 = viewproj.Transform(CVector4D(waterBounds[0].X, waterBounds[1].Y, waterBounds[1].Z, 1.0f));
		CVector4D v4 = viewproj.Transform(CVector4D(waterBounds[1].X, waterBounds[1].Y, waterBounds[1].Z, 1.0f));
		CBoundingBoxAligned screenBounds;
		#define ADDBOUND(v1, v2, v3, v4) \
			if (v1[2] >= -v1[3]) \
				screenBounds += CVector3D(v1[0], v1[1], v1[2]) * (1.0f / v1[3]); \
			else \
			{ \
				float t = v1[2] + v1[3]; \
				if (v2[2] > -v2[3]) \
				{ \
					CVector4D c2 = v1 + (v2 - v1) * (t / (t - (v2[2] + v2[3]))); \
					screenBounds += CVector3D(c2[0], c2[1], c2[2]) * (1.0f / c2[3]); \
				} \
				if (v3[2] > -v3[3]) \
				{ \
					CVector4D c3 = v1 + (v3 - v1) * (t / (t - (v3[2] + v3[3]))); \
					screenBounds += CVector3D(c3[0], c3[1], c3[2]) * (1.0f / c3[3]); \
				} \
				if (v4[2] > -v4[3]) \
				{ \
					CVector4D c4 = v1 + (v4 - v1) * (t / (t - (v4[2] + v4[3]))); \
					screenBounds += CVector3D(c4[0], c4[1], c4[2]) * (1.0f / c4[3]); \
				} \
			}
		ADDBOUND(v1, v2, v3, v4);
		ADDBOUND(v2, v1, v3, v4);
		ADDBOUND(v3, v1, v2, v4);
		ADDBOUND(v4, v1, v2, v3);
		#undef ADDBOUND
		if (screenBounds[0].X >= 1.0f || screenBounds[1].X <= -1.0f || screenBounds[0].Y >= 1.0f || screenBounds[1].Y <= -1.0f)
			continue;
		scissor += screenBounds;
	}
	return CBoundingBoxAligned(CVector3D(clamp(scissor[0].X, -1.0f, 1.0f), clamp(scissor[0].Y, -1.0f, 1.0f), -1.0f),
				  CVector3D(clamp(scissor[1].X, -1.0f, 1.0f), clamp(scissor[1].Y, -1.0f, 1.0f), 1.0f));
}
Example #2
0
void Utility::ScreenToWorld(CVector3D *out,CVector3D spos,CMatrix mProj,CMatrix mView,int w,int h){
	CMatrix	mVP;
	mVP.Viewport(0.0f,0.0f, static_cast<float>(w), static_cast<float>(h));
	CVector4D o = (mView.getInverse() * mProj.getInverse() * mVP.getInverse()) * CVector4D(spos.x,spos.y,spos.z,1);
	*out = CVector3D(o.x/o.w,o.y/o.w,o.z/o.w);
	
}
Example #3
0
void Utility::WorldToScreen(CVector3D *out,CVector3D spos,CMatrix mProj,CMatrix mView,int w,int h) {
	CMatrix	mVP;
	mVP.Viewport(0.0f, 0.0f, static_cast<float>(w), static_cast<float>(h));
	CVector4D o = mVP * mProj * mView * CVector4D(spos.x,spos.y,spos.z,1);

	*out =  CVector3D(o.x/o.w,o.y/o.w,1);

}
Example #4
0
void COBB::Transeform(CMatrix &mtx,bool trans) {
	for (int i=0;i<3;i++) {
		m_axis[i] = mtx * m_axis[i];
	}
	if(trans) {
		CVector4D v = CVector4D(m_base_center.x,m_base_center.y,m_base_center.z,1.0);
		v = mtx * v;
		m_center = CVector3D(v.x,v.y,v.z);
	}
	m_mat = mtx;
}
Example #5
0
void CCamera::GetScreenCoordinates(const CVector3D& world, float& x, float& y) const
{
	CMatrix3D transform = m_ProjMat * m_Orientation.GetInverse();

	CVector4D screenspace = transform.Transform(CVector4D(world.X, world.Y, world.Z, 1.0f));

	x = screenspace.m_X / screenspace.m_W;
	y = screenspace.m_Y / screenspace.m_W;
	x = (x + 1) * 0.5f * g_Renderer.GetWidth();
	y = (1 - y) * 0.5f * g_Renderer.GetHeight();
}
Example #6
0
void 
CMagic120Cell::fillHypercubeMap() 
{
	// Generate the 8 cube face centers.
	// The last rotation is required because of how we initially orient our 120-cell.
	double angle = -dihedral/2;
	CVector4D faces[8];
	faces[0] = CVector4D(  standardFaceOffset, 0, 0, 0 ); faces[0].rotate( 0, 2, angle );
	faces[1] = CVector4D( -standardFaceOffset, 0, 0, 0 ); faces[1].rotate( 0, 2, angle );
	faces[2] = CVector4D( 0,  standardFaceOffset, 0, 0 ); faces[2].rotate( 0, 2, angle );
	faces[3] = CVector4D( 0, -standardFaceOffset, 0, 0 ); faces[3].rotate( 0, 2, angle );
	faces[4] = CVector4D( 0, 0,  standardFaceOffset, 0 ); faces[4].rotate( 0, 2, angle );
	faces[5] = CVector4D( 0, 0, -standardFaceOffset, 0 ); faces[5].rotate( 0, 2, angle );
	faces[6] = CVector4D( 0, 0, 0,  standardFaceOffset ); faces[6].rotate( 0, 2, angle );
	faces[7] = CVector4D( 0, 0, 0, -standardFaceOffset ); faces[7].rotate( 0, 2, angle );

	int count = 0;
	for( int f=0; f<8; f++ )
	{
		for( int i=0; i<m_nCells; i++ )
		{
			double magSquared = ( m_cells[i].getCenter() - faces[f] ).magSquared();
			if( magSquared < 7 )
			{
				m_hypercubeMap.insert( int_pair( i, f ) );
				count++;
			}
		}
	}
	assert( count == m_nCells - 16 );

	// There are 16 left.  Put them in the last layer.
	for( int i=0; i<m_nCells; i++ )
	{
		std::map<__int8,__int8>::iterator map_iterator;
		map_iterator = m_hypercubeMap.find( i );
		if( map_iterator == m_hypercubeMap.end() )
			m_hypercubeMap.insert( int_pair( i, 8 ) );
	}
}
Example #7
0
int CLuaVector4Defs::Unm ( lua_State* luaVM )
{
    CLuaVector4D* pVector = NULL;

    CScriptArgReader argStream ( luaVM );
    argStream.ReadUserData ( pVector );

    if ( !argStream.HasErrors () )
    {
        lua_pushvector ( luaVM, CVector4D () - *pVector );
        return 1;
    }
    else
    {
        m_pScriptDebugging->LogCustom ( luaVM, argStream.GetFullErrorMessage() );
    }

    lua_pushboolean ( luaVM, false );
    return 1;
}
Example #8
0
void 
CMagic120Cell::generate4Cube() 
{
	// XXX - Clean up this code and make it shorter.
	// WOW! golden ratio shows up everywhere.
	const double c = standardFaceOffset /* * m_settings.m_cellDistance*/ / golden;

	m_hypercubePoints[0] = CVector4D(  -c, c, -c, c );
	m_hypercubePoints[1] = CVector4D(  -c, c, c, c );
	m_hypercubePoints[2] = CVector4D(  c, c, c, c );
	m_hypercubePoints[3] = CVector4D(  c, c, -c, c );

	m_hypercubePoints[4] = CVector4D(  -c, -c, -c, c );
	m_hypercubePoints[5] = CVector4D(  -c, -c, c, c );
	m_hypercubePoints[6] = CVector4D(  c, -c, c, c );
	m_hypercubePoints[7] = CVector4D(  c, -c, -c, c );

	m_hypercubePoints[8] = CVector4D(  -c, c, -c, -c );
	m_hypercubePoints[9] = CVector4D(  -c, c, c, -c );
	m_hypercubePoints[10] = CVector4D(  c, c, c, -c );
	m_hypercubePoints[11] = CVector4D(  c, c, -c, -c );

	m_hypercubePoints[12] = CVector4D(  -c, -c, -c, -c );
	m_hypercubePoints[13] = CVector4D(  -c, -c, c, -c );
	m_hypercubePoints[14] = CVector4D(  c, -c, c, -c );
	m_hypercubePoints[15] = CVector4D(  c, -c, -c, -c );

	// Same as 4 above but switch x/y and z/w values
	m_hypercubePoints[16] = CVector4D(  c, -c, c, -c );
	m_hypercubePoints[17] = CVector4D(  c, -c, c, c );
	m_hypercubePoints[18] = CVector4D(  c, c, c, c );
	m_hypercubePoints[19] = CVector4D(  c, c, c, -c );

	m_hypercubePoints[20] = CVector4D(  -c, -c, c, -c );
	m_hypercubePoints[21] = CVector4D(  -c, -c, c, c );
	m_hypercubePoints[22] = CVector4D(  -c, c, c, c );
	m_hypercubePoints[23] = CVector4D(  -c, c, c, -c );

	m_hypercubePoints[24] = CVector4D(  c, -c, -c, -c );
	m_hypercubePoints[25] = CVector4D(  c, -c, -c, c );
	m_hypercubePoints[26] = CVector4D(  c, c, -c, c );
	m_hypercubePoints[27] = CVector4D(  c, c, -c, -c );

	m_hypercubePoints[28] = CVector4D(  -c, -c, -c, -c );
	m_hypercubePoints[29] = CVector4D(  -c, -c, -c, c );
	m_hypercubePoints[30] = CVector4D(  -c, c, -c, c );
	m_hypercubePoints[31] = CVector4D(  -c, c, -c, -c );

	double angle = -dihedral/2;
	for( int i=0; i<32; i++ )
		m_hypercubePoints[i].rotate( 0, 2, angle );

	for( int i=0; i<32; i++ )
		m_hypercubePointsCopy[i] = m_hypercubePoints[i];
}
IModelDef::IModelDef(const CModelDefPtr& mdef, bool gpuSkinning, bool calculateTangents)
	: m_IndexArray(GL_STATIC_DRAW), m_Array(GL_STATIC_DRAW)
{
	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_UVs.resize(mdef->GetNumUVsPerVertex());
	for (size_t i = 0; i < mdef->GetNumUVsPerVertex(); i++)
	{
		m_UVs[i].type = GL_FLOAT;
		m_UVs[i].elems = 2;
		m_Array.AddAttribute(&m_UVs[i]);
	}
	
	if (gpuSkinning)
	{
		m_BlendJoints.type = GL_UNSIGNED_BYTE;
		m_BlendJoints.elems = 4;
		m_Array.AddAttribute(&m_BlendJoints);

		m_BlendWeights.type = GL_UNSIGNED_BYTE;
		m_BlendWeights.elems = 4;
		m_Array.AddAttribute(&m_BlendWeights);
	}
	
	if (calculateTangents)
	{
		// Generate tangents for the geometry:-
		
		m_Tangent.type = GL_FLOAT;
		m_Tangent.elems = 4;
		m_Array.AddAttribute(&m_Tangent);
		
		// floats per vertex; position + normal + tangent + UV*sets [+ GPUskinning]
		int numVertexAttrs = 3 + 3 + 4 + 2 * mdef->GetNumUVsPerVertex();
		if (gpuSkinning)
		{
			numVertexAttrs += 8;
		}
		
		// the tangent generation can increase the number of vertices temporarily
		// so reserve a bit more memory to avoid reallocations in GenTangents (in most cases)
		std::vector<float> newVertices;
		newVertices.reserve(numVertexAttrs * numVertices * 2);
		
		// Generate the tangents
		ModelRenderer::GenTangents(mdef, newVertices, gpuSkinning);
		
		// how many vertices do we have after generating tangents?
		int newNumVert = newVertices.size() / numVertexAttrs;
		
		std::vector<int> remapTable(newNumVert);
		std::vector<float> vertexDataOut(newNumVert * numVertexAttrs);

		// re-weld the mesh to remove duplicated vertices
		int numVertices2 = WeldMesh(&remapTable[0], &vertexDataOut[0],
					&newVertices[0], newNumVert, numVertexAttrs);

		// Copy the model data to graphics memory:-
		
		m_Array.SetNumVertices(numVertices2);
		m_Array.Layout();

		VertexArrayIterator<CVector3D> Position = m_Position.GetIterator<CVector3D>();
		VertexArrayIterator<CVector3D> Normal = m_Normal.GetIterator<CVector3D>();
		VertexArrayIterator<CVector4D> Tangent = m_Tangent.GetIterator<CVector4D>();
		
		VertexArrayIterator<u8[4]> BlendJoints;
		VertexArrayIterator<u8[4]> BlendWeights;
		if (gpuSkinning)
		{
			BlendJoints = m_BlendJoints.GetIterator<u8[4]>();
			BlendWeights = m_BlendWeights.GetIterator<u8[4]>();
		}
		
		// copy everything into the vertex array
		for (int i = 0; i < numVertices2; i++)
		{
			int q = numVertexAttrs * i;
			
			Position[i] = CVector3D(vertexDataOut[q + 0], vertexDataOut[q + 1], vertexDataOut[q + 2]);
			q += 3;
			
			Normal[i] = CVector3D(vertexDataOut[q + 0], vertexDataOut[q + 1], vertexDataOut[q + 2]);
			q += 3;

			Tangent[i] = CVector4D(vertexDataOut[q + 0], vertexDataOut[q + 1], vertexDataOut[q + 2], 
					vertexDataOut[q + 3]);
			q += 4;
			
			if (gpuSkinning)
			{
				for (size_t j = 0; j < 4; ++j)
				{
					BlendJoints[i][j] = (u8)vertexDataOut[q + 0 + 2 * j];
					BlendWeights[i][j] = (u8)vertexDataOut[q + 1 + 2 * j];
				}
				q += 8;
			}
			
			for (size_t j = 0; j < mdef->GetNumUVsPerVertex(); j++)
			{
				VertexArrayIterator<float[2]> UVit = m_UVs[j].GetIterator<float[2]>();
				UVit[i][0] = vertexDataOut[q + 0 + 2 * j];
				UVit[i][1] = vertexDataOut[q + 1 + 2 * j];
			}
		}

		// upload vertex data
		m_Array.Upload();
		m_Array.FreeBackingStore();

		m_IndexArray.SetNumVertices(mdef->GetNumFaces() * 3);
		m_IndexArray.Layout();
		
		VertexArrayIterator<u16> Indices = m_IndexArray.GetIterator();
		
		size_t idxidx = 0;	

		// reindex geometry and upload index
		for (size_t j = 0; j < mdef->GetNumFaces(); ++j) 
		{	
			Indices[idxidx++] = remapTable[j * 3 + 0];
			Indices[idxidx++] = remapTable[j * 3 + 1];
			Indices[idxidx++] = remapTable[j * 3 + 2];
		}
		
		m_IndexArray.Upload();
		m_IndexArray.FreeBackingStore();
	}
	else
	{
		// Upload model without calculating tangents:-
		
		m_Array.SetNumVertices(numVertices);
		m_Array.Layout();

		VertexArrayIterator<CVector3D> Position = m_Position.GetIterator<CVector3D>();
		VertexArrayIterator<CVector3D> Normal = m_Normal.GetIterator<CVector3D>();

		ModelRenderer::CopyPositionAndNormals(mdef, Position, Normal);

		for (size_t i = 0; i < mdef->GetNumUVsPerVertex(); i++)
		{
			VertexArrayIterator<float[2]> UVit = m_UVs[i].GetIterator<float[2]>();
			ModelRenderer::BuildUV(mdef, UVit, i);
		}

		if (gpuSkinning)
		{
			VertexArrayIterator<u8[4]> BlendJoints = m_BlendJoints.GetIterator<u8[4]>();
			VertexArrayIterator<u8[4]> BlendWeights = m_BlendWeights.GetIterator<u8[4]>();
			for (size_t i = 0; i < numVertices; ++i)
			{
				const SModelVertex& vtx = mdef->GetVertices()[i];
				for (size_t j = 0; j < 4; ++j)
				{
					BlendJoints[i][j] = vtx.m_Blend.m_Bone[j];
					BlendWeights[i][j] = (u8)(255.f * vtx.m_Blend.m_Weight[j]);
				}
			}
		}

		m_Array.Upload();
		m_Array.FreeBackingStore();

		m_IndexArray.SetNumVertices(mdef->GetNumFaces()*3);
		m_IndexArray.Layout();
		ModelRenderer::BuildIndices(mdef, m_IndexArray.GetIterator());
		m_IndexArray.Upload();
		m_IndexArray.FreeBackingStore();
	}
}
Example #10
0
void CCinemaPath::Draw() const
{
	DrawSpline(*this, CVector4D(0.2f, 0.2f, 1.f, 0.5f), 100, true);
	DrawSpline(m_TargetSpline, CVector4D(1.0f, 0.2f, 0.2f, 0.5f), 100, true);
	DrawNodes(CVector4D(0.5f, 1.0f, 0.f, 0.5f));
}
Example #11
0
		/**
		 * Vector division by a scalar.
		 */
		template<typename TType> friend CVector4D operator/ (const CVector4D& cVector, TType tScalar)
		{
			return CVector4D(cVector) / tScalar;
		}
Example #12
0
		/**
		 * Vector multiplication by a scalar.
		 */
		template<typename TType> friend CVector4D operator* (TType tScalar, const CVector4D& cVector)
		{
			return CVector4D(cVector) * tScalar;
		}
Example #13
0
// Build vertex buffer for water vertices over our patch
void CPatchRData::BuildWater()
{
	PROFILE3("build water");

	// number of vertices in each direction in each patch
	ENSURE((PATCH_SIZE % water_cell_size) == 0);

	if (m_VBWater)
	{
		g_VBMan.Release(m_VBWater);
		m_VBWater = 0;
	}
	if (m_VBWaterIndices)
	{
		g_VBMan.Release(m_VBWaterIndices);
		m_VBWaterIndices = 0;
	}
	m_WaterBounds.SetEmpty();

	// We need to use this to access the water manager or we may not have the
	// actual values but some compiled-in defaults
	CmpPtr<ICmpWaterManager> cmpWaterManager(*m_Simulation, SYSTEM_ENTITY);
	if (!cmpWaterManager)
		return;
	
	// Build data for water
	std::vector<SWaterVertex> water_vertex_data;
	std::vector<GLushort> water_indices;
	u16 water_index_map[PATCH_SIZE+1][PATCH_SIZE+1];
	memset(water_index_map, 0xFF, sizeof(water_index_map));

	// TODO: This is not (yet) exported via the ICmp interface so... we stick to these values which can be compiled in defaults
	WaterManager* WaterMgr = g_Renderer.GetWaterManager();

	if (WaterMgr->m_NeedsFullReloading && !g_AtlasGameLoop->running)
	{
		WaterMgr->m_NeedsFullReloading = false;
		WaterMgr->CreateSuperfancyInfo(m_Simulation);
	}
	CPatch* patch = m_Patch;
	CTerrain* terrain = patch->m_Parent;

	ssize_t mapSize = (size_t)terrain->GetVerticesPerSide();

	ssize_t x1 = m_Patch->m_X*PATCH_SIZE;
	ssize_t z1 = m_Patch->m_Z*PATCH_SIZE;

	// build vertices, uv, and shader varying
	for (ssize_t z = 0; z < PATCH_SIZE; z += water_cell_size)
	{
		for (ssize_t x = 0; x <= PATCH_SIZE; x += water_cell_size)
		{
			// Check that the edge at x is partially underwater
			float startTerrainHeight[2] = { terrain->GetVertexGroundLevel(x+x1, z+z1), terrain->GetVertexGroundLevel(x+x1, z+z1 + water_cell_size) };
			float startWaterHeight[2] = { cmpWaterManager->GetExactWaterLevel(x+x1, z+z1), cmpWaterManager->GetExactWaterLevel(x+x1, z+z1 + water_cell_size) };
			if (startTerrainHeight[0] >= startWaterHeight[0] && startTerrainHeight[1] >= startWaterHeight[1])
				continue;

			// Move x back one cell (unless at start of patch), then scan rightwards
			bool belowWater = true;
			ssize_t stripStart;
			for (stripStart = x = std::max(x-water_cell_size, (ssize_t)0); x <= PATCH_SIZE; x += water_cell_size)
			{
				// If this edge is not underwater, and neither is the previous edge
				// (i.e. belowWater == false), then stop this strip since we've reached
				// a cell that's entirely above water
				float terrainHeight[2] = { terrain->GetVertexGroundLevel(x+x1, z+z1), terrain->GetVertexGroundLevel(x+x1, z+z1 + water_cell_size) };
				float waterHeight[2] = { cmpWaterManager->GetExactWaterLevel(x+x1, z+z1), cmpWaterManager->GetExactWaterLevel(x+x1, z+z1 + water_cell_size) };
				if (terrainHeight[0] >= waterHeight[0] && terrainHeight[1] >= waterHeight[1])
				{
					if (!belowWater)
						break;
					belowWater = false;
				}
				else
					belowWater = true;

				// Edge (x,z)-(x,z+1) is at least partially underwater, so extend the water plane strip across it

				// Compute vertex data for the 2 points on the edge
				for (int j = 0; j < 2; j++)
				{
					// Check if we already computed this vertex from an earlier strip
					if (water_index_map[z+j*water_cell_size][x] != 0xFFFF)
						continue;

					SWaterVertex vertex;

					terrain->CalcPosition(x+x1, z+z1 + j*water_cell_size, vertex.m_Position);
					float depth = waterHeight[j] - vertex.m_Position.Y;
					vertex.m_Position.Y = waterHeight[j];
					m_WaterBounds += vertex.m_Position;

					// NB: Usually this factor is view dependent, but for performance reasons
					// we do not take it into account with basic non-shader based water.
					// Average constant Fresnel effect for non-fancy water
					float alpha = clamp(depth / WaterMgr->m_WaterFullDepth + WaterMgr->m_WaterAlphaOffset, WaterMgr->m_WaterAlphaOffset, WaterMgr->m_WaterMaxAlpha);

					// Split the depth data across 24 bits, so the fancy-water shader can reconstruct
					// the depth value while the simple-water can just use the precomputed alpha
					float depthInt = floor(depth);
					float depthFrac = depth - depthInt;
					vertex.m_DepthData = SColor4ub(
						u8(clamp(depthInt, 0.0f, 255.0f)),
						u8(clamp(-depthInt, 0.0f, 255.0f)),
						u8(clamp(depthFrac*255.0f, 0.0f, 255.0f)),
						u8(clamp(alpha*255.0f, 0.0f, 255.0f)));

					int tx = x+x1;
					int ty = z+z1 + j*water_cell_size;

					if (g_AtlasGameLoop->running)
					{
						// currently no foam is used so push whatever
						vertex.m_WaterData = CVector4D(0.0f,0.0f,0.0f,0.0f);
					}
					else
					{
						vertex.m_WaterData = CVector4D(WaterMgr->m_WaveX[tx + ty*mapSize],
												   WaterMgr->m_WaveZ[tx + ty*mapSize],
												   WaterMgr->m_DistanceToShore[tx + ty*mapSize],
												   WaterMgr->m_FoamFactor[tx + ty*mapSize]);
					}
					water_index_map[z+j*water_cell_size][x] = water_vertex_data.size();
					water_vertex_data.push_back(vertex);
				}

				// If this was not the first x in the strip, then add a quad
				// using the computed vertex data

				if (x <= stripStart)
					continue;

				water_indices.push_back(water_index_map[z + water_cell_size][x - water_cell_size]);
				water_indices.push_back(water_index_map[z][x - water_cell_size]);
				water_indices.push_back(water_index_map[z][x]);
				water_indices.push_back(water_index_map[z + water_cell_size][x]);
			}
		}
	}

	// no vertex buffers if no data generated
	if (water_indices.size() == 0)
		return;

	// allocate vertex buffer
	m_VBWater = g_VBMan.Allocate(sizeof(SWaterVertex), water_vertex_data.size(), GL_STATIC_DRAW, GL_ARRAY_BUFFER);
	m_VBWater->m_Owner->UpdateChunkVertices(m_VBWater, &water_vertex_data[0]);

	// Construct indices buffer
	m_VBWaterIndices = g_VBMan.Allocate(sizeof(GLushort), water_indices.size(), GL_STATIC_DRAW, GL_ELEMENT_ARRAY_BUFFER);
	m_VBWaterIndices->m_Owner->UpdateChunkVertices(m_VBWaterIndices, &water_indices[0]);
}