3D terrain help

F

FireflyX91

Guest
Hello Gamemakers, I'm currently trying to add 3D terrain to my game. I've read a few tutorials and came up this but... It doesn't seem to be working. If somebody could take a look and let me know where I went wrong, it would be appreciated.

Basically I'm using 2D height maps by placing the texture on a surface and writing the surface data to a buffer. Here is the code for my height map object:

obj_hm1
Create Event
Code:
height_buffer = noone;
x_divisions = 224; //vertices in x axis
y_divisions = 448; //vertices in y axis
terrain_height = 128; //Maximum height for terrain
tex_repeat = 20;

tex = sprite_get_texture(tex_floor1,0);

//create vertex format
vertex_format_begin();
vertex_format_add_position_3d();
vertex_format_add_normal();
vertex_format_add_colour();   [/B]
vertex_format_add_texcoord();
vertex_format = vertex_format_end();

Draw Event
Code:
if(height_buffer == noone){
 
    var map_width = 224;
    var map_height = 448;

    var surface = surface_create(map_width,map_height);
    surface_set_target(surface);
    //draw_sprite(spr_hm1,-1,x,y);
    sprite_get_texture(spr_hm1,0);
    surface_reset_target();

    //create height buffer
    var size = (4 * map_width * map_height) + 8;
    height_buffer = buffer_create(size,buffer_fixed,1);
 
    //write map dimensions
    buffer_write(height_buffer,buffer_f32,map_width);
    buffer_write(height_buffer,buffer_f32,map_height);
 
    //write surface data to buffer
    buffer_get_surface(height_buffer,surface,0,8,0);
 
    surface_free(surface);
 
    vertex_buffer = vertex_create_buffer();
    vertex_begin(vertex_buffer,vertex_format);
 
    for(var i = 0;i < x_divisions;i++)
    for(var j = 0;j < y_divisions;j++){
     
        //Top left corner
        var p1x = floor((i*map_width) / x_divisions);
        var p1y = floor((j*map_width) / y_divisions);
     
        //Bottom right corner
        var p2x = floor(((i+1) * map_width) / x_divisions);
        var p2y = floor(((j+1) * map_width) / y_divisions);
     
        //heights
        var pz1 = buffer_get_height(height_buffer,p1x,p1y) * terrain_height / 255;
        var pz2 = buffer_get_height(height_buffer,p2x,p1y) * terrain_height / 255;
        var pz3 = buffer_get_height(height_buffer,p1x,p2y) * terrain_height / 255;
        var pz4 = buffer_get_height(height_buffer,p2x,p2y) * terrain_height / 255;
     
        //texture coordinates
        var tx1 = (i / x_divisions) * tex_repeat;
        var ty1 = (j / y_divisions) * tex_repeat;
        var tx2 = ((i+1) / x_divisions) * tex_repeat;
        var ty2 = ((j+1) / y_divisions) * tex_repeat;
     
        //triangle 1
        vertex_position_3d(vertex_buffer,i,j,pz1);
        vertex_texcoord(vertex_buffer,tx1,ty1);
        vertex_position_3d(vertex_buffer,i,j+1,pz3);
        vertex_texcoord(vertex_buffer,tx1,ty2);
        vertex_position_3d(vertex_buffer,i+1,j,pz2);
        vertex_texcoord(vertex_buffer,tx2,ty1);
     
        //triangle 2
        vertex_position_3d(vertex_buffer,i+1,j,pz2);
        vertex_texcoord(vertex_buffer,tx2,ty1);
        vertex_position_3d(vertex_buffer,i,j+1,pz3);
        vertex_texcoord(vertex_buffer,tx1,ty2);
        vertex_position_3d(vertex_buffer,i+1,j+1,pz4);
        vertex_texcoord(vertex_buffer,tx2,ty2);
    }
 
    vertex_end(vertex_buffer);
 
}else{

    var matrix = matrix_build(x,y,terrain_height,0,0,0,1,1,1);
    matrix_set(matrix_world,matrix);

    vertex_submit(vertex_buffer, pr_trianglelist, tex);

    matrix_set(matrix_world, matrix_build_identity());
 
}
There is also the "buffer_get_height" script:

Code:
var buffer = argument0;
var xpos = argument1;
var ypos = argument2;

var height = buffer_peek(buffer,4,buffer_f32);
var offset = 4*((xpos+height)+ypos)+8;
var value = buffer_peek(buffer,offset,buffer_u8);

return(value);
When I run the game I get the following error:

FATAL ERROR in
action number 1
of Draw Event
for object obj_hm1:

VERTEX BUILDER: element already written, must write the whole vertex first

at gml_Object_obj_hm1_Draw_0 (line 54) - vertex_position_3d(vertex_buffer,i,j+1,pz3);
############################################################################################
--------------------------------------------------------------------------------------------
stack frame is
gml_Object_obj_hm1_Draw_0 (line 54)
Hope somebody can help, Thanks
 
Last edited by a moderator:

Simon Gust

Member
Seems to me like you're vertex buffer is missing a color for every vertex. The error says it, that position and color are standard to every vertex format.

It also produces an error if you don't supply the data for each vertex suggested by the vertex format.
So if you format adds position, texture, normals and color, each triangle should have 3 vertices with a position, texture, normal and a color.

Watch the tutorial again and you'll see that they add color to simulate lighting on the terrain.
And if you don't need lighting, set all the color values to c_white.

And if not, this guy knows better than me.
@GMWolf
 

GMWolf

aka fel666
And if not, this guy knows better than me.
@GMWolf
I should do, that's the code from my tutorial :)

The issue is exactly as Simon stated: you are not assigning each of the attributes.



Code:
//create vertex format 
vertex_format_begin(); 
vertex_format_add_position_3d(); 
vertex_format_add_normal(); 
vertex_format_add_colour(); 
vertex_format_add_texcoord(); 
vertex_format = vertex_format_end();
Your format declares the following: A 3d position, A normal vector, a colour and a texture coodinate.

Code:
//triangle 1
        vertex_position_3d(vertex_buffer,i,j,pz1);
        vertex_texcoord(vertex_buffer,tx1,ty1);
        vertex_position_3d(vertex_buffer,i,j+1,pz3);
        vertex_texcoord(vertex_buffer,tx1,ty2);
        vertex_position_3d(vertex_buffer,i+1,j,pz2);
        vertex_texcoord(vertex_buffer,tx2,ty1);
    
        //triangle 2
        vertex_position_3d(vertex_buffer,i+1,j,pz2);
        vertex_texcoord(vertex_buffer,tx2,ty1);
        vertex_position_3d(vertex_buffer,i,j+1,pz3);
        vertex_texcoord(vertex_buffer,tx1,ty2);
        vertex_position_3d(vertex_buffer,i+1,j+1,pz4);
        vertex_texcoord(vertex_buffer,tx2,ty2);
However, your code only adds a position, and a texture coordinate.


Either compute your normals and colour and add them to tje certex buffer, or only have the position and texcoord in your format.

If you look at the tutorials that follow, I show how normals can be computed (though, I do not add them to the VB)
 
F

FireflyX91

Guest
Well.... I can't ask for a better response than one from the guy whos tutorial i followed :D

Anyway I'll go and have a look at your other tutorials and hopefully if I add the normal and colour attributes then it should be working.

Thanks for the advice guys
 
F

FireflyX91

Guest
Ok, finally no more errors but..... It's just displaying a perfectly flat surface at whatever I set the "terrain_height" variable to.
 

GMWolf

aka fel666
Ok, finally no more errors but..... It's just displaying a perfectly flat surface at whatever I set the "terrain_height" variable to.
Use show_debug_message to print out the values and see if the problem is with the values, or the rendering.
 
F

FireflyX91

Guest
I tried doing that and every value is being printed out at "704"
 

GMWolf

aka fel666
I tried doing that and every value is being printed out at "704"
Well your problem lies somewhere with you heightmap values.
Keep printing out values at different points to find out where the problem lies.
When you know where in the code there is a problem, you can start debugging that section.
 
F

FireflyX91

Guest
Sorry for the late response (busy away from the pc) but I've been struggling to find the error until I tried to actually look at the surface that was been created. Anyway, upon inspection it does not display the heightmap sprite but rather a plain black image. So now I've figured out where the issue is... I'm not sure what I've actually done wrong with the surface code.

Code:
    var surface = surface_create(map_width,map_height);
    
    surface_set_target(surface);   
    draw_sprite(spr_hm1,-1,x,y);
    surface_reset_target();
I also noticed that in your tutorial you use the draw_background function. However, now that this is an obsolete function, is it correct for me to use draw_sprite in its place?
 
Top