mirror of
https://github.com/vercel-labs/json-render.git
synced 2026-10-02 12:00:58 +08:00
Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6225fc41eb | ||
|
|
5b32de8720 | ||
|
|
f6d1c5134b |
@@ -20,7 +20,8 @@
|
||||
"@json-render/image",
|
||||
"@json-render/mcp",
|
||||
"@json-render/svelte",
|
||||
"@json-render/solid"
|
||||
"@json-render/solid",
|
||||
"@json-render/react-three-fiber"
|
||||
]
|
||||
],
|
||||
"linked": [],
|
||||
|
||||
@@ -23,6 +23,8 @@ npm install @json-render/core @json-render/vue
|
||||
npm install @json-render/core @json-render/svelte
|
||||
# or for SolidJS
|
||||
npm install @json-render/core @json-render/solid
|
||||
# or for 3D scenes
|
||||
npm install @json-render/core @json-render/react-three-fiber @react-three/fiber @react-three/drei three
|
||||
```
|
||||
|
||||
## Why json-render?
|
||||
@@ -121,6 +123,7 @@ function Dashboard({ spec }) {
|
||||
| `@json-render/svelte` | Svelte 5 renderer with runes-based reactivity |
|
||||
| `@json-render/solid` | SolidJS renderer with fine-grained reactive contexts |
|
||||
| `@json-render/shadcn` | 36 pre-built shadcn/ui components (Radix UI + Tailwind CSS) |
|
||||
| `@json-render/react-three-fiber` | React Three Fiber renderer for 3D scenes (19 built-in components) |
|
||||
| `@json-render/react-native` | React Native renderer with standard mobile components |
|
||||
| `@json-render/remotion` | Remotion video renderer, timeline schema |
|
||||
| `@json-render/react-pdf` | React PDF renderer for generating PDF documents from specs |
|
||||
@@ -434,6 +437,47 @@ import { renderToSvg } from "@json-render/image/render";
|
||||
const svg = await renderToSvg(spec, { fonts });
|
||||
```
|
||||
|
||||
### Three.js (3D)
|
||||
|
||||
```tsx
|
||||
import { defineCatalog } from "@json-render/core";
|
||||
import { schema, defineRegistry } from "@json-render/react";
|
||||
import {
|
||||
threeComponentDefinitions,
|
||||
threeComponents,
|
||||
ThreeCanvas,
|
||||
} from "@json-render/react-three-fiber";
|
||||
|
||||
const catalog = defineCatalog(schema, {
|
||||
components: {
|
||||
Box: threeComponentDefinitions.Box,
|
||||
Sphere: threeComponentDefinitions.Sphere,
|
||||
AmbientLight: threeComponentDefinitions.AmbientLight,
|
||||
DirectionalLight: threeComponentDefinitions.DirectionalLight,
|
||||
OrbitControls: threeComponentDefinitions.OrbitControls,
|
||||
},
|
||||
actions: {},
|
||||
});
|
||||
|
||||
const { registry } = defineRegistry(catalog, {
|
||||
components: {
|
||||
Box: threeComponents.Box,
|
||||
Sphere: threeComponents.Sphere,
|
||||
AmbientLight: threeComponents.AmbientLight,
|
||||
DirectionalLight: threeComponents.DirectionalLight,
|
||||
OrbitControls: threeComponents.OrbitControls,
|
||||
},
|
||||
});
|
||||
|
||||
<ThreeCanvas
|
||||
spec={spec}
|
||||
registry={registry}
|
||||
shadows
|
||||
camera={{ position: [5, 5, 5], fov: 50 }}
|
||||
style={{ width: "100%", height: "100vh" }}
|
||||
/>;
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
### Streaming (SpecStream)
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
# web
|
||||
|
||||
## 0.1.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [5b32de8]
|
||||
- @json-render/core@0.13.0
|
||||
- @json-render/codegen@0.13.0
|
||||
- @json-render/react@0.13.0
|
||||
|
||||
## 0.1.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -0,0 +1,416 @@
|
||||
import { pageMetadata } from "@/lib/page-metadata"
|
||||
export const metadata = pageMetadata("docs/api/react-three-fiber")
|
||||
|
||||
# @json-render/react-three-fiber
|
||||
|
||||
React Three Fiber renderer for json-render. 19 built-in 3D components for meshes, lights, models, environments, text, cameras, and controls.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
npm install @json-render/react-three-fiber @json-render/core @json-render/react @react-three/fiber @react-three/drei three zod
|
||||
```
|
||||
|
||||
## Entry Points
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Entry Point</th>
|
||||
<th>Exports</th>
|
||||
<th>Use For</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>@json-render/react-three-fiber</code></td>
|
||||
<td><code>threeComponents</code>, <code>ThreeRenderer</code>, <code>ThreeCanvas</code>, schemas</td>
|
||||
<td>React Three Fiber implementations and renderer</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>@json-render/react-three-fiber/catalog</code></td>
|
||||
<td><code>threeComponentDefinitions</code></td>
|
||||
<td>Catalog schemas (no R3F dependency, safe for server)</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
## Usage
|
||||
|
||||
```tsx
|
||||
import {'{ defineCatalog }'} from "@json-render/core";
|
||||
import {'{ schema, defineRegistry }'} from "@json-render/react";
|
||||
import {'{'}
|
||||
threeComponentDefinitions,
|
||||
threeComponents,
|
||||
ThreeCanvas,
|
||||
{'}'} from "@json-render/react-three-fiber";
|
||||
|
||||
const catalog = defineCatalog(schema, {'{'}
|
||||
components: {'{'}
|
||||
Box: threeComponentDefinitions.Box,
|
||||
Sphere: threeComponentDefinitions.Sphere,
|
||||
AmbientLight: threeComponentDefinitions.AmbientLight,
|
||||
DirectionalLight: threeComponentDefinitions.DirectionalLight,
|
||||
OrbitControls: threeComponentDefinitions.OrbitControls,
|
||||
{'}'},
|
||||
actions: {'{}'},
|
||||
{'}'});
|
||||
|
||||
const {'{ registry }'} = defineRegistry(catalog, {'{'}
|
||||
components: {'{'}
|
||||
Box: threeComponents.Box,
|
||||
Sphere: threeComponents.Sphere,
|
||||
AmbientLight: threeComponents.AmbientLight,
|
||||
DirectionalLight: threeComponents.DirectionalLight,
|
||||
OrbitControls: threeComponents.OrbitControls,
|
||||
{'}'},
|
||||
{'}'});
|
||||
```
|
||||
|
||||
### ThreeCanvas (convenience)
|
||||
|
||||
```tsx
|
||||
<ThreeCanvas
|
||||
spec={'{spec}'}
|
||||
registry={'{registry}'}
|
||||
shadows
|
||||
camera={'{'}{'{ position: [5, 5, 5], fov: 50 }'}{'}'}
|
||||
style={'{'}{'{ width: "100%", height: "100vh" }'}{'}'}
|
||||
/>
|
||||
```
|
||||
|
||||
### Manual Canvas Setup
|
||||
|
||||
```tsx
|
||||
import {'{ Canvas }'} from "@react-three/fiber";
|
||||
import {'{ ThreeRenderer }'} from "@json-render/react-three-fiber";
|
||||
|
||||
<Canvas shadows>
|
||||
<ThreeRenderer spec={'{spec}'} registry={'{registry}'} />
|
||||
</Canvas>
|
||||
```
|
||||
|
||||
## Components
|
||||
|
||||
### Primitives
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Component</th>
|
||||
<th>Description</th>
|
||||
<th>Key Props</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>Box</code></td>
|
||||
<td>Box mesh (default 1x1x1)</td>
|
||||
<td><code>width</code>, <code>height</code>, <code>depth</code>, <code>material</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>Sphere</code></td>
|
||||
<td>Sphere mesh</td>
|
||||
<td><code>radius</code>, <code>widthSegments</code>, <code>heightSegments</code>, <code>material</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>Cylinder</code></td>
|
||||
<td>Cylinder mesh</td>
|
||||
<td><code>radiusTop</code>, <code>radiusBottom</code>, <code>height</code>, <code>material</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>Cone</code></td>
|
||||
<td>Cone mesh</td>
|
||||
<td><code>radius</code>, <code>height</code>, <code>material</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>Torus</code></td>
|
||||
<td>Torus (donut) mesh</td>
|
||||
<td><code>radius</code>, <code>tube</code>, <code>material</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>Plane</code></td>
|
||||
<td>Flat plane mesh</td>
|
||||
<td><code>width</code>, <code>height</code>, <code>material</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>Capsule</code></td>
|
||||
<td>Capsule mesh</td>
|
||||
<td><code>radius</code>, <code>length</code>, <code>material</code></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
All primitives share: <code>position</code>, <code>rotation</code>, <code>scale</code>, <code>castShadow</code>, <code>receiveShadow</code>, <code>material</code>.
|
||||
|
||||
### Material Schema
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Property</th>
|
||||
<th>Type</th>
|
||||
<th>Default</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>color</code></td>
|
||||
<td><code>string</code></td>
|
||||
<td><code>"#ffffff"</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>metalness</code></td>
|
||||
<td><code>number</code></td>
|
||||
<td><code>0</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>roughness</code></td>
|
||||
<td><code>number</code></td>
|
||||
<td><code>1</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>emissive</code></td>
|
||||
<td><code>string</code></td>
|
||||
<td><code>"#000000"</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>emissiveIntensity</code></td>
|
||||
<td><code>number</code></td>
|
||||
<td><code>1</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>opacity</code></td>
|
||||
<td><code>number</code></td>
|
||||
<td><code>1</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>transparent</code></td>
|
||||
<td><code>boolean</code></td>
|
||||
<td><code>false</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>wireframe</code></td>
|
||||
<td><code>boolean</code></td>
|
||||
<td><code>false</code></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
### Lights
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Component</th>
|
||||
<th>Description</th>
|
||||
<th>Key Props</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>AmbientLight</code></td>
|
||||
<td>Uniform illumination</td>
|
||||
<td><code>color</code>, <code>intensity</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>DirectionalLight</code></td>
|
||||
<td>Sunlight-style</td>
|
||||
<td><code>position</code>, <code>color</code>, <code>intensity</code>, <code>castShadow</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>PointLight</code></td>
|
||||
<td>Radiates from a point</td>
|
||||
<td><code>position</code>, <code>color</code>, <code>intensity</code>, <code>distance</code>, <code>decay</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>SpotLight</code></td>
|
||||
<td>Cone of light</td>
|
||||
<td><code>position</code>, <code>color</code>, <code>intensity</code>, <code>angle</code>, <code>penumbra</code></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
### Other Components
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Component</th>
|
||||
<th>Description</th>
|
||||
<th>Key Props</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>Group</code></td>
|
||||
<td>Container for children</td>
|
||||
<td><code>position</code>, <code>rotation</code>, <code>scale</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>Model</code></td>
|
||||
<td>GLTF/GLB model loader</td>
|
||||
<td><code>url</code>, <code>position</code>, <code>rotation</code>, <code>scale</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>Environment</code></td>
|
||||
<td>HDRI environment map</td>
|
||||
<td><code>preset</code>, <code>background</code>, <code>blur</code>, <code>intensity</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>Fog</code></td>
|
||||
<td>Linear fog effect</td>
|
||||
<td><code>color</code>, <code>near</code>, <code>far</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>GridHelper</code></td>
|
||||
<td>Reference grid</td>
|
||||
<td><code>size</code>, <code>divisions</code>, <code>color</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>Text3D</code></td>
|
||||
<td>3D text (SDF)</td>
|
||||
<td><code>text</code>, <code>fontSize</code>, <code>color</code>, <code>anchorX</code>, <code>anchorY</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>PerspectiveCamera</code></td>
|
||||
<td>Camera</td>
|
||||
<td><code>position</code>, <code>fov</code>, <code>near</code>, <code>far</code>, <code>makeDefault</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>OrbitControls</code></td>
|
||||
<td>Camera controls</td>
|
||||
<td><code>enableDamping</code>, <code>enableZoom</code>, <code>autoRotate</code></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
## Shared Schemas
|
||||
|
||||
Reusable Zod schemas for custom 3D components:
|
||||
|
||||
```tsx
|
||||
import {'{ vector3Schema, materialSchema, transformProps, shadowProps }'} from "@json-render/react-three-fiber";
|
||||
```
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Export</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>vector3Schema</code></td>
|
||||
<td><code>z.tuple([z.number(), z.number(), z.number()])</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>materialSchema</code></td>
|
||||
<td>Standard material props (color, metalness, roughness, etc.)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>transformProps</code></td>
|
||||
<td><code>{'{ position, rotation, scale }'}</code> schema fields</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>shadowProps</code></td>
|
||||
<td><code>{'{ castShadow, receiveShadow }'}</code> schema fields</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
## ThreeRenderer
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Prop</th>
|
||||
<th>Type</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>spec</code></td>
|
||||
<td><code>Spec | null</code></td>
|
||||
<td>The spec to render as a 3D scene</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>registry</code></td>
|
||||
<td><code>ComponentRegistry</code></td>
|
||||
<td>Component registry from <code>defineRegistry</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>store</code></td>
|
||||
<td><code>StateStore</code></td>
|
||||
<td>External state store (controlled mode)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>initialState</code></td>
|
||||
<td><code>Record<string, unknown></code></td>
|
||||
<td>Initial state (uncontrolled mode)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>handlers</code></td>
|
||||
<td><code>Record<string, Function></code></td>
|
||||
<td>Action handlers</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>loading</code></td>
|
||||
<td><code>boolean</code></td>
|
||||
<td>Whether the spec is streaming</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>children</code></td>
|
||||
<td><code>ReactNode</code></td>
|
||||
<td>Additional R3F elements alongside the spec</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
## ThreeCanvas
|
||||
|
||||
Extends <code>ThreeRendererProps</code> with Canvas options:
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Prop</th>
|
||||
<th>Type</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>shadows</code></td>
|
||||
<td><code>boolean</code></td>
|
||||
<td>Enable shadow maps</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>camera</code></td>
|
||||
<td><code>object</code></td>
|
||||
<td>Default camera config (position, fov, etc.)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>className</code></td>
|
||||
<td><code>string</code></td>
|
||||
<td>CSS class for the canvas container</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>style</code></td>
|
||||
<td><code>CSSProperties</code></td>
|
||||
<td>Inline styles for the canvas container</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
## Type Helpers
|
||||
|
||||
```tsx
|
||||
import type {'{ ThreeProps }'} from "@json-render/react-three-fiber";
|
||||
|
||||
type BoxProps = ThreeProps<"Box">;
|
||||
type SphereProps = ThreeProps<"Sphere">;
|
||||
```
|
||||
@@ -16,8 +16,8 @@ const SYSTEM_PROMPT = `You are a helpful documentation assistant for json-render
|
||||
|
||||
GitHub repository: https://github.com/vercel-labs/json-render
|
||||
Documentation: https://json-render.dev/docs
|
||||
npm packages: @json-render/core, @json-render/react, @json-render/vue, @json-render/svelte, @json-render/solid, @json-render/shadcn, @json-render/react-native, @json-render/react-email, @json-render/react-pdf, @json-render/image, @json-render/remotion, @json-render/codegen, @json-render/mcp, @json-render/redux, @json-render/zustand, @json-render/jotai, @json-render/xstate
|
||||
Skills: json-render ships AI agent skills that teach coding agents how to use each package. Install with "npx skills add vercel-labs/json-render --skill <name>". Available skills: core, react, react-pdf, react-email, react-native, shadcn, image, remotion, vue, svelte, solid, codegen, mcp, redux, zustand, jotai, xstate. See /docs/skills for details.
|
||||
npm packages: @json-render/core, @json-render/react, @json-render/vue, @json-render/svelte, @json-render/solid, @json-render/shadcn, @json-render/react-three-fiber, @json-render/react-native, @json-render/react-email, @json-render/react-pdf, @json-render/image, @json-render/remotion, @json-render/codegen, @json-render/mcp, @json-render/redux, @json-render/zustand, @json-render/jotai, @json-render/xstate
|
||||
Skills: json-render ships AI agent skills that teach coding agents how to use each package. Install with "npx skills add vercel-labs/json-render --skill <name>". Available skills: core, react, react-pdf, react-email, react-native, shadcn, react-three-fiber, image, remotion, vue, svelte, solid, codegen, mcp, redux, zustand, jotai, xstate. See /docs/skills for details.
|
||||
|
||||
You have access to the full json-render documentation via the bash and readFile tools. The docs are available as markdown files in the /workspace/docs/ directory.
|
||||
|
||||
|
||||
@@ -100,7 +100,7 @@ export function Header() {
|
||||
>
|
||||
<path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z" />
|
||||
</svg>
|
||||
<span>11k</span>
|
||||
<span>12k</span>
|
||||
</a>
|
||||
<ThemeToggle />
|
||||
</nav>
|
||||
|
||||
@@ -134,6 +134,10 @@ export const docsNavigation: NavSection[] = [
|
||||
{ title: "@json-render/vue", href: "/docs/api/vue" },
|
||||
{ title: "@json-render/svelte", href: "/docs/api/svelte" },
|
||||
{ title: "@json-render/solid", href: "/docs/api/solid" },
|
||||
{
|
||||
title: "@json-render/react-three-fiber",
|
||||
href: "/docs/api/react-three-fiber",
|
||||
},
|
||||
{ title: "@json-render/codegen", href: "/docs/api/codegen" },
|
||||
{ title: "@json-render/mcp", href: "/docs/api/mcp" },
|
||||
{ title: "@json-render/redux", href: "/docs/api/redux" },
|
||||
|
||||
@@ -56,6 +56,7 @@ export const PAGE_TITLES: Record<string, string> = {
|
||||
"docs/api/redux": "@json-render/redux API",
|
||||
"docs/api/zustand": "@json-render/zustand API",
|
||||
"docs/api/jotai": "@json-render/jotai API",
|
||||
"docs/api/react-three-fiber": "@json-render/react-three-fiber API",
|
||||
"docs/api/xstate": "@json-render/xstate API",
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "web",
|
||||
"version": "0.1.6",
|
||||
"version": "0.1.7",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"license": "Apache-2.0",
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
# example-chat
|
||||
|
||||
## 0.1.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [5b32de8]
|
||||
- @json-render/core@0.13.0
|
||||
- @json-render/react@0.13.0
|
||||
- @json-render/shadcn@0.13.0
|
||||
|
||||
## 0.1.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "example-chat",
|
||||
"version": "0.1.6",
|
||||
"version": "0.1.7",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
# example-dashboard
|
||||
|
||||
## 0.1.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [5b32de8]
|
||||
- @json-render/core@0.13.0
|
||||
- @json-render/codegen@0.13.0
|
||||
- @json-render/react@0.13.0
|
||||
|
||||
## 0.1.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "example-dashboard",
|
||||
"version": "0.1.6",
|
||||
"version": "0.1.7",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
# example-image
|
||||
|
||||
## 0.1.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [5b32de8]
|
||||
- @json-render/core@0.13.0
|
||||
- @json-render/image@0.13.0
|
||||
|
||||
## 0.1.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "example-image",
|
||||
"version": "0.1.3",
|
||||
"version": "0.1.4",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
||||
@@ -1,5 +1,15 @@
|
||||
# example-mcp
|
||||
|
||||
## 0.1.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [5b32de8]
|
||||
- @json-render/core@0.13.0
|
||||
- @json-render/mcp@0.13.0
|
||||
- @json-render/react@0.13.0
|
||||
- @json-render/shadcn@0.13.0
|
||||
|
||||
## 0.1.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "example-mcp",
|
||||
"version": "0.1.2",
|
||||
"version": "0.1.3",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
# example-no-ai
|
||||
|
||||
## 0.1.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [5b32de8]
|
||||
- @json-render/core@0.13.0
|
||||
- @json-render/react@0.13.0
|
||||
- @json-render/shadcn@0.13.0
|
||||
|
||||
## 0.1.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "example-no-ai",
|
||||
"version": "0.1.6",
|
||||
"version": "0.1.7",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
# example-react-email
|
||||
|
||||
## 0.1.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [5b32de8]
|
||||
- @json-render/core@0.13.0
|
||||
- @json-render/react-email@0.13.0
|
||||
|
||||
## 0.1.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "example-react-email",
|
||||
"version": "0.1.2",
|
||||
"version": "0.1.3",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
# example-react-native
|
||||
|
||||
## 0.1.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [5b32de8]
|
||||
- @json-render/core@0.13.0
|
||||
- @json-render/react-native@0.13.0
|
||||
|
||||
## 0.1.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "example-react-native",
|
||||
"version": "0.1.6",
|
||||
"version": "0.1.7",
|
||||
"private": true,
|
||||
"main": "expo-router/entry",
|
||||
"scripts": {
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
# example-react-pdf
|
||||
|
||||
## 0.1.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [5b32de8]
|
||||
- @json-render/core@0.13.0
|
||||
- @json-render/react-pdf@0.13.0
|
||||
|
||||
## 0.1.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "example-react-pdf",
|
||||
"version": "0.1.6",
|
||||
"version": "0.1.7",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
# example-react-three-fiber
|
||||
|
||||
## 0.1.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [5b32de8]
|
||||
- @json-render/core@0.13.0
|
||||
- @json-render/react-three-fiber@0.13.0
|
||||
- @json-render/react@0.13.0
|
||||
@@ -0,0 +1,20 @@
|
||||
import type { Metadata } from "next";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "json-render React Three Fiber Example",
|
||||
description: "3D scenes from JSON specs with @json-render/react-three-fiber",
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<body style={{ margin: 0, fontFamily: "system-ui, sans-serif" }}>
|
||||
{children}
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,238 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { defineCatalog } from "@json-render/core";
|
||||
import type { ComputedFunction } from "@json-render/core";
|
||||
import { schema, defineRegistry } from "@json-render/react";
|
||||
import {
|
||||
threeComponentDefinitions,
|
||||
threeComponents,
|
||||
ThreeCanvas,
|
||||
} from "@json-render/react-three-fiber";
|
||||
import { scenes } from "./scenes";
|
||||
|
||||
const catalog = defineCatalog(schema, {
|
||||
components: {
|
||||
...threeComponentDefinitions,
|
||||
},
|
||||
actions: {},
|
||||
});
|
||||
|
||||
const { registry } = defineRegistry(catalog, {
|
||||
components: {
|
||||
...threeComponents,
|
||||
},
|
||||
});
|
||||
|
||||
function num(v: unknown, fallback: number): number {
|
||||
return typeof v === "number" ? v : fallback;
|
||||
}
|
||||
|
||||
const computedFunctions: Record<string, ComputedFunction> = {
|
||||
halfHeight: (a) => num(a.h, 1) / 2,
|
||||
|
||||
helixX: (a) => {
|
||||
const angle =
|
||||
(num(a.i, 0) / num(a.count, 40)) * num(a.turns, 3) * 2 * Math.PI +
|
||||
num(a.strand, 0) * Math.PI;
|
||||
return num(a.radius, 1.8) * Math.cos(angle);
|
||||
},
|
||||
helixY: (a) => num(a.i, 0) * num(a.spacing, 0.3) + num(a.offset, -6),
|
||||
helixZ: (a) => {
|
||||
const angle =
|
||||
(num(a.i, 0) / num(a.count, 40)) * num(a.turns, 3) * 2 * Math.PI +
|
||||
num(a.strand, 0) * Math.PI;
|
||||
return num(a.radius, 1.8) * Math.sin(angle);
|
||||
},
|
||||
helixHue: (a) => {
|
||||
const t = num(a.i, 0) / num(a.count, 40);
|
||||
return `hsl(${Math.round(t * 270 + 180)}, 85%, 60%)`;
|
||||
},
|
||||
|
||||
spiralX: (a) => {
|
||||
const t = num(a.i, 0) / num(a.count, 60);
|
||||
const r = 0.5 + t * num(a.maxRadius, 7);
|
||||
const angle = t * num(a.turns, 4) * 2 * Math.PI;
|
||||
return r * Math.cos(angle);
|
||||
},
|
||||
spiralZ: (a) => {
|
||||
const t = num(a.i, 0) / num(a.count, 60);
|
||||
const r = 0.5 + t * num(a.maxRadius, 7);
|
||||
const angle = t * num(a.turns, 4) * 2 * Math.PI;
|
||||
return r * Math.sin(angle);
|
||||
},
|
||||
spiralY: (a) => {
|
||||
const i = num(a.i, 0);
|
||||
return Math.sin(i * 1.7) * 0.4;
|
||||
},
|
||||
spiralScale: (a) => {
|
||||
const t = num(a.i, 0) / num(a.count, 60);
|
||||
return 0.08 + (1 - t) * 0.25;
|
||||
},
|
||||
spiralEmissive: (a) => {
|
||||
const t = num(a.i, 0) / num(a.count, 60);
|
||||
return Math.max(0, 1 - t * 1.5);
|
||||
},
|
||||
|
||||
circleX: (a) => {
|
||||
const angle = (num(a.i, 0) / num(a.count, 8)) * 2 * Math.PI;
|
||||
return num(a.radius, 3) * Math.cos(angle);
|
||||
},
|
||||
circleZ: (a) => {
|
||||
const angle = (num(a.i, 0) / num(a.count, 8)) * 2 * Math.PI;
|
||||
return num(a.radius, 3) * Math.sin(angle);
|
||||
},
|
||||
circleY: (a) => {
|
||||
const angle = (num(a.i, 0) / num(a.count, 8)) * 2 * Math.PI;
|
||||
return Math.sin(angle * num(a.freq, 3)) * num(a.amp, 0.3);
|
||||
},
|
||||
circleAngle: (a) => {
|
||||
return (num(a.i, 0) / num(a.count, 8)) * 2 * Math.PI;
|
||||
},
|
||||
circleHue: (a) => {
|
||||
const t = num(a.i, 0) / num(a.count, 8);
|
||||
return `hsl(${Math.round(t * 360)}, ${num(a.sat, 70)}%, ${num(a.lit, 80)}%)`;
|
||||
},
|
||||
};
|
||||
|
||||
function highlightJson(json: string): string {
|
||||
return json.replace(
|
||||
/("(?:\\.|[^"\\])*")\s*(:)|("(?:\\.|[^"\\])*")|([-+]?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?)|(\btrue\b|\bfalse\b|\bnull\b)|([{}[\]:,])/g,
|
||||
(_, key, colon, str, num, lit, punct) => {
|
||||
if (key) return `<span style="color:#FF4D8D">${key}</span>${colon}`;
|
||||
if (str) return `<span style="color:#00CA50">${str}</span>`;
|
||||
if (num) return `<span style="color:#47A8FF">${num}</span>`;
|
||||
if (lit) return `<span style="color:#47A8FF">${lit}</span>`;
|
||||
if (punct) return `<span style="color:#666">${punct}</span>`;
|
||||
return _;
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
const LIST_WIDTH = 220;
|
||||
const JSON_WIDTH = 380;
|
||||
const HEADER_HEIGHT = 40;
|
||||
|
||||
const headerStyle: React.CSSProperties = {
|
||||
height: HEADER_HEIGHT,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
padding: "0 16px",
|
||||
fontSize: 11,
|
||||
fontWeight: 600,
|
||||
color: "#555",
|
||||
letterSpacing: "0.08em",
|
||||
textTransform: "uppercase",
|
||||
fontFamily: "ui-monospace, monospace",
|
||||
borderBottom: "1px solid #1e1e1e",
|
||||
flexShrink: 0,
|
||||
whiteSpace: "nowrap",
|
||||
overflow: "hidden",
|
||||
textOverflow: "ellipsis",
|
||||
boxSizing: "border-box",
|
||||
};
|
||||
|
||||
export default function Page() {
|
||||
const [selectedIndex, setSelectedIndex] = useState(0);
|
||||
const selected = scenes[selectedIndex]!;
|
||||
|
||||
return (
|
||||
<div style={{ height: "100vh", display: "flex", background: "#0a0a0a" }}>
|
||||
<div
|
||||
style={{
|
||||
width: LIST_WIDTH,
|
||||
flexShrink: 0,
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
borderRight: "1px solid #1e1e1e",
|
||||
background: "#0f0f0f",
|
||||
}}
|
||||
>
|
||||
<div style={headerStyle}>Scenes</div>
|
||||
<div style={{ flex: 1, overflowY: "auto", padding: "6px 0" }}>
|
||||
{scenes.map((scene, i) => (
|
||||
<button
|
||||
key={scene.name}
|
||||
onClick={() => setSelectedIndex(i)}
|
||||
style={{
|
||||
display: "block",
|
||||
width: "100%",
|
||||
padding: "8px 16px",
|
||||
fontSize: 13,
|
||||
border: "none",
|
||||
textAlign: "left",
|
||||
background:
|
||||
i === selectedIndex
|
||||
? "rgba(255,255,255,0.08)"
|
||||
: "transparent",
|
||||
color: i === selectedIndex ? "#fff" : "#888",
|
||||
fontWeight: i === selectedIndex ? 500 : 400,
|
||||
cursor: "pointer",
|
||||
borderLeft:
|
||||
i === selectedIndex
|
||||
? "2px solid #fff"
|
||||
: "2px solid transparent",
|
||||
fontFamily: "inherit",
|
||||
}}
|
||||
>
|
||||
{scene.name}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
style={{
|
||||
flex: 1,
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
minWidth: 0,
|
||||
}}
|
||||
>
|
||||
<div style={headerStyle}>{selected.description}</div>
|
||||
<div style={{ flex: 1, background: "#000", position: "relative" }}>
|
||||
<ThreeCanvas
|
||||
key={selectedIndex}
|
||||
spec={selected.spec}
|
||||
registry={registry}
|
||||
functions={computedFunctions}
|
||||
shadows
|
||||
camera={{ position: [0, 0, 5], fov: 50 }}
|
||||
style={{ width: "100%", height: "100%" }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
style={{
|
||||
width: JSON_WIDTH,
|
||||
flexShrink: 0,
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
borderLeft: "1px solid #1e1e1e",
|
||||
background: "#0d0d0d",
|
||||
}}
|
||||
>
|
||||
<div style={headerStyle}>Spec JSON</div>
|
||||
<pre
|
||||
style={{
|
||||
flex: 1,
|
||||
margin: 0,
|
||||
padding: 14,
|
||||
overflowY: "auto",
|
||||
overflowX: "auto",
|
||||
fontSize: 11,
|
||||
lineHeight: 1.6,
|
||||
fontFamily:
|
||||
"ui-monospace, 'Cascadia Code', 'Source Code Pro', Menlo, Consolas, monospace",
|
||||
color: "#EDEDED",
|
||||
tabSize: 2,
|
||||
}}
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: highlightJson(JSON.stringify(selected.spec, null, 2)),
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import type { Spec } from "@json-render/core";
|
||||
|
||||
export type Scene = { name: string; description: string; spec: Spec };
|
||||
|
||||
export const PI = Math.PI;
|
||||
|
||||
export function range(n: number) {
|
||||
return Array.from({ length: n }, (_, i) => ({ i }));
|
||||
}
|
||||
@@ -0,0 +1,474 @@
|
||||
import type { Scene } from "./_helpers";
|
||||
|
||||
export const clockworkOrrery: Scene = {
|
||||
name: "Clockwork Orrery",
|
||||
description:
|
||||
"Interlocking brass/gold rings spinning at different tilts and speeds with orbiting metallic planets. Like the inside of a celestial clockwork mechanism.",
|
||||
spec: {
|
||||
root: "scene",
|
||||
elements: {
|
||||
scene: {
|
||||
type: "Group",
|
||||
props: { position: null, rotation: null, scale: null },
|
||||
children: [
|
||||
"cam",
|
||||
"env",
|
||||
"ambient",
|
||||
"key",
|
||||
"warm",
|
||||
"accent",
|
||||
"center",
|
||||
"ring-spin-0",
|
||||
"ring-spin-1",
|
||||
"ring-spin-2",
|
||||
"ring-spin-3",
|
||||
"planet-orbit-0",
|
||||
"planet-orbit-1",
|
||||
"planet-orbit-2",
|
||||
"sparkles",
|
||||
"post",
|
||||
"controls",
|
||||
],
|
||||
},
|
||||
cam: {
|
||||
type: "PerspectiveCamera",
|
||||
props: {
|
||||
position: [0, 3, 8],
|
||||
fov: 50,
|
||||
makeDefault: true,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
env: {
|
||||
type: "Environment",
|
||||
props: {
|
||||
preset: "dawn",
|
||||
background: true,
|
||||
blur: 1,
|
||||
intensity: 0.5,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
ambient: {
|
||||
type: "AmbientLight",
|
||||
props: { color: "#332200", intensity: 0.4 },
|
||||
children: [],
|
||||
},
|
||||
key: {
|
||||
type: "DirectionalLight",
|
||||
props: {
|
||||
position: [5, 10, 5],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
color: "#ffddaa",
|
||||
intensity: 2,
|
||||
castShadow: true,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
warm: {
|
||||
type: "PointLight",
|
||||
props: {
|
||||
position: [0, 0, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
color: "#ffaa44",
|
||||
intensity: 30,
|
||||
distance: 15,
|
||||
decay: 2,
|
||||
castShadow: false,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
accent: {
|
||||
type: "PointLight",
|
||||
props: {
|
||||
position: [-4, 3, -4],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
color: "#ffcc88",
|
||||
intensity: 15,
|
||||
distance: 15,
|
||||
decay: 2,
|
||||
castShadow: false,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
center: {
|
||||
type: "Float",
|
||||
props: {
|
||||
position: null,
|
||||
rotation: null,
|
||||
scale: null,
|
||||
speed: 0.5,
|
||||
rotationIntensity: 0.1,
|
||||
floatIntensity: 0.2,
|
||||
enabled: true,
|
||||
},
|
||||
children: ["center-sphere"],
|
||||
},
|
||||
"center-sphere": {
|
||||
type: "GlassSphere",
|
||||
props: {
|
||||
position: null,
|
||||
rotation: null,
|
||||
scale: null,
|
||||
castShadow: false,
|
||||
receiveShadow: false,
|
||||
radius: 0.5,
|
||||
widthSegments: 32,
|
||||
heightSegments: 16,
|
||||
color: "#ffddaa",
|
||||
transmission: 1,
|
||||
thickness: 0.8,
|
||||
roughness: 0,
|
||||
chromaticAberration: 0.1,
|
||||
ior: 1.8,
|
||||
distortion: 0.05,
|
||||
distortionScale: null,
|
||||
temporalDistortion: null,
|
||||
samples: 10,
|
||||
resolution: 256,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"ring-spin-0": {
|
||||
type: "Spin",
|
||||
props: {
|
||||
position: null,
|
||||
rotation: [0.3, 0, 0.8],
|
||||
scale: null,
|
||||
speed: 1.2,
|
||||
axis: "y",
|
||||
},
|
||||
children: ["ring-torus-0"],
|
||||
},
|
||||
"ring-torus-0": {
|
||||
type: "Torus",
|
||||
props: {
|
||||
position: null,
|
||||
rotation: [1.5708, 0, 0],
|
||||
scale: null,
|
||||
castShadow: false,
|
||||
receiveShadow: false,
|
||||
material: {
|
||||
color: "#ffcc44",
|
||||
metalness: 0.95,
|
||||
roughness: 0.1,
|
||||
emissive: "#cc9900",
|
||||
emissiveIntensity: 0.4,
|
||||
opacity: null,
|
||||
transparent: null,
|
||||
wireframe: null,
|
||||
},
|
||||
radius: 2,
|
||||
tube: 0.04,
|
||||
radialSegments: null,
|
||||
tubularSegments: 128,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"ring-spin-1": {
|
||||
type: "Spin",
|
||||
props: {
|
||||
position: null,
|
||||
rotation: [0.9, 0, -0.2],
|
||||
scale: null,
|
||||
speed: -0.7,
|
||||
axis: "y",
|
||||
},
|
||||
children: ["ring-torus-1"],
|
||||
},
|
||||
"ring-torus-1": {
|
||||
type: "Torus",
|
||||
props: {
|
||||
position: null,
|
||||
rotation: [1.5708, 0, 0],
|
||||
scale: null,
|
||||
castShadow: false,
|
||||
receiveShadow: false,
|
||||
material: {
|
||||
color: "#cc8833",
|
||||
metalness: 0.95,
|
||||
roughness: 0.1,
|
||||
emissive: "#aa6622",
|
||||
emissiveIntensity: 0.3,
|
||||
opacity: null,
|
||||
transparent: null,
|
||||
wireframe: null,
|
||||
},
|
||||
radius: 3.2,
|
||||
tube: 0.035,
|
||||
radialSegments: null,
|
||||
tubularSegments: 128,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"ring-spin-2": {
|
||||
type: "Spin",
|
||||
props: {
|
||||
position: null,
|
||||
rotation: [-0.4, 0, 0.5],
|
||||
scale: null,
|
||||
speed: 0.4,
|
||||
axis: "y",
|
||||
},
|
||||
children: ["ring-torus-2"],
|
||||
},
|
||||
"ring-torus-2": {
|
||||
type: "Torus",
|
||||
props: {
|
||||
position: null,
|
||||
rotation: [1.5708, 0, 0],
|
||||
scale: null,
|
||||
castShadow: false,
|
||||
receiveShadow: false,
|
||||
material: {
|
||||
color: "#bb9944",
|
||||
metalness: 0.95,
|
||||
roughness: 0.1,
|
||||
emissive: "#886622",
|
||||
emissiveIntensity: 0.25,
|
||||
opacity: null,
|
||||
transparent: null,
|
||||
wireframe: null,
|
||||
},
|
||||
radius: 4.5,
|
||||
tube: 0.03,
|
||||
radialSegments: null,
|
||||
tubularSegments: 128,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"ring-spin-3": {
|
||||
type: "Spin",
|
||||
props: {
|
||||
position: null,
|
||||
rotation: [0.1, 0, -0.1],
|
||||
scale: null,
|
||||
speed: -0.2,
|
||||
axis: "y",
|
||||
},
|
||||
children: ["ring-torus-3"],
|
||||
},
|
||||
"ring-torus-3": {
|
||||
type: "Torus",
|
||||
props: {
|
||||
position: null,
|
||||
rotation: [1.5708, 0, 0],
|
||||
scale: null,
|
||||
castShadow: false,
|
||||
receiveShadow: false,
|
||||
material: {
|
||||
color: "#997744",
|
||||
metalness: 0.95,
|
||||
roughness: 0.1,
|
||||
emissive: "#664422",
|
||||
emissiveIntensity: 0.15,
|
||||
opacity: null,
|
||||
transparent: null,
|
||||
wireframe: null,
|
||||
},
|
||||
radius: 6,
|
||||
tube: 0.025,
|
||||
radialSegments: null,
|
||||
tubularSegments: 128,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"planet-orbit-0": {
|
||||
type: "Orbit",
|
||||
props: {
|
||||
position: [0, 0, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
speed: 0.6,
|
||||
radius: 2.5,
|
||||
tilt: 0.3,
|
||||
},
|
||||
children: ["planet-spin-0"],
|
||||
},
|
||||
"planet-spin-0": {
|
||||
type: "Spin",
|
||||
props: {
|
||||
position: null,
|
||||
rotation: null,
|
||||
scale: null,
|
||||
speed: 3,
|
||||
axis: "y",
|
||||
},
|
||||
children: ["planet-sphere-0"],
|
||||
},
|
||||
"planet-sphere-0": {
|
||||
type: "Sphere",
|
||||
props: {
|
||||
position: null,
|
||||
rotation: null,
|
||||
scale: null,
|
||||
castShadow: false,
|
||||
receiveShadow: false,
|
||||
material: {
|
||||
color: "#ffffff",
|
||||
metalness: 1,
|
||||
roughness: 0.02,
|
||||
emissive: "#ffffff",
|
||||
emissiveIntensity: 0.2,
|
||||
opacity: null,
|
||||
transparent: null,
|
||||
wireframe: null,
|
||||
},
|
||||
radius: 0.2,
|
||||
widthSegments: 32,
|
||||
heightSegments: 16,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"planet-orbit-1": {
|
||||
type: "Orbit",
|
||||
props: {
|
||||
position: [0, 0, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
speed: -0.9,
|
||||
radius: 3.8,
|
||||
tilt: 0.8,
|
||||
},
|
||||
children: ["planet-spin-1"],
|
||||
},
|
||||
"planet-spin-1": {
|
||||
type: "Spin",
|
||||
props: {
|
||||
position: null,
|
||||
rotation: null,
|
||||
scale: null,
|
||||
speed: -2,
|
||||
axis: "x",
|
||||
},
|
||||
children: ["planet-sphere-1"],
|
||||
},
|
||||
"planet-sphere-1": {
|
||||
type: "Sphere",
|
||||
props: {
|
||||
position: null,
|
||||
rotation: null,
|
||||
scale: null,
|
||||
castShadow: false,
|
||||
receiveShadow: false,
|
||||
material: {
|
||||
color: "#ffcc88",
|
||||
metalness: 0.8,
|
||||
roughness: 0.15,
|
||||
emissive: "#ffcc88",
|
||||
emissiveIntensity: 0.2,
|
||||
opacity: null,
|
||||
transparent: null,
|
||||
wireframe: null,
|
||||
},
|
||||
radius: 0.15,
|
||||
widthSegments: 32,
|
||||
heightSegments: 16,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"planet-orbit-2": {
|
||||
type: "Orbit",
|
||||
props: {
|
||||
position: [0, 0, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
speed: 0.35,
|
||||
radius: 5.2,
|
||||
tilt: -0.5,
|
||||
},
|
||||
children: ["planet-spin-2"],
|
||||
},
|
||||
"planet-spin-2": {
|
||||
type: "Spin",
|
||||
props: {
|
||||
position: null,
|
||||
rotation: null,
|
||||
scale: null,
|
||||
speed: 1.5,
|
||||
axis: "z",
|
||||
},
|
||||
children: ["planet-sphere-2"],
|
||||
},
|
||||
"planet-sphere-2": {
|
||||
type: "Sphere",
|
||||
props: {
|
||||
position: null,
|
||||
rotation: null,
|
||||
scale: null,
|
||||
castShadow: false,
|
||||
receiveShadow: false,
|
||||
material: {
|
||||
color: "#ccaa66",
|
||||
metalness: 0.9,
|
||||
roughness: 0.05,
|
||||
emissive: "#ccaa66",
|
||||
emissiveIntensity: 0.2,
|
||||
opacity: null,
|
||||
transparent: null,
|
||||
wireframe: null,
|
||||
},
|
||||
radius: 0.18,
|
||||
widthSegments: 32,
|
||||
heightSegments: 16,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
sparkles: {
|
||||
type: "Sparkles",
|
||||
props: {
|
||||
position: null,
|
||||
rotation: null,
|
||||
scale: [14, 10, 14],
|
||||
count: 150,
|
||||
speed: 0.15,
|
||||
opacity: 0.3,
|
||||
color: "#ffcc66",
|
||||
size: 1,
|
||||
noise: 2,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
post: {
|
||||
type: "EffectComposer",
|
||||
props: { enabled: true, multisampling: 8 },
|
||||
children: ["bloom", "vignette"],
|
||||
},
|
||||
bloom: {
|
||||
type: "Bloom",
|
||||
props: {
|
||||
intensity: 1.2,
|
||||
luminanceThreshold: 0.2,
|
||||
luminanceSmoothing: null,
|
||||
mipmapBlur: true,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
vignette: {
|
||||
type: "Vignette",
|
||||
props: { offset: 0.5, darkness: 0.5 },
|
||||
children: [],
|
||||
},
|
||||
controls: {
|
||||
type: "OrbitControls",
|
||||
props: {
|
||||
enableDamping: true,
|
||||
dampingFactor: null,
|
||||
enableZoom: true,
|
||||
enablePan: null,
|
||||
enableRotate: true,
|
||||
minDistance: 4,
|
||||
maxDistance: 18,
|
||||
minPolarAngle: 0.3,
|
||||
maxPolarAngle: 1.5,
|
||||
autoRotate: true,
|
||||
autoRotateSpeed: 0.3,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,692 @@
|
||||
import type { Scene } from "./_helpers";
|
||||
|
||||
export const deepSeaAbyss: Scene = {
|
||||
name: "Deep Sea Abyss",
|
||||
description:
|
||||
"Bioluminescent deep ocean. Pitch black with glowing jellyfish-like creatures.",
|
||||
spec: {
|
||||
root: "scene",
|
||||
elements: {
|
||||
scene: {
|
||||
type: "Group",
|
||||
props: { position: null, rotation: null, scale: null },
|
||||
children: [
|
||||
"cam",
|
||||
"env",
|
||||
"fog",
|
||||
"ambient",
|
||||
"jelly-1-pulse",
|
||||
"jelly-2-pulse",
|
||||
"jelly-3-pulse",
|
||||
"light-1",
|
||||
"light-2",
|
||||
"light-3",
|
||||
"tentacles-1",
|
||||
"tentacles-2",
|
||||
"tentacles-3",
|
||||
"sparkles-1",
|
||||
"sparkles-2",
|
||||
"controls",
|
||||
],
|
||||
},
|
||||
cam: {
|
||||
type: "PerspectiveCamera",
|
||||
props: {
|
||||
position: [0, 1, 6],
|
||||
fov: 55,
|
||||
makeDefault: true,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
env: {
|
||||
type: "Environment",
|
||||
props: {
|
||||
preset: "night",
|
||||
background: false,
|
||||
blur: 1,
|
||||
intensity: 0.05,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
fog: {
|
||||
type: "Fog",
|
||||
props: { color: "#000510", near: 2, far: 18 },
|
||||
children: [],
|
||||
},
|
||||
ambient: {
|
||||
type: "AmbientLight",
|
||||
props: { color: "#001133", intensity: 0.15 },
|
||||
children: [],
|
||||
},
|
||||
"jelly-1-pulse": {
|
||||
type: "Pulse",
|
||||
props: {
|
||||
position: [0, 1, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
speed: 0.6,
|
||||
min: 0.85,
|
||||
max: 1.15,
|
||||
},
|
||||
children: ["jelly-1-spin"],
|
||||
},
|
||||
"jelly-1-spin": {
|
||||
type: "Spin",
|
||||
props: {
|
||||
position: null,
|
||||
rotation: null,
|
||||
scale: null,
|
||||
speed: 0.2,
|
||||
axis: "y",
|
||||
},
|
||||
children: ["jelly-1-body"],
|
||||
},
|
||||
"jelly-1-body": {
|
||||
type: "DistortSphere",
|
||||
props: {
|
||||
position: null,
|
||||
rotation: null,
|
||||
scale: null,
|
||||
castShadow: null,
|
||||
receiveShadow: null,
|
||||
radius: 1,
|
||||
widthSegments: 64,
|
||||
heightSegments: 32,
|
||||
color: "#00ccff",
|
||||
speed: 2,
|
||||
distort: 0.5,
|
||||
metalness: 0,
|
||||
roughness: 0.2,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"jelly-2-pulse": {
|
||||
type: "Pulse",
|
||||
props: {
|
||||
position: [-4, -1, -3],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
speed: 0.8,
|
||||
min: 0.8,
|
||||
max: 1.2,
|
||||
},
|
||||
children: ["jelly-2-spin"],
|
||||
},
|
||||
"jelly-2-spin": {
|
||||
type: "Spin",
|
||||
props: {
|
||||
position: null,
|
||||
rotation: null,
|
||||
scale: null,
|
||||
speed: -0.3,
|
||||
axis: "y",
|
||||
},
|
||||
children: ["jelly-2-body"],
|
||||
},
|
||||
"jelly-2-body": {
|
||||
type: "DistortSphere",
|
||||
props: {
|
||||
position: null,
|
||||
rotation: null,
|
||||
scale: null,
|
||||
castShadow: null,
|
||||
receiveShadow: null,
|
||||
radius: 0.7,
|
||||
widthSegments: 64,
|
||||
heightSegments: 32,
|
||||
color: "#cc44ff",
|
||||
speed: 2.5,
|
||||
distort: 0.6,
|
||||
metalness: 0,
|
||||
roughness: 0.2,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"jelly-3-pulse": {
|
||||
type: "Pulse",
|
||||
props: {
|
||||
position: [3, 0.5, -5],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
speed: 0.5,
|
||||
min: 0.9,
|
||||
max: 1.1,
|
||||
},
|
||||
children: ["jelly-3-spin"],
|
||||
},
|
||||
"jelly-3-spin": {
|
||||
type: "Spin",
|
||||
props: {
|
||||
position: null,
|
||||
rotation: null,
|
||||
scale: null,
|
||||
speed: 0.15,
|
||||
axis: "y",
|
||||
},
|
||||
children: ["jelly-3-body"],
|
||||
},
|
||||
"jelly-3-body": {
|
||||
type: "DistortSphere",
|
||||
props: {
|
||||
position: null,
|
||||
rotation: null,
|
||||
scale: null,
|
||||
castShadow: null,
|
||||
receiveShadow: null,
|
||||
radius: 0.5,
|
||||
widthSegments: 64,
|
||||
heightSegments: 32,
|
||||
color: "#ff44aa",
|
||||
speed: 1.5,
|
||||
distort: 0.4,
|
||||
metalness: 0,
|
||||
roughness: 0.3,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"light-1": {
|
||||
type: "PointLight",
|
||||
props: {
|
||||
position: [0, 0, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
color: "#00ccff",
|
||||
intensity: 20,
|
||||
distance: 12,
|
||||
decay: 2,
|
||||
castShadow: null,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"light-2": {
|
||||
type: "PointLight",
|
||||
props: {
|
||||
position: [-4, -1, -3],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
color: "#cc44ff",
|
||||
intensity: 15,
|
||||
distance: 10,
|
||||
decay: 2,
|
||||
castShadow: null,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"light-3": {
|
||||
type: "PointLight",
|
||||
props: {
|
||||
position: [3, 0.5, -5],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
color: "#ff44aa",
|
||||
intensity: 10,
|
||||
distance: 8,
|
||||
decay: 2,
|
||||
castShadow: null,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"tentacles-1": {
|
||||
type: "Float",
|
||||
props: {
|
||||
position: [0, -1, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
speed: 0.3,
|
||||
rotationIntensity: 0.5,
|
||||
floatIntensity: 0.8,
|
||||
enabled: true,
|
||||
},
|
||||
children: ["t1-s1", "t1-s2", "t1-s3", "t1-s4", "t1-s5", "t1-s6"],
|
||||
},
|
||||
"t1-s1": {
|
||||
type: "GlassSphere",
|
||||
props: {
|
||||
position: [0.3, -0.5, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
castShadow: null,
|
||||
receiveShadow: null,
|
||||
radius: 0.12,
|
||||
widthSegments: 16,
|
||||
heightSegments: 8,
|
||||
color: "#00ccff",
|
||||
transmission: 0.9,
|
||||
thickness: 0.3,
|
||||
roughness: 0.1,
|
||||
chromaticAberration: 0.1,
|
||||
ior: 1.5,
|
||||
distortion: 0,
|
||||
distortionScale: null,
|
||||
temporalDistortion: null,
|
||||
samples: 6,
|
||||
resolution: 128,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"t1-s2": {
|
||||
type: "GlassSphere",
|
||||
props: {
|
||||
position: [-0.2, -1.2, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
castShadow: null,
|
||||
receiveShadow: null,
|
||||
radius: 0.1,
|
||||
widthSegments: 16,
|
||||
heightSegments: 8,
|
||||
color: "#00ccff",
|
||||
transmission: 0.9,
|
||||
thickness: 0.3,
|
||||
roughness: 0.1,
|
||||
chromaticAberration: 0.1,
|
||||
ior: 1.5,
|
||||
distortion: 0,
|
||||
distortionScale: null,
|
||||
temporalDistortion: null,
|
||||
samples: 6,
|
||||
resolution: 128,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"t1-s3": {
|
||||
type: "GlassSphere",
|
||||
props: {
|
||||
position: [0.4, -1.9, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
castShadow: null,
|
||||
receiveShadow: null,
|
||||
radius: 0.08,
|
||||
widthSegments: 16,
|
||||
heightSegments: 8,
|
||||
color: "#00ccff",
|
||||
transmission: 0.9,
|
||||
thickness: 0.3,
|
||||
roughness: 0.1,
|
||||
chromaticAberration: 0.1,
|
||||
ior: 1.5,
|
||||
distortion: 0,
|
||||
distortionScale: null,
|
||||
temporalDistortion: null,
|
||||
samples: 6,
|
||||
resolution: 128,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"t1-s4": {
|
||||
type: "GlassSphere",
|
||||
props: {
|
||||
position: [-0.3, -2.5, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
castShadow: null,
|
||||
receiveShadow: null,
|
||||
radius: 0.07,
|
||||
widthSegments: 16,
|
||||
heightSegments: 8,
|
||||
color: "#00ccff",
|
||||
transmission: 0.9,
|
||||
thickness: 0.3,
|
||||
roughness: 0.1,
|
||||
chromaticAberration: 0.1,
|
||||
ior: 1.5,
|
||||
distortion: 0,
|
||||
distortionScale: null,
|
||||
temporalDistortion: null,
|
||||
samples: 6,
|
||||
resolution: 128,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"t1-s5": {
|
||||
type: "GlassSphere",
|
||||
props: {
|
||||
position: [0.1, -3.0, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
castShadow: null,
|
||||
receiveShadow: null,
|
||||
radius: 0.06,
|
||||
widthSegments: 16,
|
||||
heightSegments: 8,
|
||||
color: "#00ccff",
|
||||
transmission: 0.9,
|
||||
thickness: 0.3,
|
||||
roughness: 0.1,
|
||||
chromaticAberration: 0.1,
|
||||
ior: 1.5,
|
||||
distortion: 0,
|
||||
distortionScale: null,
|
||||
temporalDistortion: null,
|
||||
samples: 6,
|
||||
resolution: 128,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"t1-s6": {
|
||||
type: "GlassSphere",
|
||||
props: {
|
||||
position: [-0.1, -3.4, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
castShadow: null,
|
||||
receiveShadow: null,
|
||||
radius: 0.05,
|
||||
widthSegments: 16,
|
||||
heightSegments: 8,
|
||||
color: "#00ccff",
|
||||
transmission: 0.9,
|
||||
thickness: 0.3,
|
||||
roughness: 0.1,
|
||||
chromaticAberration: 0.1,
|
||||
ior: 1.5,
|
||||
distortion: 0,
|
||||
distortionScale: null,
|
||||
temporalDistortion: null,
|
||||
samples: 6,
|
||||
resolution: 128,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"tentacles-2": {
|
||||
type: "Float",
|
||||
props: {
|
||||
position: [-4, -2.5, -3],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
speed: 0.4,
|
||||
rotationIntensity: 0.5,
|
||||
floatIntensity: 0.8,
|
||||
enabled: true,
|
||||
},
|
||||
children: ["t2-s1", "t2-s2", "t2-s3", "t2-s4", "t2-s5"],
|
||||
},
|
||||
"t2-s1": {
|
||||
type: "GlassSphere",
|
||||
props: {
|
||||
position: [0.3, -0.5, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
castShadow: null,
|
||||
receiveShadow: null,
|
||||
radius: 0.1,
|
||||
widthSegments: 16,
|
||||
heightSegments: 8,
|
||||
color: "#cc44ff",
|
||||
transmission: 0.9,
|
||||
thickness: 0.3,
|
||||
roughness: 0.1,
|
||||
chromaticAberration: 0.1,
|
||||
ior: 1.5,
|
||||
distortion: 0,
|
||||
distortionScale: null,
|
||||
temporalDistortion: null,
|
||||
samples: 6,
|
||||
resolution: 128,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"t2-s2": {
|
||||
type: "GlassSphere",
|
||||
props: {
|
||||
position: [-0.2, -1.1, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
castShadow: null,
|
||||
receiveShadow: null,
|
||||
radius: 0.08,
|
||||
widthSegments: 16,
|
||||
heightSegments: 8,
|
||||
color: "#cc44ff",
|
||||
transmission: 0.9,
|
||||
thickness: 0.3,
|
||||
roughness: 0.1,
|
||||
chromaticAberration: 0.1,
|
||||
ior: 1.5,
|
||||
distortion: 0,
|
||||
distortionScale: null,
|
||||
temporalDistortion: null,
|
||||
samples: 6,
|
||||
resolution: 128,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"t2-s3": {
|
||||
type: "GlassSphere",
|
||||
props: {
|
||||
position: [0.4, -1.7, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
castShadow: null,
|
||||
receiveShadow: null,
|
||||
radius: 0.06,
|
||||
widthSegments: 16,
|
||||
heightSegments: 8,
|
||||
color: "#cc44ff",
|
||||
transmission: 0.9,
|
||||
thickness: 0.3,
|
||||
roughness: 0.1,
|
||||
chromaticAberration: 0.1,
|
||||
ior: 1.5,
|
||||
distortion: 0,
|
||||
distortionScale: null,
|
||||
temporalDistortion: null,
|
||||
samples: 6,
|
||||
resolution: 128,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"t2-s4": {
|
||||
type: "GlassSphere",
|
||||
props: {
|
||||
position: [-0.3, -2.2, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
castShadow: null,
|
||||
receiveShadow: null,
|
||||
radius: 0.05,
|
||||
widthSegments: 16,
|
||||
heightSegments: 8,
|
||||
color: "#cc44ff",
|
||||
transmission: 0.9,
|
||||
thickness: 0.3,
|
||||
roughness: 0.1,
|
||||
chromaticAberration: 0.1,
|
||||
ior: 1.5,
|
||||
distortion: 0,
|
||||
distortionScale: null,
|
||||
temporalDistortion: null,
|
||||
samples: 6,
|
||||
resolution: 128,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"t2-s5": {
|
||||
type: "GlassSphere",
|
||||
props: {
|
||||
position: [0.1, -2.6, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
castShadow: null,
|
||||
receiveShadow: null,
|
||||
radius: 0.05,
|
||||
widthSegments: 16,
|
||||
heightSegments: 8,
|
||||
color: "#cc44ff",
|
||||
transmission: 0.9,
|
||||
thickness: 0.3,
|
||||
roughness: 0.1,
|
||||
chromaticAberration: 0.1,
|
||||
ior: 1.5,
|
||||
distortion: 0,
|
||||
distortionScale: null,
|
||||
temporalDistortion: null,
|
||||
samples: 6,
|
||||
resolution: 128,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"tentacles-3": {
|
||||
type: "Float",
|
||||
props: {
|
||||
position: [3, -1, -5],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
speed: 0.35,
|
||||
rotationIntensity: 0.5,
|
||||
floatIntensity: 0.8,
|
||||
enabled: true,
|
||||
},
|
||||
children: ["t3-s1", "t3-s2", "t3-s3", "t3-s4"],
|
||||
},
|
||||
"t3-s1": {
|
||||
type: "GlassSphere",
|
||||
props: {
|
||||
position: [0.3, -0.5, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
castShadow: null,
|
||||
receiveShadow: null,
|
||||
radius: 0.09,
|
||||
widthSegments: 16,
|
||||
heightSegments: 8,
|
||||
color: "#ff44aa",
|
||||
transmission: 0.9,
|
||||
thickness: 0.3,
|
||||
roughness: 0.1,
|
||||
chromaticAberration: 0.1,
|
||||
ior: 1.5,
|
||||
distortion: 0,
|
||||
distortionScale: null,
|
||||
temporalDistortion: null,
|
||||
samples: 6,
|
||||
resolution: 128,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"t3-s2": {
|
||||
type: "GlassSphere",
|
||||
props: {
|
||||
position: [-0.2, -1.0, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
castShadow: null,
|
||||
receiveShadow: null,
|
||||
radius: 0.07,
|
||||
widthSegments: 16,
|
||||
heightSegments: 8,
|
||||
color: "#ff44aa",
|
||||
transmission: 0.9,
|
||||
thickness: 0.3,
|
||||
roughness: 0.1,
|
||||
chromaticAberration: 0.1,
|
||||
ior: 1.5,
|
||||
distortion: 0,
|
||||
distortionScale: null,
|
||||
temporalDistortion: null,
|
||||
samples: 6,
|
||||
resolution: 128,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"t3-s3": {
|
||||
type: "GlassSphere",
|
||||
props: {
|
||||
position: [0.4, -1.5, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
castShadow: null,
|
||||
receiveShadow: null,
|
||||
radius: 0.06,
|
||||
widthSegments: 16,
|
||||
heightSegments: 8,
|
||||
color: "#ff44aa",
|
||||
transmission: 0.9,
|
||||
thickness: 0.3,
|
||||
roughness: 0.1,
|
||||
chromaticAberration: 0.1,
|
||||
ior: 1.5,
|
||||
distortion: 0,
|
||||
distortionScale: null,
|
||||
temporalDistortion: null,
|
||||
samples: 6,
|
||||
resolution: 128,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"t3-s4": {
|
||||
type: "GlassSphere",
|
||||
props: {
|
||||
position: [-0.1, -1.9, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
castShadow: null,
|
||||
receiveShadow: null,
|
||||
radius: 0.05,
|
||||
widthSegments: 16,
|
||||
heightSegments: 8,
|
||||
color: "#ff44aa",
|
||||
transmission: 0.9,
|
||||
thickness: 0.3,
|
||||
roughness: 0.1,
|
||||
chromaticAberration: 0.1,
|
||||
ior: 1.5,
|
||||
distortion: 0,
|
||||
distortionScale: null,
|
||||
temporalDistortion: null,
|
||||
samples: 6,
|
||||
resolution: 128,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"sparkles-1": {
|
||||
type: "Sparkles",
|
||||
props: {
|
||||
position: [0, 0, -3],
|
||||
rotation: null,
|
||||
scale: [15, 10, 15],
|
||||
count: 300,
|
||||
speed: 0.1,
|
||||
opacity: 0.4,
|
||||
color: "#00aaff",
|
||||
size: 1,
|
||||
noise: 3,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"sparkles-2": {
|
||||
type: "Sparkles",
|
||||
props: {
|
||||
position: [0, -2, 0],
|
||||
rotation: null,
|
||||
scale: [12, 6, 12],
|
||||
count: 150,
|
||||
speed: 0.05,
|
||||
opacity: 0.2,
|
||||
color: "#8844ff",
|
||||
size: 0.8,
|
||||
noise: 2,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
controls: {
|
||||
type: "OrbitControls",
|
||||
props: {
|
||||
enableDamping: true,
|
||||
dampingFactor: null,
|
||||
enableZoom: true,
|
||||
enablePan: null,
|
||||
enableRotate: true,
|
||||
minDistance: 3,
|
||||
maxDistance: 18,
|
||||
minPolarAngle: null,
|
||||
maxPolarAngle: null,
|
||||
autoRotate: true,
|
||||
autoRotateSpeed: 0.15,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,940 @@
|
||||
import { PI } from "./_helpers";
|
||||
import type { Scene } from "./_helpers";
|
||||
|
||||
export const floatingIslands: Scene = {
|
||||
name: "Floating Islands",
|
||||
description:
|
||||
"Sky, clouds, islands with trees and bridges floating in sunset light",
|
||||
spec: {
|
||||
root: "scene",
|
||||
elements: {
|
||||
scene: {
|
||||
type: "Group",
|
||||
props: { position: null, rotation: null, scale: null },
|
||||
children: [
|
||||
"cam",
|
||||
"sky",
|
||||
"ambient",
|
||||
"sun",
|
||||
"clouds",
|
||||
"island-main-float",
|
||||
"island-left-float",
|
||||
"island-right-float",
|
||||
"island-far-float",
|
||||
"island-tiny1-float",
|
||||
"island-tiny2-float",
|
||||
"bridge-1",
|
||||
"bridge-2",
|
||||
"sparkles",
|
||||
"controls",
|
||||
],
|
||||
},
|
||||
cam: {
|
||||
type: "PerspectiveCamera",
|
||||
props: {
|
||||
position: [8, 6, 14],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
fov: 50,
|
||||
near: 0.1,
|
||||
far: 500,
|
||||
makeDefault: true,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
sky: {
|
||||
type: "Environment",
|
||||
props: {
|
||||
preset: "dawn",
|
||||
background: true,
|
||||
blur: 0.8,
|
||||
intensity: 0.5,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
ambient: {
|
||||
type: "AmbientLight",
|
||||
props: { color: "#ffd4a0", intensity: 0.5 },
|
||||
children: [],
|
||||
},
|
||||
sun: {
|
||||
type: "DirectionalLight",
|
||||
props: {
|
||||
position: [8, 12, 6],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
color: "#ffcc88",
|
||||
intensity: 2.5,
|
||||
castShadow: true,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
clouds: {
|
||||
type: "Cloud",
|
||||
props: {
|
||||
position: [0, 4, -8],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
seed: 7,
|
||||
segments: 40,
|
||||
bounds: [25, 3, 20],
|
||||
volume: 10,
|
||||
speed: 0.1,
|
||||
fade: 25,
|
||||
opacity: 0.6,
|
||||
color: "#ffe8d0",
|
||||
growth: 8,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
|
||||
"island-main-float": {
|
||||
type: "Float",
|
||||
props: {
|
||||
position: [0, 0, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
speed: 0.4,
|
||||
rotationIntensity: 0.02,
|
||||
floatIntensity: 0.3,
|
||||
enabled: true,
|
||||
},
|
||||
children: ["island-main"],
|
||||
},
|
||||
"island-main": {
|
||||
type: "Group",
|
||||
props: { position: null, rotation: null, scale: null },
|
||||
children: [
|
||||
"main-base",
|
||||
"main-top",
|
||||
"main-underhang",
|
||||
"tree-1-trunk",
|
||||
"tree-1-canopy",
|
||||
"tree-2-trunk",
|
||||
"tree-2-canopy",
|
||||
"tree-3-trunk",
|
||||
"tree-3-canopy",
|
||||
"house-base",
|
||||
"house-roof",
|
||||
],
|
||||
},
|
||||
"main-base": {
|
||||
type: "Cylinder",
|
||||
props: {
|
||||
position: [0, -0.5, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
castShadow: true,
|
||||
receiveShadow: true,
|
||||
material: {
|
||||
color: "#8B6914",
|
||||
metalness: 0.1,
|
||||
roughness: 0.9,
|
||||
emissive: null,
|
||||
emissiveIntensity: null,
|
||||
opacity: null,
|
||||
transparent: null,
|
||||
wireframe: null,
|
||||
},
|
||||
radiusTop: 3,
|
||||
radiusBottom: 1.5,
|
||||
height: 2,
|
||||
radialSegments: 16,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"main-top": {
|
||||
type: "Cylinder",
|
||||
props: {
|
||||
position: [0, 0.55, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
castShadow: true,
|
||||
receiveShadow: true,
|
||||
material: {
|
||||
color: "#4a8c3f",
|
||||
metalness: 0,
|
||||
roughness: 0.95,
|
||||
emissive: null,
|
||||
emissiveIntensity: null,
|
||||
opacity: null,
|
||||
transparent: null,
|
||||
wireframe: null,
|
||||
},
|
||||
radiusTop: 3.1,
|
||||
radiusBottom: 3,
|
||||
height: 0.3,
|
||||
radialSegments: 16,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"main-underhang": {
|
||||
type: "Cone",
|
||||
props: {
|
||||
position: [0, -2.5, 0],
|
||||
rotation: [PI, 0, 0],
|
||||
scale: null,
|
||||
castShadow: true,
|
||||
receiveShadow: false,
|
||||
material: {
|
||||
color: "#6B4513",
|
||||
metalness: 0.1,
|
||||
roughness: 0.95,
|
||||
emissive: null,
|
||||
emissiveIntensity: null,
|
||||
opacity: null,
|
||||
transparent: null,
|
||||
wireframe: null,
|
||||
},
|
||||
radius: 1.5,
|
||||
height: 2.5,
|
||||
radialSegments: 12,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"tree-1-trunk": {
|
||||
type: "Cylinder",
|
||||
props: {
|
||||
position: [1, 1, 0.5],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
castShadow: true,
|
||||
receiveShadow: false,
|
||||
material: {
|
||||
color: "#8B5A2B",
|
||||
metalness: 0,
|
||||
roughness: 1,
|
||||
emissive: null,
|
||||
emissiveIntensity: null,
|
||||
opacity: null,
|
||||
transparent: null,
|
||||
wireframe: null,
|
||||
},
|
||||
radiusTop: 0.06,
|
||||
radiusBottom: 0.1,
|
||||
height: 1.5,
|
||||
radialSegments: 8,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"tree-1-canopy": {
|
||||
type: "Sphere",
|
||||
props: {
|
||||
position: [1, 2.2, 0.5],
|
||||
rotation: null,
|
||||
scale: [1, 1.2, 1],
|
||||
castShadow: true,
|
||||
receiveShadow: false,
|
||||
material: {
|
||||
color: "#2d7a2d",
|
||||
metalness: 0,
|
||||
roughness: 0.95,
|
||||
emissive: null,
|
||||
emissiveIntensity: null,
|
||||
opacity: null,
|
||||
transparent: null,
|
||||
wireframe: null,
|
||||
},
|
||||
radius: 0.6,
|
||||
widthSegments: 16,
|
||||
heightSegments: 12,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"tree-2-trunk": {
|
||||
type: "Cylinder",
|
||||
props: {
|
||||
position: [-1.2, 0.8, -0.8],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
castShadow: true,
|
||||
receiveShadow: false,
|
||||
material: {
|
||||
color: "#8B5A2B",
|
||||
metalness: 0,
|
||||
roughness: 1,
|
||||
emissive: null,
|
||||
emissiveIntensity: null,
|
||||
opacity: null,
|
||||
transparent: null,
|
||||
wireframe: null,
|
||||
},
|
||||
radiusTop: 0.05,
|
||||
radiusBottom: 0.08,
|
||||
height: 1.1,
|
||||
radialSegments: 8,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"tree-2-canopy": {
|
||||
type: "Sphere",
|
||||
props: {
|
||||
position: [-1.2, 1.7, -0.8],
|
||||
rotation: null,
|
||||
scale: [1, 1.3, 1],
|
||||
castShadow: true,
|
||||
receiveShadow: false,
|
||||
material: {
|
||||
color: "#3a8c3a",
|
||||
metalness: 0,
|
||||
roughness: 0.95,
|
||||
emissive: null,
|
||||
emissiveIntensity: null,
|
||||
opacity: null,
|
||||
transparent: null,
|
||||
wireframe: null,
|
||||
},
|
||||
radius: 0.45,
|
||||
widthSegments: 16,
|
||||
heightSegments: 12,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"tree-3-trunk": {
|
||||
type: "Cylinder",
|
||||
props: {
|
||||
position: [-0.3, 1.2, 1.5],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
castShadow: true,
|
||||
receiveShadow: false,
|
||||
material: {
|
||||
color: "#8B5A2B",
|
||||
metalness: 0,
|
||||
roughness: 1,
|
||||
emissive: null,
|
||||
emissiveIntensity: null,
|
||||
opacity: null,
|
||||
transparent: null,
|
||||
wireframe: null,
|
||||
},
|
||||
radiusTop: 0.07,
|
||||
radiusBottom: 0.12,
|
||||
height: 2,
|
||||
radialSegments: 8,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"tree-3-canopy": {
|
||||
type: "Sphere",
|
||||
props: {
|
||||
position: [-0.3, 2.7, 1.5],
|
||||
rotation: null,
|
||||
scale: [1.2, 1.4, 1.2],
|
||||
castShadow: true,
|
||||
receiveShadow: false,
|
||||
material: {
|
||||
color: "#1a6b1a",
|
||||
metalness: 0,
|
||||
roughness: 0.95,
|
||||
emissive: null,
|
||||
emissiveIntensity: null,
|
||||
opacity: null,
|
||||
transparent: null,
|
||||
wireframe: null,
|
||||
},
|
||||
radius: 0.7,
|
||||
widthSegments: 16,
|
||||
heightSegments: 12,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"house-base": {
|
||||
type: "RoundedBox",
|
||||
props: {
|
||||
position: [0, 0.9, -0.5],
|
||||
rotation: [0, 0.3, 0],
|
||||
scale: null,
|
||||
castShadow: true,
|
||||
receiveShadow: true,
|
||||
material: {
|
||||
color: "#e8d8c0",
|
||||
metalness: 0,
|
||||
roughness: 0.8,
|
||||
emissive: "#ffddaa",
|
||||
emissiveIntensity: 0.05,
|
||||
opacity: null,
|
||||
transparent: null,
|
||||
wireframe: null,
|
||||
},
|
||||
width: 0.8,
|
||||
height: 0.7,
|
||||
depth: 0.7,
|
||||
radius: 0.05,
|
||||
smoothness: 4,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"house-roof": {
|
||||
type: "Cone",
|
||||
props: {
|
||||
position: [0, 1.55, -0.5],
|
||||
rotation: [0, 1.085, 0],
|
||||
scale: null,
|
||||
castShadow: true,
|
||||
receiveShadow: false,
|
||||
material: {
|
||||
color: "#a04030",
|
||||
metalness: 0.1,
|
||||
roughness: 0.8,
|
||||
emissive: null,
|
||||
emissiveIntensity: null,
|
||||
opacity: null,
|
||||
transparent: null,
|
||||
wireframe: null,
|
||||
},
|
||||
radius: 0.6,
|
||||
height: 0.5,
|
||||
radialSegments: 4,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
|
||||
"island-left-float": {
|
||||
type: "Float",
|
||||
props: {
|
||||
position: [-6, 0.1, 2],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
speed: 0.6,
|
||||
rotationIntensity: 0.03,
|
||||
floatIntensity: 0.5,
|
||||
enabled: true,
|
||||
},
|
||||
children: ["island-left"],
|
||||
},
|
||||
"island-left": {
|
||||
type: "Group",
|
||||
props: { position: null, rotation: null, scale: null },
|
||||
children: [
|
||||
"left-base",
|
||||
"left-top",
|
||||
"left-under",
|
||||
"left-tree-t",
|
||||
"left-tree-c",
|
||||
],
|
||||
},
|
||||
"left-base": {
|
||||
type: "Cylinder",
|
||||
props: {
|
||||
position: [0, -0.3, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
castShadow: true,
|
||||
receiveShadow: true,
|
||||
material: {
|
||||
color: "#8B6914",
|
||||
metalness: 0.1,
|
||||
roughness: 0.9,
|
||||
emissive: null,
|
||||
emissiveIntensity: null,
|
||||
opacity: null,
|
||||
transparent: null,
|
||||
wireframe: null,
|
||||
},
|
||||
radiusTop: 1.8,
|
||||
radiusBottom: 0.8,
|
||||
height: 1.2,
|
||||
radialSegments: 12,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"left-top": {
|
||||
type: "Cylinder",
|
||||
props: {
|
||||
position: [0, 0.25, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
castShadow: true,
|
||||
receiveShadow: true,
|
||||
material: {
|
||||
color: "#4a8c3f",
|
||||
metalness: 0,
|
||||
roughness: 0.95,
|
||||
emissive: null,
|
||||
emissiveIntensity: null,
|
||||
opacity: null,
|
||||
transparent: null,
|
||||
wireframe: null,
|
||||
},
|
||||
radiusTop: 1.85,
|
||||
radiusBottom: 1.8,
|
||||
height: 0.2,
|
||||
radialSegments: 12,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"left-under": {
|
||||
type: "Cone",
|
||||
props: {
|
||||
position: [0, -1.5, 0],
|
||||
rotation: [PI, 0, 0],
|
||||
scale: null,
|
||||
castShadow: true,
|
||||
receiveShadow: false,
|
||||
material: {
|
||||
color: "#6B4513",
|
||||
metalness: 0.1,
|
||||
roughness: 0.95,
|
||||
emissive: null,
|
||||
emissiveIntensity: null,
|
||||
opacity: null,
|
||||
transparent: null,
|
||||
wireframe: null,
|
||||
},
|
||||
radius: 0.8,
|
||||
height: 1.5,
|
||||
radialSegments: 10,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"left-tree-t": {
|
||||
type: "Cylinder",
|
||||
props: {
|
||||
position: [0.3, 0.8, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
castShadow: true,
|
||||
receiveShadow: false,
|
||||
material: {
|
||||
color: "#8B5A2B",
|
||||
metalness: 0,
|
||||
roughness: 1,
|
||||
emissive: null,
|
||||
emissiveIntensity: null,
|
||||
opacity: null,
|
||||
transparent: null,
|
||||
wireframe: null,
|
||||
},
|
||||
radiusTop: 0.04,
|
||||
radiusBottom: 0.07,
|
||||
height: 1,
|
||||
radialSegments: 8,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"left-tree-c": {
|
||||
type: "Sphere",
|
||||
props: {
|
||||
position: [0.3, 1.6, 0],
|
||||
rotation: null,
|
||||
scale: [1, 1.2, 1],
|
||||
castShadow: true,
|
||||
receiveShadow: false,
|
||||
material: {
|
||||
color: "#2d7a2d",
|
||||
metalness: 0,
|
||||
roughness: 0.95,
|
||||
emissive: null,
|
||||
emissiveIntensity: null,
|
||||
opacity: null,
|
||||
transparent: null,
|
||||
wireframe: null,
|
||||
},
|
||||
radius: 0.4,
|
||||
widthSegments: 12,
|
||||
heightSegments: 8,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
|
||||
"island-right-float": {
|
||||
type: "Float",
|
||||
props: {
|
||||
position: [5.5, 0, -3],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
speed: 0.5,
|
||||
rotationIntensity: 0.02,
|
||||
floatIntensity: 0.4,
|
||||
enabled: true,
|
||||
},
|
||||
children: ["island-right"],
|
||||
},
|
||||
"island-right": {
|
||||
type: "Group",
|
||||
props: { position: null, rotation: null, scale: null },
|
||||
children: [
|
||||
"right-base",
|
||||
"right-top",
|
||||
"right-under",
|
||||
"right-tree-t",
|
||||
"right-tree-c",
|
||||
],
|
||||
},
|
||||
"right-base": {
|
||||
type: "Cylinder",
|
||||
props: {
|
||||
position: [0, -0.3, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
castShadow: true,
|
||||
receiveShadow: true,
|
||||
material: {
|
||||
color: "#8B6914",
|
||||
metalness: 0.1,
|
||||
roughness: 0.9,
|
||||
emissive: null,
|
||||
emissiveIntensity: null,
|
||||
opacity: null,
|
||||
transparent: null,
|
||||
wireframe: null,
|
||||
},
|
||||
radiusTop: 2,
|
||||
radiusBottom: 1,
|
||||
height: 1.5,
|
||||
radialSegments: 12,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"right-top": {
|
||||
type: "Cylinder",
|
||||
props: {
|
||||
position: [0, 0.4, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
castShadow: true,
|
||||
receiveShadow: true,
|
||||
material: {
|
||||
color: "#4a8c3f",
|
||||
metalness: 0,
|
||||
roughness: 0.95,
|
||||
emissive: null,
|
||||
emissiveIntensity: null,
|
||||
opacity: null,
|
||||
transparent: null,
|
||||
wireframe: null,
|
||||
},
|
||||
radiusTop: 2.05,
|
||||
radiusBottom: 2,
|
||||
height: 0.2,
|
||||
radialSegments: 12,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"right-under": {
|
||||
type: "Cone",
|
||||
props: {
|
||||
position: [0, -2, 0],
|
||||
rotation: [PI, 0, 0],
|
||||
scale: null,
|
||||
castShadow: true,
|
||||
receiveShadow: false,
|
||||
material: {
|
||||
color: "#6B4513",
|
||||
metalness: 0.1,
|
||||
roughness: 0.95,
|
||||
emissive: null,
|
||||
emissiveIntensity: null,
|
||||
opacity: null,
|
||||
transparent: null,
|
||||
wireframe: null,
|
||||
},
|
||||
radius: 1,
|
||||
height: 2,
|
||||
radialSegments: 10,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"right-tree-t": {
|
||||
type: "Cylinder",
|
||||
props: {
|
||||
position: [-0.5, 0.9, 0.3],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
castShadow: true,
|
||||
receiveShadow: false,
|
||||
material: {
|
||||
color: "#8B5A2B",
|
||||
metalness: 0,
|
||||
roughness: 1,
|
||||
emissive: null,
|
||||
emissiveIntensity: null,
|
||||
opacity: null,
|
||||
transparent: null,
|
||||
wireframe: null,
|
||||
},
|
||||
radiusTop: 0.05,
|
||||
radiusBottom: 0.09,
|
||||
height: 1.3,
|
||||
radialSegments: 8,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"right-tree-c": {
|
||||
type: "Sphere",
|
||||
props: {
|
||||
position: [-0.5, 2, 0.3],
|
||||
rotation: null,
|
||||
scale: [1, 1.3, 1],
|
||||
castShadow: true,
|
||||
receiveShadow: false,
|
||||
material: {
|
||||
color: "#3a8c3a",
|
||||
metalness: 0,
|
||||
roughness: 0.95,
|
||||
emissive: null,
|
||||
emissiveIntensity: null,
|
||||
opacity: null,
|
||||
transparent: null,
|
||||
wireframe: null,
|
||||
},
|
||||
radius: 0.5,
|
||||
widthSegments: 12,
|
||||
heightSegments: 8,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
|
||||
"island-far-float": {
|
||||
type: "Float",
|
||||
props: {
|
||||
position: [2, 3, -10],
|
||||
rotation: null,
|
||||
scale: [0.7, 0.7, 0.7],
|
||||
speed: 0.3,
|
||||
rotationIntensity: 0.01,
|
||||
floatIntensity: 0.3,
|
||||
enabled: true,
|
||||
},
|
||||
children: ["far-base", "far-top", "far-under"],
|
||||
},
|
||||
"far-base": {
|
||||
type: "Cylinder",
|
||||
props: {
|
||||
position: [0, 0, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
castShadow: true,
|
||||
receiveShadow: true,
|
||||
material: {
|
||||
color: "#8B6914",
|
||||
metalness: 0.1,
|
||||
roughness: 0.9,
|
||||
emissive: null,
|
||||
emissiveIntensity: null,
|
||||
opacity: null,
|
||||
transparent: null,
|
||||
wireframe: null,
|
||||
},
|
||||
radiusTop: 2.5,
|
||||
radiusBottom: 1.2,
|
||||
height: 1.5,
|
||||
radialSegments: 12,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"far-top": {
|
||||
type: "Cylinder",
|
||||
props: {
|
||||
position: [0, 0.65, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
castShadow: true,
|
||||
receiveShadow: true,
|
||||
material: {
|
||||
color: "#3a7a3a",
|
||||
metalness: 0,
|
||||
roughness: 0.95,
|
||||
emissive: null,
|
||||
emissiveIntensity: null,
|
||||
opacity: null,
|
||||
transparent: null,
|
||||
wireframe: null,
|
||||
},
|
||||
radiusTop: 2.55,
|
||||
radiusBottom: 2.5,
|
||||
height: 0.2,
|
||||
radialSegments: 12,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"far-under": {
|
||||
type: "Cone",
|
||||
props: {
|
||||
position: [0, -1.8, 0],
|
||||
rotation: [PI, 0, 0],
|
||||
scale: null,
|
||||
castShadow: true,
|
||||
receiveShadow: false,
|
||||
material: {
|
||||
color: "#6B4513",
|
||||
metalness: 0.1,
|
||||
roughness: 0.95,
|
||||
emissive: null,
|
||||
emissiveIntensity: null,
|
||||
opacity: null,
|
||||
transparent: null,
|
||||
wireframe: null,
|
||||
},
|
||||
radius: 1.2,
|
||||
height: 2,
|
||||
radialSegments: 10,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
|
||||
"island-tiny1-float": {
|
||||
type: "Float",
|
||||
props: {
|
||||
position: [-3, 2.5, -5],
|
||||
rotation: null,
|
||||
scale: [0.4, 0.4, 0.4],
|
||||
speed: 0.8,
|
||||
rotationIntensity: 0.05,
|
||||
floatIntensity: 0.8,
|
||||
enabled: true,
|
||||
},
|
||||
children: ["tiny1-rock"],
|
||||
},
|
||||
"tiny1-rock": {
|
||||
type: "Sphere",
|
||||
props: {
|
||||
position: null,
|
||||
rotation: null,
|
||||
scale: [1, 0.6, 1],
|
||||
castShadow: true,
|
||||
receiveShadow: false,
|
||||
material: {
|
||||
color: "#8B6914",
|
||||
metalness: 0.1,
|
||||
roughness: 0.9,
|
||||
emissive: null,
|
||||
emissiveIntensity: null,
|
||||
opacity: null,
|
||||
transparent: null,
|
||||
wireframe: null,
|
||||
},
|
||||
radius: 1,
|
||||
widthSegments: 12,
|
||||
heightSegments: 8,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"island-tiny2-float": {
|
||||
type: "Float",
|
||||
props: {
|
||||
position: [8, -0.5, 1],
|
||||
rotation: null,
|
||||
scale: [0.3, 0.3, 0.3],
|
||||
speed: 1,
|
||||
rotationIntensity: 0.08,
|
||||
floatIntensity: 1,
|
||||
enabled: true,
|
||||
},
|
||||
children: ["tiny2-rock"],
|
||||
},
|
||||
"tiny2-rock": {
|
||||
type: "Sphere",
|
||||
props: {
|
||||
position: null,
|
||||
rotation: null,
|
||||
scale: [1.2, 0.5, 0.8],
|
||||
castShadow: true,
|
||||
receiveShadow: false,
|
||||
material: {
|
||||
color: "#7a5a10",
|
||||
metalness: 0.1,
|
||||
roughness: 0.9,
|
||||
emissive: null,
|
||||
emissiveIntensity: null,
|
||||
opacity: null,
|
||||
transparent: null,
|
||||
wireframe: null,
|
||||
},
|
||||
radius: 1,
|
||||
widthSegments: 12,
|
||||
heightSegments: 8,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
|
||||
"bridge-1": {
|
||||
type: "RoundedBox",
|
||||
props: {
|
||||
position: [-3.57, -0.1, 1.19],
|
||||
rotation: [0, 0.32, 0],
|
||||
scale: null,
|
||||
castShadow: true,
|
||||
receiveShadow: true,
|
||||
material: {
|
||||
color: "#8B6914",
|
||||
metalness: 0.1,
|
||||
roughness: 0.9,
|
||||
emissive: null,
|
||||
emissiveIntensity: null,
|
||||
opacity: null,
|
||||
transparent: null,
|
||||
wireframe: null,
|
||||
},
|
||||
width: 3.5,
|
||||
height: 0.5,
|
||||
depth: 0.6,
|
||||
radius: 0.05,
|
||||
smoothness: 2,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"bridge-2": {
|
||||
type: "RoundedBox",
|
||||
props: {
|
||||
position: [3.19, -0.1, -1.74],
|
||||
rotation: [0, 0.5, 0],
|
||||
scale: null,
|
||||
castShadow: true,
|
||||
receiveShadow: true,
|
||||
material: {
|
||||
color: "#8B6914",
|
||||
metalness: 0.1,
|
||||
roughness: 0.9,
|
||||
emissive: null,
|
||||
emissiveIntensity: null,
|
||||
opacity: null,
|
||||
transparent: null,
|
||||
wireframe: null,
|
||||
},
|
||||
width: 3.0,
|
||||
height: 0.5,
|
||||
depth: 0.6,
|
||||
radius: 0.05,
|
||||
smoothness: 2,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
|
||||
sparkles: {
|
||||
type: "Sparkles",
|
||||
props: {
|
||||
position: [0, 2, 0],
|
||||
rotation: null,
|
||||
scale: [20, 8, 20],
|
||||
count: 120,
|
||||
speed: 0.1,
|
||||
opacity: 0.5,
|
||||
color: "#ffeeaa",
|
||||
size: 1.5,
|
||||
noise: 1,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
controls: {
|
||||
type: "OrbitControls",
|
||||
props: {
|
||||
enableDamping: true,
|
||||
dampingFactor: null,
|
||||
enableZoom: true,
|
||||
enablePan: null,
|
||||
enableRotate: true,
|
||||
minDistance: 8,
|
||||
maxDistance: 30,
|
||||
minPolarAngle: 0.2,
|
||||
maxPolarAngle: 1.5,
|
||||
autoRotate: true,
|
||||
autoRotateSpeed: 0.3,
|
||||
target: [0, 1, 0],
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,164 @@
|
||||
import type { Scene } from "./_helpers";
|
||||
|
||||
export const hyperspaceTunnel: Scene = {
|
||||
name: "Hyperspace Tunnel",
|
||||
description:
|
||||
"Neon warp tunnel at breakneck speed -- bloom, camera shake, particles streaming past",
|
||||
spec: {
|
||||
root: "scene",
|
||||
elements: {
|
||||
scene: {
|
||||
type: "Group",
|
||||
props: { position: null, rotation: null, scale: null },
|
||||
children: [
|
||||
"cam",
|
||||
"controls",
|
||||
"tunnel",
|
||||
"stars",
|
||||
"sparkles-core",
|
||||
"sparkles-wide",
|
||||
"ambient",
|
||||
"center-light",
|
||||
"shake",
|
||||
"post",
|
||||
"fog",
|
||||
],
|
||||
},
|
||||
cam: {
|
||||
type: "PerspectiveCamera",
|
||||
props: {
|
||||
position: [0, 0, 2],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
fov: 80,
|
||||
near: 0.1,
|
||||
far: 200,
|
||||
makeDefault: true,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
controls: {
|
||||
type: "OrbitControls",
|
||||
props: {
|
||||
enableRotate: false,
|
||||
enableZoom: false,
|
||||
enablePan: false,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
tunnel: {
|
||||
type: "WarpTunnel",
|
||||
props: {
|
||||
position: null,
|
||||
rotation: null,
|
||||
scale: null,
|
||||
ringCount: 100,
|
||||
radius: 3,
|
||||
length: 50,
|
||||
speed: 12,
|
||||
tubeRadius: 0.025,
|
||||
color1: "#00ffff",
|
||||
color2: "#ff00ff",
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
stars: {
|
||||
type: "Stars",
|
||||
props: {
|
||||
radius: 80,
|
||||
depth: 60,
|
||||
count: 6000,
|
||||
factor: 5,
|
||||
saturation: 0.4,
|
||||
fade: true,
|
||||
speed: 3,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"sparkles-core": {
|
||||
type: "Sparkles",
|
||||
props: {
|
||||
position: [0, 0, -10],
|
||||
rotation: null,
|
||||
scale: [4, 4, 30],
|
||||
count: 300,
|
||||
speed: 3,
|
||||
opacity: 1,
|
||||
color: "#00ffff",
|
||||
size: 2,
|
||||
noise: 0.5,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"sparkles-wide": {
|
||||
type: "Sparkles",
|
||||
props: {
|
||||
position: [0, 0, -15],
|
||||
rotation: null,
|
||||
scale: [6, 6, 40],
|
||||
count: 200,
|
||||
speed: 4,
|
||||
opacity: 0.7,
|
||||
color: "#ff44ff",
|
||||
size: 1.5,
|
||||
noise: 1,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
ambient: {
|
||||
type: "AmbientLight",
|
||||
props: { color: "#110033", intensity: 0.2 },
|
||||
children: [],
|
||||
},
|
||||
"center-light": {
|
||||
type: "PointLight",
|
||||
props: {
|
||||
position: [0, 0, -5],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
color: "#00ccff",
|
||||
intensity: 30,
|
||||
distance: 30,
|
||||
decay: 2,
|
||||
castShadow: false,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
shake: {
|
||||
type: "CameraShake",
|
||||
props: {
|
||||
intensity: 0.3,
|
||||
maxYaw: 0.03,
|
||||
maxPitch: 0.03,
|
||||
maxRoll: 0.02,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
post: {
|
||||
type: "EffectComposer",
|
||||
props: { enabled: true, multisampling: 8 },
|
||||
children: ["bloom", "vignette"],
|
||||
},
|
||||
bloom: {
|
||||
type: "Bloom",
|
||||
props: {
|
||||
intensity: 2.5,
|
||||
luminanceThreshold: 0.1,
|
||||
luminanceSmoothing: null,
|
||||
mipmapBlur: true,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
vignette: {
|
||||
type: "Vignette",
|
||||
props: { offset: 0.3, darkness: 0.9 },
|
||||
children: [],
|
||||
},
|
||||
fog: {
|
||||
type: "Fog",
|
||||
props: { color: "#030008", near: 5, far: 50 },
|
||||
children: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
import type { Scene } from "./_helpers";
|
||||
import { hyperspaceTunnel } from "./hyperspace-tunnel";
|
||||
import { orbitalChaos } from "./orbital-chaos";
|
||||
import { clockworkOrrery } from "./clockwork-orrery";
|
||||
import { deepSeaAbyss } from "./deep-sea-abyss";
|
||||
import { stormCell } from "./storm-cell";
|
||||
import { perpetualMotion } from "./perpetual-motion";
|
||||
import { floatingIslands } from "./floating-islands";
|
||||
import { productShowroom } from "./product-showroom";
|
||||
import { portalGallery } from "./portal-gallery";
|
||||
import { pipes } from "./pipes";
|
||||
import { mystify } from "./mystify";
|
||||
import { starfield } from "./starfield";
|
||||
export type { Scene };
|
||||
|
||||
export const scenes: Scene[] = [
|
||||
hyperspaceTunnel,
|
||||
orbitalChaos,
|
||||
clockworkOrrery,
|
||||
deepSeaAbyss,
|
||||
stormCell,
|
||||
perpetualMotion,
|
||||
floatingIslands,
|
||||
productShowroom,
|
||||
portalGallery,
|
||||
pipes,
|
||||
mystify,
|
||||
starfield,
|
||||
];
|
||||
@@ -0,0 +1,400 @@
|
||||
import type { Scene } from "./_helpers";
|
||||
|
||||
export const mystify: Scene = {
|
||||
name: "Mystify",
|
||||
description:
|
||||
"Neon wireframe torus knots orbiting in the void -- the Windows Mystify screensaver in 3D",
|
||||
spec: {
|
||||
root: "scene",
|
||||
elements: {
|
||||
scene: {
|
||||
type: "Group",
|
||||
props: { position: null, rotation: null, scale: null },
|
||||
children: [
|
||||
"cam",
|
||||
"ambient",
|
||||
"shape1",
|
||||
"shape2",
|
||||
"shape3",
|
||||
"shape4",
|
||||
"shape5",
|
||||
"shape6",
|
||||
"shape7",
|
||||
"post",
|
||||
"controls",
|
||||
],
|
||||
},
|
||||
cam: {
|
||||
type: "PerspectiveCamera",
|
||||
props: {
|
||||
position: [0, 0, 10],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
fov: 55,
|
||||
near: 0.1,
|
||||
far: 1000,
|
||||
makeDefault: true,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
ambient: {
|
||||
type: "AmbientLight",
|
||||
props: { color: "#111111", intensity: 0.1 },
|
||||
children: [],
|
||||
},
|
||||
shape1: {
|
||||
type: "Orbit",
|
||||
props: {
|
||||
position: null,
|
||||
rotation: null,
|
||||
scale: null,
|
||||
speed: 0.5,
|
||||
radius: 3,
|
||||
tilt: 0.3,
|
||||
},
|
||||
children: ["s1-spin"],
|
||||
},
|
||||
"s1-spin": {
|
||||
type: "Spin",
|
||||
props: {
|
||||
position: null,
|
||||
rotation: null,
|
||||
scale: null,
|
||||
speed: 1.5,
|
||||
axis: "x",
|
||||
},
|
||||
children: ["s1-body"],
|
||||
},
|
||||
"s1-body": {
|
||||
type: "TorusKnot",
|
||||
props: {
|
||||
position: null,
|
||||
rotation: null,
|
||||
scale: null,
|
||||
castShadow: false,
|
||||
receiveShadow: false,
|
||||
radius: 0.4,
|
||||
tube: 0.02,
|
||||
p: 2,
|
||||
q: 3,
|
||||
tubularSegments: 128,
|
||||
radialSegments: 4,
|
||||
material: {
|
||||
color: "#00ffff",
|
||||
emissive: "#00ffff",
|
||||
emissiveIntensity: 2,
|
||||
wireframe: true,
|
||||
},
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
shape2: {
|
||||
type: "Orbit",
|
||||
props: {
|
||||
position: null,
|
||||
rotation: null,
|
||||
scale: null,
|
||||
speed: -0.4,
|
||||
radius: 4,
|
||||
tilt: -0.5,
|
||||
},
|
||||
children: ["s2-spin"],
|
||||
},
|
||||
"s2-spin": {
|
||||
type: "Spin",
|
||||
props: {
|
||||
position: null,
|
||||
rotation: null,
|
||||
scale: null,
|
||||
speed: -1,
|
||||
axis: "z",
|
||||
},
|
||||
children: ["s2-body"],
|
||||
},
|
||||
"s2-body": {
|
||||
type: "Torus",
|
||||
props: {
|
||||
position: null,
|
||||
rotation: null,
|
||||
scale: null,
|
||||
castShadow: false,
|
||||
receiveShadow: false,
|
||||
radius: 0.6,
|
||||
tube: 0.015,
|
||||
radialSegments: 4,
|
||||
tubularSegments: 128,
|
||||
material: {
|
||||
color: "#ff00ff",
|
||||
emissive: "#ff00ff",
|
||||
emissiveIntensity: 2,
|
||||
wireframe: true,
|
||||
},
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
shape3: {
|
||||
type: "Orbit",
|
||||
props: {
|
||||
position: null,
|
||||
rotation: null,
|
||||
scale: null,
|
||||
speed: 0.7,
|
||||
radius: 2.5,
|
||||
tilt: 0.8,
|
||||
},
|
||||
children: ["s3-spin"],
|
||||
},
|
||||
"s3-spin": {
|
||||
type: "Spin",
|
||||
props: {
|
||||
position: null,
|
||||
rotation: null,
|
||||
scale: null,
|
||||
speed: 2,
|
||||
axis: "y",
|
||||
},
|
||||
children: ["s3-body"],
|
||||
},
|
||||
"s3-body": {
|
||||
type: "TorusKnot",
|
||||
props: {
|
||||
position: null,
|
||||
rotation: null,
|
||||
scale: null,
|
||||
castShadow: false,
|
||||
receiveShadow: false,
|
||||
radius: 0.35,
|
||||
tube: 0.015,
|
||||
p: 3,
|
||||
q: 2,
|
||||
tubularSegments: 128,
|
||||
radialSegments: 4,
|
||||
material: {
|
||||
color: "#ffff00",
|
||||
emissive: "#ffff00",
|
||||
emissiveIntensity: 2,
|
||||
wireframe: true,
|
||||
},
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
shape4: {
|
||||
type: "Orbit",
|
||||
props: {
|
||||
position: null,
|
||||
rotation: null,
|
||||
scale: null,
|
||||
speed: -0.6,
|
||||
radius: 3.5,
|
||||
tilt: -0.2,
|
||||
},
|
||||
children: ["s4-spin"],
|
||||
},
|
||||
"s4-spin": {
|
||||
type: "Spin",
|
||||
props: {
|
||||
position: null,
|
||||
rotation: null,
|
||||
scale: null,
|
||||
speed: 1.2,
|
||||
axis: "x",
|
||||
},
|
||||
children: ["s4-body"],
|
||||
},
|
||||
"s4-body": {
|
||||
type: "Torus",
|
||||
props: {
|
||||
position: null,
|
||||
rotation: null,
|
||||
scale: null,
|
||||
castShadow: false,
|
||||
receiveShadow: false,
|
||||
radius: 0.5,
|
||||
tube: 0.012,
|
||||
radialSegments: 4,
|
||||
tubularSegments: 128,
|
||||
material: {
|
||||
color: "#00ff44",
|
||||
emissive: "#00ff44",
|
||||
emissiveIntensity: 2,
|
||||
wireframe: true,
|
||||
},
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
shape5: {
|
||||
type: "Orbit",
|
||||
props: {
|
||||
position: null,
|
||||
rotation: null,
|
||||
scale: null,
|
||||
speed: 0.3,
|
||||
radius: 5,
|
||||
tilt: 0.6,
|
||||
},
|
||||
children: ["s5-spin"],
|
||||
},
|
||||
"s5-spin": {
|
||||
type: "Spin",
|
||||
props: {
|
||||
position: null,
|
||||
rotation: null,
|
||||
scale: null,
|
||||
speed: -1.8,
|
||||
axis: "z",
|
||||
},
|
||||
children: ["s5-body"],
|
||||
},
|
||||
"s5-body": {
|
||||
type: "TorusKnot",
|
||||
props: {
|
||||
position: null,
|
||||
rotation: null,
|
||||
scale: null,
|
||||
castShadow: false,
|
||||
receiveShadow: false,
|
||||
radius: 0.5,
|
||||
tube: 0.018,
|
||||
p: 2,
|
||||
q: 5,
|
||||
tubularSegments: 128,
|
||||
radialSegments: 4,
|
||||
material: {
|
||||
color: "#ff4400",
|
||||
emissive: "#ff4400",
|
||||
emissiveIntensity: 2,
|
||||
wireframe: true,
|
||||
},
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
shape6: {
|
||||
type: "Orbit",
|
||||
props: {
|
||||
position: null,
|
||||
rotation: null,
|
||||
scale: null,
|
||||
speed: -0.8,
|
||||
radius: 2,
|
||||
tilt: -0.7,
|
||||
},
|
||||
children: ["s6-spin"],
|
||||
},
|
||||
"s6-spin": {
|
||||
type: "Spin",
|
||||
props: {
|
||||
position: null,
|
||||
rotation: null,
|
||||
scale: null,
|
||||
speed: 2.5,
|
||||
axis: "y",
|
||||
},
|
||||
children: ["s6-body"],
|
||||
},
|
||||
"s6-body": {
|
||||
type: "Torus",
|
||||
props: {
|
||||
position: null,
|
||||
rotation: null,
|
||||
scale: null,
|
||||
castShadow: false,
|
||||
receiveShadow: false,
|
||||
radius: 0.3,
|
||||
tube: 0.01,
|
||||
radialSegments: 4,
|
||||
tubularSegments: 128,
|
||||
material: {
|
||||
color: "#4488ff",
|
||||
emissive: "#4488ff",
|
||||
emissiveIntensity: 2,
|
||||
wireframe: true,
|
||||
},
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
shape7: {
|
||||
type: "Orbit",
|
||||
props: {
|
||||
position: null,
|
||||
rotation: null,
|
||||
scale: null,
|
||||
speed: 0.45,
|
||||
radius: 4.5,
|
||||
tilt: 1,
|
||||
},
|
||||
children: ["s7-spin"],
|
||||
},
|
||||
"s7-spin": {
|
||||
type: "Spin",
|
||||
props: {
|
||||
position: null,
|
||||
rotation: null,
|
||||
scale: null,
|
||||
speed: -0.8,
|
||||
axis: "x",
|
||||
},
|
||||
children: ["s7-body"],
|
||||
},
|
||||
"s7-body": {
|
||||
type: "TorusKnot",
|
||||
props: {
|
||||
position: null,
|
||||
rotation: null,
|
||||
scale: null,
|
||||
castShadow: false,
|
||||
receiveShadow: false,
|
||||
radius: 0.45,
|
||||
tube: 0.015,
|
||||
p: 3,
|
||||
q: 4,
|
||||
tubularSegments: 128,
|
||||
radialSegments: 4,
|
||||
material: {
|
||||
color: "#ff88ff",
|
||||
emissive: "#ff88ff",
|
||||
emissiveIntensity: 2,
|
||||
wireframe: true,
|
||||
},
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
post: {
|
||||
type: "EffectComposer",
|
||||
props: { enabled: true, multisampling: 8 },
|
||||
children: ["bloom", "vignette"],
|
||||
},
|
||||
bloom: {
|
||||
type: "Bloom",
|
||||
props: {
|
||||
intensity: 3,
|
||||
luminanceThreshold: 0.05,
|
||||
luminanceSmoothing: 0.3,
|
||||
mipmapBlur: true,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
vignette: {
|
||||
type: "Vignette",
|
||||
props: { offset: 0.2, darkness: 0.8 },
|
||||
children: [],
|
||||
},
|
||||
controls: {
|
||||
type: "OrbitControls",
|
||||
props: {
|
||||
enableDamping: true,
|
||||
dampingFactor: null,
|
||||
enableZoom: true,
|
||||
enablePan: true,
|
||||
enableRotate: true,
|
||||
minDistance: 4,
|
||||
maxDistance: 18,
|
||||
minPolarAngle: null,
|
||||
maxPolarAngle: null,
|
||||
autoRotate: true,
|
||||
autoRotateSpeed: 0.3,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,554 @@
|
||||
import { PI } from "./_helpers";
|
||||
import type { Scene } from "./_helpers";
|
||||
|
||||
export const orbitalChaos: Scene = {
|
||||
name: "Orbital Chaos",
|
||||
description:
|
||||
"Everything in motion -- orbiting glass, spinning knots, morphing core, all with bloom",
|
||||
spec: {
|
||||
root: "scene",
|
||||
elements: {
|
||||
scene: {
|
||||
type: "Group",
|
||||
props: { position: null, rotation: null, scale: null },
|
||||
children: [
|
||||
"cam",
|
||||
"env",
|
||||
"stars",
|
||||
"ambient",
|
||||
"core-light",
|
||||
"accent-1",
|
||||
"accent-2",
|
||||
"core-pulse",
|
||||
"orbit-1",
|
||||
"orbit-2",
|
||||
"orbit-3",
|
||||
"orbit-4",
|
||||
"orbit-5",
|
||||
"orbit-6",
|
||||
"ring-spin-fast",
|
||||
"ring-spin-slow",
|
||||
"sparkles",
|
||||
"post",
|
||||
"fog",
|
||||
"controls",
|
||||
],
|
||||
},
|
||||
cam: {
|
||||
type: "PerspectiveCamera",
|
||||
props: {
|
||||
position: [0, 2, 8],
|
||||
fov: 50,
|
||||
makeDefault: true,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
env: {
|
||||
type: "Environment",
|
||||
props: { preset: "night", background: false, blur: 1, intensity: 0.15 },
|
||||
children: [],
|
||||
},
|
||||
stars: {
|
||||
type: "Stars",
|
||||
props: {
|
||||
radius: 120,
|
||||
depth: 60,
|
||||
count: 8000,
|
||||
factor: 5,
|
||||
saturation: 0.2,
|
||||
fade: true,
|
||||
speed: 0.5,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
ambient: {
|
||||
type: "AmbientLight",
|
||||
props: { color: "#110022", intensity: 0.3 },
|
||||
children: [],
|
||||
},
|
||||
"core-light": {
|
||||
type: "PointLight",
|
||||
props: {
|
||||
position: [0, 0, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
color: "#ff44aa",
|
||||
intensity: 80,
|
||||
distance: 25,
|
||||
decay: 2,
|
||||
castShadow: false,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"accent-1": {
|
||||
type: "PointLight",
|
||||
props: {
|
||||
position: [5, 3, 5],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
color: "#00ffff",
|
||||
intensity: 25,
|
||||
distance: 20,
|
||||
decay: 2,
|
||||
castShadow: false,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"accent-2": {
|
||||
type: "PointLight",
|
||||
props: {
|
||||
position: [-5, -2, -5],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
color: "#ff00ff",
|
||||
intensity: 25,
|
||||
distance: 20,
|
||||
decay: 2,
|
||||
castShadow: false,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
|
||||
"core-pulse": {
|
||||
type: "Pulse",
|
||||
props: {
|
||||
position: null,
|
||||
rotation: null,
|
||||
scale: null,
|
||||
speed: 0.8,
|
||||
min: 0.85,
|
||||
max: 1.15,
|
||||
},
|
||||
children: ["core-spin"],
|
||||
},
|
||||
"core-spin": {
|
||||
type: "Spin",
|
||||
props: {
|
||||
position: null,
|
||||
rotation: null,
|
||||
scale: null,
|
||||
speed: 0.3,
|
||||
axis: "y",
|
||||
},
|
||||
children: ["core-blob"],
|
||||
},
|
||||
"core-blob": {
|
||||
type: "DistortSphere",
|
||||
props: {
|
||||
position: null,
|
||||
rotation: null,
|
||||
scale: null,
|
||||
castShadow: false,
|
||||
receiveShadow: false,
|
||||
radius: 1.2,
|
||||
widthSegments: 64,
|
||||
heightSegments: 32,
|
||||
color: "#ff2288",
|
||||
speed: 4,
|
||||
distort: 0.6,
|
||||
metalness: 0.1,
|
||||
roughness: 0.1,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
|
||||
"orbit-1": {
|
||||
type: "Orbit",
|
||||
props: {
|
||||
position: [0, 0, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
speed: 0.8,
|
||||
radius: 4,
|
||||
tilt: 0.5,
|
||||
},
|
||||
children: ["orb-1-spin"],
|
||||
},
|
||||
"orb-1-spin": {
|
||||
type: "Spin",
|
||||
props: {
|
||||
position: null,
|
||||
rotation: null,
|
||||
scale: null,
|
||||
speed: 2,
|
||||
axis: "x",
|
||||
},
|
||||
children: ["orb-1"],
|
||||
},
|
||||
"orb-1": {
|
||||
type: "GlassSphere",
|
||||
props: {
|
||||
position: null,
|
||||
rotation: null,
|
||||
scale: null,
|
||||
castShadow: false,
|
||||
receiveShadow: false,
|
||||
radius: 0.4,
|
||||
widthSegments: 32,
|
||||
heightSegments: 16,
|
||||
color: "#00ffff",
|
||||
transmission: 1,
|
||||
thickness: 0.5,
|
||||
roughness: 0,
|
||||
chromaticAberration: 0.15,
|
||||
ior: 1.8,
|
||||
distortion: 0.1,
|
||||
distortionScale: null,
|
||||
temporalDistortion: null,
|
||||
samples: 6,
|
||||
resolution: 128,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
|
||||
"orbit-2": {
|
||||
type: "Orbit",
|
||||
props: {
|
||||
position: [0, 0.5, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
speed: -0.6,
|
||||
radius: 5.5,
|
||||
tilt: 1,
|
||||
},
|
||||
children: ["orb-2-spin"],
|
||||
},
|
||||
"orb-2-spin": {
|
||||
type: "Spin",
|
||||
props: {
|
||||
position: null,
|
||||
rotation: null,
|
||||
scale: null,
|
||||
speed: 3,
|
||||
axis: "z",
|
||||
},
|
||||
children: ["orb-2"],
|
||||
},
|
||||
"orb-2": {
|
||||
type: "TorusKnot",
|
||||
props: {
|
||||
position: null,
|
||||
rotation: null,
|
||||
scale: null,
|
||||
castShadow: false,
|
||||
receiveShadow: false,
|
||||
material: {
|
||||
color: "#ff00ff",
|
||||
metalness: 0.9,
|
||||
roughness: 0.1,
|
||||
emissive: "#ff00ff",
|
||||
emissiveIntensity: 0.8,
|
||||
opacity: null,
|
||||
transparent: null,
|
||||
wireframe: null,
|
||||
},
|
||||
radius: 0.35,
|
||||
tube: 0.12,
|
||||
tubularSegments: 64,
|
||||
radialSegments: 8,
|
||||
p: 2,
|
||||
q: 3,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
|
||||
"orbit-3": {
|
||||
type: "Orbit",
|
||||
props: {
|
||||
position: [0, -0.3, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
speed: 1.2,
|
||||
radius: 3.2,
|
||||
tilt: 0.3,
|
||||
},
|
||||
children: ["orb-3"],
|
||||
},
|
||||
"orb-3": {
|
||||
type: "GlassSphere",
|
||||
props: {
|
||||
position: null,
|
||||
rotation: null,
|
||||
scale: null,
|
||||
castShadow: false,
|
||||
receiveShadow: false,
|
||||
radius: 0.3,
|
||||
widthSegments: 32,
|
||||
heightSegments: 16,
|
||||
color: "#ff8800",
|
||||
transmission: 1,
|
||||
thickness: 0.4,
|
||||
roughness: 0,
|
||||
chromaticAberration: 0.1,
|
||||
ior: 1.6,
|
||||
distortion: 0,
|
||||
distortionScale: null,
|
||||
temporalDistortion: null,
|
||||
samples: 6,
|
||||
resolution: 128,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
|
||||
"orbit-4": {
|
||||
type: "Orbit",
|
||||
props: {
|
||||
position: [0, 1, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
speed: -1.5,
|
||||
radius: 6.5,
|
||||
tilt: -0.8,
|
||||
},
|
||||
children: ["orb-4-spin"],
|
||||
},
|
||||
"orb-4-spin": {
|
||||
type: "Spin",
|
||||
props: {
|
||||
position: null,
|
||||
rotation: null,
|
||||
scale: null,
|
||||
speed: -4,
|
||||
axis: "y",
|
||||
},
|
||||
children: ["orb-4"],
|
||||
},
|
||||
"orb-4": {
|
||||
type: "Sphere",
|
||||
props: {
|
||||
position: null,
|
||||
rotation: null,
|
||||
scale: null,
|
||||
castShadow: false,
|
||||
receiveShadow: false,
|
||||
material: {
|
||||
color: "#00ff88",
|
||||
metalness: null,
|
||||
roughness: null,
|
||||
emissive: "#00ff88",
|
||||
emissiveIntensity: 0.8,
|
||||
opacity: null,
|
||||
transparent: null,
|
||||
wireframe: true,
|
||||
},
|
||||
radius: 0.35,
|
||||
widthSegments: 16,
|
||||
heightSegments: 12,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
|
||||
"orbit-5": {
|
||||
type: "Orbit",
|
||||
props: {
|
||||
position: [0, -0.5, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
speed: 0.4,
|
||||
radius: 8,
|
||||
tilt: 0.2,
|
||||
},
|
||||
children: ["orb-5-spin"],
|
||||
},
|
||||
"orb-5-spin": {
|
||||
type: "Spin",
|
||||
props: {
|
||||
position: null,
|
||||
rotation: null,
|
||||
scale: null,
|
||||
speed: 1.5,
|
||||
axis: "z",
|
||||
},
|
||||
children: ["orb-5"],
|
||||
},
|
||||
"orb-5": {
|
||||
type: "GlassBox",
|
||||
props: {
|
||||
position: null,
|
||||
rotation: null,
|
||||
scale: null,
|
||||
castShadow: false,
|
||||
receiveShadow: false,
|
||||
width: 0.5,
|
||||
height: 0.5,
|
||||
depth: 0.5,
|
||||
color: "#ff44ff",
|
||||
transmission: 1,
|
||||
thickness: 0.4,
|
||||
roughness: 0,
|
||||
chromaticAberration: 0.08,
|
||||
ior: 1.7,
|
||||
distortion: 0,
|
||||
distortionScale: null,
|
||||
temporalDistortion: null,
|
||||
samples: 6,
|
||||
resolution: 128,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
|
||||
"orbit-6": {
|
||||
type: "Orbit",
|
||||
props: {
|
||||
position: [0, 0.8, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
speed: 2,
|
||||
radius: 2.2,
|
||||
tilt: 1.5,
|
||||
},
|
||||
children: ["orb-6"],
|
||||
},
|
||||
"orb-6": {
|
||||
type: "DistortSphere",
|
||||
props: {
|
||||
position: null,
|
||||
rotation: null,
|
||||
scale: null,
|
||||
castShadow: false,
|
||||
receiveShadow: false,
|
||||
radius: 0.25,
|
||||
widthSegments: 32,
|
||||
heightSegments: 16,
|
||||
color: "#ffcc00",
|
||||
speed: 5,
|
||||
distort: 0.7,
|
||||
metalness: 0.5,
|
||||
roughness: 0.1,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
|
||||
"ring-spin-fast": {
|
||||
type: "Spin",
|
||||
props: {
|
||||
position: null,
|
||||
rotation: [0.3, 0, 0],
|
||||
scale: null,
|
||||
speed: 0.5,
|
||||
axis: "y",
|
||||
},
|
||||
children: ["ring-inner"],
|
||||
},
|
||||
"ring-inner": {
|
||||
type: "Torus",
|
||||
props: {
|
||||
position: null,
|
||||
rotation: [PI / 2, 0, 0],
|
||||
scale: null,
|
||||
castShadow: false,
|
||||
receiveShadow: false,
|
||||
material: {
|
||||
color: "#00ffff",
|
||||
metalness: 0.9,
|
||||
roughness: 0.1,
|
||||
emissive: "#00ccff",
|
||||
emissiveIntensity: 0.5,
|
||||
opacity: 0.3,
|
||||
transparent: true,
|
||||
wireframe: null,
|
||||
},
|
||||
radius: 9,
|
||||
tube: 0.015,
|
||||
radialSegments: null,
|
||||
tubularSegments: 128,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"ring-spin-slow": {
|
||||
type: "Spin",
|
||||
props: {
|
||||
position: null,
|
||||
rotation: [-0.5, 0, 0.3],
|
||||
scale: null,
|
||||
speed: -0.3,
|
||||
axis: "y",
|
||||
},
|
||||
children: ["ring-outer"],
|
||||
},
|
||||
"ring-outer": {
|
||||
type: "Torus",
|
||||
props: {
|
||||
position: null,
|
||||
rotation: [PI / 2, 0, 0],
|
||||
scale: null,
|
||||
castShadow: false,
|
||||
receiveShadow: false,
|
||||
material: {
|
||||
color: "#ff00ff",
|
||||
metalness: 0.9,
|
||||
roughness: 0.1,
|
||||
emissive: "#ff00aa",
|
||||
emissiveIntensity: 0.3,
|
||||
opacity: 0.2,
|
||||
transparent: true,
|
||||
wireframe: null,
|
||||
},
|
||||
radius: 11,
|
||||
tube: 0.012,
|
||||
radialSegments: null,
|
||||
tubularSegments: 128,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
|
||||
sparkles: {
|
||||
type: "Sparkles",
|
||||
props: {
|
||||
position: null,
|
||||
rotation: null,
|
||||
scale: [18, 18, 18],
|
||||
count: 400,
|
||||
speed: 0.4,
|
||||
opacity: 0.5,
|
||||
color: "#ffddaa",
|
||||
size: 1.2,
|
||||
noise: 2,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
post: {
|
||||
type: "EffectComposer",
|
||||
props: { enabled: true, multisampling: 8 },
|
||||
children: ["bloom", "vignette"],
|
||||
},
|
||||
bloom: {
|
||||
type: "Bloom",
|
||||
props: {
|
||||
intensity: 2,
|
||||
luminanceThreshold: 0.15,
|
||||
luminanceSmoothing: null,
|
||||
mipmapBlur: true,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
vignette: {
|
||||
type: "Vignette",
|
||||
props: { offset: 0.4, darkness: 0.7 },
|
||||
children: [],
|
||||
},
|
||||
fog: {
|
||||
type: "Fog",
|
||||
props: { color: "#030008", near: 10, far: 40 },
|
||||
children: [],
|
||||
},
|
||||
controls: {
|
||||
type: "OrbitControls",
|
||||
props: {
|
||||
enableDamping: true,
|
||||
dampingFactor: null,
|
||||
enableZoom: true,
|
||||
enablePan: null,
|
||||
enableRotate: true,
|
||||
minDistance: 5,
|
||||
maxDistance: 25,
|
||||
minPolarAngle: null,
|
||||
maxPolarAngle: null,
|
||||
autoRotate: true,
|
||||
autoRotateSpeed: 0.4,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,307 @@
|
||||
import { PI } from "./_helpers";
|
||||
import type { Scene } from "./_helpers";
|
||||
|
||||
export const perpetualMotion: Scene = {
|
||||
name: "Perpetual Motion Machine",
|
||||
description:
|
||||
"A gyroscope/gimbal with 3 nested spinning rings on different axes, with a perfectly still glass sphere floating at the center. Clean, minimal, mesmerizing.",
|
||||
spec: {
|
||||
root: "scene",
|
||||
elements: {
|
||||
scene: {
|
||||
type: "Group",
|
||||
props: { position: null, rotation: null, scale: null },
|
||||
children: [
|
||||
"env",
|
||||
"ambient",
|
||||
"key",
|
||||
"fill",
|
||||
"outer-spin",
|
||||
"sparkles",
|
||||
"floor",
|
||||
"shadows",
|
||||
"post",
|
||||
"controls",
|
||||
],
|
||||
},
|
||||
env: {
|
||||
type: "Environment",
|
||||
props: {
|
||||
preset: "studio",
|
||||
background: false,
|
||||
blur: null,
|
||||
intensity: 1,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
ambient: {
|
||||
type: "AmbientLight",
|
||||
props: { color: "#ffffff", intensity: 0.3 },
|
||||
children: [],
|
||||
},
|
||||
key: {
|
||||
type: "DirectionalLight",
|
||||
props: {
|
||||
position: [5, 8, 5],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
color: "#ffffff",
|
||||
intensity: 2,
|
||||
castShadow: true,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
fill: {
|
||||
type: "PointLight",
|
||||
props: {
|
||||
position: [-3, 4, -3],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
color: "#aaccff",
|
||||
intensity: 10,
|
||||
distance: 15,
|
||||
decay: 2,
|
||||
castShadow: false,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"outer-spin": {
|
||||
type: "Spin",
|
||||
props: {
|
||||
position: null,
|
||||
rotation: null,
|
||||
scale: null,
|
||||
speed: 0.4,
|
||||
axis: "y",
|
||||
},
|
||||
children: ["outer-ring", "mid-spin"],
|
||||
},
|
||||
"outer-ring": {
|
||||
type: "Torus",
|
||||
props: {
|
||||
position: null,
|
||||
rotation: [PI / 2, 0, 0],
|
||||
scale: null,
|
||||
castShadow: false,
|
||||
receiveShadow: false,
|
||||
material: {
|
||||
color: "#cccccc",
|
||||
metalness: 1,
|
||||
roughness: 0.05,
|
||||
emissive: "#ffffff",
|
||||
emissiveIntensity: 0.05,
|
||||
opacity: null,
|
||||
transparent: null,
|
||||
wireframe: null,
|
||||
},
|
||||
radius: 4,
|
||||
tube: 0.06,
|
||||
radialSegments: null,
|
||||
tubularSegments: 128,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"mid-spin": {
|
||||
type: "Spin",
|
||||
props: {
|
||||
position: null,
|
||||
rotation: null,
|
||||
scale: null,
|
||||
speed: -0.8,
|
||||
axis: "x",
|
||||
},
|
||||
children: ["mid-ring", "inner-spin"],
|
||||
},
|
||||
"mid-ring": {
|
||||
type: "Torus",
|
||||
props: {
|
||||
position: null,
|
||||
rotation: [PI / 2, 0, 0],
|
||||
scale: null,
|
||||
castShadow: false,
|
||||
receiveShadow: false,
|
||||
material: {
|
||||
color: "#ffcc44",
|
||||
metalness: 0.95,
|
||||
roughness: 0.08,
|
||||
emissive: "#cc9900",
|
||||
emissiveIntensity: 0.1,
|
||||
opacity: null,
|
||||
transparent: null,
|
||||
wireframe: null,
|
||||
},
|
||||
radius: 3,
|
||||
tube: 0.05,
|
||||
radialSegments: null,
|
||||
tubularSegments: 128,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"inner-spin": {
|
||||
type: "Spin",
|
||||
props: {
|
||||
position: null,
|
||||
rotation: null,
|
||||
scale: null,
|
||||
speed: 1.5,
|
||||
axis: "z",
|
||||
},
|
||||
children: ["inner-ring", "center-float"],
|
||||
},
|
||||
"inner-ring": {
|
||||
type: "Torus",
|
||||
props: {
|
||||
position: null,
|
||||
rotation: [PI / 2, 0, 0],
|
||||
scale: null,
|
||||
castShadow: false,
|
||||
receiveShadow: false,
|
||||
material: {
|
||||
color: "#cc8844",
|
||||
metalness: 0.9,
|
||||
roughness: 0.1,
|
||||
emissive: "#996633",
|
||||
emissiveIntensity: 0.1,
|
||||
opacity: null,
|
||||
transparent: null,
|
||||
wireframe: null,
|
||||
},
|
||||
radius: 2,
|
||||
tube: 0.04,
|
||||
radialSegments: null,
|
||||
tubularSegments: 128,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"center-float": {
|
||||
type: "Float",
|
||||
props: {
|
||||
position: [0, 0, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
speed: 0.3,
|
||||
rotationIntensity: 0,
|
||||
floatIntensity: 0.1,
|
||||
enabled: true,
|
||||
},
|
||||
children: ["center-orb"],
|
||||
},
|
||||
"center-orb": {
|
||||
type: "GlassSphere",
|
||||
props: {
|
||||
position: null,
|
||||
rotation: null,
|
||||
scale: null,
|
||||
castShadow: true,
|
||||
receiveShadow: false,
|
||||
radius: 0.8,
|
||||
widthSegments: 64,
|
||||
heightSegments: 32,
|
||||
color: "#ffffff",
|
||||
transmission: 1,
|
||||
thickness: 1,
|
||||
roughness: 0,
|
||||
chromaticAberration: 0.12,
|
||||
ior: 2,
|
||||
distortion: 0.1,
|
||||
distortionScale: 0.4,
|
||||
temporalDistortion: 0.4,
|
||||
samples: 10,
|
||||
resolution: 256,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
sparkles: {
|
||||
type: "Sparkles",
|
||||
props: {
|
||||
position: [0, 0, 0],
|
||||
rotation: null,
|
||||
scale: [10, 10, 10],
|
||||
count: 100,
|
||||
speed: 0.1,
|
||||
opacity: 0.2,
|
||||
color: "#ccddff",
|
||||
size: 0.8,
|
||||
noise: 2,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
floor: {
|
||||
type: "ReflectorPlane",
|
||||
props: {
|
||||
position: [0, -4.5, 0],
|
||||
rotation: [-PI / 2, 0, 0],
|
||||
scale: null,
|
||||
width: 25,
|
||||
height: 25,
|
||||
color: "#1a1a2a",
|
||||
resolution: 1024,
|
||||
blur: 400,
|
||||
mirror: 0.7,
|
||||
mixBlur: 10,
|
||||
mixStrength: 2,
|
||||
depthScale: 0.2,
|
||||
metalness: 0.5,
|
||||
roughness: 1,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
shadows: {
|
||||
type: "ContactShadows",
|
||||
props: {
|
||||
position: [0, -4.5, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
opacity: 0.3,
|
||||
width: 12,
|
||||
height: 12,
|
||||
blur: 2.5,
|
||||
near: null,
|
||||
far: 10,
|
||||
smooth: true,
|
||||
resolution: 512,
|
||||
frames: null,
|
||||
color: "#000000",
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
post: {
|
||||
type: "EffectComposer",
|
||||
props: { enabled: true, multisampling: 8 },
|
||||
children: ["bloom", "vignette"],
|
||||
},
|
||||
bloom: {
|
||||
type: "Bloom",
|
||||
props: {
|
||||
intensity: 0.8,
|
||||
luminanceThreshold: 0.25,
|
||||
luminanceSmoothing: null,
|
||||
mipmapBlur: true,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
vignette: {
|
||||
type: "Vignette",
|
||||
props: { offset: 0.5, darkness: 0.4 },
|
||||
children: [],
|
||||
},
|
||||
controls: {
|
||||
type: "OrbitControls",
|
||||
props: {
|
||||
enableDamping: true,
|
||||
dampingFactor: null,
|
||||
enableZoom: true,
|
||||
enablePan: null,
|
||||
enableRotate: true,
|
||||
minDistance: 5,
|
||||
maxDistance: 16,
|
||||
minPolarAngle: 0.3,
|
||||
maxPolarAngle: 1.5,
|
||||
autoRotate: true,
|
||||
autoRotateSpeed: 0.3,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,764 @@
|
||||
import type { Scene } from "./_helpers";
|
||||
|
||||
const goldMaterial = {
|
||||
color: "#cc8833",
|
||||
metalness: 1,
|
||||
roughness: 0.05,
|
||||
};
|
||||
|
||||
const blueMaterial = {
|
||||
color: "#3388cc",
|
||||
metalness: 1,
|
||||
roughness: 0.05,
|
||||
};
|
||||
|
||||
const magentaMaterial = {
|
||||
color: "#cc3388",
|
||||
metalness: 1,
|
||||
roughness: 0.05,
|
||||
};
|
||||
|
||||
export const pipes: Scene = {
|
||||
name: "Pipes",
|
||||
description:
|
||||
"Classic 3D Pipes screensaver -- chrome pipes snaking through space at right angles",
|
||||
spec: {
|
||||
root: "scene",
|
||||
elements: {
|
||||
scene: {
|
||||
type: "Group",
|
||||
props: { position: null, rotation: null, scale: null },
|
||||
children: [
|
||||
"camera",
|
||||
"env",
|
||||
"ambient",
|
||||
"directional",
|
||||
"pipe-system",
|
||||
"post",
|
||||
"fog",
|
||||
"controls",
|
||||
],
|
||||
},
|
||||
camera: {
|
||||
type: "PerspectiveCamera",
|
||||
props: {
|
||||
position: [0, 3, 12],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
fov: 50,
|
||||
near: null,
|
||||
far: null,
|
||||
makeDefault: true,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
env: {
|
||||
type: "Environment",
|
||||
props: {
|
||||
preset: "warehouse",
|
||||
background: false,
|
||||
blur: 0.5,
|
||||
intensity: 1,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
ambient: {
|
||||
type: "AmbientLight",
|
||||
props: { color: "#ffffff", intensity: 0.3 },
|
||||
children: [],
|
||||
},
|
||||
directional: {
|
||||
type: "DirectionalLight",
|
||||
props: {
|
||||
position: [5, 10, 5],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
color: "#ffffff",
|
||||
intensity: 1,
|
||||
castShadow: null,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"pipe-system": {
|
||||
type: "Spin",
|
||||
props: {
|
||||
position: null,
|
||||
rotation: null,
|
||||
scale: null,
|
||||
speed: 0.1,
|
||||
axis: "y",
|
||||
},
|
||||
children: ["pipe1-group", "pipe2-group", "pipe3-group"],
|
||||
},
|
||||
"pipe1-group": {
|
||||
type: "Group",
|
||||
props: { position: null, rotation: null, scale: null },
|
||||
children: [
|
||||
"p1-j0",
|
||||
"p1-s1",
|
||||
"p1-j1",
|
||||
"p1-s2",
|
||||
"p1-j2",
|
||||
"p1-s3",
|
||||
"p1-j3",
|
||||
"p1-s4",
|
||||
"p1-j4",
|
||||
"p1-s5",
|
||||
"p1-j5",
|
||||
"p1-s6",
|
||||
"p1-j6",
|
||||
],
|
||||
},
|
||||
"p1-j0": {
|
||||
type: "Sphere",
|
||||
props: {
|
||||
position: [-3, -3, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
castShadow: null,
|
||||
receiveShadow: null,
|
||||
material: goldMaterial,
|
||||
radius: 0.12,
|
||||
widthSegments: null,
|
||||
heightSegments: null,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"p1-j1": {
|
||||
type: "Sphere",
|
||||
props: {
|
||||
position: [-3, 0, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
castShadow: null,
|
||||
receiveShadow: null,
|
||||
material: goldMaterial,
|
||||
radius: 0.12,
|
||||
widthSegments: null,
|
||||
heightSegments: null,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"p1-j2": {
|
||||
type: "Sphere",
|
||||
props: {
|
||||
position: [1, 0, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
castShadow: null,
|
||||
receiveShadow: null,
|
||||
material: goldMaterial,
|
||||
radius: 0.12,
|
||||
widthSegments: null,
|
||||
heightSegments: null,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"p1-j3": {
|
||||
type: "Sphere",
|
||||
props: {
|
||||
position: [1, 2, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
castShadow: null,
|
||||
receiveShadow: null,
|
||||
material: goldMaterial,
|
||||
radius: 0.12,
|
||||
widthSegments: null,
|
||||
heightSegments: null,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"p1-j4": {
|
||||
type: "Sphere",
|
||||
props: {
|
||||
position: [1, 2, -3],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
castShadow: null,
|
||||
receiveShadow: null,
|
||||
material: goldMaterial,
|
||||
radius: 0.12,
|
||||
widthSegments: null,
|
||||
heightSegments: null,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"p1-j5": {
|
||||
type: "Sphere",
|
||||
props: {
|
||||
position: [1, 0, -3],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
castShadow: null,
|
||||
receiveShadow: null,
|
||||
material: goldMaterial,
|
||||
radius: 0.12,
|
||||
widthSegments: null,
|
||||
heightSegments: null,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"p1-j6": {
|
||||
type: "Sphere",
|
||||
props: {
|
||||
position: [-1, 0, -3],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
castShadow: null,
|
||||
receiveShadow: null,
|
||||
material: goldMaterial,
|
||||
radius: 0.12,
|
||||
widthSegments: null,
|
||||
heightSegments: null,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"p1-s1": {
|
||||
type: "Cylinder",
|
||||
props: {
|
||||
position: [-3, -1.5, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
castShadow: null,
|
||||
receiveShadow: null,
|
||||
material: goldMaterial,
|
||||
radiusTop: 0.08,
|
||||
radiusBottom: 0.08,
|
||||
height: 3,
|
||||
radialSegments: 12,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"p1-s2": {
|
||||
type: "Cylinder",
|
||||
props: {
|
||||
position: [-1, 0, 0],
|
||||
rotation: [0, 0, 1.5708],
|
||||
scale: null,
|
||||
castShadow: null,
|
||||
receiveShadow: null,
|
||||
material: goldMaterial,
|
||||
radiusTop: 0.08,
|
||||
radiusBottom: 0.08,
|
||||
height: 4,
|
||||
radialSegments: 12,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"p1-s3": {
|
||||
type: "Cylinder",
|
||||
props: {
|
||||
position: [1, 1, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
castShadow: null,
|
||||
receiveShadow: null,
|
||||
material: goldMaterial,
|
||||
radiusTop: 0.08,
|
||||
radiusBottom: 0.08,
|
||||
height: 2,
|
||||
radialSegments: 12,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"p1-s4": {
|
||||
type: "Cylinder",
|
||||
props: {
|
||||
position: [1, 2, -1.5],
|
||||
rotation: [1.5708, 0, 0],
|
||||
scale: null,
|
||||
castShadow: null,
|
||||
receiveShadow: null,
|
||||
material: goldMaterial,
|
||||
radiusTop: 0.08,
|
||||
radiusBottom: 0.08,
|
||||
height: 3,
|
||||
radialSegments: 12,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"p1-s5": {
|
||||
type: "Cylinder",
|
||||
props: {
|
||||
position: [1, 1, -3],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
castShadow: null,
|
||||
receiveShadow: null,
|
||||
material: goldMaterial,
|
||||
radiusTop: 0.08,
|
||||
radiusBottom: 0.08,
|
||||
height: 2,
|
||||
radialSegments: 12,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"p1-s6": {
|
||||
type: "Cylinder",
|
||||
props: {
|
||||
position: [0, 0, -3],
|
||||
rotation: [0, 0, 1.5708],
|
||||
scale: null,
|
||||
castShadow: null,
|
||||
receiveShadow: null,
|
||||
material: goldMaterial,
|
||||
radiusTop: 0.08,
|
||||
radiusBottom: 0.08,
|
||||
height: 2,
|
||||
radialSegments: 12,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"pipe2-group": {
|
||||
type: "Group",
|
||||
props: { position: null, rotation: null, scale: null },
|
||||
children: [
|
||||
"p2-j0",
|
||||
"p2-s1",
|
||||
"p2-j1",
|
||||
"p2-s2",
|
||||
"p2-j2",
|
||||
"p2-s3",
|
||||
"p2-j3",
|
||||
"p2-s4",
|
||||
"p2-j4",
|
||||
"p2-s5",
|
||||
"p2-j5",
|
||||
"p2-s6",
|
||||
"p2-j6",
|
||||
],
|
||||
},
|
||||
"p2-j0": {
|
||||
type: "Sphere",
|
||||
props: {
|
||||
position: [2, -3, 2],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
castShadow: null,
|
||||
receiveShadow: null,
|
||||
material: blueMaterial,
|
||||
radius: 0.12,
|
||||
widthSegments: null,
|
||||
heightSegments: null,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"p2-j1": {
|
||||
type: "Sphere",
|
||||
props: {
|
||||
position: [2, 1, 2],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
castShadow: null,
|
||||
receiveShadow: null,
|
||||
material: blueMaterial,
|
||||
radius: 0.12,
|
||||
widthSegments: null,
|
||||
heightSegments: null,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"p2-j2": {
|
||||
type: "Sphere",
|
||||
props: {
|
||||
position: [2, 1, -2],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
castShadow: null,
|
||||
receiveShadow: null,
|
||||
material: blueMaterial,
|
||||
radius: 0.12,
|
||||
widthSegments: null,
|
||||
heightSegments: null,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"p2-j3": {
|
||||
type: "Sphere",
|
||||
props: {
|
||||
position: [-1, 1, -2],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
castShadow: null,
|
||||
receiveShadow: null,
|
||||
material: blueMaterial,
|
||||
radius: 0.12,
|
||||
widthSegments: null,
|
||||
heightSegments: null,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"p2-j4": {
|
||||
type: "Sphere",
|
||||
props: {
|
||||
position: [-1, 4, -2],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
castShadow: null,
|
||||
receiveShadow: null,
|
||||
material: blueMaterial,
|
||||
radius: 0.12,
|
||||
widthSegments: null,
|
||||
heightSegments: null,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"p2-j5": {
|
||||
type: "Sphere",
|
||||
props: {
|
||||
position: [-3, 4, -2],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
castShadow: null,
|
||||
receiveShadow: null,
|
||||
material: blueMaterial,
|
||||
radius: 0.12,
|
||||
widthSegments: null,
|
||||
heightSegments: null,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"p2-j6": {
|
||||
type: "Sphere",
|
||||
props: {
|
||||
position: [-3, 4, 1],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
castShadow: null,
|
||||
receiveShadow: null,
|
||||
material: blueMaterial,
|
||||
radius: 0.12,
|
||||
widthSegments: null,
|
||||
heightSegments: null,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"p2-s1": {
|
||||
type: "Cylinder",
|
||||
props: {
|
||||
position: [2, -1, 2],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
castShadow: null,
|
||||
receiveShadow: null,
|
||||
material: blueMaterial,
|
||||
radiusTop: 0.08,
|
||||
radiusBottom: 0.08,
|
||||
height: 4,
|
||||
radialSegments: 12,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"p2-s2": {
|
||||
type: "Cylinder",
|
||||
props: {
|
||||
position: [2, 1, 0],
|
||||
rotation: [1.5708, 0, 0],
|
||||
scale: null,
|
||||
castShadow: null,
|
||||
receiveShadow: null,
|
||||
material: blueMaterial,
|
||||
radiusTop: 0.08,
|
||||
radiusBottom: 0.08,
|
||||
height: 4,
|
||||
radialSegments: 12,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"p2-s3": {
|
||||
type: "Cylinder",
|
||||
props: {
|
||||
position: [0.5, 1, -2],
|
||||
rotation: [0, 0, 1.5708],
|
||||
scale: null,
|
||||
castShadow: null,
|
||||
receiveShadow: null,
|
||||
material: blueMaterial,
|
||||
radiusTop: 0.08,
|
||||
radiusBottom: 0.08,
|
||||
height: 3,
|
||||
radialSegments: 12,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"p2-s4": {
|
||||
type: "Cylinder",
|
||||
props: {
|
||||
position: [-1, 2.5, -2],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
castShadow: null,
|
||||
receiveShadow: null,
|
||||
material: blueMaterial,
|
||||
radiusTop: 0.08,
|
||||
radiusBottom: 0.08,
|
||||
height: 3,
|
||||
radialSegments: 12,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"p2-s5": {
|
||||
type: "Cylinder",
|
||||
props: {
|
||||
position: [-2, 4, -2],
|
||||
rotation: [0, 0, 1.5708],
|
||||
scale: null,
|
||||
castShadow: null,
|
||||
receiveShadow: null,
|
||||
material: blueMaterial,
|
||||
radiusTop: 0.08,
|
||||
radiusBottom: 0.08,
|
||||
height: 2,
|
||||
radialSegments: 12,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"p2-s6": {
|
||||
type: "Cylinder",
|
||||
props: {
|
||||
position: [-3, 4, -0.5],
|
||||
rotation: [1.5708, 0, 0],
|
||||
scale: null,
|
||||
castShadow: null,
|
||||
receiveShadow: null,
|
||||
material: blueMaterial,
|
||||
radiusTop: 0.08,
|
||||
radiusBottom: 0.08,
|
||||
height: 3,
|
||||
radialSegments: 12,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"pipe3-group": {
|
||||
type: "Group",
|
||||
props: { position: null, rotation: null, scale: null },
|
||||
children: [
|
||||
"p3-j0",
|
||||
"p3-s1",
|
||||
"p3-j1",
|
||||
"p3-s2",
|
||||
"p3-j2",
|
||||
"p3-s3",
|
||||
"p3-j3",
|
||||
"p3-s4",
|
||||
"p3-j4",
|
||||
"p3-s5",
|
||||
"p3-j5",
|
||||
],
|
||||
},
|
||||
"p3-j0": {
|
||||
type: "Sphere",
|
||||
props: {
|
||||
position: [-2, 3, 3],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
castShadow: null,
|
||||
receiveShadow: null,
|
||||
material: magentaMaterial,
|
||||
radius: 0.12,
|
||||
widthSegments: null,
|
||||
heightSegments: null,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"p3-j1": {
|
||||
type: "Sphere",
|
||||
props: {
|
||||
position: [-2, -2, 3],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
castShadow: null,
|
||||
receiveShadow: null,
|
||||
material: magentaMaterial,
|
||||
radius: 0.12,
|
||||
widthSegments: null,
|
||||
heightSegments: null,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"p3-j2": {
|
||||
type: "Sphere",
|
||||
props: {
|
||||
position: [1, -2, 3],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
castShadow: null,
|
||||
receiveShadow: null,
|
||||
material: magentaMaterial,
|
||||
radius: 0.12,
|
||||
widthSegments: null,
|
||||
heightSegments: null,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"p3-j3": {
|
||||
type: "Sphere",
|
||||
props: {
|
||||
position: [1, -2, -2],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
castShadow: null,
|
||||
receiveShadow: null,
|
||||
material: magentaMaterial,
|
||||
radius: 0.12,
|
||||
widthSegments: null,
|
||||
heightSegments: null,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"p3-j4": {
|
||||
type: "Sphere",
|
||||
props: {
|
||||
position: [1, 0, -2],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
castShadow: null,
|
||||
receiveShadow: null,
|
||||
material: magentaMaterial,
|
||||
radius: 0.12,
|
||||
widthSegments: null,
|
||||
heightSegments: null,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"p3-j5": {
|
||||
type: "Sphere",
|
||||
props: {
|
||||
position: [-3, 0, -2],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
castShadow: null,
|
||||
receiveShadow: null,
|
||||
material: magentaMaterial,
|
||||
radius: 0.12,
|
||||
widthSegments: null,
|
||||
heightSegments: null,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"p3-s1": {
|
||||
type: "Cylinder",
|
||||
props: {
|
||||
position: [-2, 0.5, 3],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
castShadow: null,
|
||||
receiveShadow: null,
|
||||
material: magentaMaterial,
|
||||
radiusTop: 0.08,
|
||||
radiusBottom: 0.08,
|
||||
height: 5,
|
||||
radialSegments: 12,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"p3-s2": {
|
||||
type: "Cylinder",
|
||||
props: {
|
||||
position: [-0.5, -2, 3],
|
||||
rotation: [0, 0, 1.5708],
|
||||
scale: null,
|
||||
castShadow: null,
|
||||
receiveShadow: null,
|
||||
material: magentaMaterial,
|
||||
radiusTop: 0.08,
|
||||
radiusBottom: 0.08,
|
||||
height: 3,
|
||||
radialSegments: 12,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"p3-s3": {
|
||||
type: "Cylinder",
|
||||
props: {
|
||||
position: [1, -2, 0.5],
|
||||
rotation: [1.5708, 0, 0],
|
||||
scale: null,
|
||||
castShadow: null,
|
||||
receiveShadow: null,
|
||||
material: magentaMaterial,
|
||||
radiusTop: 0.08,
|
||||
radiusBottom: 0.08,
|
||||
height: 5,
|
||||
radialSegments: 12,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"p3-s4": {
|
||||
type: "Cylinder",
|
||||
props: {
|
||||
position: [1, -1, -2],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
castShadow: null,
|
||||
receiveShadow: null,
|
||||
material: magentaMaterial,
|
||||
radiusTop: 0.08,
|
||||
radiusBottom: 0.08,
|
||||
height: 2,
|
||||
radialSegments: 12,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"p3-s5": {
|
||||
type: "Cylinder",
|
||||
props: {
|
||||
position: [-1, 0, -2],
|
||||
rotation: [0, 0, 1.5708],
|
||||
scale: null,
|
||||
castShadow: null,
|
||||
receiveShadow: null,
|
||||
material: magentaMaterial,
|
||||
radiusTop: 0.08,
|
||||
radiusBottom: 0.08,
|
||||
height: 4,
|
||||
radialSegments: 12,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
post: {
|
||||
type: "EffectComposer",
|
||||
props: { enabled: true, multisampling: 8 },
|
||||
children: ["bloom", "vignette"],
|
||||
},
|
||||
bloom: {
|
||||
type: "Bloom",
|
||||
props: {
|
||||
intensity: 0.4,
|
||||
luminanceThreshold: 0.4,
|
||||
luminanceSmoothing: null,
|
||||
mipmapBlur: true,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
vignette: {
|
||||
type: "Vignette",
|
||||
props: { offset: 0.3, darkness: 0.4 },
|
||||
children: [],
|
||||
},
|
||||
fog: {
|
||||
type: "Fog",
|
||||
props: { color: "#111111", near: 10, far: 30 },
|
||||
children: [],
|
||||
},
|
||||
controls: {
|
||||
type: "OrbitControls",
|
||||
props: {
|
||||
enableDamping: true,
|
||||
dampingFactor: null,
|
||||
enableZoom: null,
|
||||
enablePan: null,
|
||||
enableRotate: null,
|
||||
minDistance: 5,
|
||||
maxDistance: 20,
|
||||
minPolarAngle: null,
|
||||
maxPolarAngle: null,
|
||||
autoRotate: true,
|
||||
autoRotateSpeed: 0.6,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,809 @@
|
||||
import type { Scene } from "./_helpers";
|
||||
|
||||
export const portalGallery: Scene = {
|
||||
name: "Portal Gallery",
|
||||
description:
|
||||
"Three framed portals into different worlds -- sunset clouds, starfield, and enchanted forest",
|
||||
spec: {
|
||||
root: "scene",
|
||||
elements: {
|
||||
scene: {
|
||||
type: "Group",
|
||||
props: { position: null, rotation: null, scale: null },
|
||||
children: [
|
||||
"cam",
|
||||
"env",
|
||||
"ambient",
|
||||
"directional",
|
||||
"floor",
|
||||
"shadows",
|
||||
"frame1",
|
||||
"frame2",
|
||||
"frame3",
|
||||
"gallery-sparkles",
|
||||
"post",
|
||||
"controls",
|
||||
],
|
||||
},
|
||||
cam: {
|
||||
type: "PerspectiveCamera",
|
||||
props: {
|
||||
position: [0, 2, 7],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
fov: 50,
|
||||
near: 0.1,
|
||||
far: 200,
|
||||
makeDefault: true,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
env: {
|
||||
type: "Environment",
|
||||
props: {
|
||||
preset: "lobby",
|
||||
background: false,
|
||||
blur: 0,
|
||||
intensity: 0.4,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
ambient: {
|
||||
type: "AmbientLight",
|
||||
props: { color: "#e8e0d8", intensity: 0.5 },
|
||||
children: [],
|
||||
},
|
||||
directional: {
|
||||
type: "DirectionalLight",
|
||||
props: {
|
||||
position: [5, 8, 3],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
color: "#ffffff",
|
||||
intensity: 1,
|
||||
castShadow: true,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
floor: {
|
||||
type: "ReflectorPlane",
|
||||
props: {
|
||||
position: [0, 0, 0],
|
||||
rotation: [-1.5708, 0, 0],
|
||||
scale: null,
|
||||
width: 30,
|
||||
height: 30,
|
||||
color: "#2a2a2a",
|
||||
resolution: 1024,
|
||||
blur: 400,
|
||||
mirror: 0.4,
|
||||
mixBlur: 10,
|
||||
mixStrength: 1.5,
|
||||
metalness: 0.5,
|
||||
roughness: 0.8,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
shadows: {
|
||||
type: "ContactShadows",
|
||||
props: {
|
||||
position: [0, 0.01, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
opacity: 0.4,
|
||||
width: 20,
|
||||
height: 20,
|
||||
blur: 2.5,
|
||||
far: 8,
|
||||
color: "#000000",
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
|
||||
// --- PORTAL 1 (left, sunset world) ---
|
||||
frame1: {
|
||||
type: "Group",
|
||||
props: {
|
||||
position: [-3.5, 2, -2],
|
||||
rotation: [0, 0.3, 0],
|
||||
scale: null,
|
||||
},
|
||||
children: [
|
||||
"frame1-top",
|
||||
"frame1-bottom",
|
||||
"frame1-left",
|
||||
"frame1-right",
|
||||
"portal1-mesh",
|
||||
],
|
||||
},
|
||||
"frame1-top": {
|
||||
type: "Box",
|
||||
props: {
|
||||
position: [0, 1.55, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
width: 2.3,
|
||||
height: 0.15,
|
||||
depth: 0.15,
|
||||
material: { color: "#8B7355", metalness: 0.3, roughness: 0.6 },
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"frame1-bottom": {
|
||||
type: "Box",
|
||||
props: {
|
||||
position: [0, -1.55, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
width: 2.3,
|
||||
height: 0.15,
|
||||
depth: 0.15,
|
||||
material: { color: "#8B7355", metalness: 0.3, roughness: 0.6 },
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"frame1-left": {
|
||||
type: "Box",
|
||||
props: {
|
||||
position: [-1.075, 0, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
width: 0.15,
|
||||
height: 3.25,
|
||||
depth: 0.15,
|
||||
material: { color: "#8B7355", metalness: 0.3, roughness: 0.6 },
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"frame1-right": {
|
||||
type: "Box",
|
||||
props: {
|
||||
position: [1.075, 0, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
width: 0.15,
|
||||
height: 3.25,
|
||||
depth: 0.15,
|
||||
material: { color: "#8B7355", metalness: 0.3, roughness: 0.6 },
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"portal1-mesh": {
|
||||
type: "Plane",
|
||||
props: {
|
||||
position: null,
|
||||
rotation: null,
|
||||
scale: null,
|
||||
width: 2,
|
||||
height: 3,
|
||||
},
|
||||
children: ["portal1-mat"],
|
||||
},
|
||||
"portal1-mat": {
|
||||
type: "MeshPortalMaterial",
|
||||
props: { blend: 0, blur: 0.5, resolution: 512 },
|
||||
children: [
|
||||
"p1-sky",
|
||||
"p1-sun-light",
|
||||
"p1-cloud1",
|
||||
"p1-cloud2",
|
||||
"p1-cloud3",
|
||||
"p1-ground",
|
||||
],
|
||||
},
|
||||
"p1-sky": {
|
||||
type: "Sky",
|
||||
props: {
|
||||
distance: null,
|
||||
sunPosition: [100, 10, 50],
|
||||
inclination: null,
|
||||
azimuth: null,
|
||||
rayleigh: 2,
|
||||
turbidity: 10,
|
||||
mieCoefficient: 0.005,
|
||||
mieDirectionalG: null,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"p1-sun-light": {
|
||||
type: "DirectionalLight",
|
||||
props: {
|
||||
position: [5, 5, 3],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
color: "#ffaa55",
|
||||
intensity: 2,
|
||||
castShadow: false,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"p1-cloud1": {
|
||||
type: "Cloud",
|
||||
props: {
|
||||
position: [-3, 4, -5],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
seed: 1,
|
||||
segments: 20,
|
||||
bounds: [6, 2, 3],
|
||||
opacity: 0.6,
|
||||
color: "#ffddaa",
|
||||
speed: 0.1,
|
||||
volume: 5,
|
||||
fade: null,
|
||||
growth: null,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"p1-cloud2": {
|
||||
type: "Cloud",
|
||||
props: {
|
||||
position: [4, 5, -8],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
seed: 7,
|
||||
segments: 25,
|
||||
bounds: [8, 2, 4],
|
||||
opacity: 0.5,
|
||||
color: "#ffccaa",
|
||||
speed: 0.08,
|
||||
volume: 6,
|
||||
fade: null,
|
||||
growth: null,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"p1-cloud3": {
|
||||
type: "Cloud",
|
||||
props: {
|
||||
position: [0, 3.5, -4],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
seed: 14,
|
||||
segments: 15,
|
||||
bounds: [5, 1.5, 2.5],
|
||||
opacity: 0.7,
|
||||
color: "#ffeedd",
|
||||
speed: 0.12,
|
||||
volume: 4,
|
||||
fade: null,
|
||||
growth: null,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"p1-ground": {
|
||||
type: "Plane",
|
||||
props: {
|
||||
position: [0, -2, 0],
|
||||
rotation: [-1.5708, 0, 0],
|
||||
scale: null,
|
||||
width: 30,
|
||||
height: 30,
|
||||
material: { color: "#886644", roughness: 1 },
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
|
||||
// --- PORTAL 2 (center, night/stars world) ---
|
||||
frame2: {
|
||||
type: "Group",
|
||||
props: {
|
||||
position: [0, 2, -3],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
},
|
||||
children: [
|
||||
"frame2-top",
|
||||
"frame2-bottom",
|
||||
"frame2-left",
|
||||
"frame2-right",
|
||||
"portal2-mesh",
|
||||
],
|
||||
},
|
||||
"frame2-top": {
|
||||
type: "Box",
|
||||
props: {
|
||||
position: [0, 1.55, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
width: 2.3,
|
||||
height: 0.15,
|
||||
depth: 0.15,
|
||||
material: { color: "#555566", metalness: 0.8, roughness: 0.2 },
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"frame2-bottom": {
|
||||
type: "Box",
|
||||
props: {
|
||||
position: [0, -1.55, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
width: 2.3,
|
||||
height: 0.15,
|
||||
depth: 0.15,
|
||||
material: { color: "#555566", metalness: 0.8, roughness: 0.2 },
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"frame2-left": {
|
||||
type: "Box",
|
||||
props: {
|
||||
position: [-1.075, 0, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
width: 0.15,
|
||||
height: 3.25,
|
||||
depth: 0.15,
|
||||
material: { color: "#555566", metalness: 0.8, roughness: 0.2 },
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"frame2-right": {
|
||||
type: "Box",
|
||||
props: {
|
||||
position: [1.075, 0, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
width: 0.15,
|
||||
height: 3.25,
|
||||
depth: 0.15,
|
||||
material: { color: "#555566", metalness: 0.8, roughness: 0.2 },
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"portal2-mesh": {
|
||||
type: "Plane",
|
||||
props: {
|
||||
position: null,
|
||||
rotation: null,
|
||||
scale: null,
|
||||
width: 2,
|
||||
height: 3,
|
||||
},
|
||||
children: ["portal2-mat"],
|
||||
},
|
||||
"portal2-mat": {
|
||||
type: "MeshPortalMaterial",
|
||||
props: { blend: 0, blur: 0.5, resolution: 512 },
|
||||
children: [
|
||||
"p2-stars",
|
||||
"p2-ambient",
|
||||
"p2-nebula1",
|
||||
"p2-nebula2",
|
||||
"p2-sparkles",
|
||||
],
|
||||
},
|
||||
"p2-stars": {
|
||||
type: "Stars",
|
||||
props: {
|
||||
radius: 80,
|
||||
depth: 40,
|
||||
count: 6000,
|
||||
factor: 5,
|
||||
saturation: 0.5,
|
||||
fade: true,
|
||||
speed: 0.3,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"p2-ambient": {
|
||||
type: "AmbientLight",
|
||||
props: { color: "#111133", intensity: 0.3 },
|
||||
children: [],
|
||||
},
|
||||
"p2-nebula1": {
|
||||
type: "Sphere",
|
||||
props: {
|
||||
position: [3, 2, -8],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
radius: 2,
|
||||
widthSegments: null,
|
||||
heightSegments: null,
|
||||
material: {
|
||||
color: "#4400aa",
|
||||
emissive: "#4400aa",
|
||||
emissiveIntensity: 0.5,
|
||||
transparent: true,
|
||||
opacity: 0.3,
|
||||
},
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"p2-nebula2": {
|
||||
type: "Sphere",
|
||||
props: {
|
||||
position: [-4, -1, -10],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
radius: 3,
|
||||
widthSegments: null,
|
||||
heightSegments: null,
|
||||
material: {
|
||||
color: "#aa0044",
|
||||
emissive: "#aa0044",
|
||||
emissiveIntensity: 0.4,
|
||||
transparent: true,
|
||||
opacity: 0.25,
|
||||
},
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"p2-sparkles": {
|
||||
type: "Sparkles",
|
||||
props: {
|
||||
position: [0, 0, -5],
|
||||
rotation: null,
|
||||
scale: [10, 10, 10],
|
||||
count: 200,
|
||||
speed: 0.2,
|
||||
opacity: 0.8,
|
||||
color: "#aabbff",
|
||||
size: 2,
|
||||
noise: 3,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
|
||||
// --- PORTAL 3 (right, forest/nature world) ---
|
||||
frame3: {
|
||||
type: "Group",
|
||||
props: {
|
||||
position: [3.5, 2, -2],
|
||||
rotation: [0, -0.3, 0],
|
||||
scale: null,
|
||||
},
|
||||
children: [
|
||||
"frame3-top",
|
||||
"frame3-bottom",
|
||||
"frame3-left",
|
||||
"frame3-right",
|
||||
"portal3-mesh",
|
||||
],
|
||||
},
|
||||
"frame3-top": {
|
||||
type: "Box",
|
||||
props: {
|
||||
position: [0, 1.55, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
width: 2.3,
|
||||
height: 0.15,
|
||||
depth: 0.15,
|
||||
material: { color: "#6B4423", metalness: 0.1, roughness: 0.8 },
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"frame3-bottom": {
|
||||
type: "Box",
|
||||
props: {
|
||||
position: [0, -1.55, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
width: 2.3,
|
||||
height: 0.15,
|
||||
depth: 0.15,
|
||||
material: { color: "#6B4423", metalness: 0.1, roughness: 0.8 },
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"frame3-left": {
|
||||
type: "Box",
|
||||
props: {
|
||||
position: [-1.075, 0, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
width: 0.15,
|
||||
height: 3.25,
|
||||
depth: 0.15,
|
||||
material: { color: "#6B4423", metalness: 0.1, roughness: 0.8 },
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"frame3-right": {
|
||||
type: "Box",
|
||||
props: {
|
||||
position: [1.075, 0, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
width: 0.15,
|
||||
height: 3.25,
|
||||
depth: 0.15,
|
||||
material: { color: "#6B4423", metalness: 0.1, roughness: 0.8 },
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"portal3-mesh": {
|
||||
type: "Plane",
|
||||
props: {
|
||||
position: null,
|
||||
rotation: null,
|
||||
scale: null,
|
||||
width: 2,
|
||||
height: 3,
|
||||
},
|
||||
children: ["portal3-mat"],
|
||||
},
|
||||
"portal3-mat": {
|
||||
type: "MeshPortalMaterial",
|
||||
props: { blend: 0, blur: 0.5, resolution: 512 },
|
||||
children: [
|
||||
"p3-env",
|
||||
"p3-ambient",
|
||||
"p3-sun",
|
||||
"p3-tree1",
|
||||
"p3-tree2",
|
||||
"p3-tree3",
|
||||
"p3-tree4",
|
||||
"p3-tree5",
|
||||
"p3-ground",
|
||||
"p3-fireflies",
|
||||
],
|
||||
},
|
||||
"p3-env": {
|
||||
type: "Environment",
|
||||
props: { preset: "forest", background: false, blur: 0, intensity: 0.8 },
|
||||
children: [],
|
||||
},
|
||||
"p3-ambient": {
|
||||
type: "AmbientLight",
|
||||
props: { color: "#334422", intensity: 0.4 },
|
||||
children: [],
|
||||
},
|
||||
"p3-sun": {
|
||||
type: "DirectionalLight",
|
||||
props: {
|
||||
position: [-3, 8, 2],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
color: "#aaffaa",
|
||||
intensity: 1.5,
|
||||
castShadow: true,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"p3-tree1": {
|
||||
type: "Group",
|
||||
props: { position: [-2, -2, -4], rotation: null, scale: null },
|
||||
children: ["p3-t1-trunk", "p3-t1-leaves"],
|
||||
},
|
||||
"p3-t1-trunk": {
|
||||
type: "Cylinder",
|
||||
props: {
|
||||
position: [0, 0.6, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
radiusTop: 0.08,
|
||||
radiusBottom: 0.12,
|
||||
height: 1.2,
|
||||
radialSegments: 8,
|
||||
material: { color: "#4a3520", roughness: 0.9 },
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"p3-t1-leaves": {
|
||||
type: "Cone",
|
||||
props: {
|
||||
position: [0, 1.8, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
radius: 0.6,
|
||||
height: 1.5,
|
||||
radialSegments: 8,
|
||||
material: { color: "#2d5a1e", roughness: 0.8 },
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"p3-tree2": {
|
||||
type: "Group",
|
||||
props: { position: [1.5, -2, -6], rotation: null, scale: null },
|
||||
children: ["p3-t2-trunk", "p3-t2-leaves"],
|
||||
},
|
||||
"p3-t2-trunk": {
|
||||
type: "Cylinder",
|
||||
props: {
|
||||
position: [0, 0.8, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
radiusTop: 0.1,
|
||||
radiusBottom: 0.15,
|
||||
height: 1.6,
|
||||
radialSegments: 8,
|
||||
material: { color: "#3d2b15", roughness: 0.9 },
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"p3-t2-leaves": {
|
||||
type: "Cone",
|
||||
props: {
|
||||
position: [0, 2.3, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
radius: 0.8,
|
||||
height: 2,
|
||||
radialSegments: 8,
|
||||
material: { color: "#1e4a10", roughness: 0.8 },
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"p3-tree3": {
|
||||
type: "Group",
|
||||
props: { position: [-0.5, -2, -3], rotation: null, scale: null },
|
||||
children: ["p3-t3-trunk", "p3-t3-leaves"],
|
||||
},
|
||||
"p3-t3-trunk": {
|
||||
type: "Cylinder",
|
||||
props: {
|
||||
position: [0, 0.5, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
radiusTop: 0.06,
|
||||
radiusBottom: 0.1,
|
||||
height: 1,
|
||||
radialSegments: 8,
|
||||
material: { color: "#4a3520", roughness: 0.9 },
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"p3-t3-leaves": {
|
||||
type: "Cone",
|
||||
props: {
|
||||
position: [0, 1.5, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
radius: 0.5,
|
||||
height: 1.2,
|
||||
radialSegments: 8,
|
||||
material: { color: "#3a6b2a", roughness: 0.8 },
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"p3-tree4": {
|
||||
type: "Group",
|
||||
props: { position: [3, -2, -8], rotation: null, scale: null },
|
||||
children: ["p3-t4-trunk", "p3-t4-leaves"],
|
||||
},
|
||||
"p3-t4-trunk": {
|
||||
type: "Cylinder",
|
||||
props: {
|
||||
position: [0, 1, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
radiusTop: 0.12,
|
||||
radiusBottom: 0.18,
|
||||
height: 2,
|
||||
radialSegments: 8,
|
||||
material: { color: "#3d2b15", roughness: 0.9 },
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"p3-t4-leaves": {
|
||||
type: "Cone",
|
||||
props: {
|
||||
position: [0, 2.8, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
radius: 1,
|
||||
height: 2.5,
|
||||
radialSegments: 8,
|
||||
material: { color: "#2d5a1e", roughness: 0.8 },
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"p3-tree5": {
|
||||
type: "Group",
|
||||
props: { position: [-3.5, -2, -7], rotation: null, scale: null },
|
||||
children: ["p3-t5-trunk", "p3-t5-leaves"],
|
||||
},
|
||||
"p3-t5-trunk": {
|
||||
type: "Cylinder",
|
||||
props: {
|
||||
position: [0, 0.7, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
radiusTop: 0.09,
|
||||
radiusBottom: 0.13,
|
||||
height: 1.4,
|
||||
radialSegments: 8,
|
||||
material: { color: "#4a3520", roughness: 0.9 },
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"p3-t5-leaves": {
|
||||
type: "Cone",
|
||||
props: {
|
||||
position: [0, 2, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
radius: 0.7,
|
||||
height: 1.8,
|
||||
radialSegments: 8,
|
||||
material: { color: "#1e4a10", roughness: 0.8 },
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"p3-ground": {
|
||||
type: "Plane",
|
||||
props: {
|
||||
position: [0, -2, 0],
|
||||
rotation: [-1.5708, 0, 0],
|
||||
scale: null,
|
||||
width: 30,
|
||||
height: 30,
|
||||
material: { color: "#3a5a2a", roughness: 1 },
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"p3-fireflies": {
|
||||
type: "Sparkles",
|
||||
props: {
|
||||
position: [0, 0, -5],
|
||||
rotation: null,
|
||||
scale: [6, 4, 6],
|
||||
count: 80,
|
||||
speed: 0.3,
|
||||
opacity: 0.7,
|
||||
color: "#aaff66",
|
||||
size: 1.5,
|
||||
noise: 2,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
|
||||
"gallery-sparkles": {
|
||||
type: "Sparkles",
|
||||
props: {
|
||||
position: [0, 3, 0],
|
||||
rotation: null,
|
||||
scale: [12, 5, 10],
|
||||
count: 40,
|
||||
speed: 0.05,
|
||||
opacity: 0.2,
|
||||
color: "#ffffff",
|
||||
size: 0.3,
|
||||
noise: null,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
post: {
|
||||
type: "EffectComposer",
|
||||
props: { enabled: true, multisampling: 8 },
|
||||
children: ["bloom", "vignette"],
|
||||
},
|
||||
bloom: {
|
||||
type: "Bloom",
|
||||
props: {
|
||||
intensity: 0.6,
|
||||
luminanceThreshold: 0.3,
|
||||
luminanceSmoothing: null,
|
||||
mipmapBlur: true,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
vignette: {
|
||||
type: "Vignette",
|
||||
props: { offset: 0.4, darkness: 0.4 },
|
||||
children: [],
|
||||
},
|
||||
controls: {
|
||||
type: "OrbitControls",
|
||||
props: {
|
||||
enableDamping: true,
|
||||
dampingFactor: null,
|
||||
enableZoom: true,
|
||||
enablePan: null,
|
||||
enableRotate: true,
|
||||
minDistance: 4,
|
||||
maxDistance: 16,
|
||||
minPolarAngle: null,
|
||||
maxPolarAngle: null,
|
||||
autoRotate: true,
|
||||
autoRotateSpeed: 0.3,
|
||||
target: [0, 1.8, -2],
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,455 @@
|
||||
import type { Scene } from "./_helpers";
|
||||
|
||||
export const productShowroom: Scene = {
|
||||
name: "Product Showroom",
|
||||
description:
|
||||
"Sleek glass torus knot on a pedestal with studio lighting, reflections, and contact shadows",
|
||||
spec: {
|
||||
root: "scene",
|
||||
elements: {
|
||||
scene: {
|
||||
type: "Group",
|
||||
props: { position: null, rotation: null, scale: null },
|
||||
children: [
|
||||
"camera",
|
||||
"env",
|
||||
"backdrop",
|
||||
"ambient",
|
||||
"key-light",
|
||||
"fill-light",
|
||||
"rim-light",
|
||||
"accent-light",
|
||||
"pedestal",
|
||||
"hero-float",
|
||||
"accent1",
|
||||
"accent2",
|
||||
"accent3",
|
||||
"floor",
|
||||
"shadows",
|
||||
"sparkles",
|
||||
"fx",
|
||||
"controls",
|
||||
],
|
||||
},
|
||||
camera: {
|
||||
type: "PerspectiveCamera",
|
||||
props: {
|
||||
position: [0, 2, 6],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
fov: 40,
|
||||
near: null,
|
||||
far: null,
|
||||
makeDefault: true,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
env: {
|
||||
type: "Environment",
|
||||
props: {
|
||||
preset: "studio",
|
||||
background: false,
|
||||
blur: 0.5,
|
||||
intensity: 0.8,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
backdrop: {
|
||||
type: "Backdrop",
|
||||
props: {
|
||||
position: [0, -0.5, -3],
|
||||
rotation: null,
|
||||
scale: [25, 10, 8],
|
||||
floor: 0.3,
|
||||
segments: 30,
|
||||
receiveShadow: true,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
ambient: {
|
||||
type: "AmbientLight",
|
||||
props: { color: "#e8e0f0", intensity: 0.25 },
|
||||
children: [],
|
||||
},
|
||||
"key-light": {
|
||||
type: "SpotLight",
|
||||
props: {
|
||||
position: [5, 8, 3],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
color: "#ffffff",
|
||||
intensity: 80,
|
||||
distance: 25,
|
||||
decay: null,
|
||||
angle: 0.5,
|
||||
penumbra: 0.8,
|
||||
castShadow: true,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"fill-light": {
|
||||
type: "SpotLight",
|
||||
props: {
|
||||
position: [-4, 5, 2],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
color: "#aaccff",
|
||||
intensity: 30,
|
||||
distance: 20,
|
||||
decay: null,
|
||||
angle: 0.6,
|
||||
penumbra: 1,
|
||||
castShadow: false,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"rim-light": {
|
||||
type: "SpotLight",
|
||||
props: {
|
||||
position: [0, 3, -5],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
color: "#ffddcc",
|
||||
intensity: 40,
|
||||
distance: 15,
|
||||
decay: null,
|
||||
angle: 0.4,
|
||||
penumbra: 0.5,
|
||||
castShadow: false,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"accent-light": {
|
||||
type: "PointLight",
|
||||
props: {
|
||||
position: [0, 0.5, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
color: "#ffffff",
|
||||
intensity: 5,
|
||||
distance: 4,
|
||||
decay: null,
|
||||
castShadow: null,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
pedestal: {
|
||||
type: "Group",
|
||||
props: { position: [0, 0, 0], rotation: null, scale: null },
|
||||
children: ["ped-base", "ped-column", "ped-top"],
|
||||
},
|
||||
"ped-base": {
|
||||
type: "Cylinder",
|
||||
props: {
|
||||
position: [0, 0.05, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
radiusTop: 1.2,
|
||||
radiusBottom: 1.3,
|
||||
height: 0.1,
|
||||
radialSegments: 48,
|
||||
castShadow: null,
|
||||
receiveShadow: null,
|
||||
material: {
|
||||
color: "#1a1a1a",
|
||||
metalness: 0.9,
|
||||
roughness: 0.1,
|
||||
},
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"ped-column": {
|
||||
type: "Cylinder",
|
||||
props: {
|
||||
position: [0, 0.45, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
radiusTop: 0.6,
|
||||
radiusBottom: 0.8,
|
||||
height: 0.7,
|
||||
radialSegments: 48,
|
||||
castShadow: null,
|
||||
receiveShadow: null,
|
||||
material: {
|
||||
color: "#111111",
|
||||
metalness: 0.95,
|
||||
roughness: 0.05,
|
||||
},
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"ped-top": {
|
||||
type: "Cylinder",
|
||||
props: {
|
||||
position: [0, 0.85, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
radiusTop: 0.9,
|
||||
radiusBottom: 0.7,
|
||||
height: 0.1,
|
||||
radialSegments: 48,
|
||||
castShadow: null,
|
||||
receiveShadow: null,
|
||||
material: {
|
||||
color: "#1a1a1a",
|
||||
metalness: 0.9,
|
||||
roughness: 0.1,
|
||||
},
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"hero-float": {
|
||||
type: "Float",
|
||||
props: {
|
||||
position: [0, 2.2, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
speed: 1.5,
|
||||
rotationIntensity: 0.3,
|
||||
floatIntensity: 0.4,
|
||||
enabled: true,
|
||||
},
|
||||
children: ["hero-spin"],
|
||||
},
|
||||
"hero-spin": {
|
||||
type: "Spin",
|
||||
props: {
|
||||
position: null,
|
||||
rotation: null,
|
||||
scale: null,
|
||||
speed: 0.3,
|
||||
axis: "y",
|
||||
},
|
||||
children: ["hero-object"],
|
||||
},
|
||||
"hero-object": {
|
||||
type: "TorusKnot",
|
||||
props: {
|
||||
position: [0, 0, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
castShadow: null,
|
||||
receiveShadow: null,
|
||||
radius: 0.6,
|
||||
tube: 0.22,
|
||||
p: 2,
|
||||
q: 3,
|
||||
tubularSegments: 128,
|
||||
radialSegments: 24,
|
||||
material: {
|
||||
color: "#ffffff",
|
||||
metalness: 1,
|
||||
roughness: 0,
|
||||
},
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
accent1: {
|
||||
type: "Float",
|
||||
props: {
|
||||
position: [-1.5, 1.8, 0.5],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
speed: 2,
|
||||
rotationIntensity: 0.5,
|
||||
floatIntensity: 1.2,
|
||||
enabled: true,
|
||||
},
|
||||
children: ["accent1-body"],
|
||||
},
|
||||
"accent1-body": {
|
||||
type: "GlassSphere",
|
||||
props: {
|
||||
position: null,
|
||||
rotation: null,
|
||||
scale: null,
|
||||
castShadow: null,
|
||||
receiveShadow: null,
|
||||
radius: 0.15,
|
||||
widthSegments: null,
|
||||
heightSegments: null,
|
||||
color: "#eeeeff",
|
||||
transmission: 1,
|
||||
thickness: 0.3,
|
||||
roughness: 0,
|
||||
chromaticAberration: 0.06,
|
||||
ior: 1.5,
|
||||
distortion: null,
|
||||
distortionScale: null,
|
||||
temporalDistortion: null,
|
||||
samples: 6,
|
||||
resolution: 128,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
accent2: {
|
||||
type: "Float",
|
||||
props: {
|
||||
position: [1.2, 2.5, -0.8],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
speed: 1.8,
|
||||
rotationIntensity: 0.4,
|
||||
floatIntensity: 1.5,
|
||||
enabled: true,
|
||||
},
|
||||
children: ["accent2-body"],
|
||||
},
|
||||
"accent2-body": {
|
||||
type: "GlassSphere",
|
||||
props: {
|
||||
position: null,
|
||||
rotation: null,
|
||||
scale: null,
|
||||
castShadow: null,
|
||||
receiveShadow: null,
|
||||
radius: 0.1,
|
||||
widthSegments: null,
|
||||
heightSegments: null,
|
||||
color: "#ffeeff",
|
||||
transmission: 1,
|
||||
thickness: 0.2,
|
||||
roughness: 0,
|
||||
chromaticAberration: 0.08,
|
||||
ior: 1.6,
|
||||
distortion: null,
|
||||
distortionScale: null,
|
||||
temporalDistortion: null,
|
||||
samples: 6,
|
||||
resolution: 128,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
accent3: {
|
||||
type: "Float",
|
||||
props: {
|
||||
position: [0.8, 1.5, 1.2],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
speed: 2.2,
|
||||
rotationIntensity: 0.6,
|
||||
floatIntensity: 0.8,
|
||||
enabled: true,
|
||||
},
|
||||
children: ["accent3-body"],
|
||||
},
|
||||
"accent3-body": {
|
||||
type: "GlassSphere",
|
||||
props: {
|
||||
position: null,
|
||||
rotation: null,
|
||||
scale: null,
|
||||
castShadow: null,
|
||||
receiveShadow: null,
|
||||
radius: 0.12,
|
||||
widthSegments: null,
|
||||
heightSegments: null,
|
||||
color: "#eeffee",
|
||||
transmission: 1,
|
||||
thickness: 0.25,
|
||||
roughness: 0,
|
||||
chromaticAberration: 0.05,
|
||||
ior: 1.4,
|
||||
distortion: null,
|
||||
distortionScale: null,
|
||||
temporalDistortion: null,
|
||||
samples: 6,
|
||||
resolution: 128,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
floor: {
|
||||
type: "ReflectorPlane",
|
||||
props: {
|
||||
position: [0, -0.5, 0],
|
||||
rotation: [-1.5708, 0, 0],
|
||||
scale: null,
|
||||
width: 25,
|
||||
height: 25,
|
||||
color: "#0a0a0a",
|
||||
resolution: 1024,
|
||||
blur: 300,
|
||||
mirror: 0.6,
|
||||
mixBlur: 8,
|
||||
mixStrength: 2,
|
||||
depthScale: 0.2,
|
||||
metalness: 0.8,
|
||||
roughness: 0.3,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
shadows: {
|
||||
type: "ContactShadows",
|
||||
props: {
|
||||
position: [0, -0.49, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
opacity: 0.5,
|
||||
width: 10,
|
||||
height: 10,
|
||||
blur: 2,
|
||||
near: null,
|
||||
far: 5,
|
||||
smooth: true,
|
||||
resolution: 512,
|
||||
frames: null,
|
||||
color: "#000000",
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
sparkles: {
|
||||
type: "Sparkles",
|
||||
props: {
|
||||
position: [0, 2, 0],
|
||||
rotation: null,
|
||||
scale: [6, 4, 6],
|
||||
count: 30,
|
||||
speed: 0.05,
|
||||
opacity: 0.15,
|
||||
color: "#ffffff",
|
||||
size: 0.3,
|
||||
noise: 0.5,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
fx: {
|
||||
type: "EffectComposer",
|
||||
props: { enabled: true, multisampling: 8 },
|
||||
children: ["bloom", "vignette"],
|
||||
},
|
||||
bloom: {
|
||||
type: "Bloom",
|
||||
props: {
|
||||
intensity: 0.5,
|
||||
luminanceThreshold: 0.3,
|
||||
luminanceSmoothing: 0.4,
|
||||
mipmapBlur: true,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
vignette: {
|
||||
type: "Vignette",
|
||||
props: { offset: 0.3, darkness: 0.5 },
|
||||
children: [],
|
||||
},
|
||||
controls: {
|
||||
type: "OrbitControls",
|
||||
props: {
|
||||
enableDamping: true,
|
||||
dampingFactor: null,
|
||||
enableZoom: null,
|
||||
enablePan: null,
|
||||
enableRotate: null,
|
||||
minDistance: 3,
|
||||
maxDistance: 12,
|
||||
minPolarAngle: 0.3,
|
||||
maxPolarAngle: 1.4,
|
||||
autoRotate: true,
|
||||
autoRotateSpeed: 1,
|
||||
target: [0, 1.5, 0],
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,149 @@
|
||||
import type { Scene } from "./_helpers";
|
||||
|
||||
export const starfield: Scene = {
|
||||
name: "Starfield",
|
||||
description:
|
||||
"Classic Windows starfield screensaver -- flying through stars at warp speed",
|
||||
spec: {
|
||||
root: "scene",
|
||||
elements: {
|
||||
scene: {
|
||||
type: "Group",
|
||||
props: { position: null, rotation: null, scale: null },
|
||||
children: [
|
||||
"cam",
|
||||
"ambient",
|
||||
"tunnel",
|
||||
"stars1",
|
||||
"stars2",
|
||||
"sparkle-dust",
|
||||
"shake",
|
||||
"post",
|
||||
"controls",
|
||||
],
|
||||
},
|
||||
cam: {
|
||||
type: "PerspectiveCamera",
|
||||
props: {
|
||||
position: [0, 0, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
fov: 75,
|
||||
near: 0.1,
|
||||
far: 1000,
|
||||
makeDefault: true,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
ambient: {
|
||||
type: "AmbientLight",
|
||||
props: { color: "#111122", intensity: 0.15 },
|
||||
children: [],
|
||||
},
|
||||
tunnel: {
|
||||
type: "WarpTunnel",
|
||||
props: {
|
||||
position: [0, 0, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
ringCount: 120,
|
||||
radius: 4,
|
||||
length: 40,
|
||||
speed: 12,
|
||||
tubeRadius: 0.008,
|
||||
color1: "#ffffff",
|
||||
color2: "#aabbff",
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
stars1: {
|
||||
type: "Stars",
|
||||
props: {
|
||||
radius: 50,
|
||||
depth: 80,
|
||||
count: 10000,
|
||||
factor: 6,
|
||||
saturation: 0,
|
||||
fade: true,
|
||||
speed: 3,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
stars2: {
|
||||
type: "Stars",
|
||||
props: {
|
||||
radius: 30,
|
||||
depth: 50,
|
||||
count: 5000,
|
||||
factor: 3,
|
||||
saturation: 0.1,
|
||||
fade: true,
|
||||
speed: 5,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"sparkle-dust": {
|
||||
type: "Sparkles",
|
||||
props: {
|
||||
position: [0, 0, -5],
|
||||
rotation: null,
|
||||
scale: [8, 8, 30],
|
||||
count: 300,
|
||||
speed: 2,
|
||||
opacity: 0.6,
|
||||
color: "#ffffff",
|
||||
size: 1,
|
||||
noise: 4,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
shake: {
|
||||
type: "CameraShake",
|
||||
props: {
|
||||
intensity: 0.15,
|
||||
maxYaw: 0.02,
|
||||
maxPitch: 0.02,
|
||||
maxRoll: 0.01,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
post: {
|
||||
type: "EffectComposer",
|
||||
props: { enabled: true, multisampling: 8 },
|
||||
children: ["bloom", "vignette"],
|
||||
},
|
||||
bloom: {
|
||||
type: "Bloom",
|
||||
props: {
|
||||
intensity: 1.5,
|
||||
luminanceThreshold: 0.15,
|
||||
luminanceSmoothing: null,
|
||||
mipmapBlur: true,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
vignette: {
|
||||
type: "Vignette",
|
||||
props: { offset: 0.1, darkness: 0.9 },
|
||||
children: [],
|
||||
},
|
||||
controls: {
|
||||
type: "OrbitControls",
|
||||
props: {
|
||||
enableDamping: null,
|
||||
dampingFactor: null,
|
||||
enableZoom: false,
|
||||
enablePan: false,
|
||||
enableRotate: false,
|
||||
minDistance: null,
|
||||
maxDistance: null,
|
||||
minPolarAngle: null,
|
||||
maxPolarAngle: null,
|
||||
autoRotate: false,
|
||||
autoRotateSpeed: null,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,295 @@
|
||||
import type { Scene } from "./_helpers";
|
||||
|
||||
export const stormCell: Scene = {
|
||||
name: "Storm Cell",
|
||||
description:
|
||||
"Dark violent thunderstorm -- spinning cloud masses, pulsing lightning, rain particles, camera shake",
|
||||
spec: {
|
||||
root: "scene",
|
||||
elements: {
|
||||
scene: {
|
||||
type: "Group",
|
||||
props: { position: null, rotation: null, scale: null },
|
||||
children: [
|
||||
"sky",
|
||||
"ambient",
|
||||
"sun",
|
||||
"cloud-spin-1",
|
||||
"cloud-spin-2",
|
||||
"cloud-spin-3",
|
||||
"lightning-1-pulse",
|
||||
"lightning-2-pulse",
|
||||
"lightning-3-pulse",
|
||||
"rain",
|
||||
"shake",
|
||||
"post",
|
||||
"fog",
|
||||
"controls",
|
||||
],
|
||||
},
|
||||
sky: {
|
||||
type: "Sky",
|
||||
props: {
|
||||
distance: null,
|
||||
sunPosition: [10, 2, 0],
|
||||
inclination: null,
|
||||
azimuth: null,
|
||||
turbidity: 20,
|
||||
rayleigh: 0.5,
|
||||
mieCoefficient: 0.01,
|
||||
mieDirectionalG: 0.9,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
ambient: {
|
||||
type: "AmbientLight",
|
||||
props: { color: "#111122", intensity: 0.15 },
|
||||
children: [],
|
||||
},
|
||||
sun: {
|
||||
type: "DirectionalLight",
|
||||
props: {
|
||||
position: [10, 3, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
color: "#334466",
|
||||
intensity: 0.5,
|
||||
castShadow: true,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"cloud-spin-1": {
|
||||
type: "Spin",
|
||||
props: {
|
||||
position: [0, 4, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
speed: 0.08,
|
||||
axis: "y",
|
||||
},
|
||||
children: ["cloud-1"],
|
||||
},
|
||||
"cloud-1": {
|
||||
type: "Cloud",
|
||||
props: {
|
||||
position: [0, 0, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
seed: 1,
|
||||
segments: 40,
|
||||
bounds: [25, 3, 25],
|
||||
volume: 15,
|
||||
speed: 0.3,
|
||||
fade: 20,
|
||||
opacity: 0.9,
|
||||
color: "#222233",
|
||||
growth: 10,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"cloud-spin-2": {
|
||||
type: "Spin",
|
||||
props: {
|
||||
position: [0, 6, -3],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
speed: -0.05,
|
||||
axis: "y",
|
||||
},
|
||||
children: ["cloud-2"],
|
||||
},
|
||||
"cloud-2": {
|
||||
type: "Cloud",
|
||||
props: {
|
||||
position: [0, 0, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
seed: 42,
|
||||
segments: 30,
|
||||
bounds: [20, 2, 20],
|
||||
volume: 12,
|
||||
speed: 0.2,
|
||||
fade: 15,
|
||||
opacity: 0.7,
|
||||
color: "#1a1a2a",
|
||||
growth: 8,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"cloud-spin-3": {
|
||||
type: "Spin",
|
||||
props: {
|
||||
position: [0, 8, 2],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
speed: 0.03,
|
||||
axis: "y",
|
||||
},
|
||||
children: ["cloud-3"],
|
||||
},
|
||||
"cloud-3": {
|
||||
type: "Cloud",
|
||||
props: {
|
||||
position: [0, 0, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
seed: 99,
|
||||
segments: 25,
|
||||
bounds: [30, 2, 30],
|
||||
volume: 10,
|
||||
speed: 0.15,
|
||||
fade: 25,
|
||||
opacity: 0.5,
|
||||
color: "#2a2a3a",
|
||||
growth: 6,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"lightning-1-pulse": {
|
||||
type: "Pulse",
|
||||
props: {
|
||||
position: [2, 5, -1],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
speed: 3,
|
||||
min: 0,
|
||||
max: 1,
|
||||
},
|
||||
children: ["lightning-1"],
|
||||
},
|
||||
"lightning-1": {
|
||||
type: "PointLight",
|
||||
props: {
|
||||
position: [0, 0, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
color: "#aabbff",
|
||||
intensity: 80,
|
||||
distance: 30,
|
||||
decay: 2,
|
||||
castShadow: false,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"lightning-2-pulse": {
|
||||
type: "Pulse",
|
||||
props: {
|
||||
position: [-3, 6, 3],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
speed: 4.5,
|
||||
min: 0,
|
||||
max: 1,
|
||||
},
|
||||
children: ["lightning-2"],
|
||||
},
|
||||
"lightning-2": {
|
||||
type: "PointLight",
|
||||
props: {
|
||||
position: [0, 0, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
color: "#aaccff",
|
||||
intensity: 60,
|
||||
distance: 25,
|
||||
decay: 2,
|
||||
castShadow: false,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
"lightning-3-pulse": {
|
||||
type: "Pulse",
|
||||
props: {
|
||||
position: [0, 4, -4],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
speed: 2.5,
|
||||
min: 0,
|
||||
max: 1,
|
||||
},
|
||||
children: ["lightning-3"],
|
||||
},
|
||||
"lightning-3": {
|
||||
type: "PointLight",
|
||||
props: {
|
||||
position: [0, 0, 0],
|
||||
rotation: null,
|
||||
scale: null,
|
||||
color: "#99bbff",
|
||||
intensity: 40,
|
||||
distance: 20,
|
||||
decay: 2,
|
||||
castShadow: false,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
rain: {
|
||||
type: "Sparkles",
|
||||
props: {
|
||||
position: [0, 5, 0],
|
||||
rotation: null,
|
||||
scale: [20, 15, 20],
|
||||
count: 500,
|
||||
speed: 5,
|
||||
opacity: 0.4,
|
||||
color: "#6688aa",
|
||||
size: 0.5,
|
||||
noise: 0.5,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
shake: {
|
||||
type: "CameraShake",
|
||||
props: {
|
||||
intensity: 0.2,
|
||||
maxYaw: 0.02,
|
||||
maxPitch: 0.03,
|
||||
maxRoll: 0.01,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
post: {
|
||||
type: "EffectComposer",
|
||||
props: { enabled: true, multisampling: 8 },
|
||||
children: ["bloom", "vignette"],
|
||||
},
|
||||
bloom: {
|
||||
type: "Bloom",
|
||||
props: {
|
||||
intensity: 0.8,
|
||||
luminanceThreshold: 0.3,
|
||||
luminanceSmoothing: null,
|
||||
mipmapBlur: true,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
vignette: {
|
||||
type: "Vignette",
|
||||
props: { offset: 0.3, darkness: 0.8 },
|
||||
children: [],
|
||||
},
|
||||
fog: {
|
||||
type: "Fog",
|
||||
props: { color: "#0a0a15", near: 5, far: 30 },
|
||||
children: [],
|
||||
},
|
||||
controls: {
|
||||
type: "OrbitControls",
|
||||
props: {
|
||||
enableDamping: null,
|
||||
dampingFactor: null,
|
||||
enableZoom: true,
|
||||
enablePan: null,
|
||||
enableRotate: true,
|
||||
minDistance: 3,
|
||||
maxDistance: 20,
|
||||
minPolarAngle: 0.2,
|
||||
maxPolarAngle: 1.3,
|
||||
autoRotate: true,
|
||||
autoRotateSpeed: 0.1,
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,12 @@
|
||||
import { nextJsConfig } from "@internal/eslint-config/next-js";
|
||||
|
||||
/** @type {import("eslint").Linter.Config[]} */
|
||||
export default [
|
||||
...nextJsConfig,
|
||||
{
|
||||
rules: {
|
||||
"react/prop-types": "off",
|
||||
"react/no-unknown-property": "off",
|
||||
},
|
||||
},
|
||||
];
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
/// <reference types="next" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
import "./.next/dev/types/routes.d.ts";
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|
||||
@@ -0,0 +1,12 @@
|
||||
import type { NextConfig } from "next";
|
||||
|
||||
const nextConfig: NextConfig = {
|
||||
transpilePackages: [
|
||||
"@json-render/core",
|
||||
"@json-render/react",
|
||||
"@json-render/react-three-fiber",
|
||||
"three",
|
||||
],
|
||||
};
|
||||
|
||||
export default nextConfig;
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"name": "example-react-three-fiber",
|
||||
"version": "0.1.7",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"predev": "command -v portless >/dev/null 2>&1 || (echo '\\nportless is required but not installed. Run: npm i -g portless\\nSee: https://github.com/vercel-labs/portless\\n' && exit 1)",
|
||||
"dev": "portless react-three-fiber-demo.json-render next dev --turbopack",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "eslint --max-warnings 0",
|
||||
"check-types": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@json-render/core": "workspace:*",
|
||||
"@json-render/react": "workspace:*",
|
||||
"@json-render/react-three-fiber": "workspace:*",
|
||||
"@react-three/drei": "^10.7.7",
|
||||
"@react-three/fiber": "^9.5.0",
|
||||
"next": "16.1.6",
|
||||
"react": "19.2.4",
|
||||
"react-dom": "19.2.4",
|
||||
"three": "^0.183.2",
|
||||
"zod": "4.3.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@internal/eslint-config": "workspace:*",
|
||||
"@types/node": "^22.10.0",
|
||||
"@types/react": "19.2.3",
|
||||
"@types/react-dom": "19.2.3",
|
||||
"@types/three": "^0.183.1",
|
||||
"eslint": "^9.39.1",
|
||||
"typescript": "^5.7.2"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"extends": "../../packages/typescript-config/nextjs.json",
|
||||
"compilerOptions": {
|
||||
"plugins": [{ "name": "next" }],
|
||||
"declaration": false,
|
||||
"declarationMap": false,
|
||||
"paths": {
|
||||
"@/*": ["./*"]
|
||||
}
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
@@ -1,5 +1,13 @@
|
||||
# example-remotion
|
||||
|
||||
## 0.1.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [5b32de8]
|
||||
- @json-render/core@0.13.0
|
||||
- @json-render/remotion@0.13.0
|
||||
|
||||
## 0.1.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "example-remotion",
|
||||
"version": "0.1.6",
|
||||
"version": "0.1.7",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
# example-solid
|
||||
|
||||
## 0.1.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [5b32de8]
|
||||
- @json-render/core@0.13.0
|
||||
- @json-render/solid@0.13.0
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "example-solid",
|
||||
"version": "0.1.0",
|
||||
"version": "0.1.1",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
# com.example.json-render-demo
|
||||
|
||||
## 0.0.8
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [5b32de8]
|
||||
- @json-render/core@0.13.0
|
||||
- @json-render/react@0.13.0
|
||||
|
||||
## 0.0.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "com.example.json-render-demo",
|
||||
"version": "0.0.7",
|
||||
"version": "0.0.8",
|
||||
"description": "Test",
|
||||
"private": true,
|
||||
"license": "~~proprietary~~",
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
# com.example.json-render-fullpage-demo
|
||||
|
||||
## 0.0.8
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [5b32de8]
|
||||
- @json-render/core@0.13.0
|
||||
- @json-render/react@0.13.0
|
||||
|
||||
## 0.0.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "com.example.json-render-fullpage-demo",
|
||||
"version": "0.0.7",
|
||||
"version": "0.0.8",
|
||||
"description": "Full-page Stripe App example (alpha)",
|
||||
"private": true,
|
||||
"license": "~~proprietary~~",
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
# svelte-chat
|
||||
|
||||
## 0.0.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [5b32de8]
|
||||
- @json-render/core@0.13.0
|
||||
- @json-render/svelte@0.13.0
|
||||
|
||||
## 0.0.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "svelte-chat",
|
||||
"private": true,
|
||||
"version": "0.0.3",
|
||||
"version": "0.0.4",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite dev",
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
# example-svelte
|
||||
|
||||
## 0.1.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [5b32de8]
|
||||
- @json-render/core@0.13.0
|
||||
- @json-render/svelte@0.13.0
|
||||
|
||||
## 0.1.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "example-svelte",
|
||||
"version": "0.1.3",
|
||||
"version": "0.1.4",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
@@ -1,5 +1,16 @@
|
||||
# vite-renderers
|
||||
|
||||
## 0.1.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [5b32de8]
|
||||
- @json-render/core@0.13.0
|
||||
- @json-render/solid@0.13.0
|
||||
- @json-render/react@0.13.0
|
||||
- @json-render/svelte@0.13.0
|
||||
- @json-render/vue@0.13.0
|
||||
|
||||
## 0.1.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "vite-renderers",
|
||||
"version": "0.1.4",
|
||||
"version": "0.1.5",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
# example-vue
|
||||
|
||||
## 0.1.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [5b32de8]
|
||||
- @json-render/core@0.13.0
|
||||
- @json-render/vue@0.13.0
|
||||
|
||||
## 0.1.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "example-vue",
|
||||
"version": "0.1.4",
|
||||
"version": "0.1.5",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"predev": "command -v portless >/dev/null 2>&1 || (echo '\\nportless is required but not installed. Run: npm i -g portless\\nSee: https://github.com/vercel-labs/portless\\n' && exit 1)",
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# @json-render/codegen
|
||||
|
||||
## 0.13.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [5b32de8]
|
||||
- @json-render/core@0.13.0
|
||||
|
||||
## 0.12.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@json-render/codegen",
|
||||
"version": "0.12.1",
|
||||
"version": "0.13.0",
|
||||
"license": "Apache-2.0",
|
||||
"description": "Utilities for generating code from json-render UI trees",
|
||||
"keywords": [
|
||||
|
||||
@@ -1,5 +1,18 @@
|
||||
# @json-render/core
|
||||
|
||||
## 0.13.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- 5b32de8: Add SolidJS and React Three Fiber renderers, plus strict JSON Schema mode for LLM structured outputs.
|
||||
|
||||
### New:
|
||||
- **`@json-render/solid`** -- SolidJS renderer. JSON becomes Solid components with reactive rendering, schema export, and full catalog support.
|
||||
- **`@json-render/react-three-fiber`** -- React Three Fiber renderer. JSON becomes 3D scenes with 19 built-in components for meshes, lights, models, environments, text, cameras, and controls.
|
||||
|
||||
### Improved:
|
||||
- **`@json-render/core`** -- `jsonSchema({ strict: true })` produces a JSON Schema subset compatible with LLM structured output APIs (OpenAI, Google Gemini, Anthropic). Ensures `additionalProperties: false` on every object and all properties listed in `required`.
|
||||
|
||||
## 0.12.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@json-render/core",
|
||||
"version": "0.12.1",
|
||||
"version": "0.13.0",
|
||||
"license": "Apache-2.0",
|
||||
"description": "JSON becomes real things. Define your catalog, register your components, let AI generate.",
|
||||
"keywords": [
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# @json-render/image
|
||||
|
||||
## 0.13.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [5b32de8]
|
||||
- @json-render/core@0.13.0
|
||||
|
||||
## 0.12.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@json-render/image",
|
||||
"version": "0.12.1",
|
||||
"version": "0.13.0",
|
||||
"license": "Apache-2.0",
|
||||
"description": "Image renderer for @json-render/core. JSON becomes SVG and PNG images via Satori.",
|
||||
"keywords": [
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# @json-render/jotai
|
||||
|
||||
## 0.13.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [5b32de8]
|
||||
- @json-render/core@0.13.0
|
||||
|
||||
## 0.12.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@json-render/jotai",
|
||||
"version": "0.12.1",
|
||||
"version": "0.13.0",
|
||||
"license": "Apache-2.0",
|
||||
"description": "Jotai adapter for json-render StateStore",
|
||||
"keywords": [
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# @json-render/mcp
|
||||
|
||||
## 0.13.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [5b32de8]
|
||||
- @json-render/core@0.13.0
|
||||
|
||||
## 0.12.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@json-render/mcp",
|
||||
"version": "0.12.1",
|
||||
"version": "0.13.0",
|
||||
"license": "Apache-2.0",
|
||||
"description": "MCP Apps integration for @json-render/core. Serve json-render UIs as interactive MCP Apps in Claude, ChatGPT, Cursor, and VS Code.",
|
||||
"keywords": [
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# @json-render/react-email
|
||||
|
||||
## 0.13.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [5b32de8]
|
||||
- @json-render/core@0.13.0
|
||||
|
||||
## 0.12.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@json-render/react-email",
|
||||
"version": "0.12.1",
|
||||
"version": "0.13.0",
|
||||
"license": "Apache-2.0",
|
||||
"description": "React Email renderer for @json-render/core. JSON becomes HTML emails.",
|
||||
"keywords": [
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# @json-render/react-native
|
||||
|
||||
## 0.13.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [5b32de8]
|
||||
- @json-render/core@0.13.0
|
||||
|
||||
## 0.12.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@json-render/react-native",
|
||||
"version": "0.12.1",
|
||||
"version": "0.13.0",
|
||||
"license": "Apache-2.0",
|
||||
"description": "React Native renderer for @json-render/core. JSON becomes React Native components.",
|
||||
"keywords": [
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# @json-render/react-pdf
|
||||
|
||||
## 0.13.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [5b32de8]
|
||||
- @json-render/core@0.13.0
|
||||
|
||||
## 0.12.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@json-render/react-pdf",
|
||||
"version": "0.12.1",
|
||||
"version": "0.13.0",
|
||||
"license": "Apache-2.0",
|
||||
"description": "React PDF renderer for @json-render/core. JSON becomes PDF documents.",
|
||||
"keywords": [
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# @internal/react-state
|
||||
|
||||
## 0.8.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [5b32de8]
|
||||
- @json-render/core@0.13.0
|
||||
|
||||
## 0.8.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@internal/react-state",
|
||||
"version": "0.8.6",
|
||||
"version": "0.8.7",
|
||||
"private": true,
|
||||
"license": "Apache-2.0",
|
||||
"description": "Shared React state context for json-render renderer packages",
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
# @json-render/react-three-fiber
|
||||
|
||||
## 0.13.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- 5b32de8: Add SolidJS and React Three Fiber renderers, plus strict JSON Schema mode for LLM structured outputs.
|
||||
|
||||
### New:
|
||||
- **`@json-render/solid`** -- SolidJS renderer. JSON becomes Solid components with reactive rendering, schema export, and full catalog support.
|
||||
- **`@json-render/react-three-fiber`** -- React Three Fiber renderer. JSON becomes 3D scenes with 19 built-in components for meshes, lights, models, environments, text, cameras, and controls.
|
||||
|
||||
### Improved:
|
||||
- **`@json-render/core`** -- `jsonSchema({ strict: true })` produces a JSON Schema subset compatible with LLM structured output APIs (OpenAI, Google Gemini, Anthropic). Ensures `additionalProperties: false` on every object and all properties listed in `required`.
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [5b32de8]
|
||||
- @json-render/core@0.13.0
|
||||
- @json-render/react@0.13.0
|
||||
@@ -0,0 +1,75 @@
|
||||
# @json-render/react-three-fiber
|
||||
|
||||
React Three Fiber renderer for [`@json-render/core`](https://json-render.dev). JSON becomes 3D scenes.
|
||||
|
||||
19 built-in components for meshes, lights, models, environments, text, cameras, and controls.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
npm install @json-render/react-three-fiber @json-render/core @json-render/react @react-three/fiber @react-three/drei three zod
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```tsx
|
||||
import { defineCatalog } from "@json-render/core";
|
||||
import { schema, defineRegistry } from "@json-render/react";
|
||||
import {
|
||||
threeComponentDefinitions,
|
||||
threeComponents,
|
||||
ThreeCanvas,
|
||||
} from "@json-render/react-three-fiber";
|
||||
|
||||
// Build catalog with 3D components
|
||||
const catalog = defineCatalog(schema, {
|
||||
components: {
|
||||
Box: threeComponentDefinitions.Box,
|
||||
Sphere: threeComponentDefinitions.Sphere,
|
||||
AmbientLight: threeComponentDefinitions.AmbientLight,
|
||||
DirectionalLight: threeComponentDefinitions.DirectionalLight,
|
||||
OrbitControls: threeComponentDefinitions.OrbitControls,
|
||||
},
|
||||
actions: {},
|
||||
});
|
||||
|
||||
// Register implementations
|
||||
const { registry } = defineRegistry(catalog, {
|
||||
components: {
|
||||
Box: threeComponents.Box,
|
||||
Sphere: threeComponents.Sphere,
|
||||
AmbientLight: threeComponents.AmbientLight,
|
||||
DirectionalLight: threeComponents.DirectionalLight,
|
||||
OrbitControls: threeComponents.OrbitControls,
|
||||
},
|
||||
});
|
||||
|
||||
// Render
|
||||
<ThreeCanvas
|
||||
spec={spec}
|
||||
registry={registry}
|
||||
shadows
|
||||
camera={{ position: [5, 5, 5], fov: 50 }}
|
||||
style={{ width: "100%", height: "100vh" }}
|
||||
/>;
|
||||
```
|
||||
|
||||
## Components
|
||||
|
||||
**Primitives:** Box, Sphere, Cylinder, Cone, Torus, Plane, Capsule
|
||||
|
||||
**Lights:** AmbientLight, DirectionalLight, PointLight, SpotLight
|
||||
|
||||
**Container:** Group
|
||||
|
||||
**Model:** Model (GLTF/GLB)
|
||||
|
||||
**Environment:** Environment, Fog, GridHelper
|
||||
|
||||
**Text:** Text3D
|
||||
|
||||
**Camera/Controls:** PerspectiveCamera, OrbitControls
|
||||
|
||||
## Docs
|
||||
|
||||
Full API reference: [json-render.dev/docs/api/react-three-fiber](https://json-render.dev/docs/api/react-three-fiber)
|
||||
@@ -0,0 +1,80 @@
|
||||
{
|
||||
"name": "@json-render/react-three-fiber",
|
||||
"version": "0.13.0",
|
||||
"license": "Apache-2.0",
|
||||
"description": "React Three Fiber renderer for @json-render/core. JSON becomes 3D scenes.",
|
||||
"keywords": [
|
||||
"json",
|
||||
"3d",
|
||||
"three",
|
||||
"threejs",
|
||||
"react-three-fiber",
|
||||
"r3f",
|
||||
"webgl",
|
||||
"ai",
|
||||
"generative-ui",
|
||||
"llm",
|
||||
"renderer",
|
||||
"streaming",
|
||||
"components"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/vercel-labs/json-render.git",
|
||||
"directory": "packages/react-three-fiber"
|
||||
},
|
||||
"homepage": "https://json-render.dev",
|
||||
"bugs": {
|
||||
"url": "https://github.com/vercel-labs/json-render/issues"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.mjs",
|
||||
"types": "./dist/index.d.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"import": "./dist/index.mjs",
|
||||
"require": "./dist/index.js"
|
||||
},
|
||||
"./catalog": {
|
||||
"types": "./dist/catalog.d.ts",
|
||||
"import": "./dist/catalog.mjs",
|
||||
"require": "./dist/catalog.js"
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "tsup",
|
||||
"dev": "tsup --watch",
|
||||
"check-types": "tsc --noEmit",
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@json-render/core": "workspace:*",
|
||||
"@json-render/react": "workspace:*",
|
||||
"@react-three/postprocessing": "^3.0.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@internal/typescript-config": "workspace:*",
|
||||
"@react-three/drei": "^10.7.7",
|
||||
"@react-three/fiber": "^9.5.0",
|
||||
"@types/react": "19.2.3",
|
||||
"@types/three": "^0.183.1",
|
||||
"three": "^0.183.2",
|
||||
"tsup": "^8.0.2",
|
||||
"typescript": "^5.4.5",
|
||||
"zod": "^4.3.6"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@react-three/drei": ">=9.0.0",
|
||||
"@react-three/fiber": ">=8.0.0",
|
||||
"react": "^19.0.0",
|
||||
"three": ">=0.160.0",
|
||||
"zod": "^4.0.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,841 @@
|
||||
import { z } from "zod";
|
||||
import {
|
||||
vector3Schema,
|
||||
materialSchema,
|
||||
transformProps,
|
||||
shadowProps,
|
||||
} from "./schemas";
|
||||
|
||||
/**
|
||||
* React Three Fiber component definitions for json-render catalogs.
|
||||
*
|
||||
* These can be used directly or extended with custom 3D components.
|
||||
* All components render as React Three Fiber elements.
|
||||
*/
|
||||
export const threeComponentDefinitions = {
|
||||
// ===========================================================================
|
||||
// Primitives
|
||||
// ===========================================================================
|
||||
|
||||
Box: {
|
||||
props: z.object({
|
||||
...transformProps,
|
||||
...shadowProps,
|
||||
material: materialSchema.nullable(),
|
||||
width: z.number().nullable(),
|
||||
height: z.number().nullable(),
|
||||
depth: z.number().nullable(),
|
||||
}),
|
||||
description:
|
||||
"Box mesh. Defaults to 1x1x1. Use material for appearance, transform props for placement.",
|
||||
example: {
|
||||
position: [0, 0.5, 0],
|
||||
material: { color: "#4488ff" },
|
||||
},
|
||||
},
|
||||
|
||||
Sphere: {
|
||||
props: z.object({
|
||||
...transformProps,
|
||||
...shadowProps,
|
||||
material: materialSchema.nullable(),
|
||||
radius: z.number().nullable(),
|
||||
widthSegments: z.number().nullable(),
|
||||
heightSegments: z.number().nullable(),
|
||||
}),
|
||||
description: "Sphere mesh. Defaults to radius 1.",
|
||||
example: {
|
||||
position: [0, 1, 0],
|
||||
radius: 0.5,
|
||||
material: { color: "#ff4444" },
|
||||
},
|
||||
},
|
||||
|
||||
Cylinder: {
|
||||
props: z.object({
|
||||
...transformProps,
|
||||
...shadowProps,
|
||||
material: materialSchema.nullable(),
|
||||
radiusTop: z.number().nullable(),
|
||||
radiusBottom: z.number().nullable(),
|
||||
height: z.number().nullable(),
|
||||
radialSegments: z.number().nullable(),
|
||||
}),
|
||||
description: "Cylinder mesh. Use radiusTop/radiusBottom for tapering.",
|
||||
example: {
|
||||
position: [0, 0.5, 0],
|
||||
height: 1,
|
||||
material: { color: "#44ff44" },
|
||||
},
|
||||
},
|
||||
|
||||
Cone: {
|
||||
props: z.object({
|
||||
...transformProps,
|
||||
...shadowProps,
|
||||
material: materialSchema.nullable(),
|
||||
radius: z.number().nullable(),
|
||||
height: z.number().nullable(),
|
||||
radialSegments: z.number().nullable(),
|
||||
}),
|
||||
description: "Cone mesh.",
|
||||
example: {
|
||||
position: [0, 0.5, 0],
|
||||
material: { color: "#ffaa00" },
|
||||
},
|
||||
},
|
||||
|
||||
Torus: {
|
||||
props: z.object({
|
||||
...transformProps,
|
||||
...shadowProps,
|
||||
material: materialSchema.nullable(),
|
||||
radius: z.number().nullable(),
|
||||
tube: z.number().nullable(),
|
||||
radialSegments: z.number().nullable(),
|
||||
tubularSegments: z.number().nullable(),
|
||||
}),
|
||||
description: "Torus (donut) mesh.",
|
||||
example: {
|
||||
position: [0, 1, 0],
|
||||
material: { color: "#ff44ff" },
|
||||
},
|
||||
},
|
||||
|
||||
Plane: {
|
||||
props: z.object({
|
||||
...transformProps,
|
||||
...shadowProps,
|
||||
material: materialSchema.nullable(),
|
||||
width: z.number().nullable(),
|
||||
height: z.number().nullable(),
|
||||
}),
|
||||
slots: ["default"],
|
||||
description:
|
||||
"Flat plane mesh. Useful for floors/walls. Defaults to XY plane; rotate [-Math.PI/2, 0, 0] for ground. Pass MeshPortalMaterial as child for portal surfaces.",
|
||||
example: {
|
||||
position: [0, 0, 0],
|
||||
rotation: [-1.5708, 0, 0],
|
||||
scale: [10, 10, 1],
|
||||
material: { color: "#888888" },
|
||||
receiveShadow: true,
|
||||
},
|
||||
},
|
||||
|
||||
Capsule: {
|
||||
props: z.object({
|
||||
...transformProps,
|
||||
...shadowProps,
|
||||
material: materialSchema.nullable(),
|
||||
radius: z.number().nullable(),
|
||||
length: z.number().nullable(),
|
||||
capSegments: z.number().nullable(),
|
||||
radialSegments: z.number().nullable(),
|
||||
}),
|
||||
description: "Capsule mesh (cylinder with hemispherical caps).",
|
||||
example: {
|
||||
position: [0, 1, 0],
|
||||
material: { color: "#00cccc" },
|
||||
},
|
||||
},
|
||||
|
||||
// ===========================================================================
|
||||
// Lights
|
||||
// ===========================================================================
|
||||
|
||||
AmbientLight: {
|
||||
props: z.object({
|
||||
color: z.string().nullable(),
|
||||
intensity: z.number().nullable(),
|
||||
}),
|
||||
description:
|
||||
"Ambient light that illuminates all objects equally. No position needed.",
|
||||
example: { color: "#ffffff", intensity: 0.5 },
|
||||
},
|
||||
|
||||
DirectionalLight: {
|
||||
props: z.object({
|
||||
...transformProps,
|
||||
color: z.string().nullable(),
|
||||
intensity: z.number().nullable(),
|
||||
castShadow: z.boolean().nullable(),
|
||||
}),
|
||||
description:
|
||||
"Directional light (like sunlight). Position determines the direction it shines from.",
|
||||
example: {
|
||||
position: [5, 10, 5],
|
||||
intensity: 1,
|
||||
castShadow: true,
|
||||
},
|
||||
},
|
||||
|
||||
PointLight: {
|
||||
props: z.object({
|
||||
...transformProps,
|
||||
color: z.string().nullable(),
|
||||
intensity: z.number().nullable(),
|
||||
distance: z.number().nullable(),
|
||||
decay: z.number().nullable(),
|
||||
castShadow: z.boolean().nullable(),
|
||||
}),
|
||||
description: "Point light that radiates in all directions from a position.",
|
||||
example: {
|
||||
position: [0, 3, 0],
|
||||
intensity: 1,
|
||||
distance: 10,
|
||||
},
|
||||
},
|
||||
|
||||
SpotLight: {
|
||||
props: z.object({
|
||||
...transformProps,
|
||||
color: z.string().nullable(),
|
||||
intensity: z.number().nullable(),
|
||||
distance: z.number().nullable(),
|
||||
decay: z.number().nullable(),
|
||||
angle: z.number().nullable(),
|
||||
penumbra: z.number().nullable(),
|
||||
castShadow: z.boolean().nullable(),
|
||||
}),
|
||||
description:
|
||||
"Spot light emitting a cone of light. Angle in radians (default ~Math.PI/3).",
|
||||
example: {
|
||||
position: [0, 5, 0],
|
||||
intensity: 1,
|
||||
angle: 0.5,
|
||||
penumbra: 0.5,
|
||||
castShadow: true,
|
||||
},
|
||||
},
|
||||
|
||||
// ===========================================================================
|
||||
// Container
|
||||
// ===========================================================================
|
||||
|
||||
Group: {
|
||||
props: z.object({
|
||||
...transformProps,
|
||||
}),
|
||||
slots: ["default"],
|
||||
description:
|
||||
"Container for grouping child objects. Transforms apply to all children.",
|
||||
example: { position: [0, 0, 0] },
|
||||
},
|
||||
|
||||
// ===========================================================================
|
||||
// Model
|
||||
// ===========================================================================
|
||||
|
||||
Model: {
|
||||
props: z.object({
|
||||
...transformProps,
|
||||
...shadowProps,
|
||||
url: z.string(),
|
||||
}),
|
||||
description:
|
||||
"GLTF/GLB 3D model loader. Provide a URL to a .glb or .gltf file.",
|
||||
example: {
|
||||
url: "/models/robot.glb",
|
||||
position: [0, 0, 0],
|
||||
scale: [1, 1, 1],
|
||||
},
|
||||
},
|
||||
|
||||
// ===========================================================================
|
||||
// Environment
|
||||
// ===========================================================================
|
||||
|
||||
Environment: {
|
||||
props: z.object({
|
||||
preset: z
|
||||
.enum([
|
||||
"apartment",
|
||||
"city",
|
||||
"dawn",
|
||||
"forest",
|
||||
"lobby",
|
||||
"night",
|
||||
"park",
|
||||
"studio",
|
||||
"sunset",
|
||||
"warehouse",
|
||||
])
|
||||
.nullable(),
|
||||
background: z.boolean().nullable(),
|
||||
blur: z.number().nullable(),
|
||||
intensity: z.number().nullable(),
|
||||
}),
|
||||
description:
|
||||
"HDRI environment map for lighting and background. Choose a preset or provide a custom HDRI.",
|
||||
example: { preset: "sunset", background: true },
|
||||
},
|
||||
|
||||
Fog: {
|
||||
props: z.object({
|
||||
color: z.string().nullable(),
|
||||
near: z.number().nullable(),
|
||||
far: z.number().nullable(),
|
||||
}),
|
||||
description:
|
||||
"Linear fog effect. Objects fade between near and far distances.",
|
||||
example: { color: "#cccccc", near: 10, far: 50 },
|
||||
},
|
||||
|
||||
GridHelper: {
|
||||
props: z.object({
|
||||
...transformProps,
|
||||
size: z.number().nullable(),
|
||||
divisions: z.number().nullable(),
|
||||
color: z.string().nullable(),
|
||||
secondaryColor: z.string().nullable(),
|
||||
}),
|
||||
description: "Visual grid for reference. Renders on the XZ plane.",
|
||||
example: { size: 10, divisions: 10 },
|
||||
},
|
||||
|
||||
// ===========================================================================
|
||||
// Text
|
||||
// ===========================================================================
|
||||
|
||||
Text3D: {
|
||||
props: z.object({
|
||||
...transformProps,
|
||||
text: z.string(),
|
||||
fontSize: z.number().nullable(),
|
||||
color: z.string().nullable(),
|
||||
anchorX: z.enum(["left", "center", "right"]).nullable(),
|
||||
anchorY: z
|
||||
.enum(["top", "top-baseline", "middle", "bottom-baseline", "bottom"])
|
||||
.nullable(),
|
||||
maxWidth: z.number().nullable(),
|
||||
}),
|
||||
description:
|
||||
"3D text rendered in the scene. Uses SDF text for crisp rendering at any size.",
|
||||
example: {
|
||||
text: "Hello World",
|
||||
fontSize: 1,
|
||||
color: "#ffffff",
|
||||
position: [0, 2, 0],
|
||||
},
|
||||
},
|
||||
|
||||
// ===========================================================================
|
||||
// Effects / Atmosphere
|
||||
// ===========================================================================
|
||||
|
||||
Sparkles: {
|
||||
props: z.object({
|
||||
...transformProps,
|
||||
count: z.number().nullable(),
|
||||
speed: z.number().nullable(),
|
||||
opacity: z.number().nullable(),
|
||||
color: z.string().nullable(),
|
||||
size: z.number().nullable(),
|
||||
noise: z.number().nullable(),
|
||||
}),
|
||||
description:
|
||||
"Floating particle sparkles. Great for magic, snow, or ambient effects.",
|
||||
example: {
|
||||
count: 100,
|
||||
speed: 0.5,
|
||||
size: 2,
|
||||
color: "#ffffff",
|
||||
scale: [5, 5, 5],
|
||||
},
|
||||
},
|
||||
|
||||
Stars: {
|
||||
props: z.object({
|
||||
radius: z.number().nullable(),
|
||||
depth: z.number().nullable(),
|
||||
count: z.number().nullable(),
|
||||
factor: z.number().nullable(),
|
||||
saturation: z.number().nullable(),
|
||||
fade: z.boolean().nullable(),
|
||||
speed: z.number().nullable(),
|
||||
}),
|
||||
description:
|
||||
"Starfield background. Renders thousands of stars in a sphere around the scene.",
|
||||
example: { radius: 100, depth: 50, count: 5000, factor: 4, fade: true },
|
||||
},
|
||||
|
||||
Sky: {
|
||||
props: z.object({
|
||||
distance: z.number().nullable(),
|
||||
sunPosition: vector3Schema.nullable(),
|
||||
inclination: z.number().nullable(),
|
||||
azimuth: z.number().nullable(),
|
||||
mieCoefficient: z.number().nullable(),
|
||||
mieDirectionalG: z.number().nullable(),
|
||||
rayleigh: z.number().nullable(),
|
||||
turbidity: z.number().nullable(),
|
||||
}),
|
||||
description:
|
||||
"Procedural sky with sun. Control sun position, haze, and scattering.",
|
||||
example: { sunPosition: [100, 20, 100], turbidity: 8, rayleigh: 2 },
|
||||
},
|
||||
|
||||
Cloud: {
|
||||
props: z.object({
|
||||
...transformProps,
|
||||
seed: z.number().nullable(),
|
||||
segments: z.number().nullable(),
|
||||
bounds: vector3Schema.nullable(),
|
||||
volume: z.number().nullable(),
|
||||
speed: z.number().nullable(),
|
||||
fade: z.number().nullable(),
|
||||
opacity: z.number().nullable(),
|
||||
color: z.string().nullable(),
|
||||
growth: z.number().nullable(),
|
||||
}),
|
||||
description:
|
||||
"Volumetric cloud. Use multiple for a cloudscape. Wrap in a Clouds parent for batching.",
|
||||
example: {
|
||||
position: [0, 5, 0],
|
||||
speed: 0.2,
|
||||
opacity: 0.6,
|
||||
color: "#ffffff",
|
||||
},
|
||||
},
|
||||
|
||||
// ===========================================================================
|
||||
// Special Materials (geometry + material combos)
|
||||
// ===========================================================================
|
||||
|
||||
GlassSphere: {
|
||||
props: z.object({
|
||||
...transformProps,
|
||||
...shadowProps,
|
||||
radius: z.number().nullable(),
|
||||
widthSegments: z.number().nullable(),
|
||||
heightSegments: z.number().nullable(),
|
||||
color: z.string().nullable(),
|
||||
transmission: z.number().nullable(),
|
||||
thickness: z.number().nullable(),
|
||||
roughness: z.number().nullable(),
|
||||
chromaticAberration: z.number().nullable(),
|
||||
ior: z.number().nullable(),
|
||||
distortion: z.number().nullable(),
|
||||
distortionScale: z.number().nullable(),
|
||||
temporalDistortion: z.number().nullable(),
|
||||
samples: z.number().nullable(),
|
||||
resolution: z.number().nullable(),
|
||||
}),
|
||||
description:
|
||||
"Glass sphere with transmission/refraction. Creates photorealistic glass effect.",
|
||||
example: {
|
||||
position: [0, 1, 0],
|
||||
radius: 1,
|
||||
transmission: 1,
|
||||
thickness: 0.5,
|
||||
roughness: 0,
|
||||
chromaticAberration: 0.06,
|
||||
},
|
||||
},
|
||||
|
||||
GlassBox: {
|
||||
props: z.object({
|
||||
...transformProps,
|
||||
...shadowProps,
|
||||
width: z.number().nullable(),
|
||||
height: z.number().nullable(),
|
||||
depth: z.number().nullable(),
|
||||
color: z.string().nullable(),
|
||||
transmission: z.number().nullable(),
|
||||
thickness: z.number().nullable(),
|
||||
roughness: z.number().nullable(),
|
||||
chromaticAberration: z.number().nullable(),
|
||||
ior: z.number().nullable(),
|
||||
distortion: z.number().nullable(),
|
||||
distortionScale: z.number().nullable(),
|
||||
temporalDistortion: z.number().nullable(),
|
||||
samples: z.number().nullable(),
|
||||
resolution: z.number().nullable(),
|
||||
}),
|
||||
description:
|
||||
"Glass box with transmission/refraction. Photorealistic glass cuboid.",
|
||||
example: {
|
||||
position: [0, 0.5, 0],
|
||||
width: 1,
|
||||
height: 1,
|
||||
depth: 1,
|
||||
transmission: 1,
|
||||
thickness: 0.5,
|
||||
},
|
||||
},
|
||||
|
||||
DistortSphere: {
|
||||
props: z.object({
|
||||
...transformProps,
|
||||
...shadowProps,
|
||||
radius: z.number().nullable(),
|
||||
widthSegments: z.number().nullable(),
|
||||
heightSegments: z.number().nullable(),
|
||||
color: z.string().nullable(),
|
||||
speed: z.number().nullable(),
|
||||
distort: z.number().nullable(),
|
||||
metalness: z.number().nullable(),
|
||||
roughness: z.number().nullable(),
|
||||
}),
|
||||
description:
|
||||
"Animated distorting sphere. Organic, blobby look -- like liquid metal.",
|
||||
example: {
|
||||
position: [0, 1, 0],
|
||||
radius: 1,
|
||||
color: "#ff6600",
|
||||
speed: 2,
|
||||
distort: 0.5,
|
||||
},
|
||||
},
|
||||
|
||||
// ===========================================================================
|
||||
// Extended Geometry
|
||||
// ===========================================================================
|
||||
|
||||
TorusKnot: {
|
||||
props: z.object({
|
||||
...transformProps,
|
||||
...shadowProps,
|
||||
material: materialSchema.nullable(),
|
||||
radius: z.number().nullable(),
|
||||
tube: z.number().nullable(),
|
||||
tubularSegments: z.number().nullable(),
|
||||
radialSegments: z.number().nullable(),
|
||||
p: z.number().nullable(),
|
||||
q: z.number().nullable(),
|
||||
}),
|
||||
description:
|
||||
"Torus knot mesh. A continuous 3D curve that creates intricate knot shapes via p and q parameters.",
|
||||
example: {
|
||||
position: [0, 1, 0],
|
||||
radius: 1,
|
||||
tube: 0.3,
|
||||
p: 2,
|
||||
q: 3,
|
||||
material: { color: "#ff44ff", metalness: 0.8, roughness: 0.2 },
|
||||
},
|
||||
},
|
||||
|
||||
RoundedBox: {
|
||||
props: z.object({
|
||||
...transformProps,
|
||||
...shadowProps,
|
||||
material: materialSchema.nullable(),
|
||||
width: z.number().nullable(),
|
||||
height: z.number().nullable(),
|
||||
depth: z.number().nullable(),
|
||||
radius: z.number().nullable(),
|
||||
smoothness: z.number().nullable(),
|
||||
}),
|
||||
description:
|
||||
"Box with rounded edges. More polished look than a plain box. Great for product-style scenes.",
|
||||
example: {
|
||||
position: [0, 0.5, 0],
|
||||
width: 1,
|
||||
height: 1,
|
||||
depth: 1,
|
||||
radius: 0.1,
|
||||
material: { color: "#4488ff" },
|
||||
},
|
||||
},
|
||||
|
||||
// ===========================================================================
|
||||
// Shadows / Staging
|
||||
// ===========================================================================
|
||||
|
||||
ContactShadows: {
|
||||
props: z.object({
|
||||
...transformProps,
|
||||
opacity: z.number().nullable(),
|
||||
width: z.number().nullable(),
|
||||
height: z.number().nullable(),
|
||||
blur: z.number().nullable(),
|
||||
near: z.number().nullable(),
|
||||
far: z.number().nullable(),
|
||||
smooth: z.boolean().nullable(),
|
||||
resolution: z.number().nullable(),
|
||||
frames: z.number().nullable(),
|
||||
color: z.string().nullable(),
|
||||
}),
|
||||
description:
|
||||
"Soft contact shadows projected onto the ground plane. Place at y=0 under objects.",
|
||||
example: {
|
||||
position: [0, 0, 0],
|
||||
opacity: 0.5,
|
||||
blur: 2,
|
||||
width: 10,
|
||||
height: 10,
|
||||
},
|
||||
},
|
||||
|
||||
Float: {
|
||||
props: z.object({
|
||||
...transformProps,
|
||||
speed: z.number().nullable(),
|
||||
rotationIntensity: z.number().nullable(),
|
||||
floatIntensity: z.number().nullable(),
|
||||
enabled: z.boolean().nullable(),
|
||||
}),
|
||||
slots: ["default"],
|
||||
description:
|
||||
"Wrapper that makes children gently float and bob. Adds organic motion to any object.",
|
||||
example: { speed: 1.5, rotationIntensity: 1, floatIntensity: 1 },
|
||||
},
|
||||
|
||||
ReflectorPlane: {
|
||||
props: z.object({
|
||||
...transformProps,
|
||||
width: z.number().nullable(),
|
||||
height: z.number().nullable(),
|
||||
color: z.string().nullable(),
|
||||
resolution: z.number().nullable(),
|
||||
blur: z.number().nullable(),
|
||||
mirror: z.number().nullable(),
|
||||
mixBlur: z.number().nullable(),
|
||||
mixStrength: z.number().nullable(),
|
||||
depthScale: z.number().nullable(),
|
||||
metalness: z.number().nullable(),
|
||||
roughness: z.number().nullable(),
|
||||
}),
|
||||
description:
|
||||
"Reflective floor/surface. Real-time mirror reflections on a plane. Rotate [-PI/2,0,0] for ground.",
|
||||
example: {
|
||||
position: [0, 0, 0],
|
||||
rotation: [-1.5708, 0, 0],
|
||||
width: 20,
|
||||
height: 20,
|
||||
mirror: 0.5,
|
||||
blur: [300, 100],
|
||||
resolution: 1024,
|
||||
},
|
||||
},
|
||||
|
||||
Backdrop: {
|
||||
props: z.object({
|
||||
...transformProps,
|
||||
floor: z.number().nullable(),
|
||||
segments: z.number().nullable(),
|
||||
receiveShadow: z.boolean().nullable(),
|
||||
}),
|
||||
slots: ["default"],
|
||||
description:
|
||||
"Curved studio backdrop for product-style scenes. Set floor for how much bends.",
|
||||
example: { floor: 0.25, segments: 20, receiveShadow: true },
|
||||
},
|
||||
|
||||
// ===========================================================================
|
||||
// Crazy / Magic
|
||||
// ===========================================================================
|
||||
|
||||
MeshPortalMaterial: {
|
||||
props: z.object({
|
||||
blend: z.number().nullable(),
|
||||
blur: z.number().nullable(),
|
||||
resolution: z.number().nullable(),
|
||||
}),
|
||||
slots: ["default"],
|
||||
description:
|
||||
"Renders children into a portal surface (like a window to another world). Use as a child of a Mesh.",
|
||||
example: { blend: 0 },
|
||||
},
|
||||
|
||||
HtmlLabel: {
|
||||
props: z.object({
|
||||
...transformProps,
|
||||
text: z.string(),
|
||||
transform: z.boolean().nullable(),
|
||||
distanceFactor: z.number().nullable(),
|
||||
color: z.string().nullable(),
|
||||
fontSize: z.number().nullable(),
|
||||
center: z.boolean().nullable(),
|
||||
}),
|
||||
description: "Renders actual HTML/DOM text floating in the 3D scene.",
|
||||
example: { text: "Hello World", transform: true, distanceFactor: 10 },
|
||||
},
|
||||
|
||||
// ===========================================================================
|
||||
// Post-Processing
|
||||
// ===========================================================================
|
||||
|
||||
EffectComposer: {
|
||||
props: z.object({
|
||||
enabled: z.boolean().nullable(),
|
||||
multisampling: z.number().nullable(),
|
||||
}),
|
||||
slots: ["default"],
|
||||
description:
|
||||
"Wrapper for post-processing effects. Add effects as children.",
|
||||
example: { multisampling: 8 },
|
||||
},
|
||||
|
||||
Bloom: {
|
||||
props: z.object({
|
||||
intensity: z.number().nullable(),
|
||||
luminanceThreshold: z.number().nullable(),
|
||||
luminanceSmoothing: z.number().nullable(),
|
||||
mipmapBlur: z.boolean().nullable(),
|
||||
}),
|
||||
description: "Bloom post-processing effect. Makes emissive materials glow.",
|
||||
example: { intensity: 1.5, luminanceThreshold: 0.1, mipmapBlur: true },
|
||||
},
|
||||
|
||||
Glitch: {
|
||||
props: z.object({
|
||||
delay: z.tuple([z.number(), z.number()]).nullable(),
|
||||
duration: z.tuple([z.number(), z.number()]).nullable(),
|
||||
strength: z.tuple([z.number(), z.number()]).nullable(),
|
||||
active: z.boolean().nullable(),
|
||||
ratio: z.number().nullable(),
|
||||
}),
|
||||
description: "Cyberpunk glitch post-processing effect.",
|
||||
example: { active: true },
|
||||
},
|
||||
|
||||
Vignette: {
|
||||
props: z.object({
|
||||
offset: z.number().nullable(),
|
||||
darkness: z.number().nullable(),
|
||||
}),
|
||||
description: "Vignette post-processing effect (darkened corners).",
|
||||
example: { offset: 0.5, darkness: 0.5 },
|
||||
},
|
||||
|
||||
// ===========================================================================
|
||||
// Animation Wrappers
|
||||
// ===========================================================================
|
||||
|
||||
WarpTunnel: {
|
||||
props: z.object({
|
||||
...transformProps,
|
||||
ringCount: z.number().nullable(),
|
||||
radius: z.number().nullable(),
|
||||
length: z.number().nullable(),
|
||||
speed: z.number().nullable(),
|
||||
tubeRadius: z.number().nullable(),
|
||||
color1: z.string().nullable(),
|
||||
color2: z.string().nullable(),
|
||||
}),
|
||||
description:
|
||||
"Animated neon tunnel flythrough. Rings rush toward the camera creating a hyperspace warp effect.",
|
||||
example: {
|
||||
speed: 8,
|
||||
ringCount: 80,
|
||||
radius: 3,
|
||||
color1: "#00ffff",
|
||||
color2: "#ff00ff",
|
||||
},
|
||||
},
|
||||
|
||||
Spin: {
|
||||
props: z.object({
|
||||
...transformProps,
|
||||
speed: z.number().nullable(),
|
||||
axis: z.enum(["x", "y", "z"]).nullable(),
|
||||
}),
|
||||
slots: ["default"],
|
||||
description:
|
||||
"Wrapper that continuously spins children around an axis. Speed in radians/second.",
|
||||
example: { speed: 1, axis: "y" },
|
||||
},
|
||||
|
||||
Orbit: {
|
||||
props: z.object({
|
||||
...transformProps,
|
||||
speed: z.number().nullable(),
|
||||
radius: z.number().nullable(),
|
||||
tilt: z.number().nullable(),
|
||||
}),
|
||||
slots: ["default"],
|
||||
description:
|
||||
"Wrapper that orbits children around the Y axis at a given radius and speed.",
|
||||
example: { speed: 1, radius: 3, tilt: 0.5 },
|
||||
},
|
||||
|
||||
Pulse: {
|
||||
props: z.object({
|
||||
...transformProps,
|
||||
speed: z.number().nullable(),
|
||||
min: z.number().nullable(),
|
||||
max: z.number().nullable(),
|
||||
}),
|
||||
slots: ["default"],
|
||||
description:
|
||||
"Wrapper that pulses children's scale between min and max values.",
|
||||
example: { speed: 1, min: 0.8, max: 1.2 },
|
||||
},
|
||||
|
||||
CameraShake: {
|
||||
props: z.object({
|
||||
intensity: z.number().nullable(),
|
||||
maxYaw: z.number().nullable(),
|
||||
maxPitch: z.number().nullable(),
|
||||
maxRoll: z.number().nullable(),
|
||||
}),
|
||||
description:
|
||||
"Adds camera shake/vibration. Great for speed, impact, or unease effects.",
|
||||
example: { intensity: 0.5, maxYaw: 0.1, maxPitch: 0.1, maxRoll: 0.1 },
|
||||
},
|
||||
|
||||
// ===========================================================================
|
||||
// Camera / Controls
|
||||
// ===========================================================================
|
||||
|
||||
PerspectiveCamera: {
|
||||
props: z.object({
|
||||
...transformProps,
|
||||
fov: z.number().nullable(),
|
||||
near: z.number().nullable(),
|
||||
far: z.number().nullable(),
|
||||
makeDefault: z.boolean().nullable(),
|
||||
}),
|
||||
description:
|
||||
"Perspective camera. Set makeDefault to use as the scene's main camera.",
|
||||
example: {
|
||||
position: [5, 5, 5],
|
||||
fov: 50,
|
||||
makeDefault: true,
|
||||
},
|
||||
},
|
||||
|
||||
OrbitControls: {
|
||||
props: z.object({
|
||||
enableDamping: z.boolean().nullable(),
|
||||
dampingFactor: z.number().nullable(),
|
||||
enableZoom: z.boolean().nullable(),
|
||||
enablePan: z.boolean().nullable(),
|
||||
enableRotate: z.boolean().nullable(),
|
||||
minDistance: z.number().nullable(),
|
||||
maxDistance: z.number().nullable(),
|
||||
minPolarAngle: z.number().nullable(),
|
||||
maxPolarAngle: z.number().nullable(),
|
||||
autoRotate: z.boolean().nullable(),
|
||||
autoRotateSpeed: z.number().nullable(),
|
||||
target: z.tuple([z.number(), z.number(), z.number()]).nullable(),
|
||||
}),
|
||||
description:
|
||||
"Orbit camera controls. Allows the user to rotate, zoom, and pan around the scene.",
|
||||
example: { enableDamping: true, autoRotate: false },
|
||||
},
|
||||
};
|
||||
|
||||
// =============================================================================
|
||||
// Types
|
||||
// =============================================================================
|
||||
|
||||
export type ComponentDefinition = {
|
||||
props: z.ZodType;
|
||||
slots?: string[];
|
||||
description: string;
|
||||
example?: Record<string, unknown>;
|
||||
};
|
||||
|
||||
/**
|
||||
* Infer the props type for a Three component by name.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* type BoxProps = ThreeProps<"Box">;
|
||||
* ```
|
||||
*/
|
||||
export type ThreeProps<K extends keyof typeof threeComponentDefinitions> =
|
||||
z.output<(typeof threeComponentDefinitions)[K]["props"]>;
|
||||
@@ -0,0 +1,909 @@
|
||||
// @ts-nocheck
|
||||
import React, { type ReactNode, Suspense, useRef, useMemo } from "react";
|
||||
import { useFrame } from "@react-three/fiber";
|
||||
import {
|
||||
Environment as DreiEnvironment,
|
||||
OrbitControls as DreiOrbitControls,
|
||||
PerspectiveCamera as DreiPerspectiveCamera,
|
||||
Text as DreiText,
|
||||
Gltf,
|
||||
Sparkles as DreiSparkles,
|
||||
Stars as DreiStars,
|
||||
Sky as DreiSky,
|
||||
Cloud as DreiCloud,
|
||||
Clouds as DreiClouds,
|
||||
Float as DreiFloat,
|
||||
ContactShadows as DreiContactShadows,
|
||||
MeshTransmissionMaterial,
|
||||
MeshDistortMaterial,
|
||||
MeshReflectorMaterial,
|
||||
MeshPortalMaterial as DreiMeshPortalMaterial,
|
||||
Html as DreiHtml,
|
||||
RoundedBox as DreiRoundedBox,
|
||||
Backdrop as DreiBackdrop,
|
||||
CameraShake as DreiCameraShake,
|
||||
} from "@react-three/drei";
|
||||
// @ts-ignore
|
||||
import * as postprocessing from "@react-three/postprocessing";
|
||||
import * as THREE from "three";
|
||||
import type { BaseComponentProps } from "@json-render/react";
|
||||
import type { ThreeProps } from "./catalog";
|
||||
|
||||
// =============================================================================
|
||||
// Helpers
|
||||
// =============================================================================
|
||||
|
||||
type Vec3 = [number, number, number];
|
||||
|
||||
const DEFAULT_POS: Vec3 = [0, 0, 0];
|
||||
const DEFAULT_ROT: Vec3 = [0, 0, 0];
|
||||
const DEFAULT_SCALE: Vec3 = [1, 1, 1];
|
||||
|
||||
function pos(v: Vec3 | null | undefined): Vec3 {
|
||||
return v ?? DEFAULT_POS;
|
||||
}
|
||||
|
||||
function rot(v: Vec3 | null | undefined): Vec3 {
|
||||
return v ?? DEFAULT_ROT;
|
||||
}
|
||||
|
||||
function scl(v: Vec3 | null | undefined): Vec3 {
|
||||
return v ?? DEFAULT_SCALE;
|
||||
}
|
||||
|
||||
function MaterialComponent({
|
||||
material,
|
||||
}: {
|
||||
material: ThreeProps<"Box">["material"] | null | undefined;
|
||||
}) {
|
||||
if (!material) return <meshStandardMaterial />;
|
||||
return (
|
||||
<meshStandardMaterial
|
||||
color={material.color ?? "#ffffff"}
|
||||
metalness={material.metalness ?? 0}
|
||||
roughness={material.roughness ?? 1}
|
||||
emissive={material.emissive ?? "#000000"}
|
||||
emissiveIntensity={material.emissiveIntensity ?? 1}
|
||||
opacity={material.opacity ?? 1}
|
||||
transparent={material.transparent ?? false}
|
||||
wireframe={material.wireframe ?? false}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Component Implementations
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* React Three Fiber component implementations for json-render.
|
||||
*
|
||||
* Pass to `defineRegistry()` from `@json-render/react` to create a
|
||||
* component registry for rendering JSON specs as 3D scenes.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* import { defineRegistry } from "@json-render/react";
|
||||
* import { threeComponents } from "@json-render/react-three-fiber";
|
||||
*
|
||||
* const { registry } = defineRegistry(catalog, {
|
||||
* components: { ...threeComponents },
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
export const threeComponents = {
|
||||
// ── Primitives ─────────────────────────────────────────────────────────
|
||||
|
||||
Box: ({ props }: BaseComponentProps<ThreeProps<"Box">>) => (
|
||||
<mesh
|
||||
position={pos(props.position)}
|
||||
rotation={rot(props.rotation)}
|
||||
scale={scl(props.scale)}
|
||||
castShadow={props.castShadow ?? false}
|
||||
receiveShadow={props.receiveShadow ?? false}
|
||||
>
|
||||
<boxGeometry
|
||||
args={[props.width ?? 1, props.height ?? 1, props.depth ?? 1]}
|
||||
/>
|
||||
<MaterialComponent material={props.material} />
|
||||
</mesh>
|
||||
),
|
||||
|
||||
Sphere: ({ props }: BaseComponentProps<ThreeProps<"Sphere">>) => (
|
||||
<mesh
|
||||
position={pos(props.position)}
|
||||
rotation={rot(props.rotation)}
|
||||
scale={scl(props.scale)}
|
||||
castShadow={props.castShadow ?? false}
|
||||
receiveShadow={props.receiveShadow ?? false}
|
||||
>
|
||||
<sphereGeometry
|
||||
args={[
|
||||
props.radius ?? 1,
|
||||
props.widthSegments ?? 32,
|
||||
props.heightSegments ?? 16,
|
||||
]}
|
||||
/>
|
||||
<MaterialComponent material={props.material} />
|
||||
</mesh>
|
||||
),
|
||||
|
||||
Cylinder: ({ props }: BaseComponentProps<ThreeProps<"Cylinder">>) => (
|
||||
<mesh
|
||||
position={pos(props.position)}
|
||||
rotation={rot(props.rotation)}
|
||||
scale={scl(props.scale)}
|
||||
castShadow={props.castShadow ?? false}
|
||||
receiveShadow={props.receiveShadow ?? false}
|
||||
>
|
||||
<cylinderGeometry
|
||||
args={[
|
||||
props.radiusTop ?? 1,
|
||||
props.radiusBottom ?? 1,
|
||||
props.height ?? 1,
|
||||
props.radialSegments ?? 32,
|
||||
]}
|
||||
/>
|
||||
<MaterialComponent material={props.material} />
|
||||
</mesh>
|
||||
),
|
||||
|
||||
Cone: ({ props }: BaseComponentProps<ThreeProps<"Cone">>) => (
|
||||
<mesh
|
||||
position={pos(props.position)}
|
||||
rotation={rot(props.rotation)}
|
||||
scale={scl(props.scale)}
|
||||
castShadow={props.castShadow ?? false}
|
||||
receiveShadow={props.receiveShadow ?? false}
|
||||
>
|
||||
<coneGeometry
|
||||
args={[
|
||||
props.radius ?? 1,
|
||||
props.height ?? 1,
|
||||
props.radialSegments ?? 32,
|
||||
]}
|
||||
/>
|
||||
<MaterialComponent material={props.material} />
|
||||
</mesh>
|
||||
),
|
||||
|
||||
Torus: ({ props }: BaseComponentProps<ThreeProps<"Torus">>) => (
|
||||
<mesh
|
||||
position={pos(props.position)}
|
||||
rotation={rot(props.rotation)}
|
||||
scale={scl(props.scale)}
|
||||
castShadow={props.castShadow ?? false}
|
||||
receiveShadow={props.receiveShadow ?? false}
|
||||
>
|
||||
<torusGeometry
|
||||
args={[
|
||||
props.radius ?? 1,
|
||||
props.tube ?? 0.4,
|
||||
props.radialSegments ?? 16,
|
||||
props.tubularSegments ?? 48,
|
||||
]}
|
||||
/>
|
||||
<MaterialComponent material={props.material} />
|
||||
</mesh>
|
||||
),
|
||||
|
||||
Plane: ({
|
||||
props,
|
||||
children,
|
||||
}: BaseComponentProps<ThreeProps<"Plane">> & { children?: ReactNode }) => (
|
||||
<mesh
|
||||
position={pos(props.position)}
|
||||
rotation={rot(props.rotation)}
|
||||
scale={scl(props.scale)}
|
||||
castShadow={props.castShadow ?? false}
|
||||
receiveShadow={props.receiveShadow ?? false}
|
||||
>
|
||||
<planeGeometry args={[props.width ?? 1, props.height ?? 1]} />
|
||||
{children ?? <MaterialComponent material={props.material} />}
|
||||
</mesh>
|
||||
),
|
||||
|
||||
Capsule: ({ props }: BaseComponentProps<ThreeProps<"Capsule">>) => (
|
||||
<mesh
|
||||
position={pos(props.position)}
|
||||
rotation={rot(props.rotation)}
|
||||
scale={scl(props.scale)}
|
||||
castShadow={props.castShadow ?? false}
|
||||
receiveShadow={props.receiveShadow ?? false}
|
||||
>
|
||||
<capsuleGeometry
|
||||
args={[
|
||||
props.radius ?? 0.5,
|
||||
props.length ?? 1,
|
||||
props.capSegments ?? 4,
|
||||
props.radialSegments ?? 16,
|
||||
]}
|
||||
/>
|
||||
<MaterialComponent material={props.material} />
|
||||
</mesh>
|
||||
),
|
||||
|
||||
// ── Lights ─────────────────────────────────────────────────────────────
|
||||
|
||||
AmbientLight: ({ props }: BaseComponentProps<ThreeProps<"AmbientLight">>) => (
|
||||
<ambientLight
|
||||
color={props.color ?? "#ffffff"}
|
||||
intensity={props.intensity ?? 1}
|
||||
/>
|
||||
),
|
||||
|
||||
DirectionalLight: ({
|
||||
props,
|
||||
}: BaseComponentProps<ThreeProps<"DirectionalLight">>) => (
|
||||
<directionalLight
|
||||
position={pos(props.position)}
|
||||
rotation={rot(props.rotation)}
|
||||
color={props.color ?? "#ffffff"}
|
||||
intensity={props.intensity ?? 1}
|
||||
castShadow={props.castShadow ?? false}
|
||||
/>
|
||||
),
|
||||
|
||||
PointLight: ({ props }: BaseComponentProps<ThreeProps<"PointLight">>) => (
|
||||
<pointLight
|
||||
position={pos(props.position)}
|
||||
color={props.color ?? "#ffffff"}
|
||||
intensity={props.intensity ?? 1}
|
||||
distance={props.distance ?? 0}
|
||||
decay={props.decay ?? 2}
|
||||
castShadow={props.castShadow ?? false}
|
||||
/>
|
||||
),
|
||||
|
||||
SpotLight: ({ props }: BaseComponentProps<ThreeProps<"SpotLight">>) => (
|
||||
<spotLight
|
||||
position={pos(props.position)}
|
||||
color={props.color ?? "#ffffff"}
|
||||
intensity={props.intensity ?? 1}
|
||||
distance={props.distance ?? 0}
|
||||
decay={props.decay ?? 2}
|
||||
angle={props.angle ?? Math.PI / 3}
|
||||
penumbra={props.penumbra ?? 0}
|
||||
castShadow={props.castShadow ?? false}
|
||||
/>
|
||||
),
|
||||
|
||||
// ── Container ──────────────────────────────────────────────────────────
|
||||
|
||||
Group: ({
|
||||
props,
|
||||
children,
|
||||
}: BaseComponentProps<ThreeProps<"Group">> & { children?: ReactNode }) => (
|
||||
<group
|
||||
position={pos(props.position)}
|
||||
rotation={rot(props.rotation)}
|
||||
scale={scl(props.scale)}
|
||||
>
|
||||
{children}
|
||||
</group>
|
||||
),
|
||||
|
||||
// ── Model ──────────────────────────────────────────────────────────────
|
||||
|
||||
Model: ({ props }: BaseComponentProps<ThreeProps<"Model">>) => (
|
||||
<Suspense fallback={null}>
|
||||
<Gltf
|
||||
src={props.url}
|
||||
position={pos(props.position)}
|
||||
rotation={rot(props.rotation)}
|
||||
scale={scl(props.scale)}
|
||||
castShadow={props.castShadow ?? false}
|
||||
receiveShadow={props.receiveShadow ?? false}
|
||||
/>
|
||||
</Suspense>
|
||||
),
|
||||
|
||||
// ── Environment ────────────────────────────────────────────────────────
|
||||
|
||||
Environment: ({ props }: BaseComponentProps<ThreeProps<"Environment">>) => (
|
||||
<DreiEnvironment
|
||||
preset={props.preset ?? "sunset"}
|
||||
background={props.background ?? false}
|
||||
blur={props.blur ?? 0}
|
||||
environmentIntensity={props.intensity ?? 1}
|
||||
/>
|
||||
),
|
||||
|
||||
Fog: ({ props }: BaseComponentProps<ThreeProps<"Fog">>) => (
|
||||
<fog
|
||||
attach="fog"
|
||||
args={[props.color ?? "#cccccc", props.near ?? 10, props.far ?? 50]}
|
||||
/>
|
||||
),
|
||||
|
||||
GridHelper: ({ props }: BaseComponentProps<ThreeProps<"GridHelper">>) => (
|
||||
<group
|
||||
position={pos(props.position)}
|
||||
rotation={rot(props.rotation)}
|
||||
scale={scl(props.scale)}
|
||||
>
|
||||
<gridHelper
|
||||
args={[
|
||||
props.size ?? 10,
|
||||
props.divisions ?? 10,
|
||||
props.color ?? "#888888",
|
||||
props.secondaryColor ?? "#444444",
|
||||
]}
|
||||
/>
|
||||
</group>
|
||||
),
|
||||
|
||||
// ── Text ───────────────────────────────────────────────────────────────
|
||||
|
||||
Text3D: ({ props }: BaseComponentProps<ThreeProps<"Text3D">>) => (
|
||||
<DreiText
|
||||
position={pos(props.position)}
|
||||
rotation={rot(props.rotation)}
|
||||
scale={scl(props.scale)}
|
||||
fontSize={props.fontSize ?? 1}
|
||||
color={props.color ?? "#ffffff"}
|
||||
anchorX={props.anchorX ?? "center"}
|
||||
anchorY={props.anchorY ?? "middle"}
|
||||
maxWidth={props.maxWidth ?? undefined}
|
||||
>
|
||||
{props.text}
|
||||
</DreiText>
|
||||
),
|
||||
|
||||
// ── Effects / Atmosphere ──────────────────────────────────────────────
|
||||
|
||||
Sparkles: ({ props }: BaseComponentProps<ThreeProps<"Sparkles">>) => (
|
||||
<DreiSparkles
|
||||
position={pos(props.position)}
|
||||
rotation={rot(props.rotation)}
|
||||
scale={scl(props.scale)}
|
||||
count={props.count ?? 50}
|
||||
speed={props.speed ?? 0.4}
|
||||
opacity={props.opacity ?? 1}
|
||||
color={props.color ?? "#ffffff"}
|
||||
size={props.size ?? 1}
|
||||
noise={props.noise ?? 1}
|
||||
/>
|
||||
),
|
||||
|
||||
Stars: ({ props }: BaseComponentProps<ThreeProps<"Stars">>) => (
|
||||
<DreiStars
|
||||
radius={props.radius ?? 100}
|
||||
depth={props.depth ?? 50}
|
||||
count={props.count ?? 5000}
|
||||
factor={props.factor ?? 4}
|
||||
saturation={props.saturation ?? 0}
|
||||
fade={props.fade ?? true}
|
||||
speed={props.speed ?? 1}
|
||||
/>
|
||||
),
|
||||
|
||||
Sky: ({ props }: BaseComponentProps<ThreeProps<"Sky">>) => (
|
||||
<DreiSky
|
||||
distance={props.distance ?? 450000}
|
||||
sunPosition={props.sunPosition ?? [0, 1, 0]}
|
||||
inclination={props.inclination ?? undefined}
|
||||
azimuth={props.azimuth ?? undefined}
|
||||
mieCoefficient={props.mieCoefficient ?? 0.005}
|
||||
mieDirectionalG={props.mieDirectionalG ?? 0.8}
|
||||
rayleigh={props.rayleigh ?? 0.5}
|
||||
turbidity={props.turbidity ?? 10}
|
||||
/>
|
||||
),
|
||||
|
||||
Cloud: ({ props }: BaseComponentProps<ThreeProps<"Cloud">>) => (
|
||||
<DreiClouds limit={200}>
|
||||
<DreiCloud
|
||||
position={pos(props.position)}
|
||||
seed={props.seed ?? undefined}
|
||||
segments={props.segments ?? 20}
|
||||
bounds={props.bounds ?? [10, 2, 10]}
|
||||
volume={props.volume ?? 6}
|
||||
speed={props.speed ?? 0.2}
|
||||
fade={props.fade ?? 30}
|
||||
opacity={props.opacity ?? 0.6}
|
||||
color={props.color ?? "#ffffff"}
|
||||
growth={props.growth ?? 4}
|
||||
/>
|
||||
</DreiClouds>
|
||||
),
|
||||
|
||||
// ── Special Materials ───────────────────────────────────────────────
|
||||
|
||||
GlassSphere: ({ props }: BaseComponentProps<ThreeProps<"GlassSphere">>) => (
|
||||
<mesh
|
||||
position={pos(props.position)}
|
||||
rotation={rot(props.rotation)}
|
||||
scale={scl(props.scale)}
|
||||
castShadow={props.castShadow ?? false}
|
||||
receiveShadow={props.receiveShadow ?? false}
|
||||
>
|
||||
<sphereGeometry
|
||||
args={[
|
||||
props.radius ?? 1,
|
||||
props.widthSegments ?? 64,
|
||||
props.heightSegments ?? 32,
|
||||
]}
|
||||
/>
|
||||
<MeshTransmissionMaterial
|
||||
color={props.color ?? "#ffffff"}
|
||||
transmission={props.transmission ?? 1}
|
||||
thickness={props.thickness ?? 0.5}
|
||||
roughness={props.roughness ?? 0}
|
||||
chromaticAberration={props.chromaticAberration ?? 0.06}
|
||||
ior={props.ior ?? 1.5}
|
||||
distortion={props.distortion ?? 0}
|
||||
distortionScale={props.distortionScale ?? 0.3}
|
||||
temporalDistortion={props.temporalDistortion ?? 0.5}
|
||||
samples={props.samples ?? 10}
|
||||
resolution={props.resolution ?? 256}
|
||||
backside
|
||||
/>
|
||||
</mesh>
|
||||
),
|
||||
|
||||
GlassBox: ({ props }: BaseComponentProps<ThreeProps<"GlassBox">>) => (
|
||||
<mesh
|
||||
position={pos(props.position)}
|
||||
rotation={rot(props.rotation)}
|
||||
scale={scl(props.scale)}
|
||||
castShadow={props.castShadow ?? false}
|
||||
receiveShadow={props.receiveShadow ?? false}
|
||||
>
|
||||
<boxGeometry
|
||||
args={[props.width ?? 1, props.height ?? 1, props.depth ?? 1]}
|
||||
/>
|
||||
<MeshTransmissionMaterial
|
||||
color={props.color ?? "#ffffff"}
|
||||
transmission={props.transmission ?? 1}
|
||||
thickness={props.thickness ?? 0.5}
|
||||
roughness={props.roughness ?? 0}
|
||||
chromaticAberration={props.chromaticAberration ?? 0.06}
|
||||
ior={props.ior ?? 1.5}
|
||||
distortion={props.distortion ?? 0}
|
||||
distortionScale={props.distortionScale ?? 0.3}
|
||||
temporalDistortion={props.temporalDistortion ?? 0.5}
|
||||
samples={props.samples ?? 10}
|
||||
resolution={props.resolution ?? 256}
|
||||
backside
|
||||
/>
|
||||
</mesh>
|
||||
),
|
||||
|
||||
DistortSphere: ({
|
||||
props,
|
||||
}: BaseComponentProps<ThreeProps<"DistortSphere">>) => (
|
||||
<mesh
|
||||
position={pos(props.position)}
|
||||
rotation={rot(props.rotation)}
|
||||
scale={scl(props.scale)}
|
||||
castShadow={props.castShadow ?? false}
|
||||
receiveShadow={props.receiveShadow ?? false}
|
||||
>
|
||||
<sphereGeometry
|
||||
args={[
|
||||
props.radius ?? 1,
|
||||
props.widthSegments ?? 64,
|
||||
props.heightSegments ?? 32,
|
||||
]}
|
||||
/>
|
||||
<MeshDistortMaterial
|
||||
color={props.color ?? "#ff6600"}
|
||||
speed={props.speed ?? 2}
|
||||
distort={props.distort ?? 0.5}
|
||||
metalness={props.metalness ?? 0}
|
||||
roughness={props.roughness ?? 0.2}
|
||||
/>
|
||||
</mesh>
|
||||
),
|
||||
|
||||
// ── Extended Geometry ───────────────────────────────────────────────
|
||||
|
||||
TorusKnot: ({ props }: BaseComponentProps<ThreeProps<"TorusKnot">>) => (
|
||||
<mesh
|
||||
position={pos(props.position)}
|
||||
rotation={rot(props.rotation)}
|
||||
scale={scl(props.scale)}
|
||||
castShadow={props.castShadow ?? false}
|
||||
receiveShadow={props.receiveShadow ?? false}
|
||||
>
|
||||
<torusKnotGeometry
|
||||
args={[
|
||||
props.radius ?? 1,
|
||||
props.tube ?? 0.4,
|
||||
props.tubularSegments ?? 64,
|
||||
props.radialSegments ?? 8,
|
||||
props.p ?? 2,
|
||||
props.q ?? 3,
|
||||
]}
|
||||
/>
|
||||
<MaterialComponent material={props.material} />
|
||||
</mesh>
|
||||
),
|
||||
|
||||
RoundedBox: ({ props }: BaseComponentProps<ThreeProps<"RoundedBox">>) => (
|
||||
<DreiRoundedBox
|
||||
position={pos(props.position)}
|
||||
rotation={rot(props.rotation)}
|
||||
scale={scl(props.scale)}
|
||||
castShadow={props.castShadow ?? false}
|
||||
receiveShadow={props.receiveShadow ?? false}
|
||||
args={[props.width ?? 1, props.height ?? 1, props.depth ?? 1]}
|
||||
radius={props.radius ?? 0.05}
|
||||
smoothness={props.smoothness ?? 4}
|
||||
>
|
||||
<MaterialComponent material={props.material} />
|
||||
</DreiRoundedBox>
|
||||
),
|
||||
|
||||
// ── Shadows / Staging ───────────────────────────────────────────────
|
||||
|
||||
ContactShadows: ({
|
||||
props,
|
||||
}: BaseComponentProps<ThreeProps<"ContactShadows">>) => (
|
||||
<DreiContactShadows
|
||||
position={pos(props.position)}
|
||||
rotation={rot(props.rotation)}
|
||||
opacity={props.opacity ?? 0.5}
|
||||
width={props.width ?? 10}
|
||||
height={props.height ?? 10}
|
||||
blur={props.blur ?? 2}
|
||||
near={props.near ?? undefined}
|
||||
far={props.far ?? 10}
|
||||
smooth={props.smooth ?? true}
|
||||
resolution={props.resolution ?? 256}
|
||||
frames={props.frames ?? undefined}
|
||||
color={props.color ?? "#000000"}
|
||||
/>
|
||||
),
|
||||
|
||||
Float: ({
|
||||
props,
|
||||
children,
|
||||
}: BaseComponentProps<ThreeProps<"Float">> & { children?: ReactNode }) => (
|
||||
<DreiFloat
|
||||
position={pos(props.position)}
|
||||
rotation={rot(props.rotation)}
|
||||
scale={scl(props.scale)}
|
||||
speed={props.speed ?? 1}
|
||||
rotationIntensity={props.rotationIntensity ?? 1}
|
||||
floatIntensity={props.floatIntensity ?? 1}
|
||||
enabled={props.enabled ?? true}
|
||||
>
|
||||
{children}
|
||||
</DreiFloat>
|
||||
),
|
||||
|
||||
ReflectorPlane: ({
|
||||
props,
|
||||
}: BaseComponentProps<ThreeProps<"ReflectorPlane">>) => (
|
||||
<mesh
|
||||
position={pos(props.position)}
|
||||
rotation={rot(props.rotation)}
|
||||
scale={scl(props.scale)}
|
||||
>
|
||||
<planeGeometry args={[props.width ?? 10, props.height ?? 10]} />
|
||||
<MeshReflectorMaterial
|
||||
color={props.color ?? "#888888"}
|
||||
resolution={props.resolution ?? 1024}
|
||||
blur={props.blur ? [props.blur, props.blur] : [300, 100]}
|
||||
mirror={props.mirror ?? 0.5}
|
||||
mixBlur={props.mixBlur ?? 10}
|
||||
mixStrength={props.mixStrength ?? 2}
|
||||
depthScale={props.depthScale ?? 0.1}
|
||||
metalness={props.metalness ?? 0.5}
|
||||
roughness={props.roughness ?? 1}
|
||||
/>
|
||||
</mesh>
|
||||
),
|
||||
|
||||
Backdrop: ({
|
||||
props,
|
||||
children,
|
||||
}: BaseComponentProps<ThreeProps<"Backdrop">> & { children?: ReactNode }) => (
|
||||
<DreiBackdrop
|
||||
position={pos(props.position)}
|
||||
rotation={rot(props.rotation)}
|
||||
scale={scl(props.scale)}
|
||||
floor={props.floor ?? 0.25}
|
||||
segments={props.segments ?? 20}
|
||||
receiveShadow={props.receiveShadow ?? true}
|
||||
>
|
||||
{children}
|
||||
</DreiBackdrop>
|
||||
),
|
||||
|
||||
// ── Crazy / Magic ─────────────────────────────────────────────────────
|
||||
|
||||
MeshPortalMaterial: ({
|
||||
props,
|
||||
children,
|
||||
}: BaseComponentProps<ThreeProps<"MeshPortalMaterial">> & {
|
||||
children?: ReactNode;
|
||||
}) => (
|
||||
<DreiMeshPortalMaterial
|
||||
blend={props.blend ?? 0}
|
||||
blur={props.blur ?? 0}
|
||||
resolution={props.resolution ?? 512}
|
||||
>
|
||||
{children}
|
||||
</DreiMeshPortalMaterial>
|
||||
),
|
||||
|
||||
HtmlLabel: ({ props }: BaseComponentProps<ThreeProps<"HtmlLabel">>) => (
|
||||
<DreiHtml
|
||||
position={pos(props.position)}
|
||||
transform={props.transform ?? true}
|
||||
distanceFactor={props.distanceFactor ?? 10}
|
||||
center={props.center ?? true}
|
||||
style={{
|
||||
color: props.color ?? "#ffffff",
|
||||
fontSize: props.fontSize ? `${props.fontSize}px` : "16px",
|
||||
fontFamily: "system-ui, sans-serif",
|
||||
whiteSpace: "nowrap",
|
||||
pointerEvents: "none",
|
||||
}}
|
||||
>
|
||||
{props.text}
|
||||
</DreiHtml>
|
||||
),
|
||||
|
||||
// ── Post-Processing ────────────────────────────────────────────────────
|
||||
|
||||
EffectComposer: ({
|
||||
props,
|
||||
children,
|
||||
}: BaseComponentProps<ThreeProps<"EffectComposer">> & {
|
||||
children?: ReactNode;
|
||||
}) => {
|
||||
if (props.enabled === false) return null;
|
||||
return (
|
||||
<postprocessing.EffectComposer multisampling={props.multisampling ?? 8}>
|
||||
{children}
|
||||
</postprocessing.EffectComposer>
|
||||
);
|
||||
},
|
||||
|
||||
Bloom: ({ props }: BaseComponentProps<ThreeProps<"Bloom">>) => (
|
||||
<postprocessing.Bloom
|
||||
intensity={props.intensity ?? 1.5}
|
||||
luminanceThreshold={props.luminanceThreshold ?? 0.1}
|
||||
luminanceSmoothing={props.luminanceSmoothing ?? 0.025}
|
||||
mipmapBlur={props.mipmapBlur ?? true}
|
||||
/>
|
||||
),
|
||||
|
||||
Glitch: ({ props }: BaseComponentProps<ThreeProps<"Glitch">>) => {
|
||||
const delay = props.delay ? new THREE.Vector2(...props.delay) : undefined;
|
||||
const duration = props.duration
|
||||
? new THREE.Vector2(...props.duration)
|
||||
: undefined;
|
||||
const strength = props.strength
|
||||
? new THREE.Vector2(...props.strength)
|
||||
: undefined;
|
||||
return (
|
||||
<postprocessing.Glitch
|
||||
delay={delay}
|
||||
duration={duration}
|
||||
strength={strength}
|
||||
active={props.active ?? true}
|
||||
ratio={props.ratio ?? 0.85}
|
||||
/>
|
||||
);
|
||||
},
|
||||
|
||||
Vignette: ({ props }: BaseComponentProps<ThreeProps<"Vignette">>) => (
|
||||
<postprocessing.Vignette
|
||||
offset={props.offset ?? 0.5}
|
||||
darkness={props.darkness ?? 0.5}
|
||||
/>
|
||||
),
|
||||
|
||||
// ── Animation Wrappers ──────────────────────────────────────────────
|
||||
|
||||
WarpTunnel: (() => {
|
||||
function TunnelRing({
|
||||
index,
|
||||
ringCount,
|
||||
radius,
|
||||
length,
|
||||
speed,
|
||||
tubeRadius,
|
||||
color,
|
||||
}: {
|
||||
index: number;
|
||||
ringCount: number;
|
||||
radius: number;
|
||||
length: number;
|
||||
speed: number;
|
||||
tubeRadius: number;
|
||||
color: THREE.Color;
|
||||
}) {
|
||||
const meshRef = useRef<THREE.Mesh>(null);
|
||||
const spacing = length / ringCount;
|
||||
|
||||
useFrame((state) => {
|
||||
if (!meshRef.current) return;
|
||||
const t = state.clock.elapsedTime;
|
||||
const z =
|
||||
((((-index * spacing + t * speed) % length) + length) % length) -
|
||||
length;
|
||||
meshRef.current.position.z = z;
|
||||
const distFactor = 1 - Math.abs(z) / length;
|
||||
const s = 0.3 + distFactor * 0.7;
|
||||
meshRef.current.scale.set(s, s, s);
|
||||
});
|
||||
|
||||
return (
|
||||
<mesh ref={meshRef} rotation={[0, 0, (index * Math.PI) / 12]}>
|
||||
<torusGeometry args={[radius, tubeRadius, 16, 64]} />
|
||||
<meshStandardMaterial
|
||||
color={color}
|
||||
emissive={color}
|
||||
emissiveIntensity={3}
|
||||
transparent
|
||||
opacity={0.7}
|
||||
side={THREE.DoubleSide}
|
||||
/>
|
||||
</mesh>
|
||||
);
|
||||
}
|
||||
|
||||
return ({ props }: BaseComponentProps<ThreeProps<"WarpTunnel">>) => {
|
||||
const ringCount = props.ringCount ?? 80;
|
||||
const radius = props.radius ?? 3;
|
||||
const length = props.length ?? 40;
|
||||
const speed = props.speed ?? 8;
|
||||
const tubeRadius = props.tubeRadius ?? 0.02;
|
||||
const color1 = props.color1 ?? "#00ffff";
|
||||
const color2 = props.color2 ?? "#ff00ff";
|
||||
|
||||
const colors = useMemo(() => {
|
||||
const c1 = new THREE.Color(color1);
|
||||
const c2 = new THREE.Color(color2);
|
||||
return Array.from({ length: ringCount }, (_, i) =>
|
||||
new THREE.Color().lerpColors(c1, c2, i / ringCount),
|
||||
);
|
||||
}, [ringCount, color1, color2]);
|
||||
|
||||
return (
|
||||
<group
|
||||
position={pos(props.position)}
|
||||
rotation={rot(props.rotation)}
|
||||
scale={scl(props.scale)}
|
||||
>
|
||||
{colors.map((color, i) => (
|
||||
<TunnelRing
|
||||
key={i}
|
||||
index={i}
|
||||
ringCount={ringCount}
|
||||
radius={radius}
|
||||
length={length}
|
||||
speed={speed}
|
||||
tubeRadius={tubeRadius}
|
||||
color={color}
|
||||
/>
|
||||
))}
|
||||
</group>
|
||||
);
|
||||
};
|
||||
})(),
|
||||
|
||||
Spin: ({
|
||||
props,
|
||||
children,
|
||||
}: BaseComponentProps<ThreeProps<"Spin">> & { children?: ReactNode }) => {
|
||||
const ref = useRef<THREE.Group>(null);
|
||||
const speed = props.speed ?? 1;
|
||||
const axis = props.axis ?? "y";
|
||||
|
||||
useFrame((_, delta) => {
|
||||
if (!ref.current) return;
|
||||
if (axis === "x") ref.current.rotation.x += delta * speed;
|
||||
else if (axis === "z") ref.current.rotation.z += delta * speed;
|
||||
else ref.current.rotation.y += delta * speed;
|
||||
});
|
||||
|
||||
return (
|
||||
<group
|
||||
ref={ref}
|
||||
position={pos(props.position)}
|
||||
rotation={rot(props.rotation)}
|
||||
scale={scl(props.scale)}
|
||||
>
|
||||
{children}
|
||||
</group>
|
||||
);
|
||||
},
|
||||
|
||||
Orbit: ({
|
||||
props,
|
||||
children,
|
||||
}: BaseComponentProps<ThreeProps<"Orbit">> & { children?: ReactNode }) => {
|
||||
const ref = useRef<THREE.Group>(null);
|
||||
const speed = props.speed ?? 1;
|
||||
const radius = props.radius ?? 3;
|
||||
const tilt = props.tilt ?? 0;
|
||||
const baseY = props.position?.[1] ?? 0;
|
||||
|
||||
useFrame((state) => {
|
||||
if (!ref.current) return;
|
||||
const t = state.clock.elapsedTime * speed;
|
||||
ref.current.position.x = Math.cos(t) * radius;
|
||||
ref.current.position.z = Math.sin(t) * radius;
|
||||
ref.current.position.y = baseY + Math.sin(t * 0.7) * tilt;
|
||||
});
|
||||
|
||||
return <group ref={ref}>{children}</group>;
|
||||
},
|
||||
|
||||
Pulse: ({
|
||||
props,
|
||||
children,
|
||||
}: BaseComponentProps<ThreeProps<"Pulse">> & { children?: ReactNode }) => {
|
||||
const ref = useRef<THREE.Group>(null);
|
||||
const speed = props.speed ?? 1;
|
||||
const min = props.min ?? 0.8;
|
||||
const max = props.max ?? 1.2;
|
||||
|
||||
useFrame((state) => {
|
||||
if (!ref.current) return;
|
||||
const t = (Math.sin(state.clock.elapsedTime * speed) + 1) / 2;
|
||||
const s = min + t * (max - min);
|
||||
ref.current.scale.setScalar(s);
|
||||
});
|
||||
|
||||
return (
|
||||
<group
|
||||
ref={ref}
|
||||
position={pos(props.position)}
|
||||
rotation={rot(props.rotation)}
|
||||
>
|
||||
{children}
|
||||
</group>
|
||||
);
|
||||
},
|
||||
|
||||
CameraShake: ({ props }: BaseComponentProps<ThreeProps<"CameraShake">>) => (
|
||||
<DreiCameraShake
|
||||
intensity={props.intensity ?? 0.5}
|
||||
maxYaw={props.maxYaw ?? 0.1}
|
||||
maxPitch={props.maxPitch ?? 0.1}
|
||||
maxRoll={props.maxRoll ?? 0.1}
|
||||
/>
|
||||
),
|
||||
|
||||
// ── Camera / Controls ──────────────────────────────────────────────
|
||||
|
||||
PerspectiveCamera: ({
|
||||
props,
|
||||
}: BaseComponentProps<ThreeProps<"PerspectiveCamera">>) => (
|
||||
<DreiPerspectiveCamera
|
||||
position={pos(props.position)}
|
||||
rotation={rot(props.rotation)}
|
||||
fov={props.fov ?? 50}
|
||||
near={props.near ?? 0.1}
|
||||
far={props.far ?? 1000}
|
||||
makeDefault={props.makeDefault ?? false}
|
||||
/>
|
||||
),
|
||||
|
||||
OrbitControls: ({
|
||||
props,
|
||||
}: BaseComponentProps<ThreeProps<"OrbitControls">>) => (
|
||||
<DreiOrbitControls
|
||||
enableDamping={props.enableDamping ?? true}
|
||||
dampingFactor={props.dampingFactor ?? 0.05}
|
||||
enableZoom={props.enableZoom ?? true}
|
||||
enablePan={props.enablePan ?? true}
|
||||
enableRotate={props.enableRotate ?? true}
|
||||
minDistance={props.minDistance ?? undefined}
|
||||
maxDistance={props.maxDistance ?? undefined}
|
||||
minPolarAngle={props.minPolarAngle ?? undefined}
|
||||
maxPolarAngle={props.maxPolarAngle ?? undefined}
|
||||
autoRotate={props.autoRotate ?? false}
|
||||
autoRotateSpeed={props.autoRotateSpeed ?? 2}
|
||||
target={props.target ?? undefined}
|
||||
/>
|
||||
),
|
||||
};
|
||||
@@ -0,0 +1,25 @@
|
||||
// Component implementations
|
||||
export { threeComponents } from "./components";
|
||||
|
||||
// Catalog definitions (also available via @json-render/react-three-fiber/catalog)
|
||||
export {
|
||||
threeComponentDefinitions,
|
||||
type ComponentDefinition,
|
||||
type ThreeProps,
|
||||
} from "./catalog";
|
||||
|
||||
// Shared schemas
|
||||
export {
|
||||
vector3Schema,
|
||||
materialSchema,
|
||||
transformProps,
|
||||
shadowProps,
|
||||
} from "./schemas";
|
||||
|
||||
// Renderer
|
||||
export {
|
||||
ThreeRenderer,
|
||||
ThreeCanvas,
|
||||
type ThreeRendererProps,
|
||||
type ThreeCanvasProps,
|
||||
} from "./renderer";
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
import type { ThreeElements } from "@react-three/fiber";
|
||||
|
||||
declare module "react/jsx-runtime" {
|
||||
namespace JSX {
|
||||
interface IntrinsicElements extends ThreeElements {}
|
||||
}
|
||||
}
|
||||
|
||||
declare module "react/jsx-dev-runtime" {
|
||||
namespace JSX {
|
||||
interface IntrinsicElements extends ThreeElements {}
|
||||
}
|
||||
}
|
||||
|
||||
declare module "react" {
|
||||
namespace JSX {
|
||||
interface IntrinsicElements extends ThreeElements {}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
import { type ReactNode } from "react";
|
||||
import { Canvas, type CanvasProps } from "@react-three/fiber";
|
||||
import {
|
||||
Renderer,
|
||||
StateProvider,
|
||||
VisibilityProvider,
|
||||
ActionProvider,
|
||||
ValidationProvider,
|
||||
type ComponentRegistry,
|
||||
type ComponentRenderer,
|
||||
} from "@json-render/react";
|
||||
import type { Spec, StateStore, ComputedFunction } from "@json-render/core";
|
||||
|
||||
// =============================================================================
|
||||
// ThreeRenderer
|
||||
// =============================================================================
|
||||
|
||||
export interface ThreeRendererProps {
|
||||
/** The spec to render as a 3D scene */
|
||||
spec: Spec | null;
|
||||
/** Component registry */
|
||||
registry: ComponentRegistry;
|
||||
/**
|
||||
* External store (controlled mode). When provided, `initialState` and
|
||||
* `onStateChange` are ignored.
|
||||
*/
|
||||
store?: StateStore;
|
||||
/** Initial state model (uncontrolled mode) */
|
||||
initialState?: Record<string, unknown>;
|
||||
/** Action handlers */
|
||||
handlers?: Record<
|
||||
string,
|
||||
(params: Record<string, unknown>) => Promise<unknown> | unknown
|
||||
>;
|
||||
/** Named functions for `$computed` expressions in props */
|
||||
functions?: Record<string, ComputedFunction>;
|
||||
/** Callback when state changes (uncontrolled mode) */
|
||||
onStateChange?: (changes: Array<{ path: string; value: unknown }>) => void;
|
||||
/** Whether the spec is currently loading/streaming */
|
||||
loading?: boolean;
|
||||
/** Fallback component for unknown types */
|
||||
fallback?: ComponentRenderer;
|
||||
/** Additional R3F elements to render alongside the spec (lights, helpers, etc.) */
|
||||
children?: ReactNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a json-render spec as a React Three Fiber scene graph.
|
||||
*
|
||||
* Place inside a `<Canvas>` or use `<ThreeCanvas>` for a convenience wrapper.
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* <Canvas shadows>
|
||||
* <ThreeRenderer spec={spec} registry={registry} />
|
||||
* </Canvas>
|
||||
* ```
|
||||
*/
|
||||
export function ThreeRenderer({
|
||||
spec,
|
||||
registry,
|
||||
store,
|
||||
initialState,
|
||||
handlers,
|
||||
functions,
|
||||
onStateChange,
|
||||
loading,
|
||||
fallback,
|
||||
children,
|
||||
}: ThreeRendererProps) {
|
||||
return (
|
||||
<StateProvider
|
||||
store={store}
|
||||
initialState={initialState ?? spec?.state}
|
||||
onStateChange={onStateChange}
|
||||
>
|
||||
<VisibilityProvider>
|
||||
<ValidationProvider>
|
||||
<ActionProvider handlers={handlers}>
|
||||
<Renderer
|
||||
spec={spec}
|
||||
registry={registry}
|
||||
loading={loading}
|
||||
fallback={fallback}
|
||||
/>
|
||||
{children}
|
||||
</ActionProvider>
|
||||
</ValidationProvider>
|
||||
</VisibilityProvider>
|
||||
</StateProvider>
|
||||
);
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// ThreeCanvas
|
||||
// =============================================================================
|
||||
|
||||
export interface ThreeCanvasProps extends ThreeRendererProps {
|
||||
/** Enable shadows on the Canvas */
|
||||
shadows?: CanvasProps["shadows"];
|
||||
/** Canvas camera config */
|
||||
camera?: CanvasProps["camera"];
|
||||
/** Canvas className */
|
||||
className?: string;
|
||||
/** Canvas style */
|
||||
style?: React.CSSProperties;
|
||||
/** Additional Canvas props */
|
||||
canvasProps?: Omit<CanvasProps, "shadows" | "camera" | "className" | "style">;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience wrapper that sets up a `<Canvas>` with a `<ThreeRenderer>` inside.
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* <ThreeCanvas
|
||||
* spec={spec}
|
||||
* registry={registry}
|
||||
* shadows
|
||||
* camera={{ position: [5, 5, 5], fov: 50 }}
|
||||
* style={{ width: "100%", height: "100vh" }}
|
||||
* />
|
||||
* ```
|
||||
*/
|
||||
export function ThreeCanvas({
|
||||
shadows,
|
||||
camera,
|
||||
className,
|
||||
style,
|
||||
canvasProps,
|
||||
...rendererProps
|
||||
}: ThreeCanvasProps) {
|
||||
return (
|
||||
<Canvas
|
||||
shadows={shadows}
|
||||
camera={camera}
|
||||
className={className}
|
||||
style={style}
|
||||
{...canvasProps}
|
||||
>
|
||||
<ThreeRenderer {...rendererProps} />
|
||||
</Canvas>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import { z } from "zod";
|
||||
|
||||
export const vector3Schema = z.tuple([z.number(), z.number(), z.number()]);
|
||||
|
||||
export const materialSchema = z.object({
|
||||
color: z.string().nullable(),
|
||||
metalness: z.number().nullable(),
|
||||
roughness: z.number().nullable(),
|
||||
emissive: z.string().nullable(),
|
||||
emissiveIntensity: z.number().nullable(),
|
||||
opacity: z.number().nullable(),
|
||||
transparent: z.boolean().nullable(),
|
||||
wireframe: z.boolean().nullable(),
|
||||
});
|
||||
|
||||
export const transformProps = {
|
||||
position: vector3Schema.nullable(),
|
||||
rotation: vector3Schema.nullable(),
|
||||
scale: vector3Schema.nullable(),
|
||||
} as const;
|
||||
|
||||
export const shadowProps = {
|
||||
castShadow: z.boolean().nullable(),
|
||||
receiveShadow: z.boolean().nullable(),
|
||||
} as const;
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": "@internal/typescript-config/react-library.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "dist",
|
||||
"rootDir": "src"
|
||||
},
|
||||
"include": ["src"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import { defineConfig } from "tsup";
|
||||
|
||||
export default defineConfig({
|
||||
entry: ["src/index.ts", "src/catalog.ts"],
|
||||
format: ["cjs", "esm"],
|
||||
dts: true,
|
||||
sourcemap: true,
|
||||
clean: true,
|
||||
external: [
|
||||
"react",
|
||||
"react-dom",
|
||||
"@json-render/core",
|
||||
"@json-render/react",
|
||||
"@react-three/fiber",
|
||||
"@react-three/drei",
|
||||
"three",
|
||||
"zod",
|
||||
],
|
||||
});
|
||||
@@ -1,5 +1,12 @@
|
||||
# @json-render/react
|
||||
|
||||
## 0.13.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [5b32de8]
|
||||
- @json-render/core@0.13.0
|
||||
|
||||
## 0.12.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@json-render/react",
|
||||
"version": "0.12.1",
|
||||
"version": "0.13.0",
|
||||
"license": "Apache-2.0",
|
||||
"description": "React renderer for @json-render/core. JSON becomes React components.",
|
||||
"keywords": [
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# @json-render/redux
|
||||
|
||||
## 0.13.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [5b32de8]
|
||||
- @json-render/core@0.13.0
|
||||
|
||||
## 0.12.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@json-render/redux",
|
||||
"version": "0.12.1",
|
||||
"version": "0.13.0",
|
||||
"license": "Apache-2.0",
|
||||
"description": "Redux adapter for json-render StateStore",
|
||||
"keywords": [
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# @json-render/remotion
|
||||
|
||||
## 0.13.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [5b32de8]
|
||||
- @json-render/core@0.13.0
|
||||
|
||||
## 0.12.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@json-render/remotion",
|
||||
"version": "0.12.1",
|
||||
"version": "0.13.0",
|
||||
"license": "Apache-2.0",
|
||||
"description": "Remotion renderer for @json-render/core. JSON becomes video compositions.",
|
||||
"keywords": [
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
# @json-render/shadcn
|
||||
|
||||
## 0.13.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [5b32de8]
|
||||
- @json-render/core@0.13.0
|
||||
- @json-render/react@0.13.0
|
||||
|
||||
## 0.12.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@json-render/shadcn",
|
||||
"version": "0.12.1",
|
||||
"version": "0.13.0",
|
||||
"license": "Apache-2.0",
|
||||
"description": "shadcn/ui component library for @json-render/core. JSON becomes beautiful Tailwind-styled React components.",
|
||||
"keywords": [
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user