As santman says, why not just move all the grass into a single mesh? Theres no need for multiple meshes and 10,000 draw calls just to draw grass.
This code creates 10,000 grass tufts (each is 4 triangles, like yours) and it builds the object from scratch in under a second in AGK
Its also pretty quick to draw on my colleagues 'work' pc which doesnt even have a graphics card. Using a single mesh is quick to draw.
// Project: Stupidly simple addition of triangles to a memblock to show grass
// Created: 2018-09-04
// show all errors
SetErrorMode(2)
// set window properties
SetWindowTitle( "Simple Memblock adding of grass triangles" )
SetWindowSize( 1024, 768, 0 )
SetWindowAllowResize( 1 ) // allow the user to resize the window
// set display properties
SetVirtualResolution( 1024, 768 ) // doesn't have to match the window
SetOrientationAllowed( 1, 1, 1, 1 ) // allow both portrait and landscape on mobile devices
//SetSyncRate( 400, 0 ) // 30fps instead of 60 to save battery
SetVSync(1)
SetScissor( 0,0,0,0 ) // use the maximum available screen space, no black borders
UseNewDefaultFonts( 1 ) // since version 2.0.22 we can use nicer default fonts
SetGenerateMipmaps(1)
// Load an image to put on the grass
img = loadimage("grass.png")
// Create a simple ground plane
ground = CreateObjectPlane(100,100)
RotateObjectGlobalX(ground,90)
SetObjectColor(ground,0,80,0,255)
NumGrasses = 10000 // This is how many tufts of grass you want
// Create the memblock
id = CreateMemblockEx(4*NumGrasses)
// Add some grass to it at random locations and rotations
for i=1 to numgrasses
x# = random2(0,10000)/100.0-50
y# = 0
z# = random2(0,10000)/100.0-50
rot# = random2(0,360)
AddGrassToMemblock(id,x#,y#,z#,rot#)
next i
//Now Convert the memblock to an object
grass = CreateObjectFromMeshMemblock(id)
// Delete the memblock as we are finished with it
DeleteMemblock(id)
// Assign a texture to the grass
SetObjectImage(grass,img,0)
//SetObjectTransparency(grass,1) // make it see through
SetObjectAlphaMask(grass,1)
SetObjectCullMode(grass,0) // Draw the front and back of the grass
do
Print("This is: " + str(NumGrasses) + " grass meshes")
Print("That is: " + str(NumGrasses*4) + " triangles with transparency and double sided")
Print("FPS: "+ str( ScreenFPS()) )
// Slowly rotate the camera
SetCameraPosition(1,60*sin(Timer()*10),20,60*cos(Timer()*10))
SetCameraLookAt(1,0,0,0,0)
// update the screen
Sync()
loop
// Creates a memblock to hold the given number of triangles
function CreateMemblockEx(NumTriangles as integer)
global MB_VertexCount
global MB_IndexCount
// Create the basics
NumVertices = 3*Numtriangles
NumIndices = 0
NumAttributes = 3
VertexSize = 32 // Position, UV and Normal 8x4
AttributesSize = 36
VertexOffset = 24 + AttributesSize
IndexOffset = VertexOffset + (NumVertices*VertexSize)
TotalSize = IndexOffset + (NumIndices*4)
id = CreateMemblock(TotalSize)
// Set the values
SetMemblockInt(id,0,NumVertices)
SetMemblockInt(id,4,NumIndices)
SetMemblockInt(id,8,NumAttributes)
SetMemblockInt(id,12,VertexSize)
SetMemblockInt(id,16,VertexOffset)
SetMemblockInt(id,20,IndexOffset)
// Set the attribute Data
// VERTEX ATTRIBUTES
SetMemblockByte( id, 24, 0 ) // float
SetMemblockByte( id, 25, 3 ) // component count
SetMemblockByte( id, 26, 0 ) // normalize
SetMemblockByte( id, 27, 12 ) // string length
SetMemblockString( id, 28, "position" ) // 12 bytes
// NORMALS ATTRIBUTES
SetMemblockByte( id, 40, 0 ) // float
SetMemblockByte( id, 41, 3 ) // component count
SetMemblockByte( id, 42, 0 ) // normalize
SetMemblockByte( id, 43, 8 ) // string length
SetMemblockString( id, 44, "normal" )
// UV ATTRIBUTES
SetMemblockByte( id, 52, 0 ) // float
SetMemblockByte( id, 53, 2 ) // component count
SetMemblockByte( id, 54, 0 ) // normalize
SetMemblockByte( id, 55, 4 ) // string length
SetMemblockString( id, 56, "uv" )
// Vertex data begins at offset 60
MB_VertexCount = 0
MB_IndexCount = 0
endfunction id
// This adds 4 triangles to a memblock to make a tuft of grass
function AddGrassToMemblock(blk as integer,x#,y#,z#,rot#)
// Draw a pair of triangles
xd# = cos(rot#)
zd# = sin(rot#)
// Add 2 triangles
AddVertexToMemBlock(blk, x#-xd#,y#,z#-zd#,-zd#,0,xd#,0,1)
AddVertexToMemBlock(blk, x#+xd#,y#,z#+zd#,-zd#,0,xd#,1,1)
AddVertexToMemBlock(blk, x#+xd#,y#+2,z#+zd#,-zd#,0,xd#,1,0)
AddVertexToMemBlock(blk, x#-xd#,y#,z#-zd#,-zd#,0,xd#,0,1)
AddVertexToMemBlock(blk, x#+xd#,y#+2,z#+zd#,-zd#,0,xd#,1,0)
AddVertexToMemBlock(blk, x#-xd#,y#+2,z#-zd#,-zd#,0,xd#,0,0)
// Draw another pair at 90 to them
xd# = cos(rot#+90)
zd# = sin(rot#+90)
// Add 2 triangles
AddVertexToMemBlock(blk, x#-xd#,y#,z#-zd#,-zd#,0,xd#,0,1)
AddVertexToMemBlock(blk, x#+xd#,y#,z#+zd#,-zd#,0,xd#,1,1)
AddVertexToMemBlock(blk, x#+xd#,y#+2,z#+zd#,-zd#,0,xd#,1,0)
AddVertexToMemBlock(blk, x#-xd#,y#,z#-zd#,-zd#,0,xd#,0,1)
AddVertexToMemBlock(blk, x#+xd#,y#+2,z#+zd#,-zd#,0,xd#,1,0)
AddVertexToMemBlock(blk, x#-xd#,y#+2,z#-zd#,-zd#,0,xd#,0,0)
endfunction
// Adds a non indexed vertex to the memblock
function AddVertexToMemBlock(blk as integer,x#,y#,z#,nx#,ny#,nz#,u#,v#)
//GetCurrentOffset
offset = GetMemblockInt(blk,16) + (GetMemblockInt(blk,12)*MB_VertexCount)
// Add a vertex
SetMemblockFloat(blk,offset,x#-0.5)
SetMemblockFloat(blk,offset+4,y#)
SetMemblockFloat(blk,offset+8,z#-0.5)
SetMemblockFloat(blk,offset+12,nx#) // normal
SetMemblockFloat(blk,offset+16,ny#)
SetMemblockFloat(blk,offset+20,nz#)
SetMemblockFloat(blk,offset+24,u#)
SetMemblockFloat(blk,offset+28,v#)
inc MB_VertexCount,1
endfunction
There are very few reasons to use multiple meshes in an object unless
1) You want it to have a different texture on that mesh (even so you should be texture atlasing)
2) You want to use a different shader on it
3) You are doing it for culling reasons or plan to change one section of a very large mesh and dont want to eat bus bandwidth.