mirror of
https://github.com/dpethes/rerogue.git
synced 2025-06-07 18:58:32 +02:00
terrain viewer: use textures. Works for quads only at the moment
This commit is contained in:
parent
93536c4b64
commit
ef525fc0ff
@ -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;
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user