Skip to main content

Dynamic 3D Models

Dynamic 3D models allow you to create your own parametric 3D geometries using React-Three-Fiber that adapt based on feature values.

Basic Structure​

Dynamic models are registered in the viewer.models array:

// In Plugin.tsx
export default {
// ...
viewer: {
models: [ /* Ihre Modelle hier */ ]
}
}
React Hooks in 3D Models

If your model components use React hooks (useRef, useState, useEffect, etc.), @react-three/fiber must be explicitly imported into Plugin.tsx—otherwise, React’s singleton resolution in Module Federation will fail, and hooks will throw a runtime error:

// Plugin.tsx — zwingend erforderlich wenn Hooks genutzt werden
__PROTECTED_0__

Background: Without this import, the MF runtime resolves React lazily—too late for the first render cycle.

A single model looks like this:

__PROTECTED_1__
__PROTECTED_2__

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​

A DynamicModel consists of the following properties:

type (required)​

Unique type string for the model. Should include a vendor prefix, e.g., "vendor.my-model".

label (required)​

Display name for the model in the admin dialog for model selection.

description (required)​

Description of the model. Can be specified as a string or LocalizedString:

description: {
de: "Ein parametrischer Ring",
en: "A parametric ring"
}

component (required)​

The React Three Fiber component that renders the 3D model.

materials (required)​

Array of material names used by the model. These can be replaced by other materials in K3 actions.

materials: ["surface", "border", "detail"]

disabledForAR (required)​

Determines whether the model should be included in the AR export. Set this to true if the model uses shaders or features that are not supported in AR.

propsDialog (required)​

Defines the properties that can be set in the admin dialog. Each property has a name (key) and a ModelProp descriptor:

propsDialog: {
width: {
type: "expression",
label: "Breite"
},
baseModel: {
type: "model",
label: "Basis-Modell",
multiple: false
},
colorVariable: {
type: "variable",
label: "Farbvariable",
allowedTypes: ["color"]
}
}

Prop Types​

  • expression (default): Displays an expression editor. The value is calculated at runtime based on feature values.
  • basic: Simple text/numeric input field for static values.
  • model: Model selector. Allows selection of K3 models.
    • multiple: true allows multiple selections
  • variable: Variable selector. Allows selection of K3 characteristics.
    • allowedTypes: Array of allowed variable types (e.g., ["color", "number"])

defaultProps (required)​

Default values for all properties defined in propsDialog. For expressions, use the 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 define placeholders into which other models can be inserted via a rule. A model with slots functions as a layout model—it arranges the passed-in models in 3D space instead of drawing geometry directly into the scene itself.

In a rule’s Model Action, you can then use the slot field to specify which slot the model should be inserted into. In this case, the model is not rendered directly into the scene, but is passed to the layout model’s component as a slots prop (see 3D Layout Components).

screenshot (optional)​

URL to a preview image that is displayed in the model selection dialog.

tag (optional)​

Tag for grouping or filtering models in the admin UI.

Model Component​

The Model component is a standard React Three Fiber component that receives all properties defined in the descriptor as props:

__PROTECTED_3__
__PROTECTED_4__

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​

In addition to the user-defined props, the component automatically receives PluginModelContext:

interface PluginModelContext {
/** Nur während Screenshot-Rendering gesetzt */
screenshotCameraName?: string;
/** Die eindeutige ID der Model-Action */
modelActionId?: string;
}

Expressions in Props​

If a property is defined as an expression, it is evaluated at runtime. You can access feature values:

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" }
}

Accessing Other Models​

If you use type: "model" in a property, you gain access to other K3 models:

propsDialog: {
childModels: {
type: "model",
label: "Kind-Modelle",
multiple: true
}
},
defaultProps: {
childModels: []
}

You can then use these models within the component (see the K3 internal documentation for details on integration).

Accessing Variables​

You can use type: "variable" to reference K3 characteristics:

propsDialog: {
colorVar: {
type: "variable",
label: "Farbvariable",
allowedTypes: ["color"]
}
},
defaultProps: {
colorVar: null
}

Example: Parametric Ring​

__PROTECTED_5__

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 Components​

A layout model defines slots and is responsible for arranging the models placed within it in 3D space. The finished <group> or mesh elements of the slot models are passed to the component as a slots prop.

Type of the 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>;

Example: Layout model with two slots​

Descriptor:

__PROTECTED_6__
__PROTECTED_7__

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;

Component:

__PROTECTED_8__

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>
);
};

Model action with slot assignment​

In a rule, the slot target is specified via the slot field of the model action.