Archived
Private
Public Access
1
0

Initial commit

This commit is contained in:
2022-09-04 12:45:01 +02:00
commit f4a01d6a69
11601 changed files with 4206660 additions and 0 deletions

View File

@@ -0,0 +1,11 @@
namespace TSE {
export interface IAsset {
readonly name: string;
readonly data: any;
}
}

View File

@@ -0,0 +1,11 @@
namespace TSE {
export interface IAssetLoader {
readonly supportedExtensions: string[];
loadAsset(assetName: string): void;
}
}

View File

@@ -0,0 +1,54 @@
namespace TSE {
export const MESSAGE_ASSET_LOADER_ASSET_LOADED = "MESSAGE_ASSET_LOADER_ASSET_LOADED::";
export class AssetManager {
private static _loaders: IAssetLoader[] = [];
private static _loadedAssets: { [name: string]: IAsset } = {};
private constructor() {}
public static initialize(): void {
this.registerLoader(new ImageAssetLoader());
this.registerLoader(new JsonAssetLoader());
}
public static registerLoader(loader: IAssetLoader): void {
AssetManager._loaders.push(loader);
}
public static onAssetLoaded(asset: IAsset): void {
AssetManager._loadedAssets[asset.name] = asset;
Message.send(MESSAGE_ASSET_LOADER_ASSET_LOADED + asset.name, this, asset);
}
public static loadAsset(assetName: string): void {
let extention = assetName.split('.').pop().toLowerCase();
for (let loader of AssetManager._loaders) {
if (loader.supportedExtensions.indexOf(extention) !== -1) {
loader.loadAsset(assetName);
return;
}
}
console.warn("Unable to load asset with extention: '" + extention + "'. no loader was found.");
}
public static isAssetLoaded(assetName: string): boolean {
return AssetManager._loadedAssets[assetName] !== undefined;
}
public static getAsset(assetName: string): IAsset {
if (AssetManager.isAssetLoaded(assetName)) {
return AssetManager._loadedAssets[assetName];
} else {
AssetManager.loadAsset(assetName);
}
return undefined;
}
}
}

View File

@@ -0,0 +1,42 @@
namespace TSE {
export class ImageAsset implements IAsset {
public readonly name: string;
public readonly data: HTMLImageElement;
public constructor(name: string, data: HTMLImageElement) {
this.name = name;
this.data = data;
}
public get width(): number {
return this.data.width;
}
public get height(): number {
return this.data.height;
}
}
export class ImageAssetLoader implements IAssetLoader {
public get supportedExtensions(): string[] { return ["png", "gif", "jpg", "jpeg"] }
public loadAsset(assetName: string): void {
let image: HTMLImageElement = new Image();
image.onload = this.onImageLoaded.bind(this, assetName, image);
image.src = assetName;
}
private onImageLoaded(assetName: string, image: HTMLImageElement): void {
console.log("onImageLoaded: assetName/image", assetName, image);
let asset = new ImageAsset(assetName, image);
AssetManager.onAssetLoaded(asset);
}
}
}

View File

@@ -0,0 +1,39 @@
namespace TSE {
export class JsonAsset implements IAsset {
public readonly name: string;
public readonly data: any;
public constructor(name: string, data: any) {
this.name = name;
this.data = data;
}
}
export class JsonAssetLoader implements IAssetLoader {
public get supportedExtensions(): string[] { return ["json"] }
public loadAsset(assetName: string): void {
let request: XMLHttpRequest = new XMLHttpRequest();
request.open("GET", assetName);
request.addEventListener("load", this.onJsonLoaded.bind(this, assetName, request));
request.send();
}
private onJsonLoaded(assetName: string, request: XMLHttpRequest): void {
console.log("onJsonLoaded: assetName/request", assetName, request);
if (request.readyState === request.DONE) {
let json = JSON.parse(request.responseText);
let asset = new JsonAsset(assetName, json);
AssetManager.onAssetLoaded(asset);
}
}
}
}

View File

@@ -0,0 +1,45 @@
namespace TSE {
export class BaseBehavior {
public name: string;
protected _data: IBehaviorData;
protected _owner: SimObject;
public constructor(data: IBehaviorData) {
this._data = data;
this.name = data.name;
}
public set owner(value: SimObject) { this._owner = value; }
public update(time: number): void {}
public apply(userData: any): void {}
}
export interface IBehaviorBuilder {
get type(): string;
buildFromJson(json: any): BaseBehavior;
}
export interface IBehaviorData {
name: string;
setFromJson(json: any): void;
}
export class BehaviorManager {
private static _registeredBuilders: {[type: string]: IBehaviorBuilder} = {};
public static registerBuilder(builder: IBehaviorBuilder): void {
BehaviorManager._registeredBuilders[builder.type] = builder;
}
public static extractComponent(json: any): BaseBehavior {
if (BehaviorManager._registeredBuilders[json?.type] === undefined) return undefined;
return BehaviorManager._registeredBuilders[json.type].buildFromJson(json);
}
}
}

View File

@@ -0,0 +1,46 @@
///<reference path="behavior.ts"/>
namespace TSE {
export class RotationBehaviorData implements IBehaviorData {
public name: string;
public rotation: Vector3 = Vector3.zero;
public setFromJson(json: any): void {
if (json?.name === undefined) return;
this.name = String(json.name);
this.rotation.setFromJson(json.rotation);
}
}
export class RotationBehaviorBuilder implements IBehaviorBuilder {
public buildFromJson(json: any): TSE.BaseBehavior {
let data = new RotationBehaviorData();
data.setFromJson(json);
return new RotationBehavior(data);
}
public get type(): string { return "rotation"; }
}
export class RotationBehavior extends BaseBehavior{
private _rotation: Vector3;
public constructor(data: RotationBehaviorData) {
super(data);
this._rotation = data.rotation;
}
public update(time: number) {
this._owner.transform.rotation.add(this._rotation);
super.update(time);
}
}
BehaviorManager.registerBuilder(new RotationBehaviorBuilder());
}

View File

@@ -0,0 +1,35 @@
namespace TSE {
export abstract class BaseComponent {
public name: string;
protected _owner: SimObject;
protected _data: IComponentData;
public constructor(data: IComponentData) {
this._data = data;
this.name = data.name;
}
public get owner(): SimObject { return this._owner; }
public set owner(value: SimObject) { this._owner = value; }
public load(): void { }
public update(time: number): void { }
public render(shader: Shader): void { }
}
export interface IComponentData {
name: string;
setFromJson(json: any): void;
}
export interface IComponentBuilder {
get type(): string;
buildFromJson(json: any): BaseComponent;
}
}

View File

@@ -0,0 +1,18 @@
namespace TSE {
export class ComponentManager {
private static _registeredBuilders: {[type: string]: IComponentBuilder} = {};
public static registerBuilder(builder: IComponentBuilder): void {
ComponentManager._registeredBuilders[builder.type] = builder;
}
public static extractComponent(json: any): BaseComponent {
if (ComponentManager._registeredBuilders[json?.type] === undefined) return undefined;
return ComponentManager._registeredBuilders[json.type].buildFromJson(json);
}
}
}

View File

@@ -0,0 +1,94 @@
///<reference path="componentManager.ts"/>
namespace TSE {
export class SpriteComponentData implements IComponentData {
public name: string;
public materialName: string;
public setFromJson(json: any): void {
this.name = json.name !== undefined ? String(json.name) : undefined;
this.materialName = json.materialName !== undefined ? String(json.materialName) : undefined;
}
}
export class SpriteComponentBuilder implements IComponentBuilder {
public get type(): string { return "sprite"; }
public buildFromJson(json: any): BaseComponent {
let data = new SpriteComponentData();
data.setFromJson(json);
return new SpriteComponent(data);
}
}
export class SpriteComponent extends BaseComponent {
protected _sprite: Sprite;
public constructor(data: SpriteComponentData) {
super(data);
this._sprite = new Sprite(data.name, data.materialName);
}
public load(): void {
this._sprite.load();
}
public render(shader: Shader): void {
this._sprite.draw(shader, this.owner.worldMatrix);
super.render(shader);
}
}
export class AnimatedSpriteComponentData extends SpriteComponentData {
public frameWidth: number;
public frameHeight: number;
public frameCount: number;
public frameSequence: number[];
public setFromJson(json: any): void {
super.setFromJson(json);
this.frameWidth = Number(json.frameWidth);
this.frameHeight = Number(json.frameHeight);
this.frameCount = Number(json.frameCount);
this.frameSequence = json.frameSequence;
}
}
export class AnimatedSpriteComponentBuilder extends SpriteComponentBuilder {
public get type(): string { return "animated_sprite"; }
public buildFromJson(json: any): BaseComponent {
let data = new AnimatedSpriteComponentData();
data.setFromJson(json);
return new AnimatedSpriteComponent(data);
}
}
export class AnimatedSpriteComponent extends SpriteComponent {
public constructor(data: AnimatedSpriteComponentData) {
super(data);
this._sprite = new AnimatedSprite(this.name, data.materialName, data.frameWidth, data.frameHeight, data.frameCount, data.frameSequence);
}
public update(time: number) {
this._sprite.update(time);
super.update(time);
}
}
ComponentManager.registerBuilder(new SpriteComponentBuilder());
ComponentManager.registerBuilder(new AnimatedSpriteComponentBuilder());
}

32
C#/TSEngine/Core/GL/gl.ts Normal file
View File

@@ -0,0 +1,32 @@
namespace TSE {
/** The WebGL rendering context. */
export var gl: WebGLRenderingContext;
/**
* Responsible for setting up a WebGL rendering context.
* */
export class GLUtilities {
/**
* Initialize WebGL
* @param elementId The id of the canvas element for rendering the game output.
*/
public static initialize(elementId?: string): HTMLCanvasElement {
let canvas: HTMLCanvasElement;
if (elementId !== undefined)
canvas = document.getElementById(elementId) as HTMLCanvasElement;
if (elementId === undefined) {
canvas = document.createElement("canvas") as HTMLCanvasElement;
document.body.appendChild(canvas);
}
gl = canvas.getContext("webgl");
if (gl === undefined)
throw new Error("Unable to initialize WebGL");
return canvas;
}
}
}

View File

@@ -0,0 +1,188 @@
///<reference path="gl.ts"/>
namespace TSE {
/** Represents the Information needed for a GLBuffer Attribute. */
export class AttributeInfo {
/** The location of this Attribute */
public location: number;
/** The size (number of elements) in this Attribute (i.e Vector3 = 3). */
public size: number;
/** The number of Elements from the beginning of the Buffer. */
public offset: number;
public constructor(location: number, size: number) {
this.location = location;
this.size = size;
this.offset = 0;
}
}
/** Represents a WebGLBuffer */
export class GLBuffer {
private _hasAttributeLocation: boolean = false;
private _elementSize: number;
private _stride: number;
private _buffer: WebGLBuffer;
private _targetBufferType: number;
private _dataType: number;
private _mode: number;
private _typeSize: number;
private _data: number[] = [];
private _attributes: AttributeInfo[] = [];
/**
* Creates a new GLBuffer.
* @param elementSize The size of each Element in this Buffer.
* @param dataType The data type of this Buffer. Default: FLOAT
* @param targetBufferType The Buffer target type. ARRAY_BUFFER, ELEMENT_ARRAY_BUFFER. Default: ARRAY_BUFFER
* @param mode The drawing mode of this Buffer. TRIANGLES, LINES. Default: TRIANGLES
*/
public constructor(dataType: number = gl.FLOAT, targetBufferType: number = gl.ARRAY_BUFFER, mode: number = gl.TRIANGLES) {
this._elementSize = 0;
this._dataType = dataType;
this._targetBufferType = targetBufferType;
this._mode = mode;
// Determine Byte size
switch (this._dataType) {
case gl.FLOAT:
case gl.INT:
case gl.UNSIGNED_INT:
this._typeSize = 4;
break;
case gl.SHORT:
case gl.UNSIGNED_SHORT:
this._typeSize = 2;
break;
case gl.BYTE:
case gl.UNSIGNED_BYTE:
this._typeSize = 1;
break;
default:
throw new Error("Unrecognized data type: " + dataType.toString());
}
this._buffer = gl.createBuffer();
}
/** Destroys this Buffer. */
public destroy(): void {
gl.deleteBuffer(this._buffer);
}
/**
* Binds this Buffer.
* @param normalized Indecates if the BufferData should be normalized. Default: false
*/
public bind(normalized: boolean = false): void {
gl.bindBuffer(this._targetBufferType, this._buffer);
if (this._hasAttributeLocation) {
for (let it of this._attributes) {
gl.vertexAttribPointer(it.location, it.size, this._dataType, normalized, this._stride, it.offset * this._typeSize);
gl.enableVertexAttribArray(it.location);
}
}
}
/** Unbinds this Buffer. */
public unbind(): void {
if (this._hasAttributeLocation) {
for (let it of this._attributes) {
gl.disableVertexAttribArray(it.location);
}
}
gl.bindBuffer(this._targetBufferType, undefined);
}
/**
* Adds an Attribute with the provided info to this Buffer.
* @param info The Attribute Information.
*/
public addAttributeLocation(info: AttributeInfo): void {
this._hasAttributeLocation = true;
info.offset = this._elementSize;
this._attributes.push(info);
this._elementSize += info.size;
this._stride = this._elementSize * this._typeSize;
}
/**
* Adds Data to this Buffer.
* @param data The Data for this Buffer.
*/
public pushBackData(data: number[]): void {
for (let d of data)
this._data.push(d);
}
public clearData(): void {
this._data.length = 0;
}
public setData(data: number[]): void {
this.clearData();
this.pushBackData(data);
}
/** Upload the BufferData to the GPU */
public upload(): void {
gl.bindBuffer(this._targetBufferType, this._buffer);
let bufferData: ArrayBuffer;
switch (this._dataType) {
case gl.FLOAT:
bufferData = new Float32Array(this._data);
break;
case gl.INT:
bufferData = new Int32Array(this._data);
break;
case gl.UNSIGNED_INT:
bufferData = new Uint32Array(this._data);
break;
case gl.SHORT:
bufferData = new Int16Array(this._data);
break;
case gl.UNSIGNED_SHORT:
bufferData = new Uint16Array(this._data);
break;
case gl.BYTE:
bufferData = new Int8Array(this._data);
break;
case gl.UNSIGNED_BYTE:
bufferData = new Uint8Array(this._data);
break;
}
gl.bufferData(this._targetBufferType, bufferData, gl.STATIC_DRAW);
}
/** Draws this Buffer. */
public draw(): void {
if (this._targetBufferType === gl.ARRAY_BUFFER)
gl.drawArrays(this._mode, 0, this._data.length / this._elementSize);
if (this._targetBufferType === gl.ELEMENT_ARRAY_BUFFER)
gl.drawElements(this._mode, this._data.length, this._dataType, 0);
}
}
}

View File

@@ -0,0 +1,106 @@
namespace TSE {
export abstract class Shader {
private _name: string;
private _program: WebGLProgram;
private _attributes: { [name: string]: number } = {};
private _uniforms: { [name: string]: WebGLUniformLocation } = {};
/**
* Create a new Shader.
* @param name The name of the Shader.
* @param vertexSource The source of the VertexShader
* @param fragementSource The source of the FragmentShader
*/
public constructor(name: string) {
this._name = name;
}
protected setSource(vertexSource: string, fragementSource: string): void {
let vertexShader = this.loadShader(vertexSource, gl.VERTEX_SHADER);
let fragmentShader = this.loadShader(fragementSource, gl.FRAGMENT_SHADER);
this.createProgram(vertexShader, fragmentShader);
this.detectAtrributes();
this.detectUniforms();
}
/** Use this Shader. */
public use(): void {
gl.useProgram(this._program);
}
private loadShader(source: string, shaderType: number): WebGLShader {
let shader: WebGLShader = gl.createShader(shaderType);
gl.shaderSource(shader, source);
gl.compileShader(shader);
const error = gl.getShaderInfoLog(shader);
if (error != "")
throw new Error("Error compiling shader '" + this._name + "': " + error);
return shader;
}
/** The name of this Shader. */
public get name(): string { return this._name; }
/**
* Get the Location of an Attribute with the provided Name.
* @param name The name of the Attribute.
*/
public getAttributeLocation(name: string): number {
if (this._attributes[name] === undefined) throw new Error(`Unable to find Attribute '${name}' in shader '${this._name}'`);
return this._attributes[name];
}
/**
* Get the Location of an Uniform with the provided Name.
* @param name The name of the Uniform.
*/
public getUniformLocation(name: string): WebGLUniformLocation {
if (this._uniforms[name] === undefined) throw new Error(`Unable to find Uniform '${name}' in shader '${this._name}'`);
return this._uniforms[name];
}
private createProgram(vertexShader: WebGLShader, fragmentShader: WebGLShader): void {
this._program = gl.createProgram();
gl.attachShader(this._program, vertexShader);
gl.attachShader(this._program, fragmentShader);
gl.linkProgram(this._program);
const error = gl.getProgramInfoLog(this._program);
if (error != "")
throw new Error("Error linking shader '" + this._name + "': " + error);
}
private detectAtrributes(): void {
let attributeCount = gl.getProgramParameter(this._program, gl.ACTIVE_ATTRIBUTES);
for (let i = 0; i < attributeCount; i++) {
let attributeInfo: WebGLActiveInfo = gl.getActiveAttrib(this._program, i);
if (!attributeInfo) break;
this._attributes[attributeInfo.name] = gl.getAttribLocation(this._program, attributeInfo.name);
}
}
private detectUniforms(): void {
let uniformCount = gl.getProgramParameter(this._program, gl.ACTIVE_UNIFORMS);
for (let i = 0; i < uniformCount; i++) {
let uniformInfo: WebGLActiveInfo = gl.getActiveUniform(this._program, i);
if (!uniformInfo) break;
this._uniforms[uniformInfo.name] = gl.getUniformLocation(this._program, uniformInfo.name);
}
}
}
}

View File

@@ -0,0 +1,37 @@
namespace TSE {
export class Color {
public static get white(): Color { return new Color(255, 255, 255, 255); }
public static get black(): Color { return new Color(0, 0, 0, 255) };
public static get red(): Color { return new Color(255, 0, 0, 255) };
public static get green(): Color { return new Color(0, 255, 0, 255) };
public static get blue(): Color { return new Color(0, 0, 255, 255) };
private _red;
private _green;
private _blue;
private _alpha;
public constructor(red: number = 255, green: number = 255, blue: number = 255, alpha: number = 255) {
this._red = red;
this._green = green;
this._blue = blue;
this._alpha = alpha;
}
public get red(): number { return this._red; }
public get green(): number { return this._green; }
public get blue(): number { return this._blue; }
public get alpha(): number { return this._alpha; }
public get redFload(): number { return this._red / 255; }
public get greenFload(): number { return this._green / 255; }
public get blueFload(): number { return this._blue / 255; }
public get alphaFload(): number { return this._alpha / 255; }
public toArray(): number[] { return [this._red, this._green, this._blue, this._alpha] }
public toFloatArray(): number[] { return [this.redFload, this.greenFload, this.blueFload, this.alphaFload]; }
public toFloat32Array(): Float32Array { return new Float32Array(this.toFloatArray()); }
}
}

View File

@@ -0,0 +1,41 @@
namespace TSE {
export class Material {
private _name: string;
private _diffuseTextureName: string;
private _diffuseTexture: Texture;
private _tint: Color;
public constructor(name: string, diffuseTextureName: string, tint: Color = Color.white) {
this._name = name;
this._diffuseTextureName = diffuseTextureName;
this._tint = tint;
if (this._diffuseTextureName !== undefined)
this._diffuseTexture = TextureManager.getTexture(this._diffuseTextureName);
}
public get name(): string { return this._name; }
public get diffuseTextureName(): string { return this._diffuseTextureName; }
public get diffuseTexture(): Texture { return this._diffuseTexture; }
public get tint(): Color { return this._tint; }
public set diffuseTextureName(value: string) {
if (this._diffuseTexture !== undefined)
TextureManager.releaseTexture(this._diffuseTextureName);
if (value === undefined) return;
this.diffuseTextureName = value;
this._diffuseTexture = TextureManager.getTexture(this._diffuseTextureName);
}
public destroy(): void {
TextureManager.releaseTexture(this._diffuseTextureName);
this._diffuseTexture = undefined;
}
}
}

View File

@@ -0,0 +1,45 @@
namespace TSE {
class MaterialReferenceNode {
public material: Material;
public referenceCount: number = 1;
public constructor(material: Material) {
this.material = material;
}
}
export class MaterialManager {
private static _materials: { [name: string]: MaterialReferenceNode } = {};
private constructor() { }
public static registerMaterial(material: Material): void {
if (MaterialManager._materials[material.name] === undefined)
MaterialManager._materials[material.name] = new MaterialReferenceNode(material);
}
public static getMaterial(materialName: string): Material {
if (MaterialManager._materials[materialName] === undefined) return undefined;
MaterialManager._materials[materialName].referenceCount++;
return MaterialManager._materials[materialName].material;
}
public static releaseMaterial(materialName: string): void {
if (MaterialManager._materials[materialName] === undefined) console.warn("Cannot release an undefined Material!");
else {
MaterialManager._materials[materialName].referenceCount--;
if (MaterialManager._materials[materialName].referenceCount < 1) {
MaterialManager._materials[materialName].material.destroy();
delete MaterialManager._materials[materialName];
}
}
}
}
}

View File

@@ -0,0 +1,151 @@
namespace TSE {
export class Sprite {
protected _buffer: GLBuffer;
protected _width: number;
protected _height: number;
protected _name: string;
protected _material: Material;
protected _vertices: Vertex[] = [];
public constructor(name: string, materialName: string, width: number = 100, height: number = 100) {
this._name = name;
this._width = width;
this._height = height;
this._material = MaterialManager.getMaterial(materialName);
}
public load(): void {
this._buffer = new GLBuffer();
this._buffer.addAttributeLocation(new AttributeInfo(0, 3)); //Position
this._buffer.addAttributeLocation(new AttributeInfo(1, 2)); //UVs
this._vertices = [
new Vertex(0, 0, 0, 0, 0),
new Vertex(0, this._height, 0, 0, 1.0),
new Vertex(this._width, this._height, 0, 1.0, 1.0),
new Vertex(this._width, this._height, 0, 1.0, 1.0),
new Vertex(this._width, 0, 0, 1.0, 0),
new Vertex(0, 0, 0, 0, 0)
];
this._buffer.pushBackData(Vertex.vertexArray(this._vertices));
this._buffer.upload();
this._buffer.unbind();
}
public destroy(): void {
this._buffer.destroy();
MaterialManager.releaseMaterial(this._material.name);
delete this._material;
}
public update(time: number): void {
}
public draw(shader: Shader, model: Matrix4x4): void {
let modelLocation = shader.getUniformLocation("u_model");
gl.uniformMatrix4fv(modelLocation, false, model.toFloat32Array());
let colorLocation = shader.getUniformLocation("u_tint");
gl.uniform4fv(colorLocation, this._material.tint.toFloat32Array());
if (this._material.diffuseTexture !== undefined) {
this._material.diffuseTexture.activateAndBind(0);
let diffuseLocation = shader.getUniformLocation("u_diffuse");
gl.uniform1i(diffuseLocation, 0);
}
this._buffer.bind();
this._buffer.draw();
}
public get name(): string { return this._name; }
}
export class AnimatedSprite extends Sprite {
private _frameHeight: number;
private _frameWidth: number;
private _frameCount: number;
private _frameSequence: number[];
private _currentFrame: number = 0;
private currentTime: number = 0;
private _frameTime: number = 300;
private _frameUVs: Vector2[][];
private _loaded: boolean = false;
public constructor(name: string, materialName: string, frameWidth: number = 10, frameHeight: number = 10, frameCount: number = 1, frameSequence: number[] = []) {
super(name, materialName, frameWidth, frameHeight);
this._frameWidth = frameWidth;
this._frameHeight = frameHeight;
this._frameCount = frameCount;
this._frameSequence = frameSequence;
}
public load() {
super.load();
this.calculateUVs();
}
private calculateUVs(): void {
setTimeout(() => {
this._frameUVs = [];
let totalWidth: number = 0;
let totalHeight: number = 0;
for (let i = 0; i < this._frameCount; i++) {
totalWidth += this._frameWidth;
if (totalWidth > this._material.diffuseTexture.width) {
totalWidth = 0;
totalHeight++;
}
const u = (i * this._frameWidth) / this._material.diffuseTexture.width;
const v = (totalHeight * this._frameHeight) / this._material.diffuseTexture.height;
const um = ((i * this._frameWidth) + this._frameWidth) / this._material.diffuseTexture.width;
const vm = ((totalHeight * this._frameHeight) + this._frameHeight) / this._material.diffuseTexture.height;
this._frameUVs.push([new Vector2(u, v), new Vector2(um, vm)]);
}
this._loaded = true;
}, 0);
}
public update(time: number) {
if (!this._loaded) return;
this.currentTime += time;
if (this.currentTime > this._frameTime) {
this._currentFrame++;
this.currentTime = 0;
if (this._currentFrame >= this._frameSequence.length)
this._currentFrame = 0;
const frameUVs = this._frameUVs[this._frameSequence[this._currentFrame]];
this._vertices[0].texCoords.copyFrom(frameUVs[0]);
this._vertices[1].texCoords = new Vector2(frameUVs[0].x, frameUVs[1].y);
this._vertices[2].texCoords.copyFrom(frameUVs[1]);
this._vertices[3].texCoords.copyFrom(frameUVs[1]);
this._vertices[4].texCoords = new Vector2(frameUVs[1].x, frameUVs[0].y);
this._vertices[5].texCoords.copyFrom(frameUVs[0]);
this._buffer.setData(Vertex.vertexArray(this._vertices));
this._buffer.upload();
this._buffer.unbind();
}
super.update(time);
}
}
}

View File

@@ -0,0 +1,94 @@
namespace TSE {
const LEVEL: number = 0;
const BORDER: number = 0;
const TEMP_IMAGE_DATA: Uint8Array = new Uint8Array([255, 255, 255, 255]);
export class Texture implements IMessageHanlder {
private _name: string;
private _handle: WebGLTexture;
private _isLoaded: boolean = false;
private _width: number;
private _height: number;
public constructor(name: string, width: number = 1, height: number = 1) {
this._name = name;
this._width = width;
this._height = height;
this._handle = gl.createTexture();
Message.subscribe(MESSAGE_ASSET_LOADER_ASSET_LOADED + this._name, this);
this.bind();
gl.texImage2D(gl.TEXTURE_2D, LEVEL, gl.RGBA, 1, 1, BORDER, gl.RGBA, gl.UNSIGNED_BYTE, TEMP_IMAGE_DATA);
let asset = AssetManager.getAsset(this._name) as ImageAsset;
if (asset !== undefined) {
this.loadTextureFromAsset(asset);
}
}
public onMessage(message: Message): void {
if (message.code === MESSAGE_ASSET_LOADER_ASSET_LOADED + this._name) {
this.loadTextureFromAsset(message.context as ImageAsset);
}
}
public get name(): string { return this._name; }
public get isLoaded(): boolean { return this._isLoaded; }
public get width(): number { return this._width; }
public get height(): number { return this._height; }
public activateAndBind(textureUnit: number = 0): void {
gl.activeTexture(gl.TEXTURE0 + textureUnit);
this.bind();
}
public bind(): void {
gl.bindTexture(gl.TEXTURE_2D, this._handle);
}
public unbind(): void {
gl.bindTexture(gl.TEXTURE_2D, undefined);
}
public destroy(): void {
gl.deleteTexture(this._handle);
}
private loadTextureFromAsset(asset: ImageAsset): void {
this._width = asset.width;
this._height = asset.height;
this.bind();
gl.texImage2D(gl.TEXTURE_2D, LEVEL, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, asset.data);
if (this.isPowerOf2()) {
gl.generateMipmap(gl.TEXTURE_2D);
} else {
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
}
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
this._isLoaded = true;
}
private isPowerOf2(): boolean {
return (this.isValuePowerOf2(this.width) && this.isValuePowerOf2(this.height));
}
private isValuePowerOf2(value: number): boolean {
return (value & (value - 1)) == 0;
}
}
}

View File

@@ -0,0 +1,44 @@
namespace TSE {
class TextureReferenceNode {
public texture: Texture;
public referenceCount: number = 1;
public constructor(texture: Texture) {
this.texture = texture;
}
}
export class TextureManager {
private static _textures: { [name: string]: TextureReferenceNode } = {};
private constructor() { }
public static getTexture(textureName: string): Texture {
if (TextureManager._textures[textureName] === undefined)
TextureManager._textures[textureName] = new TextureReferenceNode(new Texture(textureName));
else
TextureManager._textures[textureName].referenceCount++;
return TextureManager._textures[textureName].texture;
}
public static releaseTexture(textureName: string): void {
if (TextureManager._textures[textureName] === undefined) {
console.warn(`A Texture named ${textureName} does not exist and cannot be released!`);
} else {
TextureManager._textures[textureName].referenceCount--;
if (TextureManager._textures[textureName].referenceCount < 1) {
TextureManager._textures[textureName].texture.destroy();
TextureManager._textures[textureName] = undefined;
delete TextureManager._textures[textureName];
}
}
}
}
}

View File

@@ -0,0 +1,30 @@
namespace TSE {
export class Vertex {
public static vertexArray(vertices: Vertex[]): number[] {
let array: number[] = [];
for (let vertex of vertices)
array = array.concat(vertex.toArray());
return array;
}
public position: Vector3;
public texCoords: Vector2;
public constructor(x: number = 0, y: number = 0, z: number = 0, u: number = 0, v: number = 0) {
this.position = new Vector3(x, y, z);
this.texCoords = new Vector2(u, v);
}
public toArray(): number[] {
let array: number[] = [];
array = array.concat(this.position.toArray(), this.texCoords.toArray());
return array;
}
public toFloat32Array(): Float32Array { return new Float32Array(this.toArray()); }
}
}

View File

@@ -0,0 +1,177 @@
namespace TSE {
export class Matrix4x4 {
private _data: number[] = [];
private constructor() {
this._data = [
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
];
}
public get data(): number[] {
return this._data;
}
public static identity(): Matrix4x4 {
return new Matrix4x4();
}
public static orthographic(left: number, right: number, bottom: number, top: number, nearClip: number, farClip: number): Matrix4x4 {
let matrix = new Matrix4x4();
let lr: number = 1.0 / (left - right);
let bt: number = 1.0 / (bottom - top);
let nf: number = 1.0 / (nearClip - farClip);
matrix._data[0] = -2.0 * lr;
matrix._data[5] = -2.0 * bt;
matrix._data[10] = 2.0 * nf;
matrix._data[12] = (left + right) * lr;
matrix._data[13] = (top + bottom) * bt;
matrix._data[14] = (farClip + nearClip) * nf;
return matrix;
}
public static translation(position: Vector3): Matrix4x4 {
let matrix = new Matrix4x4();
matrix._data[12] = position.x;
matrix._data[13] = position.y;
matrix._data[14] = position.z;
return matrix;
}
public static rotationX(angle: number): Matrix4x4 {
let matrix = new Matrix4x4();
let c = Math.cos(angle);
let s = Math.sin(angle);
matrix._data[5] = c;
matrix._data[6] = s;
matrix._data[9] = -s;
matrix._data[10] = c;
return matrix;
}
public static rotationY(angle: number): Matrix4x4 {
let matrix = new Matrix4x4();
let c = Math.cos(angle);
let s = Math.sin(angle);
matrix._data[0] = c;
matrix._data[2] = -s;
matrix._data[8] = s;
matrix._data[10] = c;
return matrix;
}
public static rotationZ(angle: number): Matrix4x4 {
let matrix = new Matrix4x4();
let c = Math.cos(angle);
let s = Math.sin(angle);
matrix._data[0] = c;
matrix._data[1] = s;
matrix._data[4] = -s;
matrix._data[5] = c;
return matrix;
}
public static rotationXYZ(x: number, y: number, z: number): Matrix4x4 {
return Matrix4x4.multiply3(Matrix4x4.rotationZ(z), Matrix4x4.rotationY(y), Matrix4x4.rotationX(x));
}
public static scale(scale: Vector3): Matrix4x4 {
let matrix = new Matrix4x4();
matrix._data[0] = scale.x;
matrix._data[5] = scale.y;
matrix._data[10] = scale.z;
return matrix;
}
public static multiply(a: Matrix4x4, b: Matrix4x4): Matrix4x4 {
let matrix = new Matrix4x4();
let b00 = b._data[0 * 4 + 0];
let b01 = b._data[0 * 4 + 1];
let b02 = b._data[0 * 4 + 2];
let b03 = b._data[0 * 4 + 3];
let b10 = b._data[1 * 4 + 0];
let b11 = b._data[1 * 4 + 1];
let b12 = b._data[1 * 4 + 2];
let b13 = b._data[1 * 4 + 3];
let b20 = b._data[2 * 4 + 0];
let b21 = b._data[2 * 4 + 1];
let b22 = b._data[2 * 4 + 2];
let b23 = b._data[2 * 4 + 3];
let b30 = b._data[3 * 4 + 0];
let b31 = b._data[3 * 4 + 1];
let b32 = b._data[3 * 4 + 2];
let b33 = b._data[3 * 4 + 3];
let a00 = a._data[0 * 4 + 0];
let a01 = a._data[0 * 4 + 1];
let a02 = a._data[0 * 4 + 2];
let a03 = a._data[0 * 4 + 3];
let a10 = a._data[1 * 4 + 0];
let a11 = a._data[1 * 4 + 1];
let a12 = a._data[1 * 4 + 2];
let a13 = a._data[1 * 4 + 3];
let a20 = a._data[2 * 4 + 0];
let a21 = a._data[2 * 4 + 1];
let a22 = a._data[2 * 4 + 2];
let a23 = a._data[2 * 4 + 3];
let a30 = a._data[3 * 4 + 0];
let a31 = a._data[3 * 4 + 1];
let a32 = a._data[3 * 4 + 2];
let a33 = a._data[3 * 4 + 3];
matrix._data[0] = b00 * a00 + b01 * a10 + b02 * a20 + b03 * a30;
matrix._data[1] = b00 * a01 + b01 * a11 + b02 * a21 + b03 * a31;
matrix._data[2] = b00 * a02 + b01 * a12 + b02 * a22 + b03 * a32;
matrix._data[3] = b00 * a03 + b01 * a13 + b02 * a23 + b03 * a33;
matrix._data[4] = b10 * a00 + b11 * a10 + b12 * a20 + b13 * a30;
matrix._data[5] = b10 * a01 + b11 * a11 + b12 * a21 + b13 * a31;
matrix._data[6] = b10 * a02 + b11 * a12 + b12 * a22 + b13 * a32;
matrix._data[7] = b10 * a03 + b11 * a13 + b12 * a23 + b13 * a33;
matrix._data[8] = b20 * a00 + b21 * a10 + b22 * a20 + b23 * a30;
matrix._data[9] = b20 * a01 + b21 * a11 + b22 * a21 + b23 * a31;
matrix._data[10] = b20 * a02 + b21 * a12 + b22 * a22 + b23 * a32;
matrix._data[11] = b20 * a03 + b21 * a13 + b22 * a23 + b23 * a33;
matrix._data[12] = b30 * a00 + b31 * a10 + b32 * a20 + b33 * a30;
matrix._data[13] = b30 * a01 + b31 * a11 + b32 * a21 + b33 * a31;
matrix._data[14] = b30 * a02 + b31 * a12 + b32 * a22 + b33 * a32;
matrix._data[15] = b30 * a03 + b31 * a13 + b32 * a23 + b33 * a33;
return matrix;
}
public static multiply3(a: Matrix4x4, b: Matrix4x4, c: Matrix4x4): Matrix4x4 {
return Matrix4x4.multiply(Matrix4x4.multiply(a, b), c);
}
public toFloat32Array(): Float32Array { return new Float32Array(this._data); }
public copyFrom(matrix: Matrix4x4): void {
for (let i = 0; i < this._data.length; i++) {
this._data[i] = matrix._data[i];
}
}
}
}

View File

@@ -0,0 +1,31 @@
namespace TSE {
export class Transform {
public position: Vector3 = Vector3.zero;
public rotation: Vector3 = Vector3.zero;
public scale: Vector3 = Vector3.one;
public copyFrom(transform: Transform): void {
this.position.copyFrom(transform.position);
this.rotation.copyFrom(transform.rotation);
this.scale.copyFrom(transform.scale);
}
public get transformationMatrix(): Matrix4x4 {
let translation = Matrix4x4.translation(this.position);
let rotation = Matrix4x4.rotationXYZ(this.rotation.x, this.rotation.y, this.rotation.z);
let scale = Matrix4x4.scale(this.scale);
return Matrix4x4.multiply3(translation, rotation, scale);
}
public setFromJson(json: any): void {
if (json.position !== undefined) this.position.setFromJson(json.position);
if (json.rotation !== undefined) this.rotation.setFromJson(json.rotation);
if (json.scale !== undefined) this.scale.setFromJson(json.scale);
}
}
}

View File

@@ -0,0 +1,34 @@
namespace TSE {
export class Vector2 {
protected _x: number;
protected _y: number;
public constructor(x: number = 0, y: number = 0) {
this._x = x;
this._y = y;
}
public get x() { return this._x; }
public set x(value: number) { this._x = value; }
public get y() { return this._y; }
public set y(value: number) { this._y = value; }
public toArray(): number[] { return [this._x, this._y]; }
public toFloat32Array(): Float32Array { return new Float32Array(this.toArray()); }
public setFromJson(json: any): void {
this._x = json.x !== undefined ? Number(json.x) : 0;
this._y = json.y !== undefined ? Number(json.y) : 0;
}
public copyFrom(vector: Vector2): Vector2 {
this._x = vector._x;
this._y = vector._y;
return this;
}
}
}

View File

@@ -0,0 +1,68 @@
namespace TSE {
export class Vector3 {
public static get zero(): Vector3 { return new Vector3(0, 0, 0); }
public static get one(): Vector3 { return new Vector3(1, 1, 1); }
protected _x: number;
protected _y: number;
protected _z: number;
public constructor(x: number = 0, y: number = 0, z: number = 0) {
this._x = x;
this._y = y;
this._z = z;
}
public get x() { return this._x; }
public set x(value: number) { this._x = value; }
public get y() { return this._y; }
public set y(value: number) { this._y = value; }
public get z() { return this._z; }
public set z(value: number) { this._z = value; }
public toArray(): number[] { return [this._x, this._y, this._z]; }
public toFloat32Array(): Float32Array { return new Float32Array(this.toArray()); }
public copyFrom(vector: Vector3): void {
this._x = vector._x;
this._y = vector._y;
this._z = vector._z;
}
public setFromJson(json: any): void {
this._x = json.x !== undefined ? Number(json.x) : 0;
this._y = json.y !== undefined ? Number(json.y) : 0;
this._z = json.z !== undefined ? Number(json.z) : 0;
}
public add(vector: Vector3): Vector3 {
this._x += vector._x;
this._y += vector._y;
this._z += vector._z;
return this;
}
public subtract(vector: Vector3): Vector3 {
this._x -= vector._x;
this._y -= vector._y;
this._z -= vector._z;
return this;
}
public multiply(vector: Vector3): Vector3 {
this._x *= vector._x;
this._y *= vector._y;
this._z *= vector._z;
return this;
}
public divide(vector: Vector3): Vector3 {
this._x /= vector._x;
this._y /= vector._y;
this._z /= vector._z;
return this;
}
}
}

View File

@@ -0,0 +1,9 @@
namespace TSE {
export interface IMessageHanlder {
onMessage(message: Message): void;
}
}

View File

@@ -0,0 +1,40 @@
namespace TSE {
export enum MessagePriority {
NORMAL,
HIGH
}
export class Message {
public code: string;
public context: any;
public sender: any;
public priority: MessagePriority;
public constructor(code: string, sender: any, context?: any, priority: MessagePriority = MessagePriority.NORMAL) {
this.code = code;
this.context = context;
this.sender = sender;
this.priority = priority;
}
public static send(code: string, sender: any, context?: any): void {
MessageBus.post(new Message(code, sender, context));
}
public static sendPriority(code: string, sender: any, context?: any): void {
MessageBus.post(new Message(code, sender, context, MessagePriority.HIGH));
}
public static subscribe(code: string, handler: IMessageHanlder): void {
MessageBus.addSubscription(code, handler);
}
public static unsubscribe(code: string, handler: IMessageHanlder): void {
MessageBus.removeSubscription(code, handler);
}
}
}

View File

@@ -0,0 +1,56 @@
namespace TSE {
export class MessageBus {
private static _subscriptions: { [code: string]: IMessageHanlder[] } = {};
private static _normalQueueMessagePerUpdate: number = 10;
private static _normalMessageQueue: MessageSubscriptionNode[] = [];
private constructor() { }
public static addSubscription(code: string, handler: IMessageHanlder): void {
if (MessageBus._subscriptions[code] === undefined)
MessageBus._subscriptions[code] = [];
if (MessageBus._subscriptions[code].indexOf(handler) !== -1)
console.warn("Attempting to add a duplicate hanlder to code: '" + code + "'. Subscription not added.");
else
MessageBus._subscriptions[code].push(handler);
}
public static removeSubscription(code: string, handler: IMessageHanlder): void {
if (MessageBus._subscriptions[code] === undefined) {
console.warn("Cannot unsubscribe handler from code: '" + code + "'. Because that code is not subscribed to.");
return;
}
let nodeIndex = MessageBus._subscriptions[code].indexOf(handler);
if (nodeIndex !== -1)
MessageBus._subscriptions[code].splice(nodeIndex, 1);
}
public static post(message: Message): void {
console.log("Message posted:", message);
let handlers = MessageBus._subscriptions[message.code];
if (handlers === undefined) return;
for (let handler of handlers) {
if (message.priority === MessagePriority.HIGH) handler.onMessage(message);
else MessageBus._normalMessageQueue.push(new MessageSubscriptionNode(message, handler));
}
}
public static update(time: number): void {
if (MessageBus._normalMessageQueue.length === 0) return;
let limit = Math.min(MessageBus._normalQueueMessagePerUpdate, MessageBus._normalMessageQueue.length);
for (let i = 0; i < limit; i++) {
let node = MessageBus._normalMessageQueue.pop();
node.handler.onMessage(node.message);
}
}
}
}

View File

@@ -0,0 +1,15 @@
namespace TSE {
export class MessageSubscriptionNode {
public message: Message;
public handler: IMessageHanlder;
public constructor(message: Message, handler: IMessageHanlder) {
this.message = message;
this.handler = handler;
}
}
}

View File

@@ -0,0 +1,47 @@
///<reference path="../GL/shader.ts"/>
namespace TSE {
export class BasicShader extends Shader {
public constructor() {
super("basic");
this.setSource(this.getVertexSource(), this.getFragmentSource());
}
private getVertexSource(): string {
return `
attribute vec3 a_position;
attribute vec2 a_texCoord;
uniform mat4 u_projection;
uniform mat4 u_model;
varying vec2 v_texCoord;
void main() {
gl_Position = u_projection * u_model * vec4(a_position, 1.0);
v_texCoord = a_texCoord;
}
`;
}
private getFragmentSource(): string {
return `
precision mediump float;
uniform vec4 u_tint;
uniform sampler2D u_diffuse;
varying vec2 v_texCoord;
void main() {
gl_FragColor = u_tint * texture2D(u_diffuse, v_texCoord);
}
`;
}
}
}

View File

@@ -0,0 +1,23 @@
namespace TSE {
export class Scene {
private _root: SimObject;
public constructor() {
this._root = new SimObject(0, "__ROOT__", this);
}
public get root(): SimObject { return this._root; }
public get isLoaded(): boolean { return this._root.isLoaded; }
public addObject(object: SimObject): void { this._root.addChild(object); }
public getObjectByName(name: string): SimObject { return this._root.getObjectByName(name); }
public load(): void { this._root.load(); }
public update(time: number): void { this._root.update(time); }
public render(shader: Shader): void { this._root.render(shader); }
}
}

View File

@@ -0,0 +1,107 @@
namespace TSE {
export class SimObject {
public name: string;
public transform: Transform = new Transform();
private _id: number;
private _children: SimObject[] = [];
private _parent: SimObject;
private _scene: Scene;
private _isLoaded: boolean = false;
private _components: BaseComponent[] = [];
private _behaviors: BaseBehavior[] = [];
private _localMatrix: Matrix4x4 = Matrix4x4.identity();
private _worldMatrix: Matrix4x4 = Matrix4x4.identity();
public constructor(id: number, name: string, scene?: Scene) {
this._id = id;
this.name = name;
this._scene = scene;
}
public get id(): number { return this._id; }
public get parent(): SimObject { return this._parent; }
public get worldMatrix(): Matrix4x4 { return this._worldMatrix; }
public get isLoaded(): boolean { return this._isLoaded; }
public addChild(child: SimObject): void {
child._parent = this;
this._children.push(child);
child.load();
child.onAdded(this._scene);
}
public removeChild(child: SimObject): void {
const index = this._children.indexOf(child);
if (index == -1) return;
child._parent = undefined;
this._children.splice(index, 1);
}
public getObjectByName(name: string): SimObject {
if (this.name === name) return this;
for (let child of this._children) {
let result = child.getObjectByName(name);
if (result !== undefined) return result;
}
return undefined;
}
public addComponent(component: BaseComponent): void {
this._components.push(component);
component.owner = this;
}
public addBehavior(behavior: BaseBehavior): void {
this._behaviors.push(behavior);
behavior.owner = this;
}
public load(): void {
this._isLoaded = true;
for (let c of this._components)
c.load();
for (let child of this._children)
child.load();
}
public update(time: number): void {
this._localMatrix = this.transform.transformationMatrix;
this.updateWorldMatrix(this._parent?._worldMatrix);
for (let c of this._components)
c.update(time);
for (let c of this._behaviors)
c.update(time);
for (let child of this._children)
child.update(time);
}
public render(shader: Shader): void {
for (let c of this._components)
c.render(shader);
for (let child of this._children)
child.render(shader);
}
protected onAdded(scene: Scene): void {
this._scene = scene;
}
private updateWorldMatrix(parentWorldMatrix: Matrix4x4): void {
if (parentWorldMatrix !== undefined)
this._worldMatrix = Matrix4x4.multiply(parentWorldMatrix, this._localMatrix);
else
this._worldMatrix.copyFrom(this._localMatrix);
}
}
}

View File

@@ -0,0 +1,86 @@
namespace TSE {
export enum ZoneState {
UNINITIALIZED,
LOADING,
UPDATING
}
export class Zone {
private _name: string;
private _description: string;
private _id: number;
private _scene: Scene;
private _state: ZoneState = ZoneState.UNINITIALIZED;
private _globalId: number = -1;
public constructor(id: number, name: string, descripton?: string) {
this._id = id;
this._name = name;
this._description = descripton;
this._scene = new Scene();
}
public get id(): number { return this._id; }
public get name(): string { return this._name; }
public get description(): string { return this._description; }
public get scene(): Scene { return this._scene; }
public get state(): ZoneState { return this._state; }
public initialize(zoneData: any): void {
if (zoneData.objects === undefined) return;
for (let object of zoneData.objects) this.loadSimObject(object, this._scene.root);
}
public load(): void {
if (this._state !== ZoneState.UNINITIALIZED) return;
this._state = ZoneState.LOADING;
this._scene.load();
this._state = ZoneState.UPDATING;
}
public unload(): void {
if (this._state === ZoneState.UNINITIALIZED) return;
this._state = ZoneState.UNINITIALIZED;
}
public update(time: number): void {
if (this._state === ZoneState.UPDATING)
this._scene.update(time);
}
public render(shader: Shader): void {
if (this._state === ZoneState.UPDATING)
this._scene.render(shader);
}
public onActivated(): void { }
public onDeactivated(): void { }
private loadSimObject(dataSection: any, parent: SimObject): void {
if (dataSection.name === undefined) throw new Error("Invalid Object data!");
let name: string = String(dataSection.name);
this._globalId++;
let simObject = new SimObject(this._globalId, name, this._scene);
if (dataSection.transform !== undefined) simObject.transform.setFromJson(dataSection.transform);
if (dataSection.components !== undefined) {
for (let component of dataSection.components)
simObject.addComponent(ComponentManager.extractComponent(component));
}
if (dataSection.behaviors !== undefined) {
for (let behavior of dataSection.behaviors)
simObject.addBehavior(BehaviorManager.extractComponent(behavior));
}
if (dataSection.children !== undefined) {
for (let object of dataSection.children) this.loadSimObject(object, simObject);
}
parent.addChild(simObject);
}
}
}

View File

@@ -0,0 +1,69 @@
namespace TSE {
export class ZoneManager implements IMessageHanlder{
private static _globalZoneId: number = -1;
//private static _zones: { [id: number]: Zone } = {};
private static _registeredZones: {[id: number]: string} = {};
private static _activeZone: Zone;
private static _instance: ZoneManager;
private constructor() { }
public static initialize(): void {
ZoneManager._instance = new ZoneManager();
ZoneManager._registeredZones[0] = "assets/zones/testZone.json";
}
public static changeZone(id: number): void {
if (ZoneManager._activeZone !== undefined) {
ZoneManager._activeZone.onDeactivated();
ZoneManager._activeZone.unload();
delete ZoneManager._activeZone;
}
let zonePath = ZoneManager._registeredZones[id];
if (zonePath === undefined) throw new Error("Zone Id does not exist!");
if (AssetManager.isAssetLoaded(zonePath)) ZoneManager.loadZone(AssetManager.getAsset(zonePath));
else {
Message.subscribe(MESSAGE_ASSET_LOADER_ASSET_LOADED + zonePath, ZoneManager._instance);
AssetManager.loadAsset(zonePath);
}
}
public static update(time: number): void {
if (ZoneManager._activeZone === undefined) return;
ZoneManager._activeZone.update(time);
}
public static render(shader: Shader): void {
if (ZoneManager._activeZone === undefined) return;
ZoneManager._activeZone.render(shader);
}
private static loadZone(asset: JsonAsset): void {
let zoneData = asset.data;
if (zoneData.id === undefined) throw new Error("Zone file format exception: Zone id not present.")
let zoneId: number = Number(zoneData.id);
let zoneName: string = String(zoneData.name);
let zoneDescription: string = String(zoneData.description);
let zone = new Zone(zoneId, zoneName, zoneDescription);
zone.initialize(zoneData);
ZoneManager._activeZone = zone;
zone.load();
zone.onActivated();
}
public onMessage(message: Message): void {
if (message.code.indexOf(MESSAGE_ASSET_LOADER_ASSET_LOADED) !== -1) {
let asset = message.context as JsonAsset;
ZoneManager.loadZone(asset);
}
}
public static get zone(): Zone { return this._activeZone; }
}
}

View File

@@ -0,0 +1,71 @@
namespace TSE {
/**
* The main GameEngine class.
* */
export class Engine {
private _canvas: HTMLCanvasElement;
private _shader: Shader;
private _projection: Matrix4x4;
private _previousTime: number = 0;
/**
* Creates a new Engine.
* */
public constructor(canvasId: string) {
this._canvas = GLUtilities.initialize(canvasId);
}
/**
* Starts this Engine.
* */
public start(): void {
AssetManager.initialize();
ZoneManager.initialize();
gl.clearColor(0, 0, 0, 1);
gl.enable(gl.BLEND);
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
//LoadMaterials
MaterialManager.registerMaterial(new Material("crate", "/assets/textures/crate.jpg"));
MaterialManager.registerMaterial(new Material("goblin", "/assets/textures/goblin.png"));
//Load Shader
this._shader = new BasicShader();
this._shader.use();
//Load
this._projection = Matrix4x4.orthographic(0, this._canvas.width, this._canvas.height, 0, -100.0, 100.0);
ZoneManager.changeZone(0);
this.loop();
}
private loop(): void {
let delta = performance.now() - this._previousTime;
MessageBus.update(delta);
ZoneManager.update(delta);
this._previousTime = performance.now();
gl.clear(gl.COLOR_BUFFER_BIT);
ZoneManager.render(this._shader);
//Set Uniforms
let projectionPosition = this._shader.getUniformLocation("u_projection");
gl.uniformMatrix4fv(projectionPosition, false, new Float32Array(this._projection.data));
requestAnimationFrame(this.loop.bind(this));
}
public resize(): void {
this._projection = Matrix4x4.orthographic(0, this._canvas.width, this._canvas.height, 0, -100.0, 100.0);
gl.viewport(0, 0, this._canvas.width, this._canvas.height);
}
}
}