blobt
4 years ago
22 changed files with 2281 additions and 0 deletions
-
1src/render/common.ts
-
400src/render/core/Application.ts
-
88src/render/core/Event.ts
-
42src/render/core/Timer.ts
-
0src/render/ds/AdapterBase.ts
-
0src/render/ds/Dictionary.ts
-
0src/render/ds/IAdapter.ts
-
0src/render/ds/IEnumerator.ts
-
0src/render/ds/List.ts
-
0src/render/ds/ListNode.ts
-
0src/render/ds/NodeB2TEnumberator.ts
-
0src/render/ds/NodeEnumeratorFactory.ts
-
0src/render/ds/NodeT2BEnumerator.ts
-
0src/render/ds/Queue.ts
-
0src/render/ds/Stack.ts
-
0src/render/ds/TreeNode.ts
-
0src/render/ds/TypedArrayList.ts
-
0src/render/ds/index.ts
-
1537src/render/math/TSM.ts
-
42src/render/math/Vec2.ts
-
161src/render/math/Vec3.ts
-
10src/render/webgl/WebGLApplication.ts
@ -0,0 +1 @@ |
|||
export let EPSILON: number = 0.0001; |
@ -0,0 +1,400 @@ |
|||
import { vec2 } from "../math/TSM"; |
|||
import { CanvasKeyBoardEvent, CanvasMouseEvent, EInputEventType } from "./Event"; |
|||
import { Timer, TimerCallback } from "./Timer"; |
|||
|
|||
|
|||
export class Application implements EventListenerObject { |
|||
|
|||
/** |
|||
* @var 定时器数组 |
|||
*/ |
|||
public timers: Timer[] = []; |
|||
|
|||
/** |
|||
* @var 定时器id |
|||
*/ |
|||
private _timerId: number = -1; |
|||
|
|||
/** |
|||
* @var 帧率 |
|||
*/ |
|||
private _fps: number = 0; |
|||
|
|||
/** |
|||
* @var |
|||
*/ |
|||
public isFlipYCoord: boolean = false; |
|||
|
|||
/** |
|||
* @var |
|||
*/ |
|||
public canvas: HTMLCanvasElement; |
|||
|
|||
/** |
|||
* @var 是否每次移动都会触发mouse事件 |
|||
*/ |
|||
public isSupportMouseMove: boolean; |
|||
|
|||
/** |
|||
* @var 鼠标是否被按下 |
|||
*/ |
|||
protected _isMouseDown: boolean; |
|||
|
|||
/** |
|||
* @var 是否按下鼠标右键 |
|||
*/ |
|||
protected _isRightMouseDown: boolean = false; |
|||
|
|||
/** |
|||
* @var 是否进入循环 |
|||
*/ |
|||
protected _start: boolean = false; |
|||
|
|||
/** |
|||
* @var 由Window对象的requestAnimationFrame返回的大于0的id号, |
|||
* 我们可以使用cancelAnimationFrame ( this ._requestId )来取消动画循环. |
|||
*/ |
|||
protected _requestId: number = -1; |
|||
|
|||
/** |
|||
* @var 计算当前更新与上一次更新之间的时间差 |
|||
*/ |
|||
protected _lastTime !: number; |
|||
protected _startTime !: number; |
|||
|
|||
|
|||
/** |
|||
* @function 渲染每帧画面的回调函数 |
|||
*/ |
|||
public frameCallback: ((app: Application) => void) | null; |
|||
|
|||
|
|||
|
|||
public constructor(canvas: HTMLCanvasElement) { |
|||
this.canvas = canvas; |
|||
|
|||
//设置鼠标的监听事件
|
|||
this.canvas.addEventListener("mousedown", this, false); |
|||
this.canvas.addEventListener("mouseup", this, false); |
|||
this.canvas.addEventListener("mousemove", this, false); |
|||
|
|||
//设置键盘监听事件
|
|||
window.addEventListener("keydown", this, false); |
|||
window.addEventListener("keyup", this, false); |
|||
window.addEventListener("keypress", this, false); |
|||
|
|||
//初始化时候mousedown 和 mousemvoe不开启
|
|||
this._isMouseDown = false; |
|||
this.isSupportMouseMove = false; |
|||
|
|||
this.frameCallback = null; |
|||
|
|||
//禁止右键菜单
|
|||
document.oncontextmenu = function () { return false }; |
|||
} |
|||
|
|||
|
|||
/** |
|||
* 判断当前Application是否一直在调用requestAnimationFrame |
|||
* @returns boolean |
|||
*/ |
|||
public isRunning(): boolean { |
|||
return this._start; |
|||
} |
|||
|
|||
/** |
|||
* 获取当前帧率 |
|||
*/ |
|||
public get fps() { |
|||
return this._fps; |
|||
} |
|||
|
|||
/** |
|||
* 启动渲染 |
|||
*/ |
|||
public start(): void { |
|||
if (this._start === false) { |
|||
this._start = true; |
|||
|
|||
this._startTime = -1; |
|||
this._lastTime = -1; |
|||
|
|||
this._requestId = requestAnimationFrame((msec: number): void => { |
|||
this.step(msec); |
|||
}); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 循环函数 |
|||
* @param timeStamp |
|||
*/ |
|||
protected step(timeStamp: number): void { |
|||
|
|||
if (this._startTime === -1) { |
|||
this._startTime = timeStamp; |
|||
} |
|||
|
|||
if (this._lastTime === -1) { |
|||
this._lastTime = timeStamp; |
|||
} |
|||
|
|||
//计算当前时间和第一次调用step时候的时间差
|
|||
let elapsedMsec = timeStamp - this._startTime; |
|||
|
|||
//计算当前时间和上一次调用step时候的时间差
|
|||
let intervalSec = timeStamp - this._lastTime; |
|||
this._lastTime = timeStamp; |
|||
|
|||
//计算帧率
|
|||
if (intervalSec !== 0) { |
|||
this._fps = 1000 / intervalSec; |
|||
} |
|||
|
|||
intervalSec /= 1000.0; //转成秒
|
|||
|
|||
//处理timer
|
|||
this._handleTimers(intervalSec); |
|||
|
|||
//更新
|
|||
this.update(elapsedMsec, intervalSec); |
|||
|
|||
//渲染
|
|||
this.render(); |
|||
|
|||
if (this.frameCallback !== null) { |
|||
this.frameCallback(this); |
|||
} |
|||
|
|||
requestAnimationFrame((elapsedMsec: number): void => { |
|||
this.step(elapsedMsec); |
|||
}) |
|||
} |
|||
|
|||
public stop(): void { |
|||
if (this._start) { |
|||
cancelAnimationFrame(this._requestId); |
|||
this._startTime = -1; |
|||
this._lastTime = -1; |
|||
this._start = false; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 虚函数 |
|||
* @param elapsedMsec 毫秒 |
|||
* @param intervalSec 秒 |
|||
*/ |
|||
public update(elapsedMsec: number, intervalSec: number): void { } |
|||
|
|||
/** |
|||
* 虚函数 |
|||
*/ |
|||
public render(): void { } |
|||
|
|||
public async run(): Promise<void> { |
|||
this.start(); |
|||
} |
|||
|
|||
handleEvent(evt: Event): void { |
|||
switch (evt.type) { |
|||
case "mousedown": |
|||
this._isMouseDown = true; |
|||
this.onMouseDown(this._toCanvasMouseEvent(evt, EInputEventType.MOUSEDOWN)); |
|||
break; |
|||
case "mouseup": |
|||
this._isMouseDown = false; |
|||
this.onMouseDown(this._toCanvasMouseEvent(evt, EInputEventType.MOUSEUP)); |
|||
break; |
|||
case "mousemove": |
|||
if (this.isSupportMouseMove) { |
|||
this.onMouseMove(this._toCanvasMouseEvent(evt, EInputEventType.MOUSEMOVE)); |
|||
} |
|||
if (this._isMouseDown) { |
|||
this.onMouseDrag(this._toCanvasMouseEvent(evt, EInputEventType.MOUSEDRAG)); |
|||
} |
|||
break; |
|||
case "keypress": |
|||
this.onKeyPress(this._toCanvasKeyBoardEvent(evt, EInputEventType.KEYPRESS)); |
|||
break; |
|||
case "keydown": |
|||
this.onKeyDown(this._toCanvasKeyBoardEvent(evt, EInputEventType.MOUSEDOWN)); |
|||
break; |
|||
case "keyup": |
|||
this.onKeyUp(this._toCanvasKeyBoardEvent(evt, EInputEventType.KEYUP)); |
|||
break; |
|||
} |
|||
} |
|||
|
|||
protected onMouseDown(evt: CanvasMouseEvent): void { |
|||
return; |
|||
} |
|||
|
|||
protected onMouseUp(evt: CanvasMouseEvent): void { |
|||
return; |
|||
} |
|||
|
|||
protected onMouseMove(evt: CanvasMouseEvent): void { |
|||
return; |
|||
} |
|||
|
|||
protected onMouseDrag(evt: CanvasMouseEvent): void { |
|||
return; |
|||
} |
|||
|
|||
protected onKeyDown(evt: CanvasKeyBoardEvent): void { |
|||
return; |
|||
} |
|||
|
|||
protected onKeyUp(evt: CanvasKeyBoardEvent): void { |
|||
return; |
|||
} |
|||
|
|||
protected onKeyPress(evt: CanvasKeyBoardEvent): void { |
|||
return; |
|||
} |
|||
|
|||
protected getMouseCanvas(): HTMLCanvasElement { |
|||
return this.canvas; |
|||
} |
|||
|
|||
/** |
|||
* 将鼠标的指针位置变换为当前canvas的位置 |
|||
* @param evt |
|||
*/ |
|||
private viewportToCanvasCoordinate(evt: MouseEvent): vec2 { |
|||
let rect: ClientRect = this.getMouseCanvas().getBoundingClientRect(); |
|||
|
|||
//target是触发事件的元素,这里是canvas
|
|||
if (evt.target) { |
|||
let x: number = evt.clientX - rect.left; |
|||
let y: number = evt.clientY - rect.top; |
|||
|
|||
if (this.isFlipYCoord) { |
|||
y = this.getMouseCanvas().height - y; |
|||
} |
|||
let pos: vec2 = new vec2([x, y]); |
|||
return pos; |
|||
} |
|||
|
|||
throw new Error("evt.target is null"); |
|||
} |
|||
|
|||
/** |
|||
* 把DOM 事件转成自己的事件 |
|||
* @param evt |
|||
* @param type |
|||
*/ |
|||
private _toCanvasMouseEvent(evt: Event, type: EInputEventType): CanvasMouseEvent { |
|||
let event: MouseEvent = evt as MouseEvent;//向下转型,将Event转换为MouseEvent
|
|||
|
|||
if (type === EInputEventType.MOUSEDOWN && event.button === 2) { |
|||
this._isRightMouseDown = true; |
|||
} else if (type === EInputEventType.MOUSEUP && event.button === 2) { |
|||
this._isRightMouseDown = false; |
|||
} |
|||
|
|||
let buttonNum: number = event.button; |
|||
|
|||
if (this._isRightMouseDown && type === EInputEventType.MOUSEDRAG) { |
|||
buttonNum = 2; |
|||
} |
|||
|
|||
//将鼠标位置变换到canvas坐标系位置
|
|||
let mousePosition: vec2 = this.viewportToCanvasCoordinate(event); |
|||
|
|||
let ret: CanvasMouseEvent = new CanvasMouseEvent(type, mousePosition, buttonNum, event.altKey, event.ctrlKey, event.shiftKey); |
|||
return ret; |
|||
} |
|||
|
|||
/** |
|||
* 把DOM 事件转成自己的事件 |
|||
* @param evt |
|||
* @param type |
|||
*/ |
|||
private _toCanvasKeyBoardEvent(evt: Event, type: EInputEventType): CanvasKeyBoardEvent { |
|||
let event: KeyboardEvent = evt as KeyboardEvent; |
|||
let ret: CanvasKeyBoardEvent = new CanvasKeyBoardEvent(type, event.key, event.keyCode, event.repeat, event.altKey, event.ctrlKey, event.shiftKey); |
|||
return ret; |
|||
} |
|||
|
|||
/** |
|||
* 添加一个定时器 |
|||
* @param callback |
|||
* @param timeout |
|||
* @param onlyOnce |
|||
* @param data |
|||
* @returns |
|||
*/ |
|||
public addTimer(callback: TimerCallback, timeout: number = 1.0, onlyOnce: boolean = false, data: any = undefined): number { |
|||
let timer: Timer; |
|||
|
|||
for (let i = 0; i < this.timers.length; i++) { |
|||
timer = this.timers[i]; |
|||
if (timer.enabled === false) { |
|||
timer.callback = callback; |
|||
timer.callbackData = data; |
|||
timer.timeout = timeout; |
|||
timer.enabled = true; |
|||
timer.onlyOnce = onlyOnce; |
|||
return timer.id; |
|||
} |
|||
} |
|||
|
|||
timer = new Timer(callback); |
|||
timer.callback = callback; |
|||
timer.callbackData = data; |
|||
timer.timeout = timeout; |
|||
timer.enabled = true; |
|||
timer.onlyOnce = onlyOnce; |
|||
|
|||
this.timers.push(timer); |
|||
timer.id = ++this._timerId; |
|||
|
|||
return timer.id; |
|||
} |
|||
|
|||
/** |
|||
* 清除定时器 |
|||
* @param id |
|||
* @returns |
|||
*/ |
|||
public removeTimer(id: number): boolean { |
|||
let ret: boolean = false; |
|||
for (let i = 0; i < this.timers.length; i++) { |
|||
if (this.timers[i].id === id) { |
|||
let timer: Timer = this.timers[i]; |
|||
timer.enabled = false; |
|||
ret = true; |
|||
break; |
|||
} |
|||
} |
|||
return ret; |
|||
} |
|||
|
|||
/** |
|||
* |
|||
* @param intervalSec 秒 |
|||
*/ |
|||
private _handleTimers(intervalSec: number): void { |
|||
for (let i = 0; i < this.timers.length; i++) { |
|||
let timer: Timer = this.timers[i]; |
|||
|
|||
if (timer.enabled === false) { |
|||
continue; |
|||
} |
|||
|
|||
//countdown 初始化为timeout, 每次减少渲染时间
|
|||
timer.countdown -= intervalSec; |
|||
|
|||
if (timer.countdown <= 0.0) { |
|||
timer.callback(timer.id, timer.callbackData); |
|||
if (timer.onlyOnce) { |
|||
this.removeTimer(timer.id); |
|||
} else { |
|||
timer.callbackData = timer.timeout; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,88 @@ |
|||
import { vec2 } from "../math/TSM"; |
|||
|
|||
/** |
|||
* appliaction所有事件 |
|||
*/ |
|||
export enum EInputEventType { |
|||
MOUSEEVENT, //总类。表示鼠标事件
|
|||
MOUSEDOWN, |
|||
MOUSEUP, |
|||
MOUSEMOVE, |
|||
MOUSEDRAG, |
|||
KEYBOARDEVENT, //总类,表示键盘事件
|
|||
KEYUP, |
|||
KEYDOWN, |
|||
KEYPRESS |
|||
} |
|||
|
|||
export class CanvasInputEvent { |
|||
/** |
|||
* @var alt键是否被按下 |
|||
*/ |
|||
public altKey: boolean; |
|||
|
|||
/** |
|||
* @var ctrl键是否被按下 |
|||
*/ |
|||
public ctrlKey: boolean; |
|||
|
|||
/** |
|||
* @var shift键是否被按下 |
|||
*/ |
|||
public shiftKey: boolean; |
|||
|
|||
/** |
|||
* @var 当前事件类型 |
|||
*/ |
|||
public type: EInputEventType; |
|||
|
|||
public constructor(type: EInputEventType, altKey: boolean = false, ctrlKey: boolean = false, shiftKey: boolean = false) { |
|||
this.type = type; |
|||
this.altKey = altKey; |
|||
this.ctrlKey = ctrlKey; |
|||
this.shiftKey = shiftKey; |
|||
} |
|||
} |
|||
|
|||
|
|||
export class CanvasMouseEvent extends CanvasInputEvent { |
|||
/** |
|||
* @var 0 : 鼠标左键 ,1 : 鼠标中键,2 : 鼠标右键 |
|||
*/ |
|||
public button: number; |
|||
|
|||
/** |
|||
* @var canvas 坐标系的位置 |
|||
*/ |
|||
public canvasPosition: vec2; |
|||
|
|||
public constructor(type: EInputEventType, canvasPos: vec2, button: number, altKey: boolean = false, ctrlKey: boolean = false, shiftKey:boolean = false){ |
|||
super(type,altKey, ctrlKey, shiftKey); |
|||
this.canvasPosition = canvasPos; |
|||
this.button = button; |
|||
} |
|||
} |
|||
|
|||
export class CanvasKeyBoardEvent extends CanvasInputEvent { |
|||
/** |
|||
* @var 当前按下按键的字符 |
|||
*/ |
|||
public key:string; |
|||
|
|||
/** |
|||
* @var 当前按下按键的ascii码 |
|||
*/ |
|||
public keyCode:number; |
|||
|
|||
/** |
|||
* @var 是否不停触发 |
|||
*/ |
|||
public repeat:boolean; |
|||
|
|||
public constructor(type: EInputEventType, key:string, keyCode:number, repeat: boolean, altKey: boolean = false, ctrlKey: boolean = false, shiftKey:boolean = false){ |
|||
super(type,altKey, ctrlKey, shiftKey); |
|||
this.key = key; |
|||
this.keyCode = keyCode; |
|||
this.repeat = repeat; |
|||
} |
|||
} |
@ -0,0 +1,42 @@ |
|||
export type TimerCallback = (id: number, data: any) => void; |
|||
|
|||
export class Timer { |
|||
/** |
|||
* @var 计时器id |
|||
*/ |
|||
public id: number = -1; |
|||
|
|||
/** |
|||
* @var 计时器是否开启 |
|||
*/ |
|||
public enabled: boolean = false; |
|||
|
|||
/** |
|||
* @function 回调函数,到时间后自动调用 |
|||
*/ |
|||
public callback: TimerCallback; |
|||
|
|||
/** |
|||
* @var 回调函数参数 |
|||
*/ |
|||
public callbackData: any = undefined; |
|||
|
|||
/** |
|||
* @var update次数 |
|||
*/ |
|||
public countdown: number = 0; |
|||
|
|||
/** |
|||
* @var 超时次数??? |
|||
*/ |
|||
public timeout: number = 0; |
|||
|
|||
/** |
|||
* @var 是否只执行一次 |
|||
*/ |
|||
public onlyOnce: boolean = false; |
|||
|
|||
constructor(callback: TimerCallback){ |
|||
this.callback = callback; |
|||
} |
|||
} |
1537
src/render/math/TSM.ts
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,42 @@ |
|||
/** |
|||
* 二维向量 |
|||
*/ |
|||
export class Vec2 { |
|||
|
|||
/** |
|||
* @var 数据存储内存 |
|||
*/ |
|||
public values = new Float32Array(2); |
|||
|
|||
public get x(): number { |
|||
return this.values[0]; |
|||
} |
|||
|
|||
public get y(): number { |
|||
return this.values[1]; |
|||
} |
|||
|
|||
public set x(value: number) { |
|||
this.values[0] = value; |
|||
} |
|||
|
|||
public set y(value: number) { |
|||
this.values[1] = value; |
|||
} |
|||
|
|||
public constructor(values: number[] | null = null) { |
|||
if (values) { |
|||
this.x = values[0]; |
|||
this.y = values[1]; |
|||
} else { |
|||
this.x = this.y = 0; |
|||
} |
|||
} |
|||
|
|||
copy(dest: Vec2 | null = null): Vec2 { |
|||
if (!dest) dest = new Vec2(); |
|||
dest.x = this.x; |
|||
dest.y = this.y; |
|||
return dest; |
|||
} |
|||
} |
@ -0,0 +1,161 @@ |
|||
import { EPSILON } from "../common"; |
|||
|
|||
export class Vec3 { |
|||
/** |
|||
* @var |
|||
*/ |
|||
public values = new Float32Array(3); |
|||
|
|||
public get x(): number { |
|||
return this.values[0]; |
|||
} |
|||
|
|||
public get y(): number { |
|||
return this.values[1]; |
|||
} |
|||
|
|||
public get z(): number { |
|||
return this.values[2]; |
|||
} |
|||
|
|||
public set x(value: number) { |
|||
this.values[0] = value; |
|||
} |
|||
|
|||
public set y(value: number) { |
|||
this.values[1] = value; |
|||
} |
|||
|
|||
public set z(value: number) { |
|||
this.values[2] = value; |
|||
} |
|||
|
|||
public constructor(values: number[] | null = null) { |
|||
if (values !== null) { |
|||
this.x = values[0]; |
|||
this.y = values[1]; |
|||
this.z = values[2]; |
|||
} else { |
|||
this.x = this.y = this.z = 0; |
|||
} |
|||
} |
|||
|
|||
public at(index: number): number { |
|||
return this.values[index]; |
|||
} |
|||
|
|||
public reset(x: number = 0, y: number = 0, z: number = 0): void { |
|||
this.x = x; |
|||
this.y = y; |
|||
this.z = z; |
|||
} |
|||
|
|||
public copy(dest: Vec3 | null = null): Vec3 { |
|||
if (!dest) dest = new Vec3(); |
|||
|
|||
dest.x = this.x; |
|||
dest.y = this.y; |
|||
dest.z = this.z; |
|||
|
|||
return dest; |
|||
} |
|||
|
|||
/** |
|||
* 取负向量 |
|||
* @param dest |
|||
* @returns |
|||
*/ |
|||
public negate(dest: Vec3 | null = null): Vec3 { |
|||
if (!dest) dest = this; |
|||
|
|||
dest.x = -this.x; |
|||
dest.y = -this.y; |
|||
dest.z = -this.z; |
|||
|
|||
return dest; |
|||
} |
|||
|
|||
/** |
|||
* 判断是否相等 |
|||
* @param vector |
|||
* @param threshold |
|||
* @returns |
|||
*/ |
|||
public equals(vector: Vec3, threshold = EPSILON): boolean { |
|||
if (Math.abs(this.x - vector.x) > threshold) |
|||
return false; |
|||
|
|||
if (Math.abs(this.y - vector.y) > threshold) |
|||
return false; |
|||
|
|||
if (Math.abs(this.z - vector.z) > threshold) |
|||
return false; |
|||
|
|||
return true; |
|||
} |
|||
|
|||
/** |
|||
* 获取向量的模 |
|||
*/ |
|||
public get length(): number { |
|||
return Math.sqrt(this.length2); |
|||
} |
|||
|
|||
/** |
|||
* 向量模的平方 |
|||
*/ |
|||
public get length2(): number { |
|||
let x = this.x, |
|||
y = this.y, |
|||
z = this.z; |
|||
|
|||
return (x * x + y * y + z * z); |
|||
} |
|||
|
|||
/** |
|||
* 向量相加 |
|||
* @param vector |
|||
* @returns |
|||
*/ |
|||
add(vector: Vec3): Vec3 { |
|||
this.x += vector.x; |
|||
this.y += vector.y; |
|||
this.z += vector.z; |
|||
|
|||
return this; |
|||
} |
|||
|
|||
/** |
|||
* 向量相减 |
|||
* @param vector |
|||
* @returns |
|||
*/ |
|||
subtract(vector: Vec3): Vec3 { |
|||
this.x -= vector.x; |
|||
this.y -= vector.y; |
|||
this.z -= vector.z; |
|||
|
|||
return this; |
|||
} |
|||
|
|||
|
|||
/** |
|||
* 向量缩放 |
|||
* @param value |
|||
* @param dest |
|||
* @returns |
|||
*/ |
|||
public scale(value: number, dest: Vec3 | null = null): Vec3 { |
|||
if (!dest) { |
|||
dest = this; |
|||
} else { |
|||
this.copy(dest); |
|||
} |
|||
|
|||
dest.x *= value; |
|||
dest.y *= value; |
|||
dest.z *= value; |
|||
|
|||
return dest; |
|||
} |
|||
} |
@ -0,0 +1,10 @@ |
|||
import { Application } from "../core/Application"; |
|||
|
|||
export class WebGLApplication extends Application { |
|||
/** |
|||
* @var WebGL |
|||
*/ |
|||
//public gl: WebGLRenderingContext;
|
|||
|
|||
|
|||
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue