Skip to content

Commit 45fcf95

Browse files
Merge pull request #110 from franciscohermida/feature/simple-editor
feat: simple editor
2 parents 9795fb0 + 68cab32 commit 45fcf95

16 files changed

+941
-0
lines changed
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<script setup lang="ts">
2+
import { useElementSize, useParentElement } from '@vueuse/core'
3+
4+
const props = defineProps({
5+
enabled: { type: Boolean, default: true },
6+
aspectRatio: { type: Number, required: true },
7+
})
8+
9+
const containerRef = ref<HTMLElement | null>(null)
10+
const parentElementRef = useParentElement()
11+
12+
const { width, height } = useElementSize(parentElementRef)
13+
14+
const containerAspectRatio = computed(() => width.value / height.value)
15+
16+
const isLandscape = computed(() => props.aspectRatio < containerAspectRatio.value)
17+
const containedWidth = computed(() => isLandscape.value ? height.value * props.aspectRatio : width.value)
18+
const containedHeight = computed(() => isLandscape.value ? height.value : width.value / props.aspectRatio)
19+
</script>
20+
21+
<template>
22+
<div
23+
ref="containerRef"
24+
class="absolute"
25+
:style="{
26+
width: `${enabled ? containedWidth : width}px`,
27+
height: `${enabled ? containedHeight : height}px`,
28+
}"
29+
>
30+
<slot />
31+
</div>
32+
</template>
33+
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<script setup lang="ts">
2+
import { extend, useTresContext } from '@tresjs/core'
3+
import { OrbitControls } from 'three-stdlib'
4+
5+
const props = defineProps({
6+
enabled: {
7+
type: Boolean,
8+
default: true,
9+
},
10+
camera: {
11+
type: Object,
12+
},
13+
})
14+
15+
const { camera: contextCamera, renderer } = useTresContext()
16+
17+
const orbitControlsRef = ref()
18+
19+
const internalCamera = computed(() => props.camera ?? contextCamera.value)
20+
21+
extend({ OrbitControls })
22+
23+
defineExpose({ value: orbitControlsRef })
24+
</script>
25+
26+
<template>
27+
<TresOrbitControls
28+
v-if="renderer"
29+
:key="internalCamera?.uuid"
30+
ref="orbitControlsRef"
31+
:args="[internalCamera, renderer?.domElement]"
32+
:camera="internalCamera"
33+
:enabled="enabled"
34+
/>
35+
</template>
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<script setup lang="ts">
2+
import { type Mesh, Color, MathUtils } from 'three'
3+
import type { BoxSceneNode } from './types'
4+
5+
const props = defineProps({
6+
properties: {
7+
type: Object as PropType<BoxSceneNode['properties']>,
8+
default: () => ({ width: 10, height: 10, length: 10 }),
9+
},
10+
first: { type: Boolean, default: false },
11+
})
12+
13+
// weird bug happening if we don't explicitly emit click instead of letting it flow through
14+
const emit = defineEmits(['click'])
15+
16+
const { properties } = toRefs(props)
17+
18+
const randomColor = new Color(MathUtils.randInt(0, 0xffffff))
19+
const color = computed(() => props.first ? new Color('#535353') : randomColor)
20+
21+
const meshRef = ref<Mesh | null>()
22+
23+
defineExpose({ mesh: meshRef })
24+
</script>
25+
26+
<template>
27+
<TresMesh
28+
ref="meshRef"
29+
@click="emit('click')"
30+
>
31+
<TresBoxGeometry
32+
:args="[properties.width, properties.height, properties.length]"
33+
/>
34+
<TresMeshBasicMaterial :color="color" />
35+
</TresMesh>
36+
</template>
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<script setup lang="ts">
2+
import type { BoxSceneNode } from './types'
3+
4+
const props = defineProps({
5+
modelValue: {
6+
type: Object as PropType<BoxSceneNode>,
7+
required: true,
8+
},
9+
})
10+
11+
const emit = defineEmits(['update:modelValue'])
12+
13+
const { modelValue } = toRefs(props)
14+
const internalModelValue = ref<BoxSceneNode>(structuredClone(toRaw(modelValue.value)))
15+
watch(modelValue, () => {
16+
internalModelValue.value = structuredClone(toRaw(modelValue.value))
17+
})
18+
function handleModelValueUpdate() {
19+
emit('update:modelValue', structuredClone(toRaw(internalModelValue.value)))
20+
}
21+
</script>
22+
23+
<template>
24+
<div class="grid grid-cols-[auto_auto] gap-1">
25+
Width:
26+
<input
27+
v-model="internalModelValue.properties.width"
28+
class="dark:text-black"
29+
@update:model-value="handleModelValueUpdate"
30+
>
31+
Length:
32+
<input
33+
v-model="internalModelValue.properties.length"
34+
class="dark:text-black"
35+
@update:model-value="handleModelValueUpdate"
36+
>
37+
Height:
38+
<input
39+
v-model="internalModelValue.properties.height"
40+
class="dark:text-black"
41+
@update:model-value="handleModelValueUpdate"
42+
>
43+
</div>
44+
</template>
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<script setup lang="ts">
2+
import type { CameraSceneNode } from './types'
3+
4+
const props = defineProps({
5+
modelValue: {
6+
type: Object as PropType<CameraSceneNode>,
7+
required: true,
8+
validation: (val: CameraSceneNode) => val.type === 'camera',
9+
},
10+
})
11+
12+
const emit = defineEmits(['update:modelValue'])
13+
14+
const { modelValue } = toRefs(props)
15+
const internalModelValue = ref<CameraSceneNode>(structuredClone(toRaw(modelValue.value)))
16+
watch(modelValue, () => {
17+
internalModelValue.value = structuredClone(toRaw(modelValue.value))
18+
})
19+
function handleModelValueUpdate() {
20+
emit('update:modelValue', structuredClone(toRaw(internalModelValue.value)))
21+
}
22+
</script>
23+
24+
<template>
25+
<div class="grid grid-cols-[auto_auto] gap-1">
26+
Fov: <input
27+
v-model="internalModelValue.properties.fov"
28+
class="dark:text-black"
29+
@update:model-value="handleModelValueUpdate"
30+
>
31+
</div>
32+
</template>
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<script setup lang="ts">
2+
import { type Object3D, Color, MathUtils } from 'three'
3+
import type { ConeSceneNode } from './types'
4+
5+
const props = defineProps({
6+
properties: {
7+
type: Object as PropType<ConeSceneNode['properties']>,
8+
default: () => ({ radius: 5, height: 10 }),
9+
},
10+
first: { type: Boolean, default: false },
11+
})
12+
13+
// weird bug happening if we don't explicitly emit click instead of letting it flow through
14+
const emit = defineEmits(['click'])
15+
16+
const { properties } = toRefs(props)
17+
18+
const randomColor = new Color(MathUtils.randInt(0, 0xffffff))
19+
const color = computed(() => props.first ? new Color('#82dbc5') : randomColor)
20+
21+
const meshRef = ref<Object3D | null>()
22+
23+
defineExpose({ mesh: meshRef })
24+
</script>
25+
26+
<template>
27+
<TresMesh
28+
ref="meshRef"
29+
@click="emit('click')"
30+
>
31+
<TresConeGeometry
32+
:args="[properties.radius, properties.height, 32, 16]"
33+
/>
34+
<TresMeshBasicMaterial :color="color" />
35+
</TresMesh>
36+
</template>
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<script setup lang="ts">
2+
import type { ConeSceneNode } from './types'
3+
4+
const props = defineProps({
5+
modelValue: {
6+
type: Object as PropType<ConeSceneNode>,
7+
required: true,
8+
},
9+
})
10+
11+
const emit = defineEmits(['update:modelValue'])
12+
13+
const { modelValue } = toRefs(props)
14+
const internalModelValue = ref<ConeSceneNode>(structuredClone(toRaw(modelValue.value)))
15+
watch(modelValue, () => {
16+
internalModelValue.value = structuredClone(toRaw(modelValue.value))
17+
})
18+
function handleModelValueUpdate() {
19+
emit('update:modelValue', structuredClone(toRaw(internalModelValue.value)))
20+
}
21+
</script>
22+
23+
<template>
24+
<div class="grid grid-cols-[auto_auto] gap-1">
25+
Radius: <input
26+
v-model="internalModelValue.properties.radius"
27+
class="dark:text-black"
28+
@update:model-value="handleModelValueUpdate"
29+
>
30+
Height: <input
31+
v-model="internalModelValue.properties.height"
32+
class="dark:text-black"
33+
@update:model-value="handleModelValueUpdate"
34+
>
35+
</div>
36+
</template>
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
<script setup lang="ts">
2+
import type { SceneNode } from './types'
3+
4+
const props = defineProps({
5+
modelValue: {
6+
type: Object as PropType<SceneNode>,
7+
required: true,
8+
},
9+
})
10+
11+
const emit = defineEmits(['update:modelValue'])
12+
13+
const { modelValue } = toRefs(props)
14+
const internalModelValue = ref<SceneNode>(structuredClone(toRaw(modelValue.value)))
15+
watch(modelValue, () => {
16+
internalModelValue.value = structuredClone(toRaw(modelValue.value))
17+
}, { deep: true })
18+
function handleModelValueUpdate() {
19+
emit('update:modelValue', structuredClone(toRaw(internalModelValue.value)))
20+
}
21+
</script>
22+
23+
<template>
24+
<div>
25+
Position:
26+
<div class="grid grid-cols-[auto_auto] gap-1">
27+
x: <input
28+
v-model.number="internalModelValue.position[0]"
29+
class="dark:text-black"
30+
@update:model-value="handleModelValueUpdate"
31+
>
32+
y: <input
33+
v-model.number="internalModelValue.position[1]"
34+
class="dark:text-black"
35+
@update:model-value="handleModelValueUpdate"
36+
>
37+
z: <input
38+
v-model.number="internalModelValue.position[2]"
39+
class="dark:text-black"
40+
@update:model-value="handleModelValueUpdate"
41+
>
42+
</div>
43+
</div>
44+
<div>
45+
Rotation:
46+
<div class="grid grid-cols-[auto_auto] gap-1">
47+
x: <input
48+
v-model.number="internalModelValue.rotation[0]"
49+
class="dark:text-black"
50+
@update:model-value="handleModelValueUpdate"
51+
>
52+
y: <input
53+
v-model.number="internalModelValue.rotation[1]"
54+
class="dark:text-black"
55+
@update:model-value="handleModelValueUpdate"
56+
>
57+
z: <input
58+
v-model.number="internalModelValue.rotation[2]"
59+
class="dark:text-black"
60+
@update:model-value="handleModelValueUpdate"
61+
>
62+
</div>
63+
</div>
64+
<div>
65+
Scale:
66+
<div class="grid grid-cols-[auto_auto] gap-1">
67+
x: <input
68+
v-model.number="internalModelValue.scale[0]"
69+
class="dark:text-black"
70+
@update:model-value="handleModelValueUpdate"
71+
>
72+
y: <input
73+
v-model.number="internalModelValue.scale[1]"
74+
class="dark:text-black"
75+
@update:model-value="handleModelValueUpdate"
76+
>
77+
z: <input
78+
v-model.number="internalModelValue.scale[2]"
79+
class="dark:text-black"
80+
@update:model-value="handleModelValueUpdate"
81+
>
82+
</div>
83+
</div>
84+
</template>
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<script setup lang="ts">
2+
import { type Object3D, Color, MathUtils } from 'three'
3+
import type { SphereSceneNode } from './types'
4+
5+
const props = defineProps({
6+
properties: {
7+
type: Object as PropType<SphereSceneNode['properties']>,
8+
default: () => ({ radius: 5 }),
9+
},
10+
first: { type: Boolean, default: false },
11+
})
12+
13+
// weird bug happening if we don't explicitly emit click instead of letting it flow through
14+
const emit = defineEmits(['click'])
15+
16+
const { properties } = toRefs(props)
17+
18+
const randomColor = new Color(MathUtils.randInt(0, 0xffffff))
19+
const color = computed(() => props.first ? new Color('#efac35') : randomColor)
20+
21+
const meshRef = ref<Object3D | null>()
22+
23+
defineExpose({ mesh: meshRef })
24+
</script>
25+
26+
<template>
27+
<TresMesh
28+
ref="meshRef"
29+
@click="emit('click')"
30+
>
31+
<TresSphereGeometry
32+
:args="[properties.radius, 32, 16]"
33+
/>
34+
<TresMeshBasicMaterial :color="color" />
35+
</TresMesh>
36+
</template>

0 commit comments

Comments
 (0)