From ef525fc0ff71618c01e8f152f0e0ac0d1fdcc104 Mon Sep 17 00:00:00 2001 From: dpethes Date: Mon, 6 Jul 2015 15:25:47 +0200 Subject: [PATCH] terrain viewer: use textures. Works for quads only at the moment --- terrain_viewer/rs_world.pas | 197 ++---------------------------- terrain_viewer/terrain_mesh.pas | 186 ++++++++++++++++++++++++++-- terrain_viewer/terrain_viewer.pas | 2 +- 3 files changed, 181 insertions(+), 204 deletions(-) diff --git a/terrain_viewer/rs_world.pas b/terrain_viewer/rs_world.pas index c5dedb4..5284753 100644 --- a/terrain_viewer/rs_world.pas +++ b/terrain_viewer/rs_world.pas @@ -20,9 +20,9 @@ type TTile = packed record texture_index: word; unknown_attrib: byte; - unknown_lo: byte; - unknown_hi: byte; - unknown: array[0..24] of byte; + height_lo: byte; + height_hi: byte; + heights: array[0..24] of byte; end; PTile = ^TTile; @@ -52,10 +52,6 @@ type procedure LoadTextures(const tex_fname, texidx_fname: string); procedure LoadHeightmap(fname: string); - procedure GenerateCompositeTexture; - procedure HeightmapToTexture; - procedure GenerateVertices; - procedure WriteToObj(const objFname: string); public heightmap: THeightmap; @@ -66,12 +62,12 @@ type property TileHeight: word read heightmap.height; procedure LoadFromFiles(const hmp, tex, texmap: string); - procedure ExportToObj(const objfname: string); constructor Create; destructor Destroy; override; end; +procedure pnm_save(const fname: string; const p: pbyte; const w, h: integer); //************************************************************************************************** implementation @@ -102,7 +98,7 @@ Begin CloseFile (f); end; -procedure convert_4bit_to_32bit(const indices: PByte; const w, h: Word; const image: PByte; const pal: TPalette_4bit); +procedure convert_4bit_to_24bit(const indices: PByte; const w, h: Word; const image: PByte; const pal: TPalette_4bit); var i: Integer; index: integer; @@ -173,7 +169,7 @@ begin image := getmem(TEX_WIDTH * TEX_HEIGHT * 3); Blockread(f, buf^, tex_size); Blockread(f, palette, palette_size); - convert_4bit_to_32bit(buf, TEX_WIDTH, TEX_HEIGHT, image, palette); + convert_4bit_to_24bit(buf, TEX_WIDTH, TEX_HEIGHT, image, palette); heightmap.textures[i] := image; end; freemem(buf); @@ -231,189 +227,10 @@ begin CloseFile(f); end; -procedure TWorld.GenerateCompositeTexture; -var - image: pbyte; - image_size: integer; - x, y, stride: integer; - tile_idx, texture_idx, texmap_idx: integer; - texture: pbyte; -begin - image_size := heightmap.width * heightmap.height * TEX_WIDTH * TEX_HEIGHT * 3; - image := GetMem(image_size); - stride := heightmap.width * TEX_WIDTH * 3; - - for y := 0 to heightmap.height - 1 do - for x := 0 to heightmap.width - 1 do begin - tile_idx := heightmap.blk[y * heightmap.width + x]; - - texmap_idx := heightmap.tiles[tile_idx].texture_index; - if texmap_idx > Length(heightmap.texture_index_map) - 1 then - texmap_idx := 0; - - texture_idx := heightmap.texture_index_map[texmap_idx]; - texture := heightmap.textures[texture_idx]; - CopyTexToXY(image, texture, x * TEX_WIDTH, (heightmap.height - y - 1) * TEX_HEIGHT, stride); - end; - - world_texture := image; - pnm_save(TEXTURE_FNAME, image, heightmap.width * TEX_WIDTH, heightmap.height * TEX_HEIGHT); -end; - -procedure TWorld.HeightmapToTexture; -const - TILE_WIDTH = 4; - SCALE = 128; -var - x, y: integer; - tile_idx: integer; - i: integer; - image_size: integer; - image: pbyte; -begin - image_size := heightmap.width * heightmap.height * TILE_WIDTH * TILE_WIDTH; - image := GetMem(image_size); - - for y := 0 to heightmap.height - 1 do begin - for x := 0 to heightmap.width - 1 do begin - tile_idx := heightmap.blk[y * heightmap.width + x]; - - CopyTileToXY(image, @(heightmap.tiles[tile_idx].unknown), - x * TILE_WIDTH, (heightmap.height - y - 1) * TILE_WIDTH, heightmap.width * TILE_WIDTH); - end; - end; - - //scale - for i := 0 to image_size - 1 do - image[i] := byte(image[i] + SCALE); - - height_texture := image; - //pgm_save('map_height.pgm', image, heightmap.width * TILE_WIDTH, heightmap.height * TILE_WIDTH); -end; - -{ - While vertical scaling is stored within file, horizontal seems to be always 0.5 - The height values need to be centered, 128 seems to be the correct offset. - The generated y coords are flipped for (opengl) rendering -} -procedure TWorld.GenerateVertices; -const - h_scale = 0.5; -var - va_size: integer; - x, y: integer; - vert: TVertex3f; - width_half, height_half: integer; - i: integer; - v_scale: single; -begin - vertex_count := heightmap.width * 4 * heightmap.height * 4; - va_size := vertex_count * SizeOf(TVertex3f); - vertex_array := getmem(va_size); - - width_half := heightmap.width * 2; - height_half := heightmap.height * 2; - v_scale := heightmap.y_scale; - - for y := 0 to heightmap.height * 4 - 1 do - for x := 0 to heightmap.width * 4 - 1 do begin - vert.x := (-width_half + x) * h_scale; - vert.z := (-height_half + y) * h_scale; - vert.u := x / (heightmap.width * 4); - vert.v := y / (heightmap.height * 4); - i := y * heightmap.width * 4 + x; - vert.y := (height_texture[i] - 128) * v_scale; - vert.y := -vert.y; - vertex_array[i] := vert; - end; -end; - - -procedure SaveMaterialFile(const obj_fname, mtl_name, texture_fname: string); -var - f: TextFile; -begin - AssignFile(f, obj_fname + '.mtl'); - Rewrite(f); - - writeln(f, '# RS heightmap'); - writeln(f, 'newmtl ', mtl_name); //begin new material - writeln(f, 'map_Kd ', texture_fname); //texture - writeln(f, 'Ka 1.000 1.000 1.000'); //ambient color - writeln(f, 'Kd 1.000 1.000 1.000'); //diffuse color - writeln(f, 'Ks 1.000 1.000 1.000'); //specular color - writeln(f, 'Ns 100.0'); //specular weight - writeln(f, 'illum 2'); //Color on and Ambient on, Highlight on - - CloseFile(f); -end; - - -procedure TWorld.WriteToObj(const objFname: string); -const - MAT_NAME = 'default'; -var - f: textfile; - i: integer; - v: TVertex3f; - x, y, stride: integer; - i2, i3: integer; - texfname: string; -begin - AssignFile(f, objFname); - Rewrite(f); - - writeln(f, '# RS heightmap'); - writeln(f, 'mtllib ', objFname + '.mtl'); - - //vertices - for i := 0 to vertex_count - 1 do begin - v := vertex_array[i]; - writeln(f, 'v ', v.x:10:6, ' ', v.y:10:6, ' ', v.z:10:6); - end; - - //uv-s - for i := 0 to vertex_count - 1 do begin - v := vertex_array[i]; - writeln(f, 'vt ', v.u:10:6, ' ', v.v:10:6); - end; - - //select material - writeln(f, 'usemtl ' + MAT_NAME); - - //faces - { - 12 2 - 3 34 - } - stride := heightmap.width * 4; - for y := 0 to heightmap.height * 4 - 2 do - for x := 0 to heightmap.width * 4 - 2 do begin - i := y * stride + x + 1; - i2 := i + 1; - i3 := i + stride; - writeln(f, Format('f %d/%d %d/%d %d/%d', [i, i, i2, i2, i3, i3])); - i := i3 + 1; - writeln(f, Format('f %d/%d %d/%d %d/%d', [i2, i2, i, i, i3, i3])); - end; - - CloseFile(f); - - SaveMaterialFile(objFname, MAT_NAME, TEXTURE_FNAME); -end; - procedure TWorld.LoadFromFiles(const hmp, tex, texmap: string); begin LoadHeightmap(hmp); - //LoadTextures(tex, texmap); -end; - -procedure TWorld.ExportToObj(const objfname: string); -begin - //GenerateCompositeTexture; - HeightmapToTexture; - GenerateVertices; - //WriteToObj(objfname); + LoadTextures(tex, texmap); end; constructor TWorld.Create; diff --git a/terrain_viewer/terrain_mesh.pas b/terrain_viewer/terrain_mesh.pas index f6d7819..642af15 100644 --- a/terrain_viewer/terrain_mesh.pas +++ b/terrain_viewer/terrain_mesh.pas @@ -17,10 +17,18 @@ type fg_to_draw: integer; end; + TTerrainBlock = packed record + texture_index: integer; + vertices: array[0..25] of TVertex3f; + end; + { TTerrainMesh } TTerrainMesh = class private terrain: TWorld; + blocks: array of array of TTerrainBlock; + textures_glidx: array of integer; + procedure TransformTiles; public destructor Destroy; override; procedure Load(const hmp_filename: string); @@ -30,6 +38,56 @@ type implementation +{ + While vertical scaling is stored within file, horizontal seems to be always 0.5 + The generated y coords are flipped for (opengl) rendering +} +procedure TTerrainMesh.TransformTiles; + + //basex/y - offset in vertices + procedure TileToBlock(var blk: TTerrainBlock; var tile: TTile; basex, basey: integer); + const + h_scale = 0.5; + var + x, y: integer; + v: TVertex3f; + width_half, height_half: integer; //size in vertices + v_scale: single; + begin + width_half := terrain.TileWidth * 2; + height_half := terrain.TileHeight * 2; + v_scale := terrain.heightmap.y_scale; + for y := 0 to 4 do + for x := 0 to 4 do begin + v.x := (-width_half + basex + x) * h_scale; + v.z := (-height_half + basey + y) * h_scale; + v.u := -x * 1/4; + v.v := -y * 1/4; + v.y := shortint(tile.heights[y+x*5]) * v_scale; //hmm... all transforms are flipped? + v.y := -v.y; + + blk.vertices[y * 5 + x] := v; + end; + blk.texture_index := tile.texture_index; + end; + +var + x, y, i, tile_idx: integer; + blk: TTerrainBlock; + tile: TTile; +begin + SetLength(blocks, terrain.TileWidth, terrain.TileHeight); + for y := 0 to terrain.TileHeight - 1 do begin + for x := 0 to terrain.TileWidth - 1 do begin + tile_idx := terrain.heightmap.blk[y * terrain.TileWidth + x]; + tile := terrain.heightmap.tiles[tile_idx]; + + TileToBlock(blk, tile, y * 4, x * 4); + blocks[y, x] := blk; + end; + end; +end; + destructor TTerrainMesh.Destroy; begin inherited Destroy; @@ -38,29 +96,131 @@ end; procedure TTerrainMesh.Load(const hmp_filename: string); begin terrain := TWorld.Create; - terrain.LoadFromFiles(hmp_filename, '', ''); - terrain.ExportToObj(''); //generate vertices + terrain.LoadFromFiles('hmp_0', 'lv_0.text', 'lv_0.tex'); + //terrain.LoadFromFiles('hmp_1', 'lv_1.text', 'lv_1.tex'); + TransformTiles; end; -//generate textures / texture atlas? +//generate textures. TODO texture atlas? procedure TTerrainMesh.InitGL; -begin -end; + procedure GenTexture(tex_idx: integer); + const + TexW = 64; + TexH = 64; + var + tex: pbyte; + y: Integer; + x: Integer; + begin + glGenTextures(1, @textures_glidx[tex_idx]); + glBindTexture(GL_TEXTURE_2D, textures_glidx[tex_idx]); + + tex := terrain.heightmap.textures[tex_idx]; + //pnm_save('tex'+IntToStr(tex_idx) + '.pnm', tex, TexW, TexH); //debug + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, TexW, TexH, 0, GL_RGB, GL_UNSIGNED_BYTE, tex); + + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + //glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); + //glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); + end; -//draw vertices / tiles around actual position -procedure TTerrainMesh.DrawGL(opts: TRenderOpts); var i: integer; +begin + glEnable(GL_TEXTURE_2D); + SetLength(textures_glidx, terrain.heightmap.texture_count); + for i := 0 to terrain.heightmap.texture_count - 1 do + GenTexture(i); +end; + +//draw vertices from each block +procedure TTerrainMesh.DrawGL(opts: TRenderOpts); + +procedure RenderBlock(var blk: TTerrainBlock); +var + i, x, y, stride: integer; v: TVertex3f; begin - glBegin(GL_POINTS); - glColor3f(0, 1, 0); - for i := 0 to terrain.vertex_count - 1 do begin - v := terrain.vertex_array[i]; - glVertex3fv(@v); - end; + stride := 5; + glBindTexture(GL_TEXTURE_2D, textures_glidx[blk.texture_index]); + { + glBegin(GL_TRIANGLES); + //glColor3f(0, 1, 0); + for y := 0 to 3 do + for x := 0 to 3 do begin + //do two triangles + i := y * stride + x; + + v := blk.vertices[i + 1]; + glVertex3fv(@v); + glTexCoord2f(v.u, v.v); + v := blk.vertices[i]; + glVertex3fv(@v); + glTexCoord2f(v.u, v.v); + v := blk.vertices[i + stride]; + glVertex3fv(@v); + glTexCoord2f(v.u, v.v); + + v := blk.vertices[i + 1]; + glVertex3fv(@v); + glTexCoord2f(v.u, v.v); + v := blk.vertices[i + stride]; + glVertex3fv(@v); + glTexCoord2f(v.u, v.v); + v := blk.vertices[i + stride + 1]; + glVertex3fv(@v); + glTexCoord2f(v.u, v.v); + end; glEnd; + + } + //quad from block corners + glBegin(GL_QUADS); + i := y * stride + x; + + v := blk.vertices[4]; + glVertex3fv(@v); + glTexCoord2f(v.u, v.v); + v := blk.vertices[0]; + glVertex3fv(@v); + glTexCoord2f(v.u, v.v); + v := blk.vertices[20]; + glVertex3fv(@v); + glTexCoord2f(v.u, v.v); + v := blk.vertices[24]; + glVertex3fv(@v); + glTexCoord2f(v.u, v.v); + glEnd; + +end; + +var + i, x, y, stride: integer; + blk: TTerrainBlock; + v: TVertex3f; +begin + if opts.points then begin + glBegin(GL_POINTS); + glColor3f(0, 1, 0); + for i := 0 to terrain.vertex_count - 1 do begin + v := terrain.vertex_array[i]; + glVertex3fv(@v); + end; + glEnd; + end else begin + if opts.wireframe then + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE) + else + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + glEnable(GL_TEXTURE_2D); + for y := 0 to terrain.TileHeight - 1 do + for x := 0 to terrain.TileWidth - 1 do begin + blk := blocks[y, x]; + RenderBlock(blk); + end; + end; end; end. diff --git a/terrain_viewer/terrain_viewer.pas b/terrain_viewer/terrain_viewer.pas index 7ba03ad..76c02a2 100644 --- a/terrain_viewer/terrain_viewer.pas +++ b/terrain_viewer/terrain_viewer.pas @@ -338,7 +338,7 @@ var begin if Paramcount < 1 then begin - writeln('specify HOB file'); + writeln('specify HMP file'); exit; end; in_file := ParamStr(1);