import { AbstractMesh, ArcRotateCamera, BoundingBoxGizmo, Color3, EventState, GizmoManager, Mesh, Observable, PickingInfo, PointerEventTypes, PointerInfo, Quaternion, Scene, UtilityLayerRenderer, Vector3 } from "@babylonjs/core"; import { AdvancedDynamicTexture, Button, Container, InputText, Rectangle, StackPanel, TextBlock } from "@babylonjs/gui"; import { ModeManager, ModeType } from "../controller/mode-manager"; import { SceneManager } from "../controller/scene-manager"; import { Game } from "../game"; import { ModelInfo } from "../model/info/model/model-info"; import { UIBase } from "../view/window-base/ui-base"; import { BabylonTool } from "./babylon-tool"; import { BabylonUIStyleTool } from "./babylon-ui-style-tool"; //transform 信息的UI export enum TransformUIType { Hide, Position, Rotation, Scale, } export class GizmoTool { static s_gizmoManager: GizmoManager; static s_boundingBoxGizmo: BoundingBoxGizmo; static s_needUpdateGizmo: NeedUpdateGizmo; //需要更新的gizmo分量 static s_transformUIInfo: TransformUIInfo; static s_camera: ArcRotateCamera; static onPickMeshInfoObservable: Observable; //pick事件 static onGizmoAimMeshObservable: Observable; static s_nowPickAim: ModelInfo;//当前pick的目标 static s_nowPickAim_mesh: AbstractMesh;//当前pick的目标 static s_btn_poisition: Button; static s_btn_rotation: Button; static s_btn_scaling: Button; static init(scene: Scene, uiRoot: AdvancedDynamicTexture, camera: ArcRotateCamera) { GizmoTool.s_camera = camera; GizmoTool.s_boundingBoxGizmo = new BoundingBoxGizmo(); GizmoTool.s_boundingBoxGizmo.setColor(new Color3(1, 0.52, 0.07)); GizmoTool.s_boundingBoxGizmo.setEnabledRotationAxis(''); GizmoTool.s_boundingBoxGizmo.setEnabledScaling(false); GizmoTool.initTransformUI(uiRoot); GizmoTool.s_needUpdateGizmo = new NeedUpdateGizmo(); GizmoTool.s_gizmoManager = new GizmoManager(scene, undefined); GizmoTool.s_gizmoManager.positionGizmoEnabled = true; GizmoTool.s_gizmoManager.rotationGizmoEnabled = true; GizmoTool.s_gizmoManager.scaleGizmoEnabled = true; GizmoTool.s_gizmoManager.gizmos.positionGizmo.planarGizmoEnabled = false; GizmoTool.s_gizmoManager.gizmos.positionGizmo.yPlaneGizmo.isEnabled = true; GizmoTool.s_gizmoManager.gizmos.positionGizmo.yPlaneGizmo.scaleRatio = 1; GizmoTool.s_gizmoManager.gizmos.positionGizmo.updateGizmoRotationToMatchAttachedMesh = false; GizmoTool.s_gizmoManager.usePointerToAttachGizmos = false; GizmoTool.s_gizmoManager.gizmos.positionGizmo.onDragStartObservable.add( GizmoTool.onPositionGizmoStart ); GizmoTool.s_gizmoManager.gizmos.positionGizmo.onDragEndObservable.add( GizmoTool.onPositionGizmoEnd ); GizmoTool.s_gizmoManager.gizmos.rotationGizmo.onDragStartObservable.add( GizmoTool.onRotationGizmoStart ); GizmoTool.s_gizmoManager.gizmos.rotationGizmo.onDragEndObservable.add( GizmoTool.onRotationGizmoEnd ); GizmoTool.s_gizmoManager.gizmos.scaleGizmo.onDragStartObservable.add( GizmoTool.onScaleGizmoStart ); GizmoTool.s_gizmoManager.gizmos.scaleGizmo.onDragEndObservable.add( GizmoTool.onScaleGizmoEnd ); scene.onPointerObservable.add(GizmoTool.onPointerObservable); GizmoTool.onGizmoAimMeshObservable = new Observable(); GizmoTool.onPickMeshInfoObservable = new Observable(); GizmoTool.onPickMeshInfoObservable.add(GizmoTool.onChangeGizmoAim); scene.onBeforeRenderObservable.add(GizmoTool.onBeforeRender); GizmoTool.onTransformUITypeChange(TransformUIType.Hide); } static dispose() { } static onBeforeRender( eventData: Scene, eventState: EventState ) { GizmoTool.updateTransformUI(); let speed = 10 / GizmoTool.s_camera.radius; speed *= 1000; GizmoTool.s_camera.panningSensibility = speed;//动态修改平移灵敏度 GizmoTool.s_camera.wheelDeltaPercentage = 0.005; if (SceneManager.Instance.cameraMode_Is2D) { let camera = SceneManager.Instance.defaultCamera; camera.beta = 0; if (camera.beta > 0.2) { SceneManager.Instance.changeCameraMode(false); } else { //GizmoTool.s_camera.size = GizmoTool.s_camera.radius * 0.01; //console.log(GizmoTool.s_camera.orthoTop); let size_width = GizmoTool.s_camera.radius * Game.instance.canvas.width * 0.0008; let size_height = GizmoTool.s_camera.radius * Game.instance.canvas.height * 0.0008; camera.orthoTop = size_height; camera.orthoBottom = - size_height; camera.orthoLeft = - size_width; camera.orthoRight = size_width; } } } //鼠标事件监听 static onPointerObservable( eventData: PointerInfo, eventState: EventState ) { switch (eventData.type) { case PointerEventTypes.POINTERDOWN: //console.log("指针按下"); break; case PointerEventTypes.POINTERUP: //console.log("指针抬起"); GizmoTool.pickRayTest(eventData.pickInfo); break; case PointerEventTypes.POINTERMOVE: //console.log("指针移动"); break; } } /** * 替换选中 * @param modelInfo 新的modelInfo */ static replacePick(modelInfo: ModelInfo) { if (GizmoTool.s_nowPickAim != modelInfo) { GizmoTool.onChangeGizmoAim(modelInfo); } } //射线检测 static pickRayTest(pickResult: PickingInfo) { if (!SceneManager.s_isPointerDrag) { if (pickResult.hit) { let meshName = pickResult.pickedMesh.name; // GizmoTool.onPickMeshObservable.notifyObservers(pickResult.pickedMesh); //GizmoTool.changeGizmoAim(pickResult.pickedMesh); } else { //GizmoTool.onPickMeshInfoObservable.notifyObservers(null); } } // console.log("pickRayTest " + pickResult.hit); } //改变gizmo目标mesh static onChangeGizmoAim(modelInfo: ModelInfo) { // console.trace("改变目标", modelInfo); let mesh = null; GizmoTool.s_nowPickAim = modelInfo; if (modelInfo != null) { mesh = modelInfo.modelBox; } GizmoTool.changeGizmoAim(mesh); if (ModeManager.currentMode == ModeType.Edit) { GizmoTool.s_boundingBoxGizmo.attachedMesh = mesh; } GizmoTool.s_transformUIInfo.Mesh = mesh; GizmoTool.s_transformUIInfo.modelInfo = modelInfo; GizmoTool.updateTransformUI(mesh != null); } //改变Gizmo目标 static changeGizmoAim(mesh: AbstractMesh, x: boolean = true, y = true, z = true) { //console.trace("目标", ModeManager.currentMode); GizmoTool.s_nowPickAim_mesh = mesh; GizmoTool.s_gizmoManager.attachToMesh(mesh); GizmoTool.s_gizmoManager.gizmos.positionGizmo.xGizmo.isEnabled = x; GizmoTool.s_gizmoManager.gizmos.positionGizmo.yGizmo.isEnabled = y; GizmoTool.s_gizmoManager.gizmos.positionGizmo.yPlaneGizmo.isEnabled = x && z; GizmoTool.s_gizmoManager.gizmos.positionGizmo.zGizmo.isEnabled = z; GizmoTool.onGizmoAimMeshObservable.notifyObservers(mesh); } //离开当前目标(如果当前在这个) static leaveTheGizmoAim(modelInfo: ModelInfo) { if (GizmoTool.s_nowPickAim != null && GizmoTool.s_nowPickAim == modelInfo) { GizmoTool.onChangeGizmoAim(null); } } //离开当前目标(如果当前在这个) static leaveTheGizmoAimMesh(mesh: AbstractMesh) { if (GizmoTool.s_nowPickAim_mesh != null && GizmoTool.s_nowPickAim_mesh == mesh) { GizmoTool.onChangeGizmoAim(null); } } //初始化 static initTransformUI(advTexture: AdvancedDynamicTexture) { let transformUIInfo = new TransformUIInfo(); GizmoTool.s_transformUIInfo = transformUIInfo; let width = 200; let height = 150; let ui_transformUIRoot = new Rectangle('GizmoWindow'); ui_transformUIRoot.width = width + 'px'; ui_transformUIRoot.height = height + 'px'; ui_transformUIRoot.background = UIBase.color_gray; ui_transformUIRoot.color = '#FFFFFF'; ui_transformUIRoot.alpha = 0.8; ui_transformUIRoot.horizontalAlignment = Container.HORIZONTAL_ALIGNMENT_LEFT; ui_transformUIRoot.verticalAlignment = Container.VERTICAL_ALIGNMENT_TOP; ui_transformUIRoot.thickness = 0; ui_transformUIRoot.cornerRadius = BabylonUIStyleTool.cornerRadius_window1; ui_transformUIRoot.zIndex = BabylonUIStyleTool.c_zIndex_gizmo; advTexture.addControl(ui_transformUIRoot); transformUIInfo.root = ui_transformUIRoot; let stackPanel = new StackPanel('verticalGroup'); stackPanel.isVertical = true; stackPanel.width = width + 'px'; stackPanel.height = height + 'px'; ui_transformUIRoot.addControl(stackPanel); let txt_title = new TextBlock('title', '信息:(x,y,z)'); transformUIInfo.titleText = txt_title; txt_title.width = width + 'px'; txt_title.height = '45px'; stackPanel.addControl(txt_title); for (let i = 0; i < 3; i++) { let l_InputInfo: UIVector3InputInfo = new UIVector3InputInfo(); let postionRoot = new StackPanel('Group' + i); stackPanel.addControl(postionRoot); postionRoot.width = width + 'px'; postionRoot.height = height * 0.23 + 'px'; postionRoot.isVertical = false; let groupName = Button.CreateSimpleButton( 'groupName', 'info:' ); groupName.width = width * 0.22 + 'px'; groupName.height = height * 0.22 + 'px'; groupName.fontSize = 12; groupName.background = '#FF8833'; groupName.color = 'white'; groupName.thickness = 0; groupName.paddingBottom = '2px'; postionRoot.addControl(groupName); groupName.onPointerClickObservable.add(() => { switch (i) { case 0: transformUIInfo.OnTypeChangeObservable.notifyObservers( TransformUIType.Position ); break; case 1: transformUIInfo.OnTypeChangeObservable.notifyObservers( TransformUIType.Rotation ); break; case 2: transformUIInfo.OnTypeChangeObservable.notifyObservers( TransformUIType.Scale ); break; } }); switch (i) { case 0: transformUIInfo.position = l_InputInfo; postionRoot.name = 'transform'; groupName.textBlock.text = '位置:'; GizmoTool.s_btn_poisition = groupName; break; case 1: transformUIInfo.rotation = l_InputInfo; postionRoot.name = 'rotation'; groupName.textBlock.text = '旋转:'; GizmoTool.s_btn_rotation = groupName; break; case 2: transformUIInfo.scale = l_InputInfo; postionRoot.name = 'scale'; groupName.textBlock.text = '缩放:'; GizmoTool.s_btn_scaling = groupName; break; } for (let j = 0; j < 3; j++) { let xyz = new InputText('xyz', '12'); xyz.width = width * 0.25 + 'px'; xyz.height = height * 0.22 + 'px'; xyz.color = 'white'; xyz.alpha = 0.8; xyz.margin = '5px'; xyz.fontSize = 13; xyz.thickness = 0.1; xyz.paddingLeft = '0.1px'; xyz.paddingBottom = '2px'; switch (i) { case 0: xyz.onTextChangedObservable.add(GizmoTool.onGizmoUIInput_Position); break; case 1: xyz.onTextChangedObservable.add(GizmoTool.onGizmoUIInput_Rotation); break; case 2: xyz.onTextChangedObservable.add(GizmoTool.onGizmoUIInput_Scale); break; } switch (j) { case 0: l_InputInfo.x = xyz; xyz.name = 'x'; xyz.background = '#C20000'; break; case 1: l_InputInfo.y = xyz; xyz.name = 'y'; xyz.background = '#00820F'; break; case 2: l_InputInfo.z = xyz; xyz.name = 'z'; xyz.background = '#0047C2'; break; } postionRoot.addControl(xyz); } } transformUIInfo.OnTypeChangeObservable.add( GizmoTool.onTransformUITypeChange ); } static onPositionGizmoStart() { GizmoTool.s_needUpdateGizmo.position = true; } static onPositionGizmoEnd() { GizmoTool.s_needUpdateGizmo.position = false; } static onRotationGizmoStart() { GizmoTool.s_needUpdateGizmo.rotation = true; } static onRotationGizmoEnd() { GizmoTool.s_needUpdateGizmo.rotation = false; } static onScaleGizmoStart() { GizmoTool.s_needUpdateGizmo.scale = true; } static onScaleGizmoEnd() { GizmoTool.s_needUpdateGizmo.scale = false; } static currentGizmoType: TransformUIType = TransformUIType.Position; //操作的类型发生变化: 隐藏、position、rotation、scale static onTransformUITypeChange(uiType: TransformUIType) { // console.log("改变type" + uiType); let transformUIInfo = GizmoTool.s_transformUIInfo; // if (uiType == TransformUIType.Hide) { // transformUIInfo.root.isVisible = false; // } else { // transformUIInfo.root.isVisible = true; // } transformUIInfo.root.isVisible = ModeManager.isDebug;//先隐藏,因为效果图中没有 let isEditMode = ModeManager.currentMode == ModeType.Edit; let isPosition = uiType == TransformUIType.Position && isEditMode; GizmoTool.s_btn_poisition.background = isPosition ? UIBase.color_yellow : UIBase.color_null; GizmoTool.s_gizmoManager.positionGizmoEnabled = isPosition; let isRotation = uiType == TransformUIType.Rotation && isEditMode; GizmoTool.s_btn_rotation.background = isRotation ? UIBase.color_yellow : UIBase.color_null; GizmoTool.s_gizmoManager.rotationGizmoEnabled = isRotation; let isScaling = uiType == TransformUIType.Scale && isEditMode; GizmoTool.s_btn_scaling.background = isScaling ? UIBase.color_yellow : UIBase.color_null; GizmoTool.s_gizmoManager.scaleGizmoEnabled = isScaling; if (uiType == TransformUIType.Hide || !ModeManager.s_isMakeMode) { //隐藏选中框 GizmoTool.s_boundingBoxGizmo.attachedMesh = null; } } //更新显示 static updateTransformUI(updateAll = false) { let transformUIInfo = GizmoTool.s_transformUIInfo; let updateGizmoInfo = GizmoTool.s_needUpdateGizmo; let mesh = transformUIInfo.mesh; if (transformUIInfo == null || mesh == null) { transformUIInfo.titleText.text = ''; return; } transformUIInfo.titleText.text = mesh.name; if (updateGizmoInfo.position || updateAll) { transformUIInfo.position.x.text = mesh.position.x.toPrecision(6); transformUIInfo.position.y.text = mesh.position.y.toPrecision(6); transformUIInfo.position.z.text = mesh.position.z.toPrecision(6); } if (updateGizmoInfo.rotation || updateAll) { let augle = mesh.rotationQuaternion.toEulerAngles(); let radian1 = BabylonTool.c_radian1; //1弧度 augle = augle.multiplyByFloats(radian1, radian1, radian1); transformUIInfo.rotation.x.text = augle.x.toPrecision(6); transformUIInfo.rotation.y.text = augle.y.toPrecision(6); transformUIInfo.rotation.z.text = augle.z.toPrecision(6); } if (updateGizmoInfo.scale || updateAll) { let originalScaling = GizmoTool.s_transformUIInfo.modelInfo.modelData.transformData .originalScaling; transformUIInfo.scale.x.text = ( mesh.absoluteScaling.x / originalScaling.x ).toPrecision(6); transformUIInfo.scale.y.text = ( mesh.absoluteScaling.y / originalScaling.y ).toPrecision(6); transformUIInfo.scale.z.text = ( mesh.absoluteScaling.z / originalScaling.z ).toPrecision(6); } } //输入gizmo 数值 - position static onGizmoUIInput_Position( eventData: InputText, eventState: EventState ) { let transformUIInfo = GizmoTool.s_transformUIInfo; switch (eventData.name) { case 'x': transformUIInfo.Mesh.position.x = Number.parseFloat( transformUIInfo.position.x.text ); break; case 'y': transformUIInfo.Mesh.position.y = Number.parseFloat( transformUIInfo.position.y.text ); break; case 'z': transformUIInfo.Mesh.position.z = Number.parseFloat( transformUIInfo.position.z.text ); break; } } //输入gizmo 数值 - rotation static onGizmoUIInput_Rotation( eventData: InputText, eventState: EventState ) { let transformUIInfo = GizmoTool.s_transformUIInfo; let eugle = new Vector3(); eugle.x = Number.parseFloat(transformUIInfo.rotation.x.text) / BabylonTool.c_radian1; eugle.y = Number.parseFloat(transformUIInfo.rotation.y.text) / BabylonTool.c_radian1; eugle.z = Number.parseFloat(transformUIInfo.rotation.z.text) / BabylonTool.c_radian1; transformUIInfo.Mesh.rotationQuaternion = Quaternion.FromEulerAngles( eugle.x, eugle.y, eugle.z ); } //输入gizmo 数值 - scale static onGizmoUIInput_Scale( eventData: InputText, eventState: EventState ) { let transformUIInfo = GizmoTool.s_transformUIInfo; let originalScaling = GizmoTool.s_transformUIInfo.modelInfo.modelData.transformData .originalScaling; switch (eventData.name) { case 'x': transformUIInfo.Mesh.scaling.x = Number.parseFloat(transformUIInfo.scale.x.text) * originalScaling.x; break; case 'y': transformUIInfo.Mesh.scaling.y = Number.parseFloat(transformUIInfo.scale.y.text) * originalScaling.y; break; case 'z': transformUIInfo.Mesh.scaling.z = Number.parseFloat(transformUIInfo.scale.z.text) * originalScaling.z; break; } } } //需要更新gizmo的类型 class NeedUpdateGizmo { public position = true; public rotation = false; public scale = false; } //存储vector3 对应文本输入UI的映射 class UIVector3InputInfo { public x: InputText; public y: InputText; public z: InputText; } //存储 transform 相关UI输入类的映射 class TransformUIInfo { public root: Rectangle; public position: UIVector3InputInfo; public rotation: UIVector3InputInfo; public scale: UIVector3InputInfo; public titleText: TextBlock; public nowType: TransformUIType = TransformUIType.Hide; public mesh: AbstractMesh; public modelInfo: ModelInfo; public onTypeChangeObservable: Observable; public onPositionChangeObservable: Observable = new Observable(); public onRotationChangeObservable: Observable = new Observable(); public onScaleChangeObservable: Observable = new Observable(); set Mesh(value: AbstractMesh) { let lastMesh = this.mesh; this.mesh = value; if (value == null) { this.NowType = TransformUIType.Hide; } else { //if (lastMesh == null) { this.NowType = GizmoTool.currentGizmoType;// TransformUIType.Position; //} } } get Mesh() { return this.mesh; } set NowType(value: TransformUIType) { this.nowType = value; // console.log("NowType====" + value); this.OnTypeChangeObservable.notifyObservers(this.nowType); } get OnTypeChangeObservable(): Observable { if (this.onTypeChangeObservable == null) { this.onTypeChangeObservable = new Observable(); } return this.onTypeChangeObservable; } }