Added Grid and grid based rendering

This commit is contained in:
Maciej Samborski 2024-12-27 22:50:07 +01:00
parent 95257a4ef3
commit e936b626f3
12 changed files with 648 additions and 101 deletions

4
src/js/colors.js Normal file
View File

@ -0,0 +1,4 @@
export let Red = [1, 0, 0, 1];
export let Green = [0, 1, 0, 1];
export let Blue = [0, 0, 1, 1];
export let Brown = [0.341, 0.337, 0.204, 1];

6
src/js/colors.ts Normal file
View File

@ -0,0 +1,6 @@
export type Color = [number, number, number, number]
export let Red : Color = [1, 0, 0, 1];
export let Green : Color = [0, 1, 0, 1];
export let Blue : Color = [0, 0, 1, 1];
export let Brown : Color = [0.341, 0.337, 0.204, 1];

View File

@ -10,6 +10,9 @@ class Vec2 {
this.x = x; this.x = x;
this.y = y; this.y = y;
} }
copy() {
return new Vec2(this.x, this.y);
}
add(other) { add(other) {
this.x += other.x; this.x += other.x;
this.y += other.y; this.y += other.y;
@ -28,13 +31,43 @@ class Vec2 {
this.x -= other.x; this.x -= other.x;
this.y -= other.y; this.y -= other.y;
} }
mult(scalar) { subNew(other) {
let vec = this.copy();
vec.x -= other.x;
vec.y -= other.y;
return vec;
}
multScalar(scalar) {
this.x *= scalar; this.x *= scalar;
this.y *= scalar; this.y *= scalar;
} }
multNew(scalar) { multScalarNew(scalar) {
return new Vec2(this.x * scalar, this.y * scalar); return new Vec2(this.x * scalar, this.y * scalar);
} }
div(other) {
if (other.x == 0 ||
other.y == 0) {
throw new Error("Division by zero in Vec4");
}
this.x /= other.x;
this.y /= other.y;
}
divNew(other) {
if (other.x == 0 ||
other.y == 0) {
throw new Error("Division by zero in Vec4");
}
let vec = this.copy();
vec.x /= other.x;
vec.y /= other.y;
return vec;
}
reduce() {
throw new Error("Can't reduce Vec2!");
}
extend(value) {
return new Vec3(this.x, this.y, value);
}
splatToArray() { splatToArray() {
return [this.x, this.y]; return [this.x, this.y];
} }
@ -60,28 +93,73 @@ class Vec3 {
this.y = y; this.y = y;
this.z = z; this.z = z;
} }
copy() {
return new Vec3(this.x, this.y, this.z);
}
add(other) { add(other) {
this.x += other.x; this.x += other.x;
this.y += other.y; this.y += other.y;
this.z += other.z; this.z += other.z;
} }
addNew(other) {
let vec = this.copy();
vec.x += other.x;
vec.y += other.y;
vec.z += other.z;
return vec;
}
sub(other) { sub(other) {
this.x -= other.x; this.x -= other.x;
this.y -= other.y; this.y -= other.y;
this.z -= other.z; this.z -= other.z;
} }
subNew(other) {
let vec = this.copy();
vec.x -= other.x;
vec.y -= other.y;
vec.z -= other.z;
return vec;
}
multScalar(scalar) { multScalar(scalar) {
this.x *= scalar; this.x *= scalar;
this.y *= scalar; this.y *= scalar;
this.z *= scalar; this.z *= scalar;
} }
multScalarNew(scalar) { multScalarNew(scalar) {
let vec = new Vec3(this.x, this.y, this.z); let vec = this.copy();
vec.x *= scalar; vec.x *= scalar;
vec.y *= scalar; vec.y *= scalar;
vec.z *= scalar; vec.z *= scalar;
return vec; return vec;
} }
div(other) {
if (other.x == 0 ||
other.y == 0 ||
other.z == 0) {
throw new Error("Division by zero in Vec4");
}
this.x /= other.x;
this.y /= other.y;
this.z /= other.z;
}
divNew(other) {
if (other.x == 0 ||
other.y == 0 ||
other.z == 0) {
throw new Error("Division by zero in Vec4");
}
let vec = this.copy();
vec.x /= other.x;
vec.y /= other.y;
vec.z /= other.z;
return vec;
}
reduce() {
return new Vec2(this.x, this.y);
}
extend(value) {
return new Vec4(this.x, this.y, this.z, value);
}
splatToArray() { splatToArray() {
return [this.x, this.y, this.z]; return [this.x, this.y, this.z];
} }
@ -97,18 +175,37 @@ class Vec4 {
this.z = z; this.z = z;
this.w = w; this.w = w;
} }
copy() {
return new Vec4(this.x, this.y, this.z, this.w);
}
add(other) { add(other) {
this.x += other.x; this.x += other.x;
this.y += other.y; this.y += other.y;
this.z += other.z; this.z += other.z;
this.w += other.w; this.w += other.w;
} }
addNew(other) {
let vec = this.copy();
vec.x += other.x;
vec.y += other.y;
vec.z += other.z;
vec.w += other.w;
return vec;
}
sub(other) { sub(other) {
this.x -= other.x; this.x -= other.x;
this.y -= other.y; this.y -= other.y;
this.z -= other.z; this.z -= other.z;
this.w -= other.w; this.w -= other.w;
} }
subNew(other) {
let vec = this.copy();
vec.x -= other.x;
vec.y -= other.y;
vec.z -= other.z;
vec.w -= other.w;
return vec;
}
multScalar(scalar) { multScalar(scalar) {
this.x *= scalar; this.x *= scalar;
this.y *= scalar; this.y *= scalar;
@ -116,7 +213,7 @@ class Vec4 {
this.w *= scalar; this.w *= scalar;
} }
multScalarNew(scalar) { multScalarNew(scalar) {
let vec = new Vec4(this.x, this.y, this.z, this.w); let vec = this.copy();
vec.x *= scalar; vec.x *= scalar;
vec.y *= scalar; vec.y *= scalar;
vec.z *= scalar; vec.z *= scalar;
@ -142,7 +239,7 @@ class Vec4 {
other.w == 0) { other.w == 0) {
throw new Error("Division by zero in Vec4"); throw new Error("Division by zero in Vec4");
} }
let vec = new Vec4(this.x, this.y, this.z, this.w); let vec = this.copy();
vec.x /= other.x; vec.x /= other.x;
vec.y /= other.y; vec.y /= other.y;
vec.z /= other.z; vec.z /= other.z;
@ -152,11 +249,15 @@ class Vec4 {
reduce() { reduce() {
return new Vec3(this.x, this.y, this.z); return new Vec3(this.x, this.y, this.z);
} }
extend(_value) {
throw new Error("Can't extend Vec4");
}
} }
class Mat4 { class Mat4 {
data; data;
constructor(i) { constructor(i) {
if (i instanceof Float32Array) { if (i instanceof Float32Array) {
console.assert(i.length == 16, "Mat4 has to have 16 elements");
this.data = i; this.data = i;
return; return;
} }

View File

@ -5,14 +5,27 @@ function initializeContext(canvasId: string): WebGL2RenderingContext | null {
return ctx; return ctx;
} }
type Color = [number, number, number, number]
// TODO: Make all vectors follow one interface // TODO: Make all vectors follow one interface
interface Vector { interface Vector<P, T, N> {
copy(): T;
add(other: T): void;
addNew(other: T): T;
sub(other: T): void;
subNew(other: T): T;
multScalar(scalar: number): void;
multScalarNew(scalar: number): T;
div(other: T): void;
divNew(other: T): T;
reduce(): P;
extend(value: number): N;
} }
class Vec2 { class Vec2 implements Vector<void, Vec2, Vec3> {
x: number; x: number;
y: number; y: number;
@ -21,6 +34,10 @@ class Vec2 {
this.y = y; this.y = y;
} }
copy(): Vec2 {
return new Vec2(this.x, this.y);
}
add(other: Vec2) { add(other: Vec2) {
this.x += other.x; this.x += other.x;
this.y += other.y; this.y += other.y;
@ -44,15 +61,58 @@ class Vec2 {
this.y -= other.y; this.y -= other.y;
} }
mult(scalar: number) { subNew(other: Vec2): Vec2 {
let vec = this.copy();
vec.x -= other.x;
vec.y -= other.y;
return vec;
}
multScalar(scalar: number) {
this.x *= scalar; this.x *= scalar;
this.y *= scalar; this.y *= scalar;
} }
multNew(scalar: number): Vec2 { multScalarNew(scalar: number): Vec2 {
return new Vec2(this.x * scalar, this.y * scalar); return new Vec2(this.x * scalar, this.y * scalar);
} }
div(other: Vec2) {
if ( other.x == 0 ||
other.y == 0)
{
throw new Error("Division by zero in Vec4");
}
this.x /= other.x;
this.y /= other.y;
}
divNew(other: Vec2): Vec2 {
if ( other.x == 0 ||
other.y == 0)
{
throw new Error("Division by zero in Vec4");
}
let vec = this.copy();
vec.x /= other.x;
vec.y /= other.y;
return vec;
}
reduce(): null {
throw new Error("Can't reduce Vec2!");
}
extend(value: number): Vec3 {
return new Vec3(this.x, this.y, value);
}
splatToArray(): Array<number> { splatToArray(): Array<number> {
return [this.x, this.y]; return [this.x, this.y];
} }
@ -74,7 +134,7 @@ class Vec2 {
} }
} }
class Vec3 { class Vec3 implements Vector<Vec2, Vec3, Vec4> {
x: number; x: number;
y: number; y: number;
z: number; z: number;
@ -85,18 +145,42 @@ class Vec3 {
this.z = z; this.z = z;
} }
copy(): Vec3 {
return new Vec3(this.x, this.y, this.z);
}
add(other: Vec3) { add(other: Vec3) {
this.x += other.x; this.x += other.x;
this.y += other.y; this.y += other.y;
this.z += other.z; this.z += other.z;
} }
addNew(other: Vec3): Vec3 {
let vec = this.copy();
vec.x += other.x;
vec.y += other.y;
vec.z += other.z;
return vec;
}
sub(other: Vec3) { sub(other: Vec3) {
this.x -= other.x; this.x -= other.x;
this.y -= other.y; this.y -= other.y;
this.z -= other.z; this.z -= other.z;
} }
subNew(other: Vec3): Vec3 {
let vec = this.copy();
vec.x -= other.x;
vec.y -= other.y;
vec.z -= other.z;
return vec;
}
multScalar(scalar: number) { multScalar(scalar: number) {
this.x *= scalar; this.x *= scalar;
this.y *= scalar; this.y *= scalar;
@ -104,7 +188,7 @@ class Vec3 {
} }
multScalarNew(scalar: number): Vec3 { multScalarNew(scalar: number): Vec3 {
let vec = new Vec3(this.x, this.y, this.z); let vec = this.copy();
vec.x *= scalar; vec.x *= scalar;
vec.y *= scalar; vec.y *= scalar;
@ -113,12 +197,50 @@ class Vec3 {
return vec; return vec;
} }
div(other: Vec3) {
if ( other.x == 0 ||
other.y == 0 ||
other.z == 0)
{
throw new Error("Division by zero in Vec4");
}
this.x /= other.x;
this.y /= other.y;
this.z /= other.z;
}
divNew(other: Vec3): Vec3 {
if ( other.x == 0 ||
other.y == 0 ||
other.z == 0)
{
throw new Error("Division by zero in Vec4");
}
let vec = this.copy();
vec.x /= other.x;
vec.y /= other.y;
vec.z /= other.z;
return vec;
}
reduce(): Vec2 {
return new Vec2(this.x, this.y);
}
extend(value: number): Vec4 {
return new Vec4(this.x, this.y, this.z, value);
}
splatToArray(): Array<number> { splatToArray(): Array<number> {
return [this.x, this.y, this.z]; return [this.x, this.y, this.z];
} }
} }
class Vec4 { class Vec4 implements Vector<Vec3, Vec4, void> {
x: number; x: number;
y: number; y: number;
z: number; z: number;
@ -131,6 +253,10 @@ class Vec4 {
this.w = w; this.w = w;
} }
copy(): Vec4 {
return new Vec4(this.x, this.y, this.z, this.w);
}
add(other: Vec4) { add(other: Vec4) {
this.x += other.x; this.x += other.x;
this.y += other.y; this.y += other.y;
@ -138,6 +264,17 @@ class Vec4 {
this.w += other.w; this.w += other.w;
} }
addNew(other: Vec4): Vec4 {
let vec = this.copy();
vec.x += other.x;
vec.y += other.y;
vec.z += other.z;
vec.w += other.w;
return vec;
}
sub(other: Vec4) { sub(other: Vec4) {
this.x -= other.x; this.x -= other.x;
this.y -= other.y; this.y -= other.y;
@ -145,6 +282,17 @@ class Vec4 {
this.w -= other.w; this.w -= other.w;
} }
subNew(other: Vec4): Vec4 {
let vec = this.copy();
vec.x -= other.x;
vec.y -= other.y;
vec.z -= other.z;
vec.w -= other.w;
return vec;
}
multScalar(scalar: number) { multScalar(scalar: number) {
this.x *= scalar; this.x *= scalar;
this.y *= scalar; this.y *= scalar;
@ -153,7 +301,7 @@ class Vec4 {
} }
multScalarNew(scalar: number): Vec4 { multScalarNew(scalar: number): Vec4 {
let vec = new Vec4(this.x, this.y, this.z, this.w); let vec = this.copy();
vec.x *= scalar; vec.x *= scalar;
vec.y *= scalar; vec.y *= scalar;
@ -187,7 +335,7 @@ class Vec4 {
throw new Error("Division by zero in Vec4"); throw new Error("Division by zero in Vec4");
} }
let vec = new Vec4(this.x, this.y, this.z, this.w); let vec = this.copy();
vec.x /= other.x; vec.x /= other.x;
vec.y /= other.y; vec.y /= other.y;
@ -200,6 +348,10 @@ class Vec4 {
reduce(): Vec3 { reduce(): Vec3 {
return new Vec3(this.x, this.y, this.z); return new Vec3(this.x, this.y, this.z);
} }
extend(_value: number): void {
throw new Error("Can't extend Vec4");
}
} }
type Mat4Init = number | Float32Array; type Mat4Init = number | Float32Array;
@ -216,6 +368,7 @@ class Mat4 {
constructor(i: Mat4Init) { constructor(i: Mat4Init) {
if (i instanceof Float32Array) { if (i instanceof Float32Array) {
console.assert(i.length == 16, "Mat4 has to have 16 elements");
this.data = i; this.data = i;
return; return;
} else if (typeof(i) === 'number') { } else if (typeof(i) === 'number') {
@ -374,4 +527,4 @@ class Mat4 {
} }
} }
export { initializeContext, Mat4, Vec2, Vec3, Vec4, Color }; export { initializeContext, Mat4, Vec2, Vec3, Vec4 };

View File

@ -1,5 +1,17 @@
import { Vec3, Vec2 } from "./common.js"; import { Vec3, Vec2 } from "./common.js";
import { Texture } from "./graphics.js"; import { Texture } from "./graphics.js";
import { TileEdge } from "./world.js";
function cull(bb, screen) {
if (bb.x > screen.x)
return false;
if (bb.x + bb.z < screen.x + screen.z)
return false;
if (bb.y > screen.y)
return false;
if (bb.y + bb.w < screen.y + screen.w)
return false;
return true;
}
export function drawTriangle(gfx, positions, color) { export function drawTriangle(gfx, positions, color) {
const a_position = gfx.getAttribute("a_position"); const a_position = gfx.getAttribute("a_position");
const a_color = gfx.getAttribute("a_color"); const a_color = gfx.getAttribute("a_color");
@ -69,7 +81,7 @@ export function drawRectangle(gfx, corners, color) {
gfx.ctx.drawArrays(gfx.ctx.TRIANGLES, 0, 6); gfx.ctx.drawArrays(gfx.ctx.TRIANGLES, 0, 6);
} }
} }
export function drawIsometricCube(gfx, position, exts, color) { export function drawIsometricCube(gfx, position, exts, color, edge) {
let points = [ let points = [
position, position,
new Vec3(position.x, position.y + exts.y, position.z), new Vec3(position.x, position.y + exts.y, position.z),
@ -80,26 +92,60 @@ export function drawIsometricCube(gfx, position, exts, color) {
new Vec3(position.x + 0.5 * exts.x, position.y - 0.5 * exts.y, position.z + 0.25 * exts.z), new Vec3(position.x + 0.5 * exts.x, position.y - 0.5 * exts.y, position.z + 0.25 * exts.z),
]; ];
gfx.ctx.uniform1i(gfx.getUniform("u_isIso"), 1); gfx.ctx.uniform1i(gfx.getUniform("u_isIso"), 1);
// Top
drawRectangle(gfx, [ drawRectangle(gfx, [
points[0], points[0],
points[1], points[1],
points[3], points[3],
points[2], points[2],
], color); ], color);
drawRectangle(gfx, [ // Right Edge
points[3], if (edge == TileEdge.Right || edge == TileEdge.Both) {
points[2], drawRectangle(gfx, [
points[4], points[3],
points[5], points[2],
], color); points[4],
drawRectangle(gfx, [ points[5],
points[0], ], color);
points[2], }
points[4], // Left Edge
points[6], if (edge == TileEdge.Left || edge == TileEdge.Both) {
], color); drawRectangle(gfx, [
points[0],
points[2],
points[4],
points[6],
], color);
}
gfx.ctx.uniform1i(gfx.getUniform("u_isIso"), 0); gfx.ctx.uniform1i(gfx.getUniform("u_isIso"), 0);
} }
export function drawIsometricGrid(gfx, grid) {
let position = { ...grid.position };
let exts = new Vec3(grid.tileSize, grid.tileSize, 0);
let tileCoord = new Vec3(0, 0, 0);
// Optimize this by:
// 1. Skip all empty tiles by using lowTiles
// 2. tba
for (let k = 0; k < grid.height; ++k) {
for (let j = 0; j < grid.length; ++j) {
for (let i = 0; i < grid.width; ++i) {
tileCoord.x = i;
tileCoord.y = j;
tileCoord.z = k;
let tile = grid.getTile(tileCoord);
if (tile === null) {
position.x += grid.tileSize;
continue;
}
drawIsometricCube(gfx, new Vec3(position.x - grid.tileSize / 2 * k, position.y + grid.tileSize / 2 * k, position.z), exts, tile.fill, tile.edge);
position.x += grid.tileSize;
}
position.y -= grid.tileSize;
position.x = grid.position.x;
}
position.y = grid.position.y;
}
}
export function drawRectangleExts(gfx, position, exts, color) { export function drawRectangleExts(gfx, position, exts, color) {
const a_position = gfx.getAttribute("a_position"); const a_position = gfx.getAttribute("a_position");
const points = [ const points = [
@ -142,7 +188,7 @@ export function drawCircle(gfx, position, radius, color) {
let a = 0; let a = 0;
for (let i = 0; i < precision; ++i) { for (let i = 0; i < precision; ++i) {
var vec = Vec2.angle(a); var vec = Vec2.angle(a);
vec.mult(radius); vec.multScalar(radius);
a += angle; a += angle;
points.push(vec); points.push(vec);
} }

View File

@ -1,5 +1,17 @@
import { Vec3, Vec2, Color } from "./common.js" import {Color} from "./colors.js";
import { Vec3, Vec2, Vec4 } from "./common.js"
import { Graphics, Texture } from "./graphics.js"; import { Graphics, Texture } from "./graphics.js";
import {Grid, TileEdge} from "./world.js";
function cull(bb: Vec4, screen: Vec4): boolean {
if (bb.x > screen.x) return false;
if (bb.x + bb.z < screen.x + screen.z) return false;
if (bb.y > screen.y) return false;
if (bb.y + bb.w < screen.y + screen.w) return false;
return true;
}
export function drawTriangle(gfx: Graphics, positions: [Vec2, Vec2, Vec2], color: Color) { export function drawTriangle(gfx: Graphics, positions: [Vec2, Vec2, Vec2], color: Color) {
const a_position = gfx.getAttribute("a_position"); const a_position = gfx.getAttribute("a_position");
@ -87,7 +99,7 @@ export function drawRectangle(gfx: Graphics, corners: [Vec3, Vec3, Vec3, Vec3],
} }
export function drawIsometricCube(gfx: Graphics, position: Vec3, exts: Vec3, color: Color | Texture) { export function drawIsometricCube(gfx: Graphics, position: Vec3, exts: Vec3, color: Color | Texture, edge: TileEdge) {
let points = [ let points = [
position, position,
new Vec3(position.x, position.y + exts.y, position.z), new Vec3(position.x, position.y + exts.y, position.z),
@ -100,6 +112,7 @@ export function drawIsometricCube(gfx: Graphics, position: Vec3, exts: Vec3, col
gfx.ctx.uniform1i(gfx.getUniform("u_isIso"), 1); gfx.ctx.uniform1i(gfx.getUniform("u_isIso"), 1);
// Top
drawRectangle( drawRectangle(
gfx, gfx,
[ [
@ -110,29 +123,69 @@ export function drawIsometricCube(gfx: Graphics, position: Vec3, exts: Vec3, col
], ],
color); color);
drawRectangle( // Right Edge
gfx,
[
points[3],
points[2],
points[4],
points[5],
],
color);
drawRectangle( if (edge == TileEdge.Right || edge == TileEdge.Both) {
gfx, drawRectangle(
[ gfx,
points[0], [
points[2], points[3],
points[4], points[2],
points[6], points[4],
], points[5],
color); ],
color);
}
// Left Edge
if (edge == TileEdge.Left || edge == TileEdge.Both) {
drawRectangle(
gfx,
[
points[0],
points[2],
points[4],
points[6],
],
color);
}
gfx.ctx.uniform1i(gfx.getUniform("u_isIso"), 0); gfx.ctx.uniform1i(gfx.getUniform("u_isIso"), 0);
} }
export function drawIsometricGrid(gfx: Graphics, grid: Grid) {
let position = {...grid.position} as Vec3;
let exts = new Vec3(grid.tileSize, grid.tileSize, 0);
let tileCoord = new Vec3(0, 0, 0);
// Optimize this by:
// 1. Skip all empty tiles by using lowTiles
// 2. tba
for (let k = 0; k < grid.height; ++k) {
for (let j = 0; j < grid.length; ++j) {
for (let i = 0; i < grid.width; ++i) {
tileCoord.x = i;
tileCoord.y = j;
tileCoord.z = k;
let tile = grid.getTile(tileCoord);
if (tile === null) {
position.x += grid.tileSize;
continue;
}
drawIsometricCube(gfx, new Vec3(position.x - grid.tileSize / 2 * k, position.y + grid.tileSize / 2 * k, position.z), exts, tile.fill, tile.edge);
position.x += grid.tileSize;
}
position.y -= grid.tileSize;
position.x = grid.position.x;
}
position.y = grid.position.y;
}
}
export function drawRectangleExts(gfx: Graphics, position: Vec3, exts: Vec2, color: Color | Texture) { export function drawRectangleExts(gfx: Graphics, position: Vec3, exts: Vec2, color: Color | Texture) {
const a_position = gfx.getAttribute("a_position"); const a_position = gfx.getAttribute("a_position");
@ -184,7 +237,7 @@ export function drawCircle(gfx: Graphics, position: Vec2, radius: number, color:
let a = 0; let a = 0;
for (let i = 0; i < precision; ++i) { for (let i = 0; i < precision; ++i) {
var vec = Vec2.angle(a); var vec = Vec2.angle(a);
vec.mult(radius); vec.multScalar(radius);
a += angle; a += angle;
points.push(vec); points.push(vec);
} }

View File

@ -110,7 +110,11 @@ export class Attribute {
} }
export class Texture { export class Texture {
tex; tex;
constructor(tex) { width = 0;
height = 0;
constructor(tex, width, height) {
this.height = height;
this.width = width;
this.tex = tex; this.tex = tex;
} }
static async load(ctx, path) { static async load(ctx, path) {
@ -122,7 +126,7 @@ export class Texture {
ctx.texParameteri(ctx.TEXTURE_2D, ctx.TEXTURE_MIN_FILTER, ctx.LINEAR_MIPMAP_NEAREST); ctx.texParameteri(ctx.TEXTURE_2D, ctx.TEXTURE_MIN_FILTER, ctx.LINEAR_MIPMAP_NEAREST);
ctx.generateMipmap(ctx.TEXTURE_2D); ctx.generateMipmap(ctx.TEXTURE_2D);
ctx.bindTexture(ctx.TEXTURE_2D, null); ctx.bindTexture(ctx.TEXTURE_2D, null);
return new Texture(tex); return new Texture(tex, image.width, image.height);
} }
bind(gfx) { bind(gfx) {
gfx.ctx.uniform1i(gfx.getUniform("u_isTex"), 1); gfx.ctx.uniform1i(gfx.getUniform("u_isTex"), 1);

View File

@ -146,8 +146,12 @@ export class Attribute {
export class Texture { export class Texture {
tex: WebGLTexture | null; tex: WebGLTexture | null;
width: number = 0;
height: number = 0;
constructor(tex: WebGLTexture) { constructor(tex: WebGLTexture, width: number, height: number) {
this.height = height;
this.width = width;
this.tex = tex; this.tex = tex;
} }
@ -162,7 +166,7 @@ export class Texture {
ctx.generateMipmap(ctx.TEXTURE_2D); ctx.generateMipmap(ctx.TEXTURE_2D);
ctx.bindTexture(ctx.TEXTURE_2D, null); ctx.bindTexture(ctx.TEXTURE_2D, null);
return new Texture(tex); return new Texture(tex, image.width, image.height);
} }
bind(gfx: Graphics) { bind(gfx: Graphics) {

View File

@ -1,8 +1,9 @@
import { initializeContext, Vec3, Mat4 } from "./common.js"; import { initializeContext, Vec3, Mat4, Vec2 } from "./common.js";
import { Graphics, fullscreenCanvas, Texture, Camera } from "./graphics.js"; import { Graphics, fullscreenCanvas, Camera } from "./graphics.js";
import * as drawing from "./draw.js"; import * as drawing from "./draw.js";
import * as wasm from "./wasm.js"; import * as wasm from "./wasm.js";
import { Input } from "./input.js"; import { Input } from "./input.js";
import { Grid, Tile, tree } from "./world.js";
const vertexShader = `#version 300 es const vertexShader = `#version 300 es
in vec3 a_position; in vec3 a_position;
@ -59,30 +60,30 @@ const fragmentShader = `#version 300 es
} }
} }
`; `;
function draw(gfx, camera, dt, tex) { function draw(gfx, camera, dt, grid) {
gfx.clear(0, 0, 0, 0); gfx.clear(0, 0, 0, 0);
camera.update(dt); camera.update(dt);
let right = gfx.ctx.canvas.width; let zoom = 2;
let left = 0; let right = gfx.ctx.canvas.width * zoom;
let top = gfx.ctx.canvas.height; let left = -right * zoom;
let bottom = 0; let top = gfx.ctx.canvas.height * zoom;
let bottom = -top * zoom;
let near = -100; let near = -100;
let far = 100; let far = 100;
let mo = Mat4.orthographic(left, right, bottom, top, near, far); let mo = Mat4.orthographic(left, right, bottom, top, near, far);
let mt = Mat4.translate(camera.position); let mt = Mat4.translate(camera.position);
let mi = Mat4.isometric();
let m = mo.multNew(mt); let m = mo.multNew(mt);
gfx.ctx.uniformMatrix4fv(gfx.getUniform("u_matrix"), false, m.splat()); gfx.ctx.uniformMatrix4fv(gfx.getUniform("u_matrix"), false, m.splat());
let exts = new Vec3(50, 50, 20); //let exts = new Vec3(50, 50, 20);
for (let i = 0; i < 10; ++i) { //for (let i = 0; i < 10; ++i) {
for (let j = 0; j < 10; ++j) { // for (let j = 0; j < 10; ++j) {
//drawing.drawIsometricCube(gfx, new Vec3(exts.x * i, 1000 - exts.y * j, 0), exts, [Math.sin(angle * i + j), Math.cos(angle * i + j), -Math.sin(angle * i + j), 1]); // if ((i + j) % 2)
if ((i + j) % 2) // drawing.drawIsometricCube(gfx, new Vec3(exts.x * i, 1000 - exts.y * j, 0), exts, [1, 0, 1, 1], TileEdge.None);
drawing.drawIsometricCube(gfx, new Vec3(exts.x * i, 1000 - exts.y * j, 0), exts, [1, 1, 1, 1]); // else
else // drawing.drawIsometricCube(gfx, new Vec3(exts.x * i, 1000 - exts.y * j, 0), exts, [1, 1, 0, 1], TileEdge.None);
drawing.drawIsometricCube(gfx, new Vec3(exts.x * i, 1000 - exts.y * j, 0), exts, tex); // }
} //}
} drawing.drawIsometricGrid(gfx, grid);
} }
function addDefaultKeybinds(input, camera) { function addDefaultKeybinds(input, camera) {
input.addKeyAction("KeyA", [], camera, (c) => { input.addKeyAction("KeyA", [], camera, (c) => {
@ -122,15 +123,20 @@ function addDefaultKeybinds(input, camera) {
gfx.createUniform("u_matrix"); gfx.createUniform("u_matrix");
gfx.createUniform("u_isTex"); gfx.createUniform("u_isTex");
gfx.createUniform("u_isIso"); gfx.createUniform("u_isIso");
let city = await Texture.load(ctx, "../../assets/genetica/rt/City Night.jpg"); //let city = await Texture.load(ctx, "../../assets/genetica/rt/City Night.jpg");
let wall = await Texture.load(ctx, "../../assets/wall.png"); //let wall = await Texture.load(ctx, "../../assets/wall.png");
let camera = new Camera(new Vec3(0, 0, 0)); let camera = new Camera(new Vec3(0, 0, 0));
let grid = new Grid(new Vec3(-800, 0, 0), 100, 20, 20, 10);
grid.fillLayer(new Tile([0, 0, 1, 1]), 0);
for (let i = 0; i < 5; i++) {
tree(grid, new Vec2(Math.floor(Math.random() * 19), Math.floor(Math.random() * 19)));
}
let prevTimestamp = 0; let prevTimestamp = 0;
const frame = (timestamp) => { const frame = (timestamp) => {
const deltaTime = (timestamp - prevTimestamp) / 1000; const deltaTime = (timestamp - prevTimestamp) / 1000;
prevTimestamp = timestamp; prevTimestamp = timestamp;
fullscreenCanvas(gfx, "game"); fullscreenCanvas(gfx, "game");
draw(gfx, camera, deltaTime, wall); draw(gfx, camera, deltaTime, grid);
window.requestAnimationFrame(frame); window.requestAnimationFrame(frame);
}; };
window.requestAnimationFrame((timestamp) => { window.requestAnimationFrame((timestamp) => {

View File

@ -3,6 +3,7 @@ import { Graphics, fullscreenCanvas, Texture, Camera } from "./graphics.js";
import * as drawing from "./draw.js"; import * as drawing from "./draw.js";
import * as wasm from "./wasm.js"; import * as wasm from "./wasm.js";
import { Input } from "./input.js"; import { Input } from "./input.js";
import {Grid, Tile, tree} from "./world.js";
const vertexShader = const vertexShader =
`#version 300 es `#version 300 es
@ -64,24 +65,20 @@ const fragmentShader =
} }
`; `;
function draw(gfx: Graphics, camera: Camera, dt: number, grid: Grid) {
function draw(gfx: Graphics, camera: Camera, dt: number, tex: Texture) {
gfx.clear(0, 0, 0, 0); gfx.clear(0, 0, 0, 0);
camera.update(dt); camera.update(dt);
let zoom = 2;
let right = gfx.ctx.canvas.width; let right = gfx.ctx.canvas.width * zoom;
let left = 0; let left = -right * zoom;
let top = gfx.ctx.canvas.height; let top = gfx.ctx.canvas.height * zoom;
let bottom = 0; let bottom = -top * zoom;
let near = -100; let near = -100;
let far = 100; let far = 100;
let mo = Mat4.orthographic(left, right, bottom, top, near, far); let mo = Mat4.orthographic(left, right, bottom, top, near, far);
let mt = Mat4.translate(camera.position); let mt = Mat4.translate(camera.position);
let mi = Mat4.isometric();
let m = mo.multNew(mt); let m = mo.multNew(mt);
gfx.ctx.uniformMatrix4fv( gfx.ctx.uniformMatrix4fv(
@ -90,17 +87,17 @@ function draw(gfx: Graphics, camera: Camera, dt: number, tex: Texture) {
m.splat() m.splat()
); );
let exts = new Vec3(50, 50, 20); //let exts = new Vec3(50, 50, 20);
for (let i = 0; i < 10; ++i) { //for (let i = 0; i < 10; ++i) {
for (let j = 0; j < 10; ++j) { // for (let j = 0; j < 10; ++j) {
//drawing.drawIsometricCube(gfx, new Vec3(exts.x * i, 1000 - exts.y * j, 0), exts, [Math.sin(angle * i + j), Math.cos(angle * i + j), -Math.sin(angle * i + j), 1]); // if ((i + j) % 2)
// drawing.drawIsometricCube(gfx, new Vec3(exts.x * i, 1000 - exts.y * j, 0), exts, [1, 0, 1, 1], TileEdge.None);
// else
// drawing.drawIsometricCube(gfx, new Vec3(exts.x * i, 1000 - exts.y * j, 0), exts, [1, 1, 0, 1], TileEdge.None);
// }
//}
if ((i + j) % 2) drawing.drawIsometricGrid(gfx, grid);
drawing.drawIsometricCube(gfx, new Vec3(exts.x * i, 1000 - exts.y * j, 0), exts, [1, 1, 1, 1]);
else
drawing.drawIsometricCube(gfx, new Vec3(exts.x * i, 1000 - exts.y * j, 0), exts, tex);
}
}
} }
function addDefaultKeybinds(input: Input, camera: Camera) { function addDefaultKeybinds(input: Input, camera: Camera) {
@ -158,18 +155,26 @@ function addDefaultKeybinds(input: Input, camera: Camera) {
gfx.createUniform("u_isTex"); gfx.createUniform("u_isTex");
gfx.createUniform("u_isIso"); gfx.createUniform("u_isIso");
let city = await Texture.load(ctx, "../../assets/genetica/rt/City Night.jpg"); //let city = await Texture.load(ctx, "../../assets/genetica/rt/City Night.jpg");
let wall = await Texture.load(ctx, "../../assets/wall.png"); //let wall = await Texture.load(ctx, "../../assets/wall.png");
let camera = new Camera(new Vec3(0, 0, 0)); let camera = new Camera(new Vec3(0, 0, 0));
let grid = new Grid(new Vec3(-800, 0, 0), 100, 20, 20, 10);
grid.fillLayer(new Tile([0, 0, 1, 1]), 0);
for (let i = 0; i < 5; i++) {
tree(grid, new Vec2(Math.floor(Math.random() * 19), Math.floor(Math.random() * 19)));
}
let prevTimestamp = 0; let prevTimestamp = 0;
const frame = (timestamp: number) => { const frame = (timestamp: number) => {
const deltaTime = (timestamp - prevTimestamp)/1000; const deltaTime = (timestamp - prevTimestamp)/1000;
prevTimestamp = timestamp; prevTimestamp = timestamp;
fullscreenCanvas(gfx, "game"); fullscreenCanvas(gfx, "game");
draw(gfx, camera, deltaTime, wall); draw(gfx, camera, deltaTime, grid);
window.requestAnimationFrame(frame); window.requestAnimationFrame(frame);
} }

73
src/js/world.js Normal file
View File

@ -0,0 +1,73 @@
import * as Colors from "./colors.js";
import { Vec3 } from "./common.js";
export var TileEdge;
(function (TileEdge) {
TileEdge[TileEdge["None"] = 0] = "None";
TileEdge[TileEdge["Left"] = 1] = "Left";
TileEdge[TileEdge["Right"] = 2] = "Right";
TileEdge[TileEdge["Both"] = 3] = "Both";
})(TileEdge || (TileEdge = {}));
export class Tile {
fill = [1, 0, 1, 1];
edge = TileEdge.None;
constructor(fill, edge) {
if (fill !== undefined)
this.fill = fill;
if (edge !== undefined)
this.edge = edge;
}
}
export class Grid {
position;
tiles3d;
tileSize;
width;
length;
height;
constructor(position, tileSize, width, length, height) {
this.tiles3d = new Array(height);
this.position = position;
this.tileSize = tileSize;
this.width = width;
this.length = length;
this.height = height;
let layer = new Array(width * length);
for (let i = 0; i < this.height; ++i)
this.tiles3d[i] = { ...layer };
}
fillLayer(tile, z) {
for (let i = 0; i < this.width; ++i) {
for (let j = 0; j < this.length; ++j) {
this.tiles3d[z][i + j * this.width] = { ...tile };
}
}
for (let i = 0; i < this.width; ++i) {
this.tiles3d[z][this.length * i - 1] = { ...tile, edge: TileEdge.Right };
}
for (let i = 0; i < this.length - 1; ++i) {
this.tiles3d[z][this.width * (this.length - 1) + i] = { ...tile, edge: TileEdge.Left };
}
this.tiles3d[z][this.width * this.length - 1] = { ...tile, edge: TileEdge.Both };
}
setTile(tile, coord) {
let index = coord.x + coord.y * this.width;
this.tiles3d[coord.z][index] = { ...tile };
}
getTile(coord) {
let index = coord.x + coord.y * this.width;
let tile = this.tiles3d[coord.z][index];
if (tile === undefined)
return null;
return tile;
}
}
export function tree(grid, position) {
grid.setTile(new Tile(Colors.Brown, TileEdge.Both), new Vec3(position.x, position.y, 1));
grid.setTile(new Tile(Colors.Green, TileEdge.Both), new Vec3(position.x, position.y, 4));
grid.setTile(new Tile(Colors.Green, TileEdge.Both), new Vec3(position.x, position.y + 1, 3));
grid.setTile(new Tile(Colors.Green, TileEdge.Both), new Vec3(position.x, position.y + 1, 4));
grid.setTile(new Tile(Colors.Green, TileEdge.Both), new Vec3(position.x, position.y, 5));
grid.setTile(new Tile(Colors.Green, TileEdge.Both), new Vec3(position.x + 1, position.y, 3));
grid.setTile(new Tile(Colors.Green, TileEdge.Both), new Vec3(position.x + 1, position.y, 4));
grid.setTile(new Tile(Colors.Green, TileEdge.Both), new Vec3(position.x, position.y, 6));
}

92
src/js/world.ts Normal file
View File

@ -0,0 +1,92 @@
import * as Colors from "./colors.js";
import {Vec2, Vec3} from "./common.js";
import {Texture} from "./graphics.js";
export enum TileEdge {
None,
Left,
Right,
Both,
}
export class Tile {
fill: Texture | Colors.Color = [1, 0, 1, 1];
edge: TileEdge = TileEdge.None;
constructor(fill?: Texture | Colors.Color, edge?: TileEdge) {
if (fill !== undefined)
this.fill = fill;
if (edge !== undefined)
this.edge = edge;
}
}
export class Grid {
position: Vec3;
tiles3d: Tile[][];
tileSize: number;
width: number;
length: number;
height: number;
constructor(position: Vec3, tileSize: number, width: number, length: number, height: number) {
this.tiles3d = new Array<Tile[]>(height);
this.position = position;
this.tileSize = tileSize;
this.width = width;
this.length = length;
this.height = height;
let layer = new Array(width * length);
for (let i = 0; i < this.height; ++i)
this.tiles3d[i] = {...layer};
}
fillLayer(tile: Tile, z: number) {
for (let i = 0; i < this.width; ++i) {
for (let j = 0; j < this.length; ++j) {
this.tiles3d[z][i + j * this.width] = {...tile};
}
}
for (let i = 0; i < this.width; ++i) {
this.tiles3d[z][this.length * i - 1] = {...tile, edge: TileEdge.Right};
}
for (let i = 0; i < this.length - 1; ++i) {
this.tiles3d[z][this.width * (this.length - 1) + i] = {...tile, edge: TileEdge.Left};
}
this.tiles3d[z][this.width * this.length - 1] = {...tile, edge: TileEdge.Both};
}
setTile(tile: Tile, coord: Vec3) {
let index = coord.x + coord.y * this.width;
this.tiles3d[coord.z][index] = {...tile};
}
getTile(coord: Vec3): Tile | null {
let index = coord.x + coord.y * this.width;
let tile = this.tiles3d[coord.z][index];
if (tile === undefined) return null;
return tile;
}
}
export function tree(grid: Grid, position: Vec2) {
grid.setTile(new Tile(Colors.Brown, TileEdge.Both), new Vec3(position.x, position.y, 1));
grid.setTile(new Tile(Colors.Green, TileEdge.Both), new Vec3(position.x, position.y, 4));
grid.setTile(new Tile(Colors.Green, TileEdge.Both), new Vec3(position.x, position.y + 1, 3));
grid.setTile(new Tile(Colors.Green, TileEdge.Both), new Vec3(position.x, position.y + 1, 4));
grid.setTile(new Tile(Colors.Green, TileEdge.Both), new Vec3(position.x, position.y, 5));
grid.setTile(new Tile(Colors.Green, TileEdge.Both), new Vec3(position.x + 1, position.y, 3));
grid.setTile(new Tile(Colors.Green, TileEdge.Both), new Vec3(position.x + 1, position.y, 4));
grid.setTile(new Tile(Colors.Green, TileEdge.Both), new Vec3(position.x, position.y, 6));
}