123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202 |
- import type { ComputedRef } from 'vue'
- import { computed, nextTick, reactive, ref, toRefs } from 'vue'
- import type { DevtoolsContextPayload, DevtoolsPerformancePayload, DevtoolsEvent, TresObject } from '@tresjs/core'
- import type { Mesh, Object3D } from 'three'
- import { DoubleSide, MeshBasicMaterial } from 'three'
- import { editSceneObject } from '@tresjs/core'
- import type { GraphObject } from '../types'
- import { HightlightMesh } from '../utils/highlightedMesh'
- interface DevtoolsState {
- connected: boolean
- scene: Object3D
- selectedObject: Object3D | null
- prevInstance: Object3D | null
- highlightMesh: Mesh | null
- invalidate: (frames?: number) => void
- }
- interface DevtoolsHookReturn {
- state: DevtoolsState
- graph: ComputedRef<GraphObject>
- highlightObject: (object: TresObject) => void
- unhighlightObject: () => void
- selectObject: (object: TresObject) => void
- editSceneObjectByPath: (path: string, value: any) => void
- }
- const state = reactive<DevtoolsState>({
- connected: false,
- scene: {} as Object3D,
- selectedObject: null as Object3D | null,
- prevInstance: null as Object3D | null,
- highlightMesh: null as Mesh | null,
- invalidate: () => {},
- })
- function handlePerformanceEvent(payload: DevtoolsPerformancePayload) {
- }
- function handleContextEvent(payload: DevtoolsContextPayload) {
- payload.scene.traverse((children) => {
- if (payload.scene.__vnode) delete children.__vnode
- delete children.userData.tres__context
- delete children.userData.tres__root
- })
- const { _vnode, ...rest } = payload.scene
- state.scene = {
- ...rest,
- getObjectByProperty: payload.scene.getObjectByProperty,
- traverse: payload.scene.traverse,
- }
- if (state.selectedObject) {
- selectObject(state.selectedObject)
- }
- // On-demand rendering
- state.invalidate = payload.invalidate
- }
- const icons: Record<string, string> = {
- scene: 'i-carbon-web-services-container',
- perspectivecamera: 'i-carbon-video',
- mesh: 'i-carbon-cube',
- group: 'i-carbon-group-objects',
- ambientlight: 'i-carbon-light',
- directionallight: 'i-carbon-light',
- spotlight: 'i-iconoir-project-curve-3d',
- position: 'i-iconoir-axes',
- rotation: 'i-carbon-rotate-clockwise',
- scale: 'i-iconoir-ellipse-3d-three-points',
- bone: 'i-ph-bone',
- skinnedmesh: 'carbon:3d-print-mesh',
- }
- function createNode(object: TresObject) {
- const node: GraphObject = {
- uuid: object.uuid,
- name: object.name,
- type: object.type,
- icon: icons[object.type.toLowerCase()] || 'i-carbon-cube',
- position: {
- x: object.position.x,
- y: object.position.y,
- z: object.position.z,
- },
- rotation: {
- x: object.rotation.x,
- y: object.rotation.y,
- z: object.rotation.z,
- },
- children: [],
- }
- if (object.type === 'Mesh') {
- node.material = object.material
- node.geometry = object.geometry
- node.scale = {
- x: object.scale.x,
- y: object.scale.y,
- z: object.scale.z,
- }
- }
- if (object.type.includes('Light')) {
- node.color = object.color.getHexString()
- node.intensity = object.intensity
- }
- return node
- }
- const graph = computed(() => {
- const traverse = (object: TresObject, node: GraphObject) => {
- object.children.forEach((child) => {
- const childNode = createNode(child)
- if (child.type !== 'HightlightMesh') {
- node.children.push(childNode)
- }
- traverse(child, childNode)
- })
- }
- const root = createNode(state.scene)
- traverse(state.scene, root)
- return root
- })
- function createHighlightMesh(object: Object3D): Mesh {
- const highlightMaterial = new MeshBasicMaterial({
- color: 0xa7e6d7, // Highlight color
- transparent: true,
- opacity: 0.2,
- depthTest: false, // So the highlight is always visible
- side: DoubleSide, // To e
- })
- // Clone the geometry of the object. You might need a more complex approach
- // if the object's geometry is not straightforward.
- const highlightMesh = new HightlightMesh(state.invalidate, object.geometry.clone(), highlightMaterial)
- return highlightMesh
- }
- function highlightObject(object: TresObject) {
- const instance = state.scene.getObjectByProperty('uuid', object.uuid)
- unhighlightObject()
-
- if (instance && instance.isMesh) {
- const newHighlightMesh = createHighlightMesh(instance)
- instance.add(newHighlightMesh)
- state.highlightMesh = newHighlightMesh
- state.prevInstance = instance
- }
- }
- function unhighlightObject() {
- if (state.prevInstance && state.highlightMesh && state.highlightMesh.parent) {
- state.prevInstance.remove(state.highlightMesh)
- }
- }
- function selectObject(object: TresObject) {
- const instance = state.scene.getObjectByProperty('uuid', object.uuid)
- state.selectedObject = {}
- state.selectedObject = { ...instance }
- }
- function editSceneObjectByPath(path: string, value: any) {
- editSceneObject(state.scene, state.selectedObject.uuid, path.split('.'), value)
- }
- export function useDevtoolsHook(): DevtoolsHookReturn {
- function cb(event: DevtoolsEvent) {
- state.connected = true
- if (event.type === 'performance') {
- handlePerformanceEvent(event.payload)
- }
- else if (event.type === 'context') {
- handleContextEvent(event.payload)
- }
- }
- // Init devtools hook
- const tresGlobalHook = {
- cb,
- }
- if (!window.parent.parent.__TRES__DEVTOOLS__) {
- window.parent.parent.__TRES__DEVTOOLS__ = tresGlobalHook
- }
- return {
- state,
- graph,
- ...toRefs(state),
- highlightObject,
- unhighlightObject,
- selectObject,
- editSceneObjectByPath,
- }
- }
|