Skip to content

Commit

Permalink
added gizmo; no interaction with objects yet
Browse files Browse the repository at this point in the history
  • Loading branch information
99-Knots committed Jun 4, 2024
1 parent adba986 commit 710f927
Show file tree
Hide file tree
Showing 3 changed files with 172 additions and 12 deletions.
162 changes: 162 additions & 0 deletions src/components/GizmoManager.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
import { TransformNode } from "@babylonjs/core/Meshes/transformNode";
import { Mesh } from "@babylonjs/core/Meshes/mesh";
import { Vector3, Quaternion, Color3 } from "@babylonjs/core/Maths/math";
import { Scene } from "@babylonjs/core/scene";
import { UtilityLayerRenderer } from "@babylonjs/core/Rendering";
import { HighlightLayer } from '@babylonjs/core/Layers/highlightLayer';
import { Gizmo, PositionGizmo, RotationGizmo, BoundingBoxGizmo } from "@babylonjs/core/Gizmos";


export type Transformation = {
position: Vector3,
rotation: Quaternion,
scaling: Vector3
}

export enum GizmoMode {
Translate,
Rotate,
Scale
}

export enum GizmoSpace {
World,
Local
}


export class GizmoManager {

private root: TransformNode; // actual attached node of the gizmo; serves to transfer changes to the meshes
private initialTransform: Transformation; // transformation of the root before an axis gets dragged; to calculate the difference between start and end of drag
private inWorldSpace: boolean; // indicates if the gizmo is currently using world orientation
private nodes: [TransformNode, Transformation][]; // list of meshes and their transformatin currently attached to the gizmo
private positionGizmo: PositionGizmo; // gizmo for translation
private rotationGizmo: RotationGizmo; // gizmo for rotation
//private boundingBoxGizmo: CustomBoundingBoxGizmo; // gizmo for scaling
private currentGizmo: Gizmo; // the gizmo currently active
private layer: UtilityLayerRenderer;
private hlLayer: HighlightLayer;


constructor(scene: Scene, thickness?: number, scale?: number) {
this.root = new TransformNode('GizmoRoot', scene);
this.layer = new UtilityLayerRenderer(scene);
this.hlLayer = new HighlightLayer('SelectionHLLayer', scene);

this.root.rotationQuaternion = new Quaternion(0, 0, 0, 1);

this.initialTransform = {
position: this.root.position.clone(),
rotation: this.root.rotationQuaternion.clone(),
scaling: this.root.scaling.clone(),
}

this.inWorldSpace = false;
this.nodes = [];

this.positionGizmo = new PositionGizmo(this.layer, thickness ?? 1);
this.positionGizmo.scaleRatio = scale ?? 1;
//this.initPositionGizmo();

this.rotationGizmo = new RotationGizmo(this.layer, undefined, undefined, thickness ?? 1);
this.rotationGizmo.scaleRatio = scale ?? 1;
//this.initRotationGizmo();

//this.boundingBoxGizmo = new CustomBoundingBoxGizmo(bjs.Color3.Gray(), utilLayer, this);
//this.boundingBoxGizmo.attachedNode = this.root;

this.changeMode(GizmoMode.Translate);


console.log('Gizmo created')
}

public changeMode(mode: GizmoMode) {
this.rotationGizmo.attachedNode = null;
this.positionGizmo.attachedNode = null;
//this.boundingBoxGizmo.attachedNode = null;
this.root.scaling = Vector3.One();

switch (mode) { // select the relevant gizmo
case GizmoMode.Rotate:
this.currentGizmo = this.rotationGizmo;
break;
case GizmoMode.Translate:
this.currentGizmo = this.positionGizmo;
break;
case GizmoMode.Scale:
//this.currentGizmo = this.boundingBoxGizmo;
break;
}
if (this.nodes.length > 0) {
this.currentGizmo.attachedNode = this.root;
//this.boundingBoxGizmo.updateGizmo();
}
}

public setRootPosition() {
if (this.nodes.length > 0) {
let minmax = this.nodes[0][0].getHierarchyBoundingVectors(true);
let min = minmax.min;
let max = minmax.max

this.nodes.forEach(n => {
minmax = n[0].getHierarchyBoundingVectors(true);
min.minimizeInPlace(minmax.min);
max.maximizeInPlace(minmax.max);
});
this.root.position = min.add(max).scale(0.5);
}
}

public setRootRotation() {
if (this.nodes.length == 1 && !this.inWorldSpace) {
this.root.rotationQuaternion = this.nodes[0][1].rotation.clone()
}
else {
this.root.rotationQuaternion.set(0, 0, 0, 1);
}
}

public addNode(node: TransformNode) {
if (!this.nodes.find(n => n[0].name == node.name)) { // only add node if it wasn't already attached
node.computeWorldMatrix(true);
// add the node to the list of attached nodes, together with its world matrix
if (node.rotationQuaternion == null) {
node.rotationQuaternion = Quaternion.Identity();
}
this.nodes.push([node, {
position: node.position.clone(),
rotation: node.rotationQuaternion.clone(),
scaling: node.scaling.clone()
}]);
this.hlLayer.addMesh(node as Mesh, new Color3(1, 0.7, 0));
this.setRootPosition();
this.setRootRotation();
this.currentGizmo.attachedNode = this.root;
}
//if (this.currentGizmo == this.boundingBoxGizmo) { // if scaling is selected update the bounding box
// this.boundingBoxGizmo.updateGizmo();
//}
}

public removeNode(node: TransformNode) {
let i = this.nodes.indexOf(this.nodes.find(n => n[0].name == node.name));
this.hlLayer.removeMesh(node as Mesh);
this.nodes.splice(i, 1);
if (this.nodes.length < 1) {
this.currentGizmo.attachedNode = null;
}

this.setRootPosition();
this.setRootRotation();
}

public removeAllNodes() {
this.nodes = [];
this.hlLayer.removeAllMeshes();
this.currentGizmo.attachedNode = null;
}

}
16 changes: 8 additions & 8 deletions src/components/canvas.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import { Texture } from '@babylonjs/core/Materials/Textures/texture';
import { Tools } from '@babylonjs/core/Misc/tools'
import { PointerEventTypes } from "@babylonjs/core/Events/pointerEvents";
import { Ray } from "@babylonjs/core/Culling/ray";
import { HighlightLayer } from '@babylonjs/core/Layers/highlightLayer';
import { Mesh } from "@babylonjs/core/Meshes/mesh";

import { GizmoManager } from "./gizmoManager";

import floor_tex from '../../assets/floortiles.png';
import floor_norm from '../../assets/floortiles_normal.png';
Expand Down Expand Up @@ -44,7 +44,8 @@ const CanvasRenderer: React.ForwardRefRenderFunction<CanvasHandle, CanvasProps>

const canvas = React.useRef<HTMLCanvasElement>(null);
const engine = React.useRef<Engine>(null);
const scene = React.useRef<Scene|null>(null);
const scene = React.useRef<Scene>(null);
const gizmo = React.useRef<GizmoManager>(null);


const setupCamera = async () => {
Expand Down Expand Up @@ -112,9 +113,9 @@ const CanvasRenderer: React.ForwardRefRenderFunction<CanvasHandle, CanvasProps>
}

const setupGizmo = async () => {
gizmo.current = new GizmoManager(scene.current, 3.5, 1);
let pressedTimestamp = 0;
const ray = new Ray(Vector3.Zero(), Vector3.Zero())
const hlLayer = new HighlightLayer('hlLayer', scene.current);
const ray = new Ray(Vector3.Zero(), Vector3.Zero()) // necessary to ensure import of Ray
scene.current.onPointerObservable.add((pointerinfo) => {
if (pointerinfo.type == PointerEventTypes.POINTERDOWN && pointerinfo.event.button == 0) {
pressedTimestamp = Date.now();
Expand All @@ -125,10 +126,9 @@ const CanvasRenderer: React.ForwardRefRenderFunction<CanvasHandle, CanvasProps>
let elapsedSincePressed = Date.now() - pressedTimestamp;
if (elapsedSincePressed < 200) {
let node = pointerinfo.pickInfo.pickedMesh;
hlLayer.removeAllMeshes();
gizmo.current.removeAllNodes();
if (node.metadata?.selectable) {
let n = node as Mesh;
hlLayer.addMesh(n, new Color3(255, 255, 255));
gizmo.current.addNode(node);
}
};
}
Expand Down
6 changes: 2 additions & 4 deletions webpack.config.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
//const WebpackObfuscator = require('webpack-obfuscator');
//const webpack = require('webpack');
//new webpack.EnvironmentPlugin(['NODE_ENV', 'DEBUG']);

module.exports = {
entry: './src/index.tsx',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
path: path.resolve(__dirname, 'dist'),
clean: true
},
resolve: {
extensions: ['.ts', '.tsx', '.js']
Expand Down

0 comments on commit 710f927

Please sign in to comment.