diff --git a/source/GLRender/BasicRenderer.ts b/source/GLRender/BasicRenderer.ts new file mode 100644 index 0000000..661ebd2 --- /dev/null +++ b/source/GLRender/BasicRenderer.ts @@ -0,0 +1,143 @@ +import { AbstractRenderer, IRendererParam, IAnyObject } from "@Model/Renderer"; +import { EventType } from "@Model/Emitter"; +import { GLCanvas, GLCanvasOption } from "./GLCanvas"; +import { GLContext } from "./GLContext"; +import { Clock } from "@Model/Clock"; + +interface IRendererOwnParams {} + +/** + * 渲染器参数 + */ +type IRendererParams = IRendererOwnParams & GLCanvasOption; + +abstract class BasicRenderer< + P extends IRendererParam = {}, + M extends IAnyObject = {}, + E extends Record = {} +> extends AbstractRenderer { + + /** + * 渲染器参数 + */ + public param: Partial = {}; + + /** + * 使用的画布 + */ + public canvas: GLCanvas; + + /** + * 渲染时钟 + */ + protected clock: Clock; + + public constructor(canvas: HTMLCanvasElement, param: Partial = {}) { + super(); + + // 初始化参数 + this.param = { + autoResize: param.autoResize ?? true, + mouseEvent: param.autoResize ?? true, + eventLog: param.eventLog ?? false, + className: param.className ?? "" + } as M & IRendererParams; + + // 实例化画布对象 + this.canvas = new GLCanvas(canvas, this.param); + + // 尝试 webgl2 + this.gl = this.canvas.can.getContext("webgl2") as any; + if (this.gl) { + this.glVersion = 2; + console.log("Render: Using WebGL2 :)"); + } else { + + // 尝试 WebGL1 + this.gl = this.canvas.can.getContext("webgl") as any; + if (this.gl){ + this.glVersion = 1; + console.log("Render: Using WebGL1 :("); + } + + // 获取失败发出警告 + else { + console.error("Render: Not supported WebGL!"); + } + } + + /** + * 实例化时钟 + */ + this.clock = new Clock(); + + /** + * 初始化 + */ + this.onLoad(param); + } + + /** + * 执行渲染 + */ + protected run() { + this.clock.setFn(this.loop.bind(this)); + this.clock.run(); + } + + /** + * 大小变化 + */ + protected resize() { + this.loop(0); + this.gl.viewport(0, 0, this.canvas.width, this.canvas.height); + } + + /** + * 开启自动大小调整 + */ + protected autoResize() { + this.canvas.on("resize", this.resize.bind(this)); + } + + /** + * 清屏颜色 + */ + public cleanColor: [number, number, number, number] = [.1, .1, .1, 1.]; + + /** + * 清屏 + */ + public cleanCanvas(){ + this.gl.clearColor( + this.cleanColor[0], this.cleanColor[1], + this.cleanColor[2], this.cleanColor[3] + ); + this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT); + } + + /** + * GL 上下文 + */ + public gl: GLContext; + + /** + * WebGL 版本 + */ + public glVersion: (0 | 1 | 2) = 0; + + /** + * 初始化 + * @param param 渲染器参数 + */ + abstract onLoad(param: Partial): void; + + /** + * 渲染器执行 + */ + abstract loop(dur: number): void; + +} + +export default BasicRenderer; +export { BasicRenderer, IRendererParams }; \ No newline at end of file diff --git a/source/GLRender/ClassicRenderer.ts b/source/GLRender/ClassicRenderer.ts index 6ec9b6c..690e197 100644 --- a/source/GLRender/ClassicRenderer.ts +++ b/source/GLRender/ClassicRenderer.ts @@ -1,49 +1,30 @@ -import { AbstractRenderer, ObjectID, ObjectData, ICommonParam } from "@Model/Renderer"; -import { GLCanvas, GLCanvasOption } from "./GLCanvas"; +import { ObjectID, ObjectData, ICommonParam } from "@Model/Renderer"; +import { BasicRenderer, IRendererParams } from "./BasicRenderer"; -interface IRendererOwnParams {} +interface IClassicRendererParams {} -/** - * 渲染器参数 - */ -type IRendererParams = IRendererOwnParams & GLCanvasOption; +class ClassicRenderer extends BasicRenderer<{}, IClassicRendererParams> { -class ClassicRenderer extends AbstractRenderer<{}, IRendererParams> { + onLoad(param: Partial): void { + this.run(); + this.autoResize(); + } - /** - * 渲染器参数 - */ - public param: IRendererParams; + clean(id?: ObjectID | ObjectID[]): this { + throw new Error("Method not implemented."); + } - /** - * 使用的画布 - */ - public canvas: GLCanvas; + points(id: ObjectID, position: ObjectData, param?: ICommonParam): this { + throw new Error("Method not implemented."); + } - public constructor(canvas: HTMLCanvasElement, param: IRendererParams = {}) { - super(); + cube(id: ObjectID, position: ObjectData, param?: ICommonParam): this { + throw new Error("Method not implemented."); + } - // 初始化参数 - this.param = { - autoResize: param.autoResize ?? true, - mouseEvent: param.autoResize ?? true, - eventLog: param.eventLog ?? false, - clasName: param.clasName ?? "" - } - - // 实例化画布对象 - this.canvas = new GLCanvas(canvas, this.param); - } - - clean(id?: ObjectID | ObjectID[]): this { - throw new Error("Method not implemented."); - } - points(id: ObjectID, position: ObjectData, param?: ICommonParam): this { - throw new Error("Method not implemented."); - } - cube(id: ObjectID, position: ObjectData, param?: ICommonParam): this { - throw new Error("Method not implemented."); - } + loop(): void { + this.cleanCanvas(); + } } export default ClassicRenderer; diff --git a/source/GLRender/GLCanvas.ts b/source/GLRender/GLCanvas.ts index b0b9b33..5b412c0 100644 --- a/source/GLRender/GLCanvas.ts +++ b/source/GLRender/GLCanvas.ts @@ -27,7 +27,7 @@ interface GLCanvasOption { /** * 节点类名 */ - clasName?: string + className?: string } type GLCanvasEvent = { @@ -270,7 +270,7 @@ class GLCanvas extends Emitter { this.canvas = ele ?? document.createElement("canvas"); this.div = document.createElement("div"); - this.div.className = opt.clasName ?? ""; + this.div.className = opt.className ?? ""; this.div.appendChild(this.canvas); this.canvas.style.width = "100%"; diff --git a/source/Model/Clock.ts b/source/Model/Clock.ts new file mode 100644 index 0000000..1c10c9b --- /dev/null +++ b/source/Model/Clock.ts @@ -0,0 +1,75 @@ +export {Clock, LoopFunction}; + +type LoopFunction = (t: number)=>void; + +/** + * 时钟 + */ +class Clock { + + /** + * 总用时 + */ + private allTime: number = 0; + + /** + * 速率 + */ + public speed: number = 1; + + /** + * 主函数 + */ + private fn: LoopFunction; + + /** + * 动画循环 + * @param fn 循环函数 + */ + public constructor(fn?: LoopFunction){ + this.fn = fn ?? ((t) => {}); + } + + /** + * 设置函数 + * @param fn 循环函数 + */ + public setFn(fn:LoopFunction){ + this.fn = fn; + } + + /** + * 开始 + */ + public run(){ + + // 主循环 + let loop = (t:number)=>{ + + // 时差 + let dur = (t - this.allTime) * this.speed / 1000; + + // 检测由于失焦导致的丢帧 + if (t - this.allTime < 100) { + this.fn(dur); + } + + // 更新时间 + this.allTime = t; + + // 继续循环 + requestAnimationFrame(loop); + + } + + // 获取时间 + requestAnimationFrame((t)=>{ + + // 记录初始时间 + this.allTime = t; + + // 开启循环 + requestAnimationFrame(loop); + }) + } +} \ No newline at end of file diff --git a/source/Model/Renderer.ts b/source/Model/Renderer.ts index c0cff94..a3c457e 100644 --- a/source/Model/Renderer.ts +++ b/source/Model/Renderer.ts @@ -57,6 +57,9 @@ interface IRendererConstructor< /** * 渲染器 API + * @template P 渲染器绘制参数 + * @template M 渲染器参数 + * @template E 渲染器事件 */ abstract class AbstractRenderer< P extends IRendererParam = {}, @@ -67,7 +70,7 @@ abstract class AbstractRenderer< /** * 渲染器参数 */ - abstract param: M; + abstract param: Partial; /** * 类型断言 @@ -119,4 +122,8 @@ abstract class AbstractRenderer< } export default AbstractRenderer; -export { AbstractRenderer, ObjectID, ICommonParam, ObjectData, IRendererConstructor }; \ No newline at end of file +export { + AbstractRenderer, ObjectID, IAnyObject, + ICommonParam, IRendererParam, + ObjectData, IRendererConstructor +}; \ No newline at end of file diff --git a/source/Page/Laboratory/Laboratory.scss b/source/Page/Laboratory/Laboratory.scss index 24529af..f6e7474 100644 --- a/source/Page/Laboratory/Laboratory.scss +++ b/source/Page/Laboratory/Laboratory.scss @@ -2,6 +2,7 @@ div.main-canvas { position: fixed; width: 100%; height: 100%; + background-color: rgba($color: #000000, $alpha: .9); div.canvas { width: 100%; diff --git a/source/Page/Laboratory/Laboratory.tsx b/source/Page/Laboratory/Laboratory.tsx index 8637ee4..8b3ca17 100644 --- a/source/Page/Laboratory/Laboratory.tsx +++ b/source/Page/Laboratory/Laboratory.tsx @@ -23,7 +23,7 @@ class Laboratory extends Component { const canvas = document.createElement("canvas"); const renderer = new ClassicRenderer(canvas, { - clasName: "canvas" + className: "canvas" }); console.log(renderer);