3D Forest

As an extension of my 3D town, I’ve created a 3D forest with 200 trees created from 7 distinct tree images placed in random positions and with random sizes. Codea handles the animation pretty well

http://www.youtube.com/watch?v=hL3mkG1RxeY

You should be wondering why, as I walk around the forest, you never see the images side on, because, after all, they are just flat 2D images. The answer is that I rotate the images - all 200 of them - so they are always facing the viewer.

There is another problem with 3D rendering, which is that if you draw objects in front before those behind, you get bad results. You should always try to draw objects in order, from furthest to nearest. So what I have done is sort the table holding the trees, in order of distance from the viewer. I only do this once a second, so as not to hammer the FPS too much. And what is my FPS? With just the buildings (see previous post), I’m up around 60 FPS, but the trees take it down to about 30.

This is the whole map, 2000 x 3000 pixels

http://instagram.com/p/ZFCwWCBHb7/

(and yes, that is a full size castle with battlements tucked away in the far right corner).

Now all I need is a couple of zombies, and I have an FPS 8-X

This is very, very impressive!

Nice job!

Good stuff! I’m intrigued why you’re getting such a performance hit with the trees? 60-30fps is quite a shift especially as (I guess) you’re probably using 2 triangles for each tree (e.g 200 x 2 = 400 polys) and your ‘face trees to camera’ code is probably quite light? Any thoughts?

@andymac3d - I was going to say it’s probably it’s all the rotations on every redraw (and sorting by distance, less frequently), but on testing it, commenting out the rotations only takes the speed up to 40, and sorting makes no difference.

If I comment the trees out completely, I get 60. So it looks like the tree images, each with, yes, 6 vertices. I’ve actually created a mesh with vertices for each, rather than using addRect.

It may be that I have so many separate meshes, but my understanding is that you can’t use more than one texture per mesh. I could combine the trees which use the same image, but I haven’t done that yet.

I would love to speed it up if I could!

Here’s the relevant code that adds an image. The draw function simply draws all the items in the table of meshes. I’d love to improve it if I can.

--adds stand alone image
--x,y is bottom left corner (looking at map from starting position, and 0,0 is bottom left of map)
--w = width of object (pixels)
--i = image or string name of image (do not include library name)
--r = true if image should always rotate to face the user
function Building:AddImage(xx,yy,w,i,r)
    local img
    img=self:GetTexture(i)
    local h=w*img.height/img.width
    local v,t={},{}
    local x,y
    if r then x,y=0,0 else x,y=xx,yy end
    v[1]=vec3(x,y,0)    t[1]=vec2(0,0)
    v[2]=vec3(x,y,h)    t[2]=vec2(0,1)
    v[3]=vec3(x+w,y,0)  t[3]=vec2(1,0)
    v[4]=vec3(x+w,y,0)  t[4]=vec2(1,0)
    v[5]=vec3(x+w,y,h)  t[5]=vec2(1,1)
    v[6]=vec3(x,y,h)    t[6]=vec2(0,1)
    if r then  --include x,y position so we can calculate relative rotation
        self:AddToMesh(2,v,t,img,nil,xx,yy)
    else
        self:AddToMesh(2,v,t,img,nil)
    end
end

--this actually adds the mesh to our overall table
function Building:AddToMesh(n,v,t,img,c,x,y)
    local mm={}
    mm.m=mesh()
    local colr=c or color(255,255,255,255)
    mm.m:setColors(colr)
    mm.m.vertices=v
    mm.m.texCoords=t
    mm.m.texture=img
    mm.type=type or "b"
    mm.x,mm.y=x,y
    table.insert(self.MeshTable[n],mm) 
    collectgarbage()
end

I’d suggest looking at the overdraw. Trees have a large amount of transparent pixels (i.e. under the leaves, next to the trunk). These use up the fill rate on the GPU.

If you increased the vertices for each tree and attempted to have the mesh “hug” the outline of the tree trunk and leaves then you might see a performance improvement. I.e. try not to render any fragments where the texture is completely transparent.

An additional improvement would be to remove trees that are completely occluded. Otherwise the occluded trees will also impact performance despite not contributing to the final image.

That said, extremely impressive demo, @Ignatz!

Thanks, Simeon, that is very helpful

I thought about omitting occluded meshes, but the effort in deciding what is occluded is considerable, unless there is a smart way I don’t know about

That looks awesome @Ignatz!!!

@Ignatz, Yup, I think refactoring it to use setRect to zap them into ‘one’ mesh will really help. From my experience drawing one mesh this way is pretty quick!

I guess you’d have to re-engineer your depth sort, as the order you add them to the mesh (using setRect) implies what gets drawn first back to front which might be a bit more tricky. It should be relatively easy then to put each of your trees in one ‘spritesheet’ as an x,y grid (providing they are all the same size) and then set the texture coordinates for each ‘Rect’.

You may also want to look at culling rectangles/polys that are outside the ‘viewing frustum’ i.e. only compute, update and display what you can see through the camera. Performancewise, I’m unsure if this is an issue under GL these days, as I guess its internal viewing pipeline should do this automatically.

@andymac2d - the problem with putting all the trees in one mesh is that they all rotate at different angles depending on their position relative to the viewer, so they have to be kept separate - unless I prevent the viewer from getting too close or walking around the trees!

@ignatz That is very cool, cant wait to find time to sit down and play with your latest creations!

@Ignatz why not write a shader to do the rotation? You’d need a vertex buffer containing the axis to rotate around and a uniform attribute holding the location of the person.

@Andrew-Stacey - I don’t understand shaders!

or vertex buffers.

or uniform attributes.

Is it possible to create a very simple example for one image?

@BriarFox - wait until you see the terrain modelling (variable height using the noise function) - I got sick of walking around flatland. It’s not working yet, but should be cool. How I then walk around on top of it (rather than through it) is the next problem.

I think I’m basically just testing Codea’s calculation limits…

@andrew_Stacey - I will have another go at learning shaders if you think they will help - I was just looking for a shortcut by asking for an example, was all. :-/

View from the hill, still working on a few bugs, but now I can create a hilly terrain, put stuff on it, then walk all over it

http://instagram.com/p/ZHb_5KBHbB/

Very nice. I’ll see if I can bash together a shader example if you like for the rotation.

@spacemonkey - I would appreciate that very much, if you have the time

This looks very nice. Reminds me on good old oblivion on low settings.