From 2195918c770255a9b8a1890ead7b518ae8c22063 Mon Sep 17 00:00:00 2001 From: tiendat3699 <96950844+tiendat3699@users.noreply.github.com> Date: Fri, 7 Jun 2024 14:10:54 +0700 Subject: [PATCH] feat: add gizmos --- assets/_Game/Scripts/Gizmos/Define.ts | 4 + assets/_Game/Scripts/Gizmos/Define.ts.meta | 9 + assets/_Game/Scripts/Gizmos/Ext.ts | 10 + assets/_Game/Scripts/Gizmos/Ext.ts.meta | 9 + assets/_Game/Scripts/Gizmos/Gizmos2D.ts | 400 ++++++++++++ assets/_Game/Scripts/Gizmos/Gizmos2D.ts.meta | 9 + assets/_Game/Scripts/Gizmos/Gizmos3D.ts | 619 +++++++++++++++++++ assets/_Game/Scripts/Gizmos/Gizmos3D.ts.meta | 9 + assets/_Game/Scripts/Pool/IPoolable.ts | 4 +- 9 files changed, 1071 insertions(+), 2 deletions(-) create mode 100644 assets/_Game/Scripts/Gizmos/Define.ts create mode 100644 assets/_Game/Scripts/Gizmos/Define.ts.meta create mode 100644 assets/_Game/Scripts/Gizmos/Ext.ts create mode 100644 assets/_Game/Scripts/Gizmos/Ext.ts.meta create mode 100644 assets/_Game/Scripts/Gizmos/Gizmos2D.ts create mode 100644 assets/_Game/Scripts/Gizmos/Gizmos2D.ts.meta create mode 100644 assets/_Game/Scripts/Gizmos/Gizmos3D.ts create mode 100644 assets/_Game/Scripts/Gizmos/Gizmos3D.ts.meta diff --git a/assets/_Game/Scripts/Gizmos/Define.ts b/assets/_Game/Scripts/Gizmos/Define.ts new file mode 100644 index 0000000..d43a133 --- /dev/null +++ b/assets/_Game/Scripts/Gizmos/Define.ts @@ -0,0 +1,4 @@ +import { EDITOR, EDITOR_NOT_IN_PREVIEW } from 'cc/env'; + +export const cce = EDITOR_NOT_IN_PREVIEW && (window as any).cce; +export const Editor = EDITOR && (window as any).Editor; diff --git a/assets/_Game/Scripts/Gizmos/Define.ts.meta b/assets/_Game/Scripts/Gizmos/Define.ts.meta new file mode 100644 index 0000000..adfc2f9 --- /dev/null +++ b/assets/_Game/Scripts/Gizmos/Define.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.23", + "importer": "typescript", + "imported": true, + "uuid": "6380d835-23a8-4a24-9114-22ef1765f260", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/assets/_Game/Scripts/Gizmos/Ext.ts b/assets/_Game/Scripts/Gizmos/Ext.ts new file mode 100644 index 0000000..da5c399 --- /dev/null +++ b/assets/_Game/Scripts/Gizmos/Ext.ts @@ -0,0 +1,10 @@ +import { NodeActivator } from 'cc'; + +declare module 'cc' { + interface Component { + /** + * Call per frame in editor (Only use in editor) + */ + onDrawGizmos(): void; + } +} diff --git a/assets/_Game/Scripts/Gizmos/Ext.ts.meta b/assets/_Game/Scripts/Gizmos/Ext.ts.meta new file mode 100644 index 0000000..6210b72 --- /dev/null +++ b/assets/_Game/Scripts/Gizmos/Ext.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.23", + "importer": "typescript", + "imported": true, + "uuid": "f1fafa13-305c-4113-8348-33754b0582a8", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/assets/_Game/Scripts/Gizmos/Gizmos2D.ts b/assets/_Game/Scripts/Gizmos/Gizmos2D.ts new file mode 100644 index 0000000..b6536e8 --- /dev/null +++ b/assets/_Game/Scripts/Gizmos/Gizmos2D.ts @@ -0,0 +1,400 @@ +import { _decorator, CCObject, Color, Component, Graphics, IVec2Like, Layers, Node, toRadian, Vec3 } from 'cc'; +import { Editor } from './Define'; + +const { ccclass, executeInEditMode } = _decorator; + +class GizmosRenderer { + private _graphic: Graphics; + private _drawCalls: ((graphic: Graphics) => void)[] = []; + + public addDrawCall(drawCall: (graphic: Graphics) => void) { + this._drawCalls.push(drawCall); + } + + constructor(graphic: Graphics) { + this._graphic = graphic; + } + + public clear() { + this._graphic.clear(); + } + + public setLayer(layer: Layers.Enum) { + this._graphic.node.layer = layer; + } + + public draw() { + this._drawCalls.forEach((drawCall) => { + drawCall(this._graphic); + }); + + this._drawCalls = []; + } +} + +@ccclass('Gizmos2D.GizmosDebugDraw') +@executeInEditMode +class GizmosDebugDraw extends Component { + private _renderers: Map = new Map(); + private _color: Color = Gizmos2D.DEFAULT_COLOR; + private _useLocalPosition: boolean = false; + private _layer: Layers.Enum = Gizmos2D.DEFAULT_LAYER; + private _parentNode: Node; + + protected update(dt: number): void { + //only call in editor + this.callNodeDrawGizmos(); + + this._renderers.forEach((renderer) => { + renderer.clear(); + renderer.draw(); + }); + + this._color = Gizmos2D.DEFAULT_COLOR; + this._useLocalPosition = false; + this._layer = Gizmos2D.DEFAULT_LAYER; + } + + private callNodeDrawGizmos() { + if (Editor && this._parentNode) { + const selectedList: string[] = Editor.Selection.getSelected('node'); + if (selectedList.includes(this._parentNode.uuid)) { + const comps = this._parentNode.components; + for (let i = 0; i < comps.length; i++) { + const comp: Component = comps[i]; + comp.onDrawGizmos?.(); + } + } + } + } + + private createRenderer(color: Color) { + const hex = color.toHEX(); + const g = new Node(`color ${hex}`).addComponent(Graphics); + g.lineWidth = 5; + g.strokeColor = color; + g.fillColor = color; + g.node.layer = this.node.layer; + g.node.parent = this.node; + const renderer = new GizmosRenderer(g); + return renderer; + } + + private getRenderer(color: Color): GizmosRenderer { + const hex = color.toHEX(); + let renderer = this._renderers.get(hex); + if (!renderer) { + renderer = this.createRenderer(color); + this._renderers.set(hex, renderer); + } + return renderer; + } + + private worldToLocal(world: IVec2Like): IVec2Like { + const local = new Vec3(); + this.node.inverseTransformPoint(local, new Vec3(world.x, world.y)); + return local; + } + + public registerDrawGizmos(node: Node) { + this._parentNode = node; + } + + public setColor(color: Color) { + this._color = color; + } + + public setUseLocalPosition(value: boolean) { + this._useLocalPosition = value; + } + + public setLayer(layer: Layers.Enum) { + this._layer = layer; + } + + public clearAll() { + this._renderers.forEach((renderer) => renderer.clear()); + } + + public drawLine(point1: IVec2Like, point2: IVec2Like) { + const color = this._color.clone(); + const renderer = this.getRenderer(color); + renderer.setLayer(this._layer); + const p1 = this._useLocalPosition ? point1 : this.worldToLocal(point1); + const p2 = this._useLocalPosition ? point1 : this.worldToLocal(point2); + renderer.addDrawCall((g) => { + g.moveTo(p1.x, p1.y); + g.lineTo(p2.x, p2.y); + g.stroke(); + }); + } + + public drawLineList(points: IVec2Like[], close: boolean = false) { + const color = this._color.clone(); + const renderer = this.getRenderer(color); + renderer.setLayer(this._layer); + renderer.addDrawCall((g) => { + if (points.length > 0) { + const p0 = this.worldToLocal(points[0]); + g.moveTo(p0.x, p0.y); + for (let i = 1; i < points.length; i++) { + const p = this.worldToLocal(points[i]); + g.lineTo(p.x, p.y); + } + if (close) g.close(); + g.stroke(); + } + }); + } + + public drawCircle(center: IVec2Like, radius: number) { + const color = this._color.clone(); + const renderer = this.getRenderer(color); + renderer.setLayer(this._layer); + const c = this._useLocalPosition ? center : this.worldToLocal(center); + renderer.addDrawCall((g) => { + g.circle(c.x, c.y, radius); + g.stroke(); + }); + } + + public drawSolidCircle(center: IVec2Like, radius: number) { + const color = this._color.clone(); + const renderer = this.getRenderer(color); + renderer.setLayer(this._layer); + const c = this._useLocalPosition ? center : this.worldToLocal(center); + renderer.addDrawCall((g) => { + g.circle(c.x, c.y, radius); + g.fill(); + }); + } + + public drawRect(position: IVec2Like, width: number, height: number) { + const color = this._color.clone(); + const renderer = this.getRenderer(color); + renderer.setLayer(this._layer); + const p = this._useLocalPosition ? position : this.worldToLocal(position); + renderer.addDrawCall((g) => { + g.rect(p.x, p.y, width, height); + g.stroke(); + }); + } + + public drawSolidRect(center: IVec2Like, width: number, height: number) { + const color = this._color.clone(); + const renderer = this.getRenderer(color); + renderer.setLayer(this._layer); + const c = this._useLocalPosition ? center : this.worldToLocal(center); + renderer.addDrawCall((g) => { + g.rect(c.x, c.y, width, height); + g.fill(); + }); + } + + public drawEllipse(center: IVec2Like, radiusX: number, radiusY: number) { + const color = this._color.clone(); + const renderer = this.getRenderer(color); + renderer.setLayer(this._layer); + const c = this._useLocalPosition ? center : this.worldToLocal(center); + renderer.addDrawCall((g) => { + g.ellipse(c.x, c.y, radiusX, radiusY); + g.stroke(); + }); + } + + public drawSolidEllipse(center: IVec2Like, radiusX: number, radiusY: number) { + const color = this._color.clone(); + const renderer = this.getRenderer(color); + renderer.setLayer(this._layer); + const c = this._useLocalPosition ? center : this.worldToLocal(center); + renderer.addDrawCall((g) => { + g.ellipse(c.x, c.y, radiusX, radiusY); + g.fill(); + }); + } + + public drawArc( + center: IVec2Like, + radius: number, + startAngle: number, + endAngle: number, + counterclockwise: boolean = false, + ) { + const color = this._color.clone(); + const renderer = this.getRenderer(color); + renderer.setLayer(this._layer); + const c = this._useLocalPosition ? center : this.worldToLocal(center); + renderer.addDrawCall((g) => { + g.arc(c.x, c.y, radius, toRadian(startAngle), toRadian(endAngle), counterclockwise); + g.stroke(); + }); + } + + public drawSolidArc( + center: IVec2Like, + radius: number, + startAngle: number, + endAngle: number, + counterclockwise: boolean = false, + ) { + const color = this._color.clone(); + const renderer = this.getRenderer(color); + renderer.setLayer(this._layer); + const c = this._useLocalPosition ? center : this.worldToLocal(center); + renderer.addDrawCall((g) => { + g.arc(c.x, c.y, radius, toRadian(startAngle), toRadian(endAngle), counterclockwise); + g.lineTo(c.x, c.y); + g.fill(); + }); + } + + public drawBezierCurves(point1: IVec2Like, point2: IVec2Like, point3: IVec2Like, point4: IVec2Like) { + const color = this._color.clone(); + const renderer = this.getRenderer(color); + renderer.setLayer(this._layer); + const p1 = this._useLocalPosition ? point1 : this.worldToLocal(point1); + const p2 = this._useLocalPosition ? point2 : this.worldToLocal(point2); + const p3 = this._useLocalPosition ? point3 : this.worldToLocal(point3); + const p4 = this._useLocalPosition ? point4 : this.worldToLocal(point4); + renderer.addDrawCall((g) => { + g.moveTo(p1.x, p1.y); + g.bezierCurveTo(p2.x, p2.y, p3.x, p3.y, p4.x, p4.y); + g.stroke(); + }); + } + + public drawQuadraticCurve(point1: IVec2Like, point2: IVec2Like, point3: IVec2Like) { + const color = this._color.clone(); + const renderer = this.getRenderer(color); + renderer.setLayer(this._layer); + const p1 = this._useLocalPosition ? point1 : this.worldToLocal(point1); + const p2 = this._useLocalPosition ? point2 : this.worldToLocal(point2); + const p3 = this._useLocalPosition ? point3 : this.worldToLocal(point3); + renderer.addDrawCall((g) => { + g.moveTo(p1.x, p1.y); + g.quadraticCurveTo(p2.x, p2.y, p3.x, p3.y); + g.stroke(); + }); + } +} + +export default class Gizmos2D { + public static readonly DEFAULT_COLOR = Color.BLUE; + public static readonly DEFAULT_LAYER = Layers.Enum.GIZMOS; + + private static getDebugNode(node: Node) { + let debugNode = node.getComponentInChildren(GizmosDebugDraw); + if (!debugNode) { + debugNode = new Node('DEBUG_DRAW_NODE').addComponent(GizmosDebugDraw); + debugNode.node.layer = this.DEFAULT_LAYER; + debugNode.node.hideFlags |= CCObject.Flags.DontSave | CCObject.Flags.HideInHierarchy; + debugNode.node.parent = node; + } + + return debugNode; + } + + public static registerDrawGizmos(node: Node) { + this.getDebugNode(node).registerDrawGizmos(node); + } + + public static beginColor(node: Node, color: Color) { + this.getDebugNode(node).setColor(color); + } + + public static beginLocalPosition(node: Node) { + this.getDebugNode(node).setUseLocalPosition(true); + } + + public static endLocalPosition(node: Node) { + this.getDebugNode(node).setUseLocalPosition(false); + } + + public static beginLayer(node: Node, layer: Layers.Enum) { + this.getDebugNode(node).setLayer(layer); + } + + public static clearAll(node: Node) { + this.getDebugNode(node).clearAll(); + } + + public static drawLine(node: Node, point1: IVec2Like, point2: IVec2Like) { + const debugNode = this.getDebugNode(node); + debugNode.drawLine(point1, point2); + } + + public static drawLineList(node: Node, points: IVec2Like[], close: boolean = false) { + const debugNode = this.getDebugNode(node); + debugNode.drawLineList(points, close); + } + + public static drawCircle(node: Node, center, radius: number) { + const debugNode = this.getDebugNode(node); + debugNode.drawCircle(center, radius); + } + + public static drawSolidCircle(node: Node, center, radius: number) { + const debugNode = this.getDebugNode(node); + debugNode.drawSolidCircle(center, radius); + } + + public static drawRect(node: Node, position: IVec2Like, width: number, height: number) { + const debugNode = this.getDebugNode(node); + debugNode.drawRect(position, width, height); + } + + public static drawSolidRect(node: Node, center: IVec2Like, width: number, height: number) { + const debugNode = this.getDebugNode(node); + debugNode.drawSolidRect(center, width, height); + } + + public static drawEllipse(node: Node, center: IVec2Like, radiusX: number, radiusY: number) { + const debugNode = this.getDebugNode(node); + debugNode.drawEllipse(center, radiusX, radiusY); + } + + public static drawSolidEllipse(node: Node, center: IVec2Like, radiusX: number, radiusY: number) { + const debugNode = this.getDebugNode(node); + debugNode.drawSolidEllipse(center, radiusX, radiusY); + } + + public static drawArc( + node: Node, + center: IVec2Like, + radius: number, + startAngle: number, + endAngle: number, + counterclockwise: boolean = false, + ) { + const debugNode = this.getDebugNode(node); + debugNode.drawArc(center, radius, startAngle, endAngle, counterclockwise); + } + + public static drawSolidArc( + node: Node, + center: IVec2Like, + radius: number, + startAngle: number, + endAngle: number, + counterclockwise: boolean = false, + ) { + const debugNode = this.getDebugNode(node); + debugNode.drawSolidArc(center, radius, startAngle, endAngle, counterclockwise); + } + + public static drawBezierCurves( + node: Node, + point1: IVec2Like, + point2: IVec2Like, + point3: IVec2Like, + point4: IVec2Like, + ) { + const debugNode = this.getDebugNode(node); + debugNode.drawBezierCurves(point1, point2, point3, point4); + } + + public static drawQuadraticCurve(node: Node, point1: IVec2Like, point2: IVec2Like, point3: IVec2Like) { + const debugNode = this.getDebugNode(node); + debugNode.drawQuadraticCurve(point1, point2, point3); + } +} diff --git a/assets/_Game/Scripts/Gizmos/Gizmos2D.ts.meta b/assets/_Game/Scripts/Gizmos/Gizmos2D.ts.meta new file mode 100644 index 0000000..181ea50 --- /dev/null +++ b/assets/_Game/Scripts/Gizmos/Gizmos2D.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.23", + "importer": "typescript", + "imported": true, + "uuid": "a7db21b7-2ab4-4180-b95b-86a2019340e5", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/assets/_Game/Scripts/Gizmos/Gizmos3D.ts b/assets/_Game/Scripts/Gizmos/Gizmos3D.ts new file mode 100644 index 0000000..758c585 --- /dev/null +++ b/assets/_Game/Scripts/Gizmos/Gizmos3D.ts @@ -0,0 +1,619 @@ +import { + _decorator, + Camera, + CCObject, + Color, + Component, + director, + geometry, + GeometryRenderer, + gfx, + Layers, + Mat4, + Material, + Node, + toRadian, + Vec3, +} from 'cc'; +import { cce, Editor } from './Define'; + +const { ccclass, executeInEditMode, property } = _decorator; + +class GizmosRenderer { + private _geometryRenderer; + private _drawCalls: ((geometry) => void)[] = []; + + constructor(geometryRenderer: GeometryRenderer) { + this._geometryRenderer = geometryRenderer; + } + + public addDrawCall(drawCall: (geometry: GeometryRenderer) => void) { + this._drawCalls.push(drawCall); + } + + public clear() { + this._drawCalls = []; + } + + public draw() { + this._drawCalls.forEach((drawCall) => { + drawCall(this._geometryRenderer); + }); + } +} + +@ccclass('Gizmos3D.GizmosDebugDraw') +@executeInEditMode +class GizmosDebugDraw extends Component { + private _renderers: Map = new Map(); + private _color: Color = Gizmos3D.DEFAULT_COLOR; + private _renderer: GeometryRenderer = null; + private _depthTest: boolean = false; + private _useLocalPosition: boolean = false; + private _parentNode: Node; + + protected onLoad(): void { + if (cce) { + cce.Camera._camera.camera.initGeometryRenderer(); + this._renderer = cce.Camera._camera.camera.geometryRenderer; + } else { + const camera = director.getScene().getComponentInChildren(Camera).camera; + camera.initGeometryRenderer(); + this._renderer = camera.geometryRenderer; + } + + if (!this._renderer) { + console.warn( + 'Unable to initialize geometryRenderer for Gizmos3D, please ensure the Geometry Renderer feature is enabled in project settings if you want to use Gizmos3D in play mode', + ); + } + } + + protected update(dt: number): void { + //only call in editor + this.callNodeDrawGizmos(); + + this._renderers.forEach((renderer) => { + renderer.draw(); + renderer.clear(); + }); + + this._color = Gizmos3D.DEFAULT_COLOR; + this._useLocalPosition = false; + } + + private callNodeDrawGizmos() { + if (Editor && this._parentNode) { + const selectedList: string[] = Editor.Selection.getSelected('node'); + if (selectedList.includes(this._parentNode.uuid)) { + const comps = this._parentNode.components; + for (let i = 0; i < comps.length; i++) { + const comp: Component = comps[i]; + comp.onDrawGizmos?.(); + } + } + } + } + + private createRenderer(color: Color) { + const renderer = new GizmosRenderer(this._renderer); + return renderer; + } + + private getRenderer(color: Color): GizmosRenderer { + const hex = color.toHEX(); + let renderer = this._renderers.get(hex); + if (!renderer) { + renderer = this.createRenderer(color); + this._renderers.set(hex, renderer); + } + return renderer; + } + + private worldToLocal(world: Vec3): Vec3 { + const local = new Vec3(); + Vec3.add(local, this.node.worldPosition, world); + return local; + } + + public registerDrawGizmos(node: Node) { + this._parentNode = node; + } + + public setDepthTest(value: boolean) { + this._depthTest = value; + } + + public setColor(color: Color) { + this._color = color; + } + + public setUseLocalPosition(value: boolean) { + this._useLocalPosition = value; + } + + public clearAll() { + this._renderers.forEach((renderer) => renderer.clear()); + } + + private rotate(pos: Vec3, rot: Vec3 = Vec3.ZERO): Mat4 { + let result = new Mat4(); + let transform = new Mat4(); + + Mat4.fromTranslation(result, pos); + + Mat4.fromXRotation(transform, toRadian(rot.x)); + result.multiply(transform); + + Mat4.fromYRotation(transform, toRadian(rot.y)); + result.multiply(transform); + + Mat4.fromZRotation(transform, toRadian(rot.z)); + result.multiply(transform); + + Mat4.fromTranslation(transform, new Vec3(-pos.x, -pos.y, -pos.z)); + result.multiply(transform); + + return result; + } + + public drawLine(point1: Vec3, point2: Vec3) { + const color = this._color.clone(); + const renderer = this.getRenderer(color); + const p1 = this._useLocalPosition ? this.worldToLocal(point1) : point1; + const p2 = this._useLocalPosition ? this.worldToLocal(point2) : point2; + renderer.addDrawCall((geometry) => { + geometry?.addLine(p1, p2, color, this._depthTest); + }); + } + + public drawLineList(points: Vec3[], close: boolean = false) { + const color = this._color.clone(); + const renderer = this.getRenderer(color); + const pointList = this._useLocalPosition ? points.map((p) => this.worldToLocal(p)) : points; + renderer.addDrawCall((geometry) => { + if (pointList.length > 0) { + for (let i = 0; i < pointList.length - 1; i++) { + geometry?.addLine(pointList[i], pointList[i + 1], color, this._depthTest); + } + + if (close) { + geometry?.addLine(pointList[pointList.length - 1], pointList[0], color, this._depthTest); + } + } + }); + } + + public drawDashLine(point1: Vec3, point2: Vec3) { + const color = this._color.clone(); + const renderer = this.getRenderer(color); + const p1 = this._useLocalPosition ? this.worldToLocal(point1) : point1; + const p2 = this._useLocalPosition ? this.worldToLocal(point2) : point2; + renderer.addDrawCall((geometry) => { + geometry?.addDashedLine(p1, p2, color, this._depthTest); + }); + } + + public drawDashLineList(points: Vec3[], close: boolean = false) { + const color = this._color.clone(); + const renderer = this.getRenderer(color); + const pointList = this._useLocalPosition ? points.map((p) => this.worldToLocal(p)) : points; + renderer.addDrawCall((geometry) => { + if (pointList.length > 0) { + for (let i = 0; i < pointList.length - 1; i++) { + geometry?.addDashedLine(pointList[i], pointList[i + 1], color, this._depthTest); + } + + if (close) { + geometry?.addDashedLine(pointList[pointList.length - 1], pointList[0], color, this._depthTest); + } + } + }); + } + + public drawCircle(center: Vec3, radius: number, rot: Vec3 = Vec3.ZERO) { + const color = this._color.clone(); + const renderer = this.getRenderer(color); + const c = this._useLocalPosition ? this.worldToLocal(center) : center; + const transform = this.rotate(c, rot); + renderer.addDrawCall((geometry) => { + geometry?.addCircle(c, radius, color, 32, this._depthTest, true, transform); + }); + } + + public drawDisc(center: Vec3, radius: number, rot: Vec3 = Vec3.ZERO, wireFrame: boolean = false) { + const color = this._color.clone(); + const renderer = this.getRenderer(color); + const c = this._useLocalPosition ? this.worldToLocal(center) : center; + const transform = this.rotate(c, rot); + renderer.addDrawCall((geometry) => { + geometry?.addDisc(c, radius, color, 32, wireFrame, this._depthTest, true, true, transform); + }); + } + + public drawQuad(point1: Vec3, point2: Vec3, point3: Vec3, point4: Vec3, wireFrame: boolean = false) { + const color = this._color.clone(); + const renderer = this.getRenderer(color); + const p1 = this._useLocalPosition ? this.worldToLocal(point1) : point1; + const p2 = this._useLocalPosition ? this.worldToLocal(point2) : point2; + const p3 = this._useLocalPosition ? this.worldToLocal(point3) : point3; + const p4 = this._useLocalPosition ? this.worldToLocal(point4) : point4; + renderer.addDrawCall((geometry) => { + geometry?.addQuad(p1, p2, p3, p4, color, wireFrame, this._depthTest); + }); + } + + public drawSphere(center: Vec3, radius: number, wireFrame: boolean = false) { + const color = this._color.clone(); + const renderer = this.getRenderer(color); + const c = this._useLocalPosition ? this.worldToLocal(center) : center; + renderer.addDrawCall((geometry) => { + geometry?.addSphere(c, radius, color, 32, 16, wireFrame, this._depthTest); + }); + } + + public drawArc(center: Vec3, radius: number, startAngle: number, endAngle: number, rot: Vec3 = Vec3.ZERO) { + const color = this._color.clone(); + const renderer = this.getRenderer(color); + const c = this._useLocalPosition ? this.worldToLocal(center) : center; + const transform = this.rotate(c, rot); + renderer.addDrawCall((geometry) => { + geometry?.addArc(c, radius, color, startAngle, endAngle, 32, this._depthTest, true, transform); + }); + } + + public drawSolidArc( + center: Vec3, + radius: number, + startAngle: number, + endAngle: number, + rot: Vec3 = Vec3.ZERO, + wireFrame: boolean = false, + ) { + const color = this._color.clone(); + const renderer = this.getRenderer(color); + const c = this._useLocalPosition ? this.worldToLocal(center) : center; + const transform = this.rotate(c, rot); + renderer.addDrawCall((geometry) => { + geometry?.addSector( + c, + radius, + color, + startAngle, + endAngle, + 32, + wireFrame, + this._depthTest, + true, + true, + transform, + ); + }); + } + + public drawPolygon( + position: Vec3, + radius: number, + segments: number, + rot: Vec3 = Vec3.ZERO, + wireFrame: boolean = false, + ) { + const color = this._color.clone(); + const renderer = this.getRenderer(color); + const p = this._useLocalPosition ? this.worldToLocal(position) : position; + const transform = this.rotate(p, rot); + renderer.addDrawCall((geometry) => { + geometry?.addPolygon(p, radius, color, segments, wireFrame, this._depthTest, true, true, transform); + }); + } + + public drawOctahedron(position: Vec3, radius: number, rot: Vec3 = Vec3.ZERO, wireFrame: boolean = false) { + const color = this._color.clone(); + const renderer = this.getRenderer(color); + const p = this._useLocalPosition ? this.worldToLocal(position) : position; + const transform = this.rotate(p, rot); + renderer.addDrawCall((geometry) => { + geometry?.addOctahedron(p, radius, color, wireFrame, this._depthTest, false, true, transform); + }); + } + + public drawCross(position: Vec3, size: number) { + const color = this._color.clone(); + const renderer = this.getRenderer(color); + const p = this._useLocalPosition ? this.worldToLocal(position) : position; + renderer.addDrawCall((geometry) => { + geometry?.addCross(p, size, color, this._depthTest); + }); + } + + public drawCapsule( + position: Vec3, + radius: number, + height: number, + rot: Vec3 = Vec3.ZERO, + wireFrame: boolean = false, + ) { + const color = this._color.clone(); + const renderer = this.getRenderer(color); + const p = this._useLocalPosition ? this.worldToLocal(position) : position; + const transform = this.rotate(p, rot); + renderer.addDrawCall((geometry) => { + geometry?.addCapsule(p, radius, height, color, 32, 8, wireFrame, this._depthTest, false, true, transform); + }); + } + + public drawBox(position: Vec3, size: Vec3, rot: Vec3 = Vec3.ZERO, wireFrame: boolean = false) { + const color = this._color.clone(); + const renderer = this.getRenderer(color); + const p = this._useLocalPosition ? this.worldToLocal(position) : position; + const transform = this.rotate(p, rot); + let box = geometry?.AABB.create(p.x, p.y, p.z, size.x, size.y, size.z); + renderer.addDrawCall((geometry) => { + geometry?.addBoundingBox(box, color, wireFrame, this._depthTest, false, true, transform); + }); + } + + public drawCylinder( + position: Vec3, + radius: number, + height: number, + rot: Vec3 = Vec3.ZERO, + wireFrame: boolean = false, + ) { + const color = this._color.clone(); + const renderer = this.getRenderer(color); + const p = this._useLocalPosition ? this.worldToLocal(position) : position; + const transform = this.rotate(p, rot); + renderer.addDrawCall((geometry) => { + geometry?.addCylinder(p, radius, height, color, 32, wireFrame, this._depthTest, false, true, transform); + }); + } + + public drawCone(position: Vec3, radius: number, height: number, rot: Vec3 = Vec3.ZERO, wireFrame: boolean = false) { + const color = this._color.clone(); + const renderer = this.getRenderer(color); + const p = this._useLocalPosition ? this.worldToLocal(position) : position; + const transform = this.rotate(p, rot); + renderer.addDrawCall((geometry) => { + geometry?.addCone(p, radius, height, color, 32, wireFrame, this._depthTest, false, true, transform); + }); + } + + public drawBezier(point1: Vec3, point2: Vec3, point3: Vec3, point4: Vec3, rot: Vec3 = Vec3.ZERO) { + const color = this._color.clone(); + const renderer = this.getRenderer(color); + const p1 = this._useLocalPosition ? this.worldToLocal(point1) : point1; + const p2 = this._useLocalPosition ? this.worldToLocal(point2) : point2; + const p3 = this._useLocalPosition ? this.worldToLocal(point3) : point3; + const p4 = this._useLocalPosition ? this.worldToLocal(point4) : point4; + const transform = this.rotate(p1, rot); + renderer.addDrawCall((geometry) => { + geometry?.addBezier(p1, p2, p3, p4, color, 32, this._depthTest, true, transform); + }); + } + + public drawSpline(knots: Vec3[], mode: geometry.SplineMode = geometry?.SplineMode.BEZIER, knotSize = 0.5) { + const color = this._color.clone(); + const renderer = this.getRenderer(color); + const knotsList = this._useLocalPosition ? knots.map((knot) => this.worldToLocal(knot)) : knots; + let spline = geometry?.Spline.create(mode, knotsList); + renderer.addDrawCall((geometry) => { + geometry?.addSpline(spline, color, 0xffffffff, knotSize, 32, this._depthTest); + }); + } +} + +export default class Gizmos3D { + private static _mat: Material; + + public static get DEFAULT_MAT() { + if (!this._mat) { + this._mat = new Material(); + this._mat.initialize({ + effectName: 'builtin-unlit', + defines: { USE_VERTEX_COLOR: true }, + states: { primitive: gfx.PrimitiveMode.LINE_LOOP }, + }); + this._mat.passes.forEach((v) => v.tryCompile()); + } + + return this._mat; + } + + public static readonly DEFAULT_COLOR = Color.BLUE; + public static readonly DEFAULT_LAYER = Layers.Enum.GIZMOS; + + private static getDebugNode(node: Node) { + let debugNode = node.getComponentInChildren(GizmosDebugDraw); + if (!debugNode) { + debugNode = new Node('DEBUG_DRAW_NODE').addComponent(GizmosDebugDraw); + debugNode.node.layer = this.DEFAULT_LAYER; + debugNode.node.hideFlags |= CCObject.Flags.DontSave | CCObject.Flags.HideInHierarchy; + debugNode.node.parent = node; + } + + return debugNode; + } + + public static registerDrawGizmos(node: Node) { + this.getDebugNode(node).registerDrawGizmos(node); + } + + public static beginColor(node: Node, color: Color) { + this.getDebugNode(node).setColor(color); + } + + static beginLocalPosition(node: Node) { + this.getDebugNode(node).setUseLocalPosition(true); + } + + static endLocalPosition(node: Node) { + this.getDebugNode(node).setUseLocalPosition(false); + } + + public static clearAll(node: Node) { + this.getDebugNode(node).clearAll(); + } + + public static drawLine(node: Node, point1: Vec3, point2: Vec3) { + const debugNode = this.getDebugNode(node); + debugNode.drawLine(point1, point2); + } + + public static drawLineList(node: Node, points: Vec3[], close: boolean = false) { + const debugNode = this.getDebugNode(node); + debugNode.drawLineList(points, close); + } + + public static drawDashLine(node: Node, point1: Vec3, point2: Vec3) { + const debugNode = this.getDebugNode(node); + debugNode.drawDashLine(point1, point2); + } + + public static drawDashLineList(node: Node, points: Vec3[], close: boolean = false) { + const debugNode = this.getDebugNode(node); + debugNode.drawLineList(points, close); + } + + public static drawCircle(node: Node, center: Vec3, radius: number, rot: Vec3 = Vec3.ZERO) { + const debugNode = this.getDebugNode(node); + debugNode.drawCircle(center, radius, rot); + } + + public static drawDisc( + node: Node, + center: Vec3, + radius: number, + rot: Vec3 = Vec3.ZERO, + wireFrame: boolean = false, + ) { + const debugNode = this.getDebugNode(node); + debugNode.drawDisc(center, radius, rot, wireFrame); + } + + public static drawQuad( + node: Node, + point1: Vec3, + point2: Vec3, + point3: Vec3, + point4: Vec3, + wireFrame: boolean = false, + ) { + const debugNode = this.getDebugNode(node); + debugNode.drawQuad(point1, point2, point3, point4, wireFrame); + } + + public static drawSphere(node: Node, center: Vec3, radius: number, wireFrame: boolean = false) { + const debugNode = this.getDebugNode(node); + debugNode.drawSphere(center, radius, wireFrame); + } + + public static drawArc( + node: Node, + center: Vec3, + radius: number, + startAngle: number, + endAngle: number, + rot: Vec3 = Vec3.ZERO, + ) { + const debugNode = this.getDebugNode(node); + debugNode.drawArc(center, radius, startAngle, endAngle, rot); + } + + public static drawSolidArc( + node: Node, + center: Vec3, + radius: number, + startAngle: number, + endAngle: number, + rot: Vec3 = Vec3.ZERO, + wireFrame: boolean = false, + ) { + const debugNode = this.getDebugNode(node); + debugNode.drawSolidArc(center, radius, startAngle, endAngle, rot, wireFrame); + } + + public static drawPolygon( + node: Node, + position: Vec3, + radius: number, + segments: number, + rot: Vec3 = Vec3.ZERO, + wireFrame: boolean = false, + ) { + const debugNode = this.getDebugNode(node); + debugNode.drawPolygon(position, radius, segments, rot, wireFrame); + } + + public static drawOctahedron( + node: Node, + position: Vec3, + radius: number, + rot: Vec3 = Vec3.ZERO, + wireFrame: boolean = false, + ) { + const debugNode = this.getDebugNode(node); + debugNode.drawOctahedron(position, radius, rot, wireFrame); + } + + public static drawCross(node: Node, center: Vec3, size: number) { + const debugNode = this.getDebugNode(node); + debugNode.drawCross(center, size); + } + + public static drawCapsule( + node: Node, + position: Vec3, + radius: number, + height: number, + rot?: Vec3, + wireFrame?: boolean, + ) { + const debugNode = this.getDebugNode(node); + debugNode.drawCapsule(position, radius, height, rot, wireFrame); + } + + public static drawBox(node: Node, center: Vec3, size: Vec3, rot?: Vec3, wireFrame?: boolean) { + const debugNode = this.getDebugNode(node); + debugNode.drawBox(center, size, rot, wireFrame); + } + + public static drawCylinder( + node: Node, + position: Vec3, + radius: number, + height: number, + rot: Vec3 = Vec3.ZERO, + wireFrame: boolean = false, + ) { + const debugNode = this.getDebugNode(node); + debugNode.drawCylinder(position, radius, height, rot, wireFrame); + } + + public static drawCone( + node: Node, + position: Vec3, + radius: number, + height: number, + rot: Vec3 = Vec3.ZERO, + wireFrame: boolean = false, + ) { + const debugNode = this.getDebugNode(node); + debugNode.drawCone(position, radius, height, rot, wireFrame); + } + + public static drawBezier(node: Node, point1: Vec3, point2: Vec3, point3: Vec3, point4: Vec3, rot?: Vec3) { + const debugNode = this.getDebugNode(node); + debugNode.drawBezier(point1, point2, point3, point4, rot); + } + + public static drawSpline( + node: Node, + knots: Vec3[], + mode: geometry.SplineMode = geometry?.SplineMode.BEZIER, + knotSize = 0.5, + ) { + const debugNode = this.getDebugNode(node); + debugNode.drawSpline(knots, mode, knotSize); + } +} diff --git a/assets/_Game/Scripts/Gizmos/Gizmos3D.ts.meta b/assets/_Game/Scripts/Gizmos/Gizmos3D.ts.meta new file mode 100644 index 0000000..a4cacfe --- /dev/null +++ b/assets/_Game/Scripts/Gizmos/Gizmos3D.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.23", + "importer": "typescript", + "imported": true, + "uuid": "e5842e38-cb01-4432-81b7-6dcc6b33e922", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/assets/_Game/Scripts/Pool/IPoolable.ts b/assets/_Game/Scripts/Pool/IPoolable.ts index 131e2c6..52508c5 100644 --- a/assets/_Game/Scripts/Pool/IPoolable.ts +++ b/assets/_Game/Scripts/Pool/IPoolable.ts @@ -1,4 +1,4 @@ export default interface IPoolable { - onGet(); - onRelease(); + onGet(): void; + onRelease(): void; }