Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: edges #390

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
1 change: 1 addition & 0 deletions docs/.vitepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ export default defineConfig({
{ text: 'useFBO', link: '/guide/abstractions/use-fbo' },
{ text: 'useSurfaceSampler', link: '/guide/abstractions/use-surface-sampler' },
{ text: 'Sampler', link: '/guide/abstractions/sampler' },
{ text: 'Edges', link: '/guide/abstractions/edges' },
{ text: 'PositionalAudio', link: '/guide/abstractions/positional-audio' },
],
},
Expand Down
100 changes: 100 additions & 0 deletions docs/.vitepress/theme/components/EdgesDemo.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
<script setup lang="ts">
import { TresCanvas, useSeek } from '@tresjs/core'
import { OrbitControls, Edges, Box, ContactShadows } from '@tresjs/cientos'
import { gsap } from 'gsap'

const gl = {
clearColor: '#f6f6f6',
alpha: false,
}

const dataBoxes = [{
color: '#82DBC5',
edgeColor: '#292929',
},
{
color: '#505050',
edgeColor: '#292929',
},
{
color: '#F6B03B',
edgeColor: '#292929',
}]

const { seek } = useSeek()

function onPointerEnter(ev) {
const object = ev.object
const edge = seek(object, 'type', 'LineSegments')

gsap.to(object.position, {
y: .25,
duration: 1,
overwrite: true,
ease: 'elastic.out(1,0.4)',
})

gsap.to(edge?.scale, {
x: 1.1,
y: 1.1,
z: 1.1,
duration: 1,
overwrite: true,
ease: 'elastic.out(1,0.4)',
})
}

function onPointerLeave(ev) {
const object = ev
const edge = seek(object, 'type', 'LineSegments')

gsap.to(object.position, {
y: 0,
duration: .25,
overwrite: true,
ease: 'power3.out',
})

gsap.to(edge?.scale, {
x: 1,
y: 1,
z: 1,
duration: .25,
overwrite: true,
ease: 'power3.out',
})
}
</script>

<template>
<TresCanvas
v-bind="gl"
>
<TresPerspectiveCamera :position="[0, 2, 5]" />
<OrbitControls
make-default
auto-rotate
:auto-rotate-speed="1"
/>

<Box
v-for="(x, index) in [-1.5, 0, 1.5]"
:key="`docs-edges-demo-box-${index}`"
:position="[x, 0, 0]"
@pointer-enter="onPointerEnter"
@pointer-leave="onPointerLeave"
>
<TresMeshBasicMaterial
:color="dataBoxes[index].color"
/>
<Edges :color="dataBoxes[index].edgeColor" />
</Box>

<ContactShadows
:blur="2"
:resolution="512"
:opacity=".25"
:position-y="-1"
/>
</TresCanvas>
</template>
63 changes: 63 additions & 0 deletions docs/guide/abstractions/edges.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Edges

<DocsDemo>
<EdgesDemo />
</DocsDemo>

The `cientos` package provides an abstraction of [EdgesGeometry](https://threejs.org/docs/#api/en/geometries/EdgesGeometry) from Three.js, `<Edges>` is specifically designed for rendering visible edges of objects in a scene graph. This enhances the visual quality by highlighting contours and providing a stylized appearance which contributes to the artistic aspect of 3D visualizations.

## Usage

The `<Edges>` component is easy to set up as it automatically derives geometry from its parent. You can simply wrap it around any [Object3D](https://threejs.org/docs/#api/en/core/Object3D), [Mesh](https://threejs.org/docs/#api/en/objects/Mesh), or [primitive](https://docs.tresjs.org/advanced/primitive.html) to automatically apply edge rendering. You can give to `<Edges>` a custom material. *(see code bellow)*


```vue
<script setup lang="ts">
import { Edges, Box } from '@tresjs/cientos'
</script>

<template>
<Box>
<TresMeshNormalMaterial />

<!-- Usage with the default material (LineBasicMaterial) -->
<Edges color="#FF0000" />
<!-- ———— -->

<!-- Usage with an custom material -->
<Edges>
<TresMeshBasicMaterial color="#00FF00" />
</Edges>
<!-- ———— -->
</Box>
</template>
```

## Props

`<Edges>` is based on [LineSegments](https://threejs.org/docs/#api/en/objects/LineSegments) and supports all of its props.

| Prop | Description | Default |
| :---------------- | :--------------------------------------------------- | ------------------------- |
| **color** | `THREE.Color` — Color of the edges. <br> More informations : [TresColor](https://docs.tresjs.org/api/instances-arguments-and-props.html#colors) — [THREE.Color](https://threejs.org/docs/#api/en/math/Color) | `#ff0000` |
| **threshold** | `number` — An edge is only rendered if the angle (in degrees) between the face normals of the adjoining faces exceeds this value | `1` |
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here I would add one more column with the LineSegments as a general (properly indicated in the description) or all the linesSegment props individually (better to be explicit, trust me we target several non-advance users)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just for knowledge which props support LineSegments? I couldn't find any :o
The ThreeJs Docs sends me to Line (but this doesn't have any specific props, I was spectic line width or line-width but couldn't find any)


## Exposed properties

| Event | Description |
| :---------- | :--------------------------------------------------------------- |
| `root` | Root reference — Inheritance of [LineSegments](https://threejs.org/docs/#api/en/objects/LineSegments).|


```typescript{1}
const edgesRef = shallowRef(null)

console.log(edgesRef.value.root) // root properties
```

```vue{2}
<template>
<Edges ref="edgesRef" />
</template>
```

67 changes: 67 additions & 0 deletions playground/src/pages/abstractions/EdgesDemo.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<script setup lang="ts">
import { TresCanvas } from '@tresjs/core'
import { Edges, OrbitControls, Box } from '@tresjs/cientos'
import { useControls, TresLeches } from '@tresjs/leches'
import '@tresjs/leches/styles'

const gl = {
powerPreference: 'high-performance',
precision: 'highp',
clearColor: '#F6B03B',
}

const { edgeColor, edgeThreshold } = useControls({
edgeColor: { value: '#292929', type: 'color', label: 'Color' },
edgeThreshold: {
label: 'Threshold Angle',
value: 15,
min: 1,
max: 100,
step: 1,
},
})
</script>

<template>
<TresLeches />

<TresCanvas
v-bind="gl"
>
<TresPerspectiveCamera
:position="[0, 2, 5]"
/>

<OrbitControls
make-default
/>

<TresGridHelper
:args="[10, 10]"
:position-y="-.5"
/>

<Box :position="[-1, 0, 0]">
<TresMeshBasicMaterial color="#f6f6f6" />

<Edges
:scale="1.1"
:threshold="edgeThreshold.value"
>
<TresMeshBasicMaterial
:color="edgeColor.value"
/>
</Edges>
</Box>

<Box :position="[1, 0, 0]">
<TresMeshBasicMaterial color="#292929" />

<Edges
:scale="1.1"
:threshold="edgeThreshold.value"
color="#f6f6f6"
/>
</Box>
</TresCanvas>
</template>
5 changes: 5 additions & 0 deletions playground/src/router/routes/abstractions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ export const abstractionsRoutes = [
name: 'Sampler',
component: () => import('../../pages/abstractions/Sampler.vue'),
},
{
path: '/abstractions/edges',
name: 'Edges',
component: () => import('../../pages/abstractions/EdgesDemo.vue'),
},
{
path: '/abstractions/positional-audio',
name: 'PositionalAudio',
Expand Down
68 changes: 68 additions & 0 deletions src/core/abstractions/Edges.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<script setup lang="ts">
import { computed, toRefs, watch, shallowRef, useSlots, ref } from 'vue'
import type { LineSegments, BufferGeometry } from 'three'
import { EdgesGeometry } from 'three'
import type { TresColor } from '@tresjs/core'

export interface EdgesProps {
color?: TresColor
threshold?: number
}

const props = withDefaults(defineProps<EdgesProps>(), {
color: '#ff0000',
threshold: 15,
})

const { color, threshold } = toRefs(props)

const lineSegmentsRef = shallowRef<LineSegments>()
const saveGeometry = ref<BufferGeometry | null>(null)
const saveThreshold = ref<number>(1)

const slots = useSlots()

const hasChildren = computed(() => !!slots.default)

defineExpose({
root: lineSegmentsRef,
})

// Watch for changes in lineSegments, thresholdAngle, and color.
watch(
() => [lineSegmentsRef.value, threshold.value],
() => {
if (lineSegmentsRef.value) {
const parent = lineSegmentsRef.value.parent

if (parent) {
const geometry = parent.geometry

// Update geometry and threshold if necessary.
if (
geometry !== saveGeometry.value || threshold.value !== saveThreshold.value
) {
saveGeometry.value = geometry
saveThreshold.value = threshold.value

lineSegmentsRef.value.geometry = new EdgesGeometry(geometry, threshold.value)
}
}
}
},
)
</script>

<template>
<TresLineSegments
ref="lineSegmentsRef"
v-bind="$attrs"
>
<template v-if="hasChildren">
<slot />
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, here is better to use default slots (others component uses too)
for example


the TresLineBasicMaterial will act as fallback if any other slot has passed
Official docs here in case you want to check more about it: https://vuejs.org/guide/components/slots.html#fallback-content

So we can delete lines (23 and 25)

</template>
<template v-else>
<TresLineBasicMaterial :color="color" />
</template>
</TresLineSegments>
</template>
2 changes: 2 additions & 0 deletions src/core/abstractions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { GlobalAudio } from './GlobalAudio'
import Lensflare from './Lensflare/component.vue'
import Fbo from './useFBO/component.vue'
import Sampler from './useSurfaceSampler/component.vue'
import Edges from './Edges.vue'
import PositionalAudio from './PositionalAudio.vue'

export * from './useFBO/'
Expand All @@ -22,5 +23,6 @@ export {
GlobalAudio,
Fbo,
Sampler,
Edges,
PositionalAudio,
}