Dynamische 3D-Modelle
Dynamische 3D-Modelle ermöglichen es, eigene parametrische 3D-Geometrien mit React-Three-Fiber zu erstellen, die sich basierend auf Merkmalswerten anpassen.
Grundstruktur
Dynamische Modelle werden im viewer.models Array registriert:
// In Plugin.tsx
export default {
// ...
viewer: {
models: [ /* Ihre Modelle hier */ ]
}
}
Wenn Ihre Modell-Komponenten React-Hooks (useRef, useState, useEffect etc.) verwenden, muss @react-three/fiber explizit in Plugin.tsx importiert werden — andernfalls schlägt die Singleton-Auflösung von React im Module Federation fehl und Hooks werfen einen Laufzeitfehler:
// Plugin.tsx — zwingend erforderlich wenn Hooks genutzt werden
import "@react-three/fiber";
Hintergrund: Ohne diesen Import löst das MF-Runtime React lazy auf — zu spät für den ersten Render-Zyklus.
Ein einzelnes Modell sieht so aus:
import type { DynamicModel } from "k3-plugin-api";
import { MyDynamicModel } from "./MyDynamicModel";
const myModel: DynamicModel = {
type: "vendor.my-model",
label: "Mein dynamisches Modell",
description: "Ein parametrisches 3D-Modell",
screenshot: "/path/to/preview.png",
materials: ["surface", "edge"],
disabledForAR: false,
component: MyDynamicModel,
propsDialog: {
radius: {
type: "expression",
label: "Radius"
},
height: {
type: "expression",
label: "Höhe"
},
color: {
type: "basic",
label: "Farbe"
}
},
defaultProps: {
radius: { expression: "1" },
height: { expression: "2" },
color: "#ff0000"
}
};
export default myModel;
Model Descriptor
Ein DynamicModel besteht aus folgenden Eigenschaften:
type (erforderlich)
Eindeutiger Typ-String für das Modell. Sollte mit Vendor-Präfix versehen sein, z.B. "vendor.my-model".
label (erforderlich)
Anzeigename für das Modell im Admin-Dialog zur Modellauswahl.
description (erforderlich)
Beschreibung des Modells. Kann als String oder LocalizedString angegeben werden:
description: {
de: "Ein parametrischer Ring",
en: "A parametric ring"
}
component (erforderlich)
Die React-Three-Fiber Komponente, die das 3D-Modell rendert.
materials (erforderlich)
Array von Material-Namen, die das Modell verwendet. Diese können in K3-Aktionen durch andere Materialien ersetzt werden.
materials: ["surface", "border", "detail"]
disabledForAR (erforderlich)
Bestimmt, ob das Modell im AR-Export enthalten sein soll. Setzen Sie dies auf true, wenn das Modell Shader oder Features verwendet, die in AR nicht unterstützt werden.
propsDialog (erforderlich)
Definiert die Eigenschaften, die im Admin-Dialog eingestellt werden können. Jede Eigenschaft hat einen Namen (key) und einen ModelProp Descriptor:
propsDialog: {
width: {
type: "expression",
label: "Breite"
},
baseModel: {
type: "model",
label: "Basis-Modell",
multiple: false
},
colorVariable: {
type: "variable",
label: "Farbvariable",
allowedTypes: ["color"]
}
}
Prop-Typen
expression(Default): Zeigt einen Expressions-Editor. Der Wert wird zur Laufzeit basierend auf Merkmalswerten berechnet.basic: Einfaches Text-/Zahleneingabefeld für statische Werte.model: Modell-Selektor. Ermöglicht die Auswahl von K3-Modellen.multiple: trueerlaubt Mehrfachauswahl
variable: Variablen-Selektor. Ermöglicht die Auswahl von K3-Merkmalen.allowedTypes: Array von erlaubten Variablentypen (z.B.["color", "number"])
defaultProps (erforderlich)
Standardwerte für alle in propsDialog definierten Properties. Für Expressions verwenden Sie das Format { expression: "..." }:
defaultProps: {
width: { expression: "merkmal.breite * 2" },
height: { expression: "1.5" },
color: "#ff0000",
baseModel: [],
colorVariable: null,
slots: [
{ id: "base", name: "Basis", defaultModelId: null },
{ id: "subtract", name: "Subtraktion", defaultModelId: null },
]
}
slots definieren Platzhalter, in die andere Modelle per Regel eingesetzt werden können. Ein Modell mit Slots fungiert als Layout-Modell – es ordnet die übergebenen Modelle im 3D-Raum an, anstatt selbst Geometrie direkt in die Szene zu zeichnen.
In der Modell-Aktion einer Regel kann dann über das Feld slot angegeben werden, in welchen Slot das Modell eingesetzt werden soll. Das Modell wird in diesem Fall nicht direkt in die Szene gerendert, sondern der Komponente des Layout-Modells als slots-Prop übergeben (siehe 3D-Layout-Komponenten).
screenshot (optional)
URL zu einem Vorschaubild, das im Modell-Auswahldialog angezeigt wird.
tag (optional)
Tag zum Gruppieren oder Filtern von Modellen im Admin-UI.
Model-Komponente
Die Model-Komponente ist eine normale React-Three-Fiber Komponente, die alle im Descriptor definierten Properties als Props erhält:
import { useRef } from "react";
import { Mesh } from "three";
interface MyDynamicModelProps {
radius: number;
height: number;
color: string;
// Weitere Props aus propsDialog
}
export const MyDynamicModel: React.FC<MyDynamicModelProps> = (props) => {
const meshRef = useRef<Mesh>(null);
return (
<group>
<mesh ref={meshRef}>
<cylinderGeometry args={[props.radius, props.radius, props.height, 32]} />
<meshStandardMaterial color={props.color} />
</mesh>
</group>
);
};
Context-Props
Zusätzlich zu den benutzerdefinierten Props erhält die Komponente automatisch PluginModelContext:
interface PluginModelContext {
/** Nur während Screenshot-Rendering gesetzt */
screenshotCameraName?: string;
/** Die eindeutige ID der Model-Action */
modelActionId?: string;
}
Expressions in Props
Wenn eine Property als Expression definiert ist, wird sie zur Laufzeit evaluiert. Sie können auf Merkmalswerte zugreifen:
defaultProps: {
// Einfache Zahl
fixed: { expression: "10" },
// Merkmalswert verwenden
width: { expression: "merkmal.breite" },
// Berechnungen
height: { expression: "merkmal.hoehe * 2 + 5" },
// Komplexe Ausdrücke
total: { expression: "merkmal.breite * merkmal.hoehe * merkmal.tiefe" }
}
Zugriff auf andere Modelle
Wenn Sie type: "model" in einer Property verwenden, erhalten Sie Zugriff auf andere K3-Modelle:
propsDialog: {
childModels: {
type: "model",
label: "Kind-Modelle",
multiple: true
}
},
defaultProps: {
childModels: []
}
In der Komponente können Sie diese Modelle dann verwenden (Details zur Integration siehe K3-interne Dokumentation).
Zugriff auf Variablen
Mit type: "variable" können Sie auf K3-Merkmale referenzieren:
propsDialog: {
colorVar: {
type: "variable",
label: "Farbvariable",
allowedTypes: ["color"]
}
},
defaultProps: {
colorVar: null
}
Beispiel: Parametrischer Ring
import * as THREE from "three";
interface RingModelProps {
radius: number;
thickness: number;
segmentCount: number;
}
export const RingModel: React.FC<RingModelProps> = (props) => {
const geometry = useMemo(() => {
const geo = new THREE.TorusGeometry(
props.radius,
props.thickness,
16,
props.segmentCount
);
return geo;
}, [props.radius, props.thickness, props.segmentCount]);
return (
<mesh geometry={geometry}>
<meshStandardMaterial color="#silver" metalness={0.8} roughness={0.2} />
</mesh>
);
};
3D-Layout-Komponenten
Ein Layout-Modell definiert slots und ist dafür zuständig, die darin platzierten Modelle im 3D-Raum anzuordnen. Die fertigen <group>- bzw. Mesh-Elemente der Slot-Modelle werden der Komponente als slots-Prop übergeben.
Typ der slots-Prop
type SlotEntry = {
/** Die React-Komponente des eingesetzten Modells */
component: React.ComponentType<any>;
/** Props, die an die Komponente weitergegeben werden */
props: Record<string, any>;
};
type Slots = Record<string, SlotEntry>;
Beispiel: Layout-Modell mit zwei Slots
Descriptor:
import type { DynamicModel } from "k3-plugin-api";
import { BaseSubtractLayout } from "./BaseSubtractLayout";
const baseSubtractModel: DynamicModel = {
type: "vendor.base-subtract-layout",
label: "Basis + Subtraktion",
description: "Ordnet ein Basis- und ein Subtraktions-Modell übereinander an.",
materials: [],
disabledForAR: false,
slots: [
{ id: "base", name: "Basis", defaultModelId: null },
{ id: "subtract", name: "Subtraktion", defaultModelId: null },
],
propsDialog: {
offsetY: { type: "expression", label: "Y-Versatz (Subtraktion)" },
},
defaultProps: {
offsetY: { expression: "0" },
base: null,
subtract: null,
},
component: BaseSubtractLayout,
};
export default baseSubtractModel;
Komponente:
import type { Slots } from "k3-plugin-api";
interface BaseSubtractLayoutProps {
offsetY: number;
slots: Slots;
}
export const BaseSubtractLayout: React.FC<BaseSubtractLayoutProps> = ({ offsetY, slots }) => {
const BaseComponent = slots.base?.component;
const SubtractComponent = slots.subtract?.component;
return (
<group>
{/* Basis-Modell an Ursprung */}
{BaseComponent && <BaseComponent {...slots.base.props} />}
{/* Subtraktions-Modell mit Y-Versatz */}
<group position={[0, offsetY, 0]}>
{SubtractComponent && <SubtractComponent {...slots.subtract.props} />}
</group>
</group>
);
};
Modell-Aktion mit Slot-Zuweisung
In einer Regel wird das Slot-Ziel über das Feld slot der Modell-Aktion angegeben.