134 lines
3.3 KiB
Svelte
134 lines
3.3 KiB
Svelte
<script lang="ts">
|
|
import type { Node } from "@nodes/types";
|
|
import { getContext, onMount } from "svelte";
|
|
import NodeHeader from "./NodeHeader.svelte";
|
|
import NodeParameter from "./NodeParameter.svelte";
|
|
import { activeNodeId, selectedNodes } from "../graph/stores.js";
|
|
import { colors } from "../graph/stores";
|
|
import { T } from "@threlte/core";
|
|
import { Color, type Mesh } from "three";
|
|
import NodeFrag from "./Node.frag";
|
|
import NodeVert from "./Node.vert";
|
|
|
|
export let node: Node;
|
|
export let inView = true;
|
|
export let z = 2;
|
|
|
|
$: isActive = $activeNodeId === node.id;
|
|
$: isSelected = !!$selectedNodes?.has(node.id);
|
|
|
|
const updateNodePosition =
|
|
getContext<(n: Node) => void>("updateNodePosition");
|
|
|
|
const getNodeHeight = getContext<(n: string) => number>("getNodeHeight");
|
|
|
|
const type = node?.tmp?.type;
|
|
|
|
const zOffset = (node.tmp?.random || 0) * 0.5;
|
|
const zLimit = 2 - zOffset;
|
|
|
|
const parameters = Object.entries(type?.inputs || {})
|
|
.filter((p) => p[1].type !== "seed")
|
|
.filter((p) => !("setting" in p[1]));
|
|
|
|
let ref: HTMLDivElement;
|
|
let meshRef: Mesh;
|
|
|
|
const height = getNodeHeight(node.type);
|
|
|
|
$: if (node && ref && meshRef) {
|
|
node.tmp = node.tmp || {};
|
|
node.tmp.ref = ref;
|
|
node.tmp.mesh = meshRef;
|
|
updateNodePosition(node);
|
|
}
|
|
|
|
onMount(() => {
|
|
node.tmp = node.tmp || {};
|
|
node.tmp.ref = ref;
|
|
node.tmp.mesh = meshRef;
|
|
updateNodePosition(node);
|
|
});
|
|
</script>
|
|
|
|
<T.Mesh
|
|
position.x={node.position[0] + 10}
|
|
position.z={node.position[1] + height / 2}
|
|
position.y={0.8}
|
|
rotation.x={-Math.PI / 2}
|
|
bind:ref={meshRef}
|
|
visible={inView && z < 7}
|
|
>
|
|
<T.PlaneGeometry args={[20, height]} radius={1} />
|
|
<T.ShaderMaterial
|
|
vertexShader={NodeVert}
|
|
fragmentShader={NodeFrag}
|
|
transparent
|
|
uniforms={{
|
|
uColorBright: { value: new Color("#171717") },
|
|
uColorDark: { value: new Color("#151515") },
|
|
uStrokeColor: { value: new Color("#9d5f28") },
|
|
uStrokeWidth: { value: 1.0 },
|
|
uWidth: { value: 20 },
|
|
uHeight: { value: height },
|
|
}}
|
|
uniforms.uColorBright.value={$colors.layer2}
|
|
uniforms.uColorDark.value={$colors.layer1}
|
|
uniforms.uStrokeColor.value={isSelected
|
|
? $colors.selected
|
|
: isActive
|
|
? $colors.active
|
|
: $colors.outline}
|
|
uniforms.uStrokeWidth.value={(7 - z) / 3}
|
|
/>
|
|
</T.Mesh>
|
|
|
|
<div
|
|
class="node"
|
|
class:active={isActive}
|
|
style:--cz={z + zOffset}
|
|
style:display={inView && z > zLimit ? "block" : "none"}
|
|
class:selected={isSelected}
|
|
class:out-of-view={!inView}
|
|
data-node-id={node.id}
|
|
bind:this={ref}
|
|
>
|
|
<NodeHeader {node} />
|
|
|
|
{#each parameters as [key, value], i}
|
|
<NodeParameter
|
|
bind:node
|
|
id={key}
|
|
input={value}
|
|
isLast={i == parameters.length - 1}
|
|
/>
|
|
{/each}
|
|
</div>
|
|
|
|
<style>
|
|
.node {
|
|
position: absolute;
|
|
box-sizing: border-box;
|
|
user-select: none !important;
|
|
cursor: pointer;
|
|
width: 200px;
|
|
color: var(--text-color);
|
|
transform: translate3d(var(--nx), var(--ny), 0);
|
|
z-index: 1;
|
|
opacity: calc((var(--cz) - 2.5) / 3.5);
|
|
font-weight: 300;
|
|
--stroke: var(--outline);
|
|
--stroke-width: 2px;
|
|
}
|
|
|
|
.node.active {
|
|
--stroke: var(--active);
|
|
--stroke-width: 2px;
|
|
}
|
|
|
|
.node.selected {
|
|
--stroke: var(--selected);
|
|
--stroke-width: 2px;
|
|
}
|
|
</style>
|