337 lines
7.5 KiB
C#
337 lines
7.5 KiB
C#
using System;
|
|
using System.IO;
|
|
using System.Collections.Generic;
|
|
|
|
using ImageSharp;
|
|
using ImageSharp.PixelFormats;
|
|
|
|
using OpenTK.Graphics.OpenGL4;
|
|
using OpenTK;
|
|
|
|
using org.hwo.contracts;
|
|
using org.budnhead.exceptions;
|
|
using org.budnhead.graphics;
|
|
|
|
namespace org.budnhead.core
|
|
{
|
|
public class SquaredMap : WorldObject
|
|
{
|
|
public static SquaredMap activeMap;
|
|
|
|
public static float edge = 5;
|
|
public static readonly float maxHeight = 64.0f;
|
|
|
|
private int width,
|
|
height;
|
|
|
|
public Vector3[] corners;
|
|
|
|
private MapModel3D model;
|
|
|
|
public SquaredMap(int width,int height)
|
|
{
|
|
activeMap = this;
|
|
|
|
this.width = width;
|
|
this.height = height;
|
|
|
|
generateBaseMap();
|
|
}
|
|
|
|
public override Model3D getModel3D()
|
|
{
|
|
return this.model;
|
|
}
|
|
|
|
public SquaredMap(Image heightMap)
|
|
{
|
|
activeMap = this;
|
|
|
|
this.width = heightMap.Width-1;
|
|
this.height = heightMap.Height-1;
|
|
|
|
generateBaseMap();
|
|
loadHeightMap( heightMap );
|
|
|
|
model.rebind();
|
|
}
|
|
|
|
|
|
protected void generateBaseMap(){
|
|
this.model = new MapModel3D(this,width,height);
|
|
|
|
this.corners = new Vector3[8];
|
|
this.corners[0] = new Vector3();
|
|
this.corners[1] = new Vector3(width * edge,0,0);
|
|
this.corners[2] = new Vector3(width * edge,height * edge,0);
|
|
this.corners[3] = new Vector3(0,height * edge,0);
|
|
this.corners[4] = new Vector3(0,0,maxHeight);
|
|
this.corners[5] = new Vector3(width * edge,0,maxHeight);
|
|
this.corners[6] = new Vector3(width * edge,height * edge,maxHeight);
|
|
this.corners[7] = new Vector3(0,height * edge,maxHeight);
|
|
|
|
for (int y=0;y<height;y++){
|
|
for (int x=0;x<width;x++){
|
|
int bi = 4 * (x + (y * width));
|
|
float cx = (edge * x) + (edge / 2);
|
|
float cy = (edge * y) + (edge / 2);
|
|
Vector3 a = new Vector3(),b = new Vector3(),c = new Vector3();
|
|
|
|
a.X = (edge * (x+1));
|
|
a.Y = (edge * y);
|
|
b.X = (edge * x);
|
|
b.Y = (edge * y);
|
|
c.X = cx;
|
|
c.Y = cy;
|
|
model.triangles[bi].vertexes(c,b,a);
|
|
|
|
a.X = (edge * (x+1));
|
|
a.Y = (edge * (y+1));
|
|
b.X = (edge * (x+1));
|
|
b.Y = (edge * y);
|
|
c.X = cx;
|
|
c.Y = cy;
|
|
model.triangles[bi+1].vertexes(c,b,a);
|
|
|
|
a.X = (edge * x);
|
|
a.Y = (edge * (y+1));
|
|
b.X = (edge * (x+1));
|
|
b.Y = (edge * (y+1));
|
|
c.X = cx;
|
|
c.Y = cy;
|
|
model.triangles[bi+2].vertexes(c,b,a);
|
|
|
|
a.X = (edge * x);
|
|
a.Y = (edge * y);
|
|
b.X = (edge * x);
|
|
b.Y = (edge * (y+1));
|
|
c.X = cx;
|
|
c.Y = cy;
|
|
model.triangles[bi+3].vertexes(c,b,a);
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
public void loadHeightMap(Image _heightMap){
|
|
Rgba32[] pixels = _heightMap.Pixels;
|
|
|
|
for (int y = 0; y < height; y++)
|
|
{
|
|
for (int x = 0; x < width; x++)
|
|
{
|
|
Rgba32[] pixel = new Rgba32[4];
|
|
float[] h = new float[4];
|
|
float ah = 0;
|
|
|
|
pixel[0] = pixels[ x + ((y+0) * (width + 1)) + 0 ];
|
|
pixel[1] = pixels[ x + ((y+0) * (width + 1)) + 1 ];
|
|
pixel[2] = pixels[ x + ((y+1) * (width + 1)) + 1 ];
|
|
pixel[3] = pixels[ x + ((y+1) * (width + 1)) + 0 ];
|
|
|
|
for (int n=0;n<4;n++){
|
|
h[n] = (pixel[n].R + pixel[n].G + pixel[n].B) * maxHeight / 768;
|
|
ah += h[n]/4;
|
|
}
|
|
|
|
int bi = 12 * (x + (y * width));
|
|
|
|
model.vertexes[ bi++ ].Z = ah;
|
|
model.vertexes[ bi++ ].Z = h[0];
|
|
model.vertexes[ bi++ ].Z = h[1];
|
|
model.vertexes[ bi++ ].Z = ah;
|
|
model.vertexes[ bi++ ].Z = h[1];
|
|
model.vertexes[ bi++ ].Z = h[2];
|
|
model.vertexes[ bi++ ].Z = ah;
|
|
model.vertexes[ bi++ ].Z = h[2];
|
|
model.vertexes[ bi++ ].Z = h[3];
|
|
model.vertexes[ bi++ ].Z = ah;
|
|
model.vertexes[ bi++ ].Z = h[3];
|
|
model.vertexes[ bi++ ].Z = h[0];
|
|
|
|
}
|
|
}
|
|
|
|
model.computeNormals();
|
|
model.colorMap();
|
|
model.smoothNormals();
|
|
|
|
model.rebind();
|
|
}
|
|
|
|
|
|
private int tileIndex(int x,int y){
|
|
return (x + (width * y)) * 4;
|
|
}
|
|
|
|
public Vector3 ground(Vector2 _xy){
|
|
Vector2 xy = _xy / edge;
|
|
|
|
if ((xy.X < 0)||(xy.Y<0)||(xy.X>=width)||(xy.Y>=height)){
|
|
throw new OutOfWorldException();
|
|
}
|
|
|
|
int i1 = tileIndex((int)xy.X,(int)xy.Y);
|
|
|
|
for (int n=0;n<4;n++){
|
|
Vector3 A,B,C,P,V;
|
|
Vector3 p = new Vector3();
|
|
|
|
A = model.triangles[i1+n].VertexA;
|
|
B = model.triangles[i1+n].VertexB;
|
|
C = model.triangles[i1+n].VertexC;
|
|
|
|
P = new Vector3(_xy);
|
|
V = Vector3.UnitZ;
|
|
|
|
if (Geometry.intersectTriangle(P,V,A,B,C,out p)){
|
|
Console.WriteLine("Grounded to: {0}",p);
|
|
return p;
|
|
} else {
|
|
Console.WriteLine("No Ground at triangle {0}",n);
|
|
}
|
|
|
|
}
|
|
|
|
throw new OutOfWorldException();
|
|
}
|
|
|
|
public Vector2 toTileBorderless(Vector2 world){
|
|
Vector2 tile = world / edge;
|
|
return new Vector2((int)tile.X,(int)tile.Y);
|
|
}
|
|
|
|
public Vector2 toTile(Vector2 world){
|
|
Vector2 tile = new Vector2();
|
|
|
|
new BooleanConditional()
|
|
.does(delegate { tile = world / edge; })
|
|
.requires(tile.X >= 0)
|
|
.requires(tile.Y >= 0)
|
|
.requires(tile.X < width)
|
|
.requires(tile.Y < height)
|
|
.throws<OutOfWorldException>();
|
|
|
|
return new Vector2((int)tile.X,(int)tile.Y);
|
|
}
|
|
|
|
public Vector2 intersectTile(Vector3 P,Vector3 V){
|
|
return toTile( intersect(P,V).Xy );
|
|
}
|
|
|
|
public Vector3 intersect(Vector3 P,Vector3 V){
|
|
Vector3
|
|
A = new Vector3(), // Lower plane intersection
|
|
B = new Vector3(); // Upper plane intersection
|
|
|
|
Console.WriteLine("P: {0} V: {1}",P,V);
|
|
|
|
A = P + ( V * ( -P.Z / V.Z) );
|
|
Console.WriteLine("Lower Plane Intersection: {0}",A);
|
|
|
|
B = P + ( V * ( (SquaredMap.maxHeight-P.Z) / V.Z) );
|
|
Console.WriteLine("Higher Plane Intersection: {0}",B);
|
|
|
|
Vector2 start = Vector2.ComponentMin(A.Xy,B.Xy);
|
|
Vector2 finish = Vector2.ComponentMax(A.Xy,B.Xy);
|
|
Vector2 step = finish - start;
|
|
|
|
float steps = (step.X * step.Y) / 25.0f;
|
|
step /= steps;
|
|
|
|
int isteps = (int)steps;
|
|
|
|
Vector2 pos = start,
|
|
tile = toTileBorderless(pos);
|
|
|
|
Console.WriteLine("Interpolation: {0} - {1} - {2}",start,step,finish);
|
|
|
|
for (int n=0;n<=isteps;){
|
|
Console.WriteLine("IP RUN[{0}]: {1} Tile: {2}",n,pos,tile);
|
|
if (new BooleanConditional()
|
|
.requires( tile.X >= 0)
|
|
.requires( tile.Y >= 0)
|
|
.requires( tile.X < width)
|
|
.requires( tile.Y < height)
|
|
.isSuccess()
|
|
){
|
|
Vector3 isect = new Vector3();
|
|
for (int nt=0;nt<4;nt++){
|
|
try {
|
|
Model3D.Triangle t = model.triangles[tileIndex((int)tile.X,(int)tile.Y)];
|
|
if (Geometry.intersectTriangle(
|
|
P,
|
|
V,
|
|
t.VertexA,
|
|
t.VertexB,
|
|
t.VertexC,
|
|
out isect
|
|
)){
|
|
return isect;
|
|
}
|
|
|
|
} catch (OutOfWorldException){
|
|
}
|
|
}
|
|
}
|
|
|
|
Vector2 nextTile = tile;
|
|
while (nextTile.Equals(tile)){
|
|
pos += step;
|
|
nextTile = toTileBorderless(pos);
|
|
n++;
|
|
}
|
|
tile = nextTile;
|
|
}
|
|
throw new OutOfWorldException();
|
|
}
|
|
|
|
public void highlight(Vector2 tile){
|
|
new BooleanConditional()
|
|
.requires(tile.X >= 0)
|
|
.requires(tile.Y >= 0)
|
|
.requires(tile.X < width)
|
|
.requires(tile.Y < height)
|
|
.does(delegate {
|
|
|
|
for (int n=0;n<4;n++){
|
|
Model3D.Triangle t = model.triangles[tileIndex((int)tile.X,(int)tile.Y)+n];
|
|
t.ColorA *= 2;
|
|
t.ColorB *= 2;
|
|
t.ColorC *= 2;
|
|
}
|
|
|
|
model.rebind();
|
|
});
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
class MapModel3D : Model3D {
|
|
|
|
SquaredMap map;
|
|
|
|
public MapModel3D(SquaredMap map,int width,int height){
|
|
this.map = map;
|
|
prepareBuffers(width * height * 4);
|
|
colorMap();
|
|
}
|
|
|
|
public void colorMap(){
|
|
for (int n=0; n < vertexes.Length; n++){
|
|
colors[n] = new Vector4(
|
|
0.50f + (0.2f * vertexes[n].Z / SquaredMap.maxHeight),
|
|
0.25f + (0.50f * vertexes[n].Z / SquaredMap.maxHeight),
|
|
0.10f + (0.80f * vertexes[n].Z / SquaredMap.maxHeight),
|
|
1.0f
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
}
|