mirror of
https://github.com/dpethes/rerogue.git
synced 2025-06-07 18:58:32 +02:00
terrain viewer: use vertex arrays + glDrawElements.
Vertex normals are fake, needs proper per-vertex normals
This commit is contained in:
parent
2d5c549847
commit
0d8ff0407e
@ -1,21 +1,26 @@
|
||||
12B: zeros
|
||||
4B float: always 0x3f000000 (0.5)
|
||||
4B float: terrain height scale
|
||||
4B float: always 0x3f000000
|
||||
2B int : tile count
|
||||
2B int : ?
|
||||
4B int : offset to tiles
|
||||
4B int : offset to some data?
|
||||
2B int : width in BLK
|
||||
2B int : height in BLK
|
||||
array[width * height] of 2B int: tile indices
|
||||
xB ?
|
||||
tiles
|
||||
HMP
|
||||
{
|
||||
12B: zeros
|
||||
4B float: always 0x3f000000 (0.5)
|
||||
4B float: terrain height scale
|
||||
4B float: always 0x3f000000
|
||||
2B int : tile count
|
||||
2B int : ?
|
||||
4B int : offset to tiles
|
||||
4B int : offset to some data?
|
||||
2B int : width in BLK
|
||||
2B int : height in BLK
|
||||
array[width * height] of 2B int: tile indices
|
||||
xB ?
|
||||
array[tile count] of TILE
|
||||
2B 0x0000
|
||||
}
|
||||
|
||||
TILE [30B]
|
||||
{
|
||||
2B int: texmap idx (from texture index file)
|
||||
1B int: ?
|
||||
1B uint: lo - minimum height in tile (probably for terrain LOD?)
|
||||
1B uint: hi - maximum height in tile
|
||||
array[25] of uint8: - 5x5 heights
|
||||
1B int: lo - minimum height in tile (probably for terrain LOD? clipping?)
|
||||
1B int: hi - maximum height in tile
|
||||
array[25] of int8: 5x5 height values
|
||||
}
|
||||
2B 0x0000
|
||||
|
@ -20,9 +20,9 @@ type
|
||||
TTile = packed record
|
||||
texture_index: word;
|
||||
unknown_attrib: byte;
|
||||
height_lo: byte;
|
||||
height_hi: byte;
|
||||
heights: array[0..24] of byte;
|
||||
height_lo: shortint;
|
||||
height_hi: shortint;
|
||||
heights: array[0..24] of shortint;
|
||||
end;
|
||||
PTile = ^TTile;
|
||||
|
||||
@ -37,12 +37,6 @@ type
|
||||
texture_index_map: array of integer;
|
||||
end;
|
||||
|
||||
TVertex3f = record
|
||||
x, y, z: single;
|
||||
u, v: single
|
||||
end;
|
||||
PVertex3f = ^TVertex3f;
|
||||
|
||||
{ TWorld }
|
||||
|
||||
TWorld = class
|
||||
@ -55,7 +49,6 @@ type
|
||||
|
||||
public
|
||||
heightmap: THeightmap;
|
||||
vertex_array: PVertex3f;
|
||||
vertex_count: integer;
|
||||
|
||||
property TileWidth: word read heightmap.width;
|
||||
@ -217,7 +210,7 @@ begin
|
||||
heightmap.blk := blk;
|
||||
|
||||
//tiles
|
||||
//writeln('tiles: ', tile_count);
|
||||
//writeln('filepos: ', FilePos(f)); writeln('tile pos: ', tile_offset);
|
||||
Seek(f, tile_offset);
|
||||
heightmap.tile_count := tile_count;
|
||||
heightmap.tiles := getmem(tile_count * 30);
|
||||
@ -241,13 +234,11 @@ end;
|
||||
constructor TWorld.Create;
|
||||
begin
|
||||
height_texture := nil;
|
||||
vertex_array := nil;
|
||||
end;
|
||||
|
||||
destructor TWorld.Destroy;
|
||||
begin
|
||||
if height_texture <> nil then Freemem(height_texture);
|
||||
if vertex_array <> nil then Freemem(vertex_array);
|
||||
inherited Destroy;
|
||||
end;
|
||||
|
||||
|
@ -4,9 +4,9 @@ unit terrain_mesh;
|
||||
interface
|
||||
|
||||
uses
|
||||
Classes, SysUtils,
|
||||
Classes, SysUtils, matrix,
|
||||
gl, glext, glu,
|
||||
rs_world;
|
||||
rs_world, vector_util;
|
||||
|
||||
type
|
||||
TRenderOpts = record
|
||||
@ -17,17 +17,21 @@ type
|
||||
fg_to_draw: integer;
|
||||
end;
|
||||
|
||||
TTerrainBlock = packed record
|
||||
TTerrainBlock = record
|
||||
texture_index: integer;
|
||||
vertices: array[0..25] of TVertex3f;
|
||||
end;
|
||||
vertices: array[0..24] of Tvector3_single; //25*3*4 = 300B
|
||||
normals: array[0..24] of Tvector3_single; //25*3*4 = 300B
|
||||
end; //~600B per block
|
||||
|
||||
{ TTerrainMesh }
|
||||
TTerrainMesh = class
|
||||
private
|
||||
terrain: TWorld;
|
||||
blocks: array of array of TTerrainBlock;
|
||||
block_texcoords: array[0..24] of Tvector2_single; //static, 25*2*4 = 200B
|
||||
block_face_indices: array[0..16*2*3 - 1] of byte; //static, 96B
|
||||
textures_glidx: array of integer;
|
||||
procedure InitBlockStaticData;
|
||||
procedure TransformTiles;
|
||||
public
|
||||
destructor Destroy; override;
|
||||
@ -44,36 +48,60 @@ implementation
|
||||
}
|
||||
procedure TTerrainMesh.TransformTiles;
|
||||
|
||||
//basex/y - offset in vertices
|
||||
//TODO solve flipped coords
|
||||
procedure TileToBlock(var blk: TTerrainBlock; var tile: TTile; basex, basey: integer);
|
||||
//basex/y - position in block units for given dimension (0..block_size-1)
|
||||
//todo fix: the params are flipped.. and so are the calculations
|
||||
function TileToBlock(var tile: TTile; basex, basey: integer): TTerrainBlock;
|
||||
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;
|
||||
result.texture_index := tile.texture_index;
|
||||
//dim * vertices_per_tile - half_tile_dim * vertices_per_tile
|
||||
basey := basey * 4 - terrain.TileHeight * 2;
|
||||
basex := basex * 4 - terrain.TileWidth * 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.v := (1 - x/4);
|
||||
v.u := y/4;
|
||||
v.y := shortint(tile.heights[y+x*5]) * v_scale;
|
||||
v.y := -v.y;
|
||||
|
||||
blk.vertices[y * 5 + x] := v;
|
||||
result.vertices[y * 5 + x].init( //x,y,z
|
||||
(basex + x) * h_scale,
|
||||
tile.heights[y+x*5] * v_scale * -1,
|
||||
(basey + y) * h_scale
|
||||
);
|
||||
end;
|
||||
end;
|
||||
|
||||
//todo do proper per-vertex normal:
|
||||
//this only calculates face normals and sets them to face's vertices, overwriting the value set
|
||||
//from previous face
|
||||
procedure FakeNormals(var blk: TTerrainBlock);
|
||||
procedure SetTriData(const tri_idx: integer; const i0, i1, i2: byte);
|
||||
var
|
||||
normal: Tvector3_single;
|
||||
begin
|
||||
normal := GetNormal(blk.vertices[i0], blk.vertices[i1], blk.vertices[i2]);
|
||||
blk.normals[i0] := normal;
|
||||
blk.normals[i1] := normal;
|
||||
blk.normals[i2] := normal;
|
||||
end;
|
||||
const
|
||||
VertexStride = 5;
|
||||
var
|
||||
x, y, i, tri_idx: integer;
|
||||
begin
|
||||
tri_idx := 0;
|
||||
for y := 0 to 3 do
|
||||
for x := 0 to 3 do begin
|
||||
i := y * VertexStride + x;
|
||||
SetTriData(tri_idx, i+1, i, i+VertexStride);
|
||||
SetTriData(tri_idx + 1, i+1, i+VertexStride, i+VertexStride+1);
|
||||
tri_idx += 2;
|
||||
end;
|
||||
blk.texture_index := tile.texture_index;
|
||||
end;
|
||||
|
||||
var
|
||||
x, y, i, tile_idx: integer;
|
||||
x, y, tile_idx: integer;
|
||||
blk: TTerrainBlock;
|
||||
tile: TTile;
|
||||
begin
|
||||
@ -83,12 +111,46 @@ begin
|
||||
tile_idx := terrain.heightmap.blk[y * terrain.TileWidth + x];
|
||||
tile := terrain.heightmap.tiles[tile_idx];
|
||||
|
||||
TileToBlock(blk, tile, y * 4, x * 4);
|
||||
blk := TileToBlock(tile, y, x);
|
||||
FakeNormals(blk);
|
||||
blocks[y, x] := blk;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
{ InitBlockStaticData
|
||||
Initializes data shared between tiles: face vertex indices, texture coords
|
||||
}
|
||||
procedure TTerrainMesh.InitBlockStaticData;
|
||||
|
||||
procedure SetTriData(const tri_idx: integer; const i0, i1, i2: byte);
|
||||
begin
|
||||
block_face_indices[tri_idx * 3 + 0] := i0;
|
||||
block_face_indices[tri_idx * 3 + 1] := i1;
|
||||
block_face_indices[tri_idx * 3 + 2] := i2;
|
||||
end;
|
||||
|
||||
const
|
||||
VertexStride = 5;
|
||||
var
|
||||
x, y, i, tri_idx: integer;
|
||||
begin
|
||||
tri_idx := 0;
|
||||
//init face indices
|
||||
for y := 0 to 3 do
|
||||
for x := 0 to 3 do begin
|
||||
i := y * VertexStride + x;
|
||||
SetTriData(tri_idx, i+1, i, i+VertexStride);
|
||||
SetTriData(tri_idx+1, i+1, i+VertexStride, i+VertexStride+1);
|
||||
tri_idx += 2;
|
||||
end;
|
||||
//init uv coords
|
||||
for y := 0 to 4 do
|
||||
for x := 0 to 4 do begin
|
||||
block_texcoords[y * 5 + x].init(y/4, 1 - x/4); //u, v
|
||||
end;
|
||||
end;
|
||||
|
||||
destructor TTerrainMesh.Destroy;
|
||||
begin
|
||||
inherited Destroy;
|
||||
@ -100,6 +162,10 @@ begin
|
||||
terrain.LoadFromFiles('hmp_0', 'lv_0.text', 'lv_0.tex');
|
||||
//terrain.LoadFromFiles('hmp_1', 'lv_1.text', 'lv_1.tex');
|
||||
TransformTiles;
|
||||
InitBlockStaticData;
|
||||
WriteLn(Format('terrain size: %dx%d, tris: %d',
|
||||
[terrain.TileWidth, terrain.TileHeight,
|
||||
terrain.TileWidth * terrain.TileHeight * 4 * 4 * 2]));
|
||||
end;
|
||||
|
||||
//generate textures. TODO texture atlas?
|
||||
@ -139,91 +205,42 @@ begin
|
||||
end;
|
||||
|
||||
|
||||
//cross product + normalize
|
||||
function GetNormalv(const v0, v1, v2: TVertex3f): TVertex3f;
|
||||
var
|
||||
a, b: TVertex3f;
|
||||
len: single;
|
||||
begin
|
||||
a.x := v0.x - v1.x;
|
||||
a.y := v0.y - v1.y;
|
||||
a.z := v0.z - v1.z;
|
||||
|
||||
b.x := v1.x - v2.x;
|
||||
b.y := v1.y - v2.y;
|
||||
b.z := v1.z - v2.z;
|
||||
|
||||
result.x := (a.y * b.z) - (a.z * b.y);
|
||||
result.y := (a.z * b.x) - (a.x * b.z);
|
||||
result.z := (a.x * b.y) - (a.y * b.x);
|
||||
|
||||
len := sqrt( sqr(result.x) + sqr(result.y) + sqr(result.z) );
|
||||
if len = 0 then len := 1;
|
||||
|
||||
result.x /= len;
|
||||
result.y /= len;
|
||||
result.z /= len;
|
||||
end;
|
||||
|
||||
|
||||
//draw vertices from each block
|
||||
procedure TTerrainMesh.DrawGL(opts: TRenderOpts);
|
||||
|
||||
procedure RenderBlock(var blk: TTerrainBlock);
|
||||
procedure RenderTri(i0, i1, i2:integer);
|
||||
var
|
||||
v, n: TVertex3f;
|
||||
begin
|
||||
n := GetNormalv(blk.vertices[i0], blk.vertices[i1], blk.vertices[i2]);
|
||||
v := blk.vertices[i0];
|
||||
glNormal3f(n.x, n.y, n.z);
|
||||
glTexCoord2f(v.u, v.v);
|
||||
glVertex3fv(@v);
|
||||
v := blk.vertices[i1];
|
||||
glTexCoord2f(v.u, v.v);
|
||||
glVertex3fv(@v);
|
||||
v := blk.vertices[i2];
|
||||
glTexCoord2f(v.u, v.v);
|
||||
glVertex3fv(@v);
|
||||
end;
|
||||
var
|
||||
i, x, y, stride: integer;
|
||||
begin
|
||||
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;
|
||||
RenderTri(i+1, i, i+stride);
|
||||
RenderTri(i+1, i+stride, i+stride+1);
|
||||
end;
|
||||
glEnd;
|
||||
{ //only 2 tris per block
|
||||
glBegin(GL_TRIANGLES);
|
||||
i := y * stride + x;
|
||||
RenderTri(0, 4, 24);
|
||||
RenderTri(24, 20, 0);
|
||||
glEnd;
|
||||
}
|
||||
end;
|
||||
|
||||
var
|
||||
x, y: integer;
|
||||
blk: TTerrainBlock;
|
||||
last_tex_index: integer;
|
||||
begin
|
||||
if opts.wireframe then
|
||||
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
|
||||
else
|
||||
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
||||
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
glEnableClientState(GL_NORMAL_ARRAY);
|
||||
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
last_tex_index := -1;
|
||||
|
||||
for y := 0 to terrain.TileHeight - 1 do
|
||||
for x := 0 to terrain.TileWidth - 1 do begin
|
||||
blk := blocks[y, x];
|
||||
RenderBlock(blk);
|
||||
//repeated texture binding slows this down a lot (68->30fps)
|
||||
//todo sort by texture?
|
||||
if last_tex_index <> blk.texture_index then begin
|
||||
last_tex_index := blk.texture_index;
|
||||
glBindTexture(GL_TEXTURE_2D, textures_glidx[last_tex_index]);
|
||||
end;
|
||||
|
||||
glVertexPointer(3, GL_FLOAT, sizeof(Tvector3_single), @blk.vertices[0].data[0]);
|
||||
glNormalPointer(GL_FLOAT, sizeof(Tvector3_single), @blk.normals[0].data[0]);
|
||||
glTexCoordPointer(2, GL_FLOAT, sizeof(Tvector2_single), @block_texcoords[0].data[0]);
|
||||
|
||||
if opts.points then
|
||||
glDrawArrays(GL_POINTS, 0, 25)
|
||||
else
|
||||
glDrawElements(GL_TRIANGLES, 16*2*3, GL_UNSIGNED_BYTE, @block_face_indices);
|
||||
end;
|
||||
end;
|
||||
|
||||
|
@ -37,7 +37,7 @@
|
||||
<PackageName Value="LCL"/>
|
||||
</Item1>
|
||||
</RequiredPackages>
|
||||
<Units Count="3">
|
||||
<Units Count="4">
|
||||
<Unit0>
|
||||
<Filename Value="terrain_viewer.pas"/>
|
||||
<IsPartOfProject Value="True"/>
|
||||
@ -53,6 +53,11 @@
|
||||
<IsPartOfProject Value="True"/>
|
||||
<UnitName Value="rs_world"/>
|
||||
</Unit2>
|
||||
<Unit3>
|
||||
<Filename Value="vector_util.pas"/>
|
||||
<IsPartOfProject Value="True"/>
|
||||
<UnitName Value="vector_util"/>
|
||||
</Unit3>
|
||||
</Units>
|
||||
</ProjectOptions>
|
||||
<CompilerOptions>
|
||||
|
@ -22,7 +22,7 @@ program terrain_viewer;
|
||||
uses
|
||||
sysutils, math,
|
||||
gl, glu, glext, sdl,
|
||||
terrain_mesh, rs_world;
|
||||
terrain_mesh;
|
||||
|
||||
const
|
||||
SCR_W_fscrn = 1024;
|
||||
|
33
terrain_viewer/vector_util.pas
Normal file
33
terrain_viewer/vector_util.pas
Normal file
@ -0,0 +1,33 @@
|
||||
unit vector_util;
|
||||
{$mode objfpc}{$H+}
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
matrix;
|
||||
|
||||
function GetNormal(v0, v1, v2: Tvector3_single): Tvector3_single;
|
||||
|
||||
implementation
|
||||
|
||||
//cross product + normalize
|
||||
function GetNormal(v0, v1, v2: Tvector3_single): Tvector3_single;
|
||||
var
|
||||
a, b: Tvector3_single;
|
||||
len: single;
|
||||
begin
|
||||
a := v0 - v1;
|
||||
b := v1 - v2;
|
||||
|
||||
result.data[0] := (a.data[1] * b.data[2]) - (a.data[2] * b.data[1]);
|
||||
result.data[1] := (a.data[2] * b.data[0]) - (a.data[0] * b.data[2]);
|
||||
result.data[2] := (a.data[0] * b.data[1]) - (a.data[1] * b.data[0]);
|
||||
|
||||
len := result.length;
|
||||
if len = 0 then len := 1;
|
||||
|
||||
result /= len;
|
||||
end;
|
||||
|
||||
end.
|
||||
|
Loading…
x
Reference in New Issue
Block a user