catalog.ts 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888
  1. import { z } from "zod";
  2. import {
  3. vector3Schema,
  4. materialSchema,
  5. transformProps,
  6. shadowProps,
  7. } from "./schemas";
  8. /**
  9. * React Three Fiber component definitions for json-render catalogs.
  10. *
  11. * These can be used directly or extended with custom 3D components.
  12. * All components render as React Three Fiber elements.
  13. */
  14. export const threeComponentDefinitions = {
  15. // ===========================================================================
  16. // Primitives
  17. // ===========================================================================
  18. Box: {
  19. props: z.object({
  20. ...transformProps,
  21. ...shadowProps,
  22. material: materialSchema.nullable(),
  23. width: z.number().nullable(),
  24. height: z.number().nullable(),
  25. depth: z.number().nullable(),
  26. }),
  27. description:
  28. "Box mesh. Defaults to 1x1x1. Use material for appearance, transform props for placement.",
  29. example: {
  30. position: [0, 0.5, 0],
  31. material: { color: "#4488ff" },
  32. },
  33. },
  34. Sphere: {
  35. props: z.object({
  36. ...transformProps,
  37. ...shadowProps,
  38. material: materialSchema.nullable(),
  39. radius: z.number().nullable(),
  40. widthSegments: z.number().nullable(),
  41. heightSegments: z.number().nullable(),
  42. }),
  43. description: "Sphere mesh. Defaults to radius 1.",
  44. example: {
  45. position: [0, 1, 0],
  46. radius: 0.5,
  47. material: { color: "#ff4444" },
  48. },
  49. },
  50. Cylinder: {
  51. props: z.object({
  52. ...transformProps,
  53. ...shadowProps,
  54. material: materialSchema.nullable(),
  55. radiusTop: z.number().nullable(),
  56. radiusBottom: z.number().nullable(),
  57. height: z.number().nullable(),
  58. radialSegments: z.number().nullable(),
  59. }),
  60. description: "Cylinder mesh. Use radiusTop/radiusBottom for tapering.",
  61. example: {
  62. position: [0, 0.5, 0],
  63. height: 1,
  64. material: { color: "#44ff44" },
  65. },
  66. },
  67. Cone: {
  68. props: z.object({
  69. ...transformProps,
  70. ...shadowProps,
  71. material: materialSchema.nullable(),
  72. radius: z.number().nullable(),
  73. height: z.number().nullable(),
  74. radialSegments: z.number().nullable(),
  75. }),
  76. description: "Cone mesh.",
  77. example: {
  78. position: [0, 0.5, 0],
  79. material: { color: "#ffaa00" },
  80. },
  81. },
  82. Torus: {
  83. props: z.object({
  84. ...transformProps,
  85. ...shadowProps,
  86. material: materialSchema.nullable(),
  87. radius: z.number().nullable(),
  88. tube: z.number().nullable(),
  89. radialSegments: z.number().nullable(),
  90. tubularSegments: z.number().nullable(),
  91. }),
  92. description: "Torus (donut) mesh.",
  93. example: {
  94. position: [0, 1, 0],
  95. material: { color: "#ff44ff" },
  96. },
  97. },
  98. Plane: {
  99. props: z.object({
  100. ...transformProps,
  101. ...shadowProps,
  102. material: materialSchema.nullable(),
  103. width: z.number().nullable(),
  104. height: z.number().nullable(),
  105. }),
  106. slots: ["default"],
  107. description:
  108. "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.",
  109. example: {
  110. position: [0, 0, 0],
  111. rotation: [-1.5708, 0, 0],
  112. scale: [10, 10, 1],
  113. material: { color: "#888888" },
  114. receiveShadow: true,
  115. },
  116. },
  117. Capsule: {
  118. props: z.object({
  119. ...transformProps,
  120. ...shadowProps,
  121. material: materialSchema.nullable(),
  122. radius: z.number().nullable(),
  123. length: z.number().nullable(),
  124. capSegments: z.number().nullable(),
  125. radialSegments: z.number().nullable(),
  126. }),
  127. description: "Capsule mesh (cylinder with hemispherical caps).",
  128. example: {
  129. position: [0, 1, 0],
  130. material: { color: "#00cccc" },
  131. },
  132. },
  133. // ===========================================================================
  134. // Lights
  135. // ===========================================================================
  136. AmbientLight: {
  137. props: z.object({
  138. color: z.string().nullable(),
  139. intensity: z.number().nullable(),
  140. }),
  141. description:
  142. "Ambient light that illuminates all objects equally. No position needed.",
  143. example: { color: "#ffffff", intensity: 0.5 },
  144. },
  145. DirectionalLight: {
  146. props: z.object({
  147. ...transformProps,
  148. color: z.string().nullable(),
  149. intensity: z.number().nullable(),
  150. castShadow: z.boolean().nullable(),
  151. }),
  152. description:
  153. "Directional light (like sunlight). Position determines the direction it shines from.",
  154. example: {
  155. position: [5, 10, 5],
  156. intensity: 1,
  157. castShadow: true,
  158. },
  159. },
  160. PointLight: {
  161. props: z.object({
  162. ...transformProps,
  163. color: z.string().nullable(),
  164. intensity: z.number().nullable(),
  165. distance: z.number().nullable(),
  166. decay: z.number().nullable(),
  167. castShadow: z.boolean().nullable(),
  168. }),
  169. description: "Point light that radiates in all directions from a position.",
  170. example: {
  171. position: [0, 3, 0],
  172. intensity: 1,
  173. distance: 10,
  174. },
  175. },
  176. SpotLight: {
  177. props: z.object({
  178. ...transformProps,
  179. color: z.string().nullable(),
  180. intensity: z.number().nullable(),
  181. distance: z.number().nullable(),
  182. decay: z.number().nullable(),
  183. angle: z.number().nullable(),
  184. penumbra: z.number().nullable(),
  185. castShadow: z.boolean().nullable(),
  186. }),
  187. description:
  188. "Spot light emitting a cone of light. Angle in radians (default ~Math.PI/3).",
  189. example: {
  190. position: [0, 5, 0],
  191. intensity: 1,
  192. angle: 0.5,
  193. penumbra: 0.5,
  194. castShadow: true,
  195. },
  196. },
  197. // ===========================================================================
  198. // Container
  199. // ===========================================================================
  200. Group: {
  201. props: z.object({
  202. ...transformProps,
  203. }),
  204. slots: ["default"],
  205. description:
  206. "Container for grouping child objects. Transforms apply to all children.",
  207. example: { position: [0, 0, 0] },
  208. },
  209. // ===========================================================================
  210. // Model
  211. // ===========================================================================
  212. Model: {
  213. props: z.object({
  214. ...transformProps,
  215. ...shadowProps,
  216. url: z.string(),
  217. }),
  218. description:
  219. "GLTF/GLB 3D model loader. Provide a URL to a .glb or .gltf file.",
  220. example: {
  221. url: "/models/robot.glb",
  222. position: [0, 0, 0],
  223. scale: [1, 1, 1],
  224. },
  225. },
  226. // ===========================================================================
  227. // Environment
  228. // ===========================================================================
  229. Environment: {
  230. props: z.object({
  231. preset: z
  232. .enum([
  233. "apartment",
  234. "city",
  235. "dawn",
  236. "forest",
  237. "lobby",
  238. "night",
  239. "park",
  240. "studio",
  241. "sunset",
  242. "warehouse",
  243. ])
  244. .nullable(),
  245. background: z.boolean().nullable(),
  246. blur: z.number().nullable(),
  247. intensity: z.number().nullable(),
  248. }),
  249. description:
  250. "HDRI environment map for lighting and background. Choose a preset or provide a custom HDRI.",
  251. example: { preset: "sunset", background: true },
  252. },
  253. Fog: {
  254. props: z.object({
  255. color: z.string().nullable(),
  256. near: z.number().nullable(),
  257. far: z.number().nullable(),
  258. }),
  259. description:
  260. "Linear fog effect. Objects fade between near and far distances.",
  261. example: { color: "#cccccc", near: 10, far: 50 },
  262. },
  263. GridHelper: {
  264. props: z.object({
  265. ...transformProps,
  266. size: z.number().nullable(),
  267. divisions: z.number().nullable(),
  268. color: z.string().nullable(),
  269. secondaryColor: z.string().nullable(),
  270. }),
  271. description: "Visual grid for reference. Renders on the XZ plane.",
  272. example: { size: 10, divisions: 10 },
  273. },
  274. // ===========================================================================
  275. // Text
  276. // ===========================================================================
  277. Text3D: {
  278. props: z.object({
  279. ...transformProps,
  280. text: z.string(),
  281. fontSize: z.number().nullable(),
  282. color: z.string().nullable(),
  283. anchorX: z.enum(["left", "center", "right"]).nullable(),
  284. anchorY: z
  285. .enum(["top", "top-baseline", "middle", "bottom-baseline", "bottom"])
  286. .nullable(),
  287. maxWidth: z.number().nullable(),
  288. }),
  289. description:
  290. "3D text rendered in the scene. Uses SDF text for crisp rendering at any size.",
  291. example: {
  292. text: "Hello World",
  293. fontSize: 1,
  294. color: "#ffffff",
  295. position: [0, 2, 0],
  296. },
  297. },
  298. ExtrudedText: {
  299. props: z.object({
  300. ...transformProps,
  301. ...shadowProps,
  302. material: materialSchema.nullable(),
  303. text: z.string(),
  304. font: z.string().nullable(),
  305. size: z.number().nullable(),
  306. depth: z.number().nullable(),
  307. curveSegments: z.number().nullable(),
  308. bevelEnabled: z.boolean().nullable(),
  309. bevelThickness: z.number().nullable(),
  310. bevelSize: z.number().nullable(),
  311. bevelSegments: z.number().nullable(),
  312. centered: z.boolean().nullable(),
  313. }),
  314. description:
  315. "Extruded 3D text with depth. Uses a typeface JSON font for geometry-based rendering with bevel support.",
  316. example: {
  317. text: "Hello",
  318. size: 1,
  319. depth: 0.2,
  320. position: [0, 2, 0],
  321. },
  322. },
  323. // ===========================================================================
  324. // Effects / Atmosphere
  325. // ===========================================================================
  326. Sparkles: {
  327. props: z.object({
  328. ...transformProps,
  329. count: z.number().nullable(),
  330. speed: z.number().nullable(),
  331. opacity: z.number().nullable(),
  332. color: z.string().nullable(),
  333. size: z.number().nullable(),
  334. noise: z.number().nullable(),
  335. }),
  336. description:
  337. "Floating particle sparkles. Great for magic, snow, or ambient effects.",
  338. example: {
  339. count: 100,
  340. speed: 0.5,
  341. size: 2,
  342. color: "#ffffff",
  343. scale: [5, 5, 5],
  344. },
  345. },
  346. Stars: {
  347. props: z.object({
  348. radius: z.number().nullable(),
  349. depth: z.number().nullable(),
  350. count: z.number().nullable(),
  351. factor: z.number().nullable(),
  352. saturation: z.number().nullable(),
  353. fade: z.boolean().nullable(),
  354. speed: z.number().nullable(),
  355. }),
  356. description:
  357. "Starfield background. Renders thousands of stars in a sphere around the scene.",
  358. example: { radius: 100, depth: 50, count: 5000, factor: 4, fade: true },
  359. },
  360. Sky: {
  361. props: z.object({
  362. distance: z.number().nullable(),
  363. sunPosition: vector3Schema.nullable(),
  364. inclination: z.number().nullable(),
  365. azimuth: z.number().nullable(),
  366. mieCoefficient: z.number().nullable(),
  367. mieDirectionalG: z.number().nullable(),
  368. rayleigh: z.number().nullable(),
  369. turbidity: z.number().nullable(),
  370. }),
  371. description:
  372. "Procedural sky with sun. Control sun position, haze, and scattering.",
  373. example: { sunPosition: [100, 20, 100], turbidity: 8, rayleigh: 2 },
  374. },
  375. Cloud: {
  376. props: z.object({
  377. ...transformProps,
  378. seed: z.number().nullable(),
  379. segments: z.number().nullable(),
  380. bounds: vector3Schema.nullable(),
  381. volume: z.number().nullable(),
  382. speed: z.number().nullable(),
  383. fade: z.number().nullable(),
  384. opacity: z.number().nullable(),
  385. color: z.string().nullable(),
  386. growth: z.number().nullable(),
  387. }),
  388. description:
  389. "Volumetric cloud. Use multiple for a cloudscape. Wrap in a Clouds parent for batching.",
  390. example: {
  391. position: [0, 5, 0],
  392. speed: 0.2,
  393. opacity: 0.6,
  394. color: "#ffffff",
  395. },
  396. },
  397. // ===========================================================================
  398. // Special Materials (geometry + material combos)
  399. // ===========================================================================
  400. GlassSphere: {
  401. props: z.object({
  402. ...transformProps,
  403. ...shadowProps,
  404. radius: z.number().nullable(),
  405. widthSegments: z.number().nullable(),
  406. heightSegments: z.number().nullable(),
  407. color: z.string().nullable(),
  408. transmission: z.number().nullable(),
  409. thickness: z.number().nullable(),
  410. roughness: z.number().nullable(),
  411. chromaticAberration: z.number().nullable(),
  412. ior: z.number().nullable(),
  413. distortion: z.number().nullable(),
  414. distortionScale: z.number().nullable(),
  415. temporalDistortion: z.number().nullable(),
  416. samples: z.number().nullable(),
  417. resolution: z.number().nullable(),
  418. }),
  419. description:
  420. "Glass sphere with transmission/refraction. Creates photorealistic glass effect.",
  421. example: {
  422. position: [0, 1, 0],
  423. radius: 1,
  424. transmission: 1,
  425. thickness: 0.5,
  426. roughness: 0,
  427. chromaticAberration: 0.06,
  428. },
  429. },
  430. GlassBox: {
  431. props: z.object({
  432. ...transformProps,
  433. ...shadowProps,
  434. width: z.number().nullable(),
  435. height: z.number().nullable(),
  436. depth: z.number().nullable(),
  437. color: z.string().nullable(),
  438. transmission: z.number().nullable(),
  439. thickness: z.number().nullable(),
  440. roughness: z.number().nullable(),
  441. chromaticAberration: z.number().nullable(),
  442. ior: z.number().nullable(),
  443. distortion: z.number().nullable(),
  444. distortionScale: z.number().nullable(),
  445. temporalDistortion: z.number().nullable(),
  446. samples: z.number().nullable(),
  447. resolution: z.number().nullable(),
  448. }),
  449. description:
  450. "Glass box with transmission/refraction. Photorealistic glass cuboid.",
  451. example: {
  452. position: [0, 0.5, 0],
  453. width: 1,
  454. height: 1,
  455. depth: 1,
  456. transmission: 1,
  457. thickness: 0.5,
  458. },
  459. },
  460. DistortSphere: {
  461. props: z.object({
  462. ...transformProps,
  463. ...shadowProps,
  464. radius: z.number().nullable(),
  465. widthSegments: z.number().nullable(),
  466. heightSegments: z.number().nullable(),
  467. color: z.string().nullable(),
  468. speed: z.number().nullable(),
  469. distort: z.number().nullable(),
  470. metalness: z.number().nullable(),
  471. roughness: z.number().nullable(),
  472. }),
  473. description:
  474. "Animated distorting sphere. Organic, blobby look -- like liquid metal.",
  475. example: {
  476. position: [0, 1, 0],
  477. radius: 1,
  478. color: "#ff6600",
  479. speed: 2,
  480. distort: 0.5,
  481. },
  482. },
  483. // ===========================================================================
  484. // Extended Geometry
  485. // ===========================================================================
  486. TorusKnot: {
  487. props: z.object({
  488. ...transformProps,
  489. ...shadowProps,
  490. material: materialSchema.nullable(),
  491. radius: z.number().nullable(),
  492. tube: z.number().nullable(),
  493. tubularSegments: z.number().nullable(),
  494. radialSegments: z.number().nullable(),
  495. p: z.number().nullable(),
  496. q: z.number().nullable(),
  497. }),
  498. description:
  499. "Torus knot mesh. A continuous 3D curve that creates intricate knot shapes via p and q parameters.",
  500. example: {
  501. position: [0, 1, 0],
  502. radius: 1,
  503. tube: 0.3,
  504. p: 2,
  505. q: 3,
  506. material: { color: "#ff44ff", metalness: 0.8, roughness: 0.2 },
  507. },
  508. },
  509. RoundedBox: {
  510. props: z.object({
  511. ...transformProps,
  512. ...shadowProps,
  513. material: materialSchema.nullable(),
  514. width: z.number().nullable(),
  515. height: z.number().nullable(),
  516. depth: z.number().nullable(),
  517. radius: z.number().nullable(),
  518. smoothness: z.number().nullable(),
  519. }),
  520. description:
  521. "Box with rounded edges. More polished look than a plain box. Great for product-style scenes.",
  522. example: {
  523. position: [0, 0.5, 0],
  524. width: 1,
  525. height: 1,
  526. depth: 1,
  527. radius: 0.1,
  528. material: { color: "#4488ff" },
  529. },
  530. },
  531. // ===========================================================================
  532. // Shadows / Staging
  533. // ===========================================================================
  534. ContactShadows: {
  535. props: z.object({
  536. ...transformProps,
  537. opacity: z.number().nullable(),
  538. width: z.number().nullable(),
  539. height: z.number().nullable(),
  540. blur: z.number().nullable(),
  541. near: z.number().nullable(),
  542. far: z.number().nullable(),
  543. smooth: z.boolean().nullable(),
  544. resolution: z.number().nullable(),
  545. frames: z.number().nullable(),
  546. color: z.string().nullable(),
  547. }),
  548. description:
  549. "Soft contact shadows projected onto the ground plane. Place at y=0 under objects.",
  550. example: {
  551. position: [0, 0, 0],
  552. opacity: 0.5,
  553. blur: 2,
  554. width: 10,
  555. height: 10,
  556. },
  557. },
  558. Float: {
  559. props: z.object({
  560. ...transformProps,
  561. speed: z.number().nullable(),
  562. rotationIntensity: z.number().nullable(),
  563. floatIntensity: z.number().nullable(),
  564. enabled: z.boolean().nullable(),
  565. }),
  566. slots: ["default"],
  567. description:
  568. "Wrapper that makes children gently float and bob. Adds organic motion to any object.",
  569. example: { speed: 1.5, rotationIntensity: 1, floatIntensity: 1 },
  570. },
  571. ReflectorPlane: {
  572. props: z.object({
  573. ...transformProps,
  574. width: z.number().nullable(),
  575. height: z.number().nullable(),
  576. color: z.string().nullable(),
  577. resolution: z.number().nullable(),
  578. blur: z.number().nullable(),
  579. mirror: z.number().nullable(),
  580. mixBlur: z.number().nullable(),
  581. mixStrength: z.number().nullable(),
  582. depthScale: z.number().nullable(),
  583. metalness: z.number().nullable(),
  584. roughness: z.number().nullable(),
  585. }),
  586. description:
  587. "Reflective floor/surface. Real-time mirror reflections on a plane. Rotate [-PI/2,0,0] for ground.",
  588. example: {
  589. position: [0, 0, 0],
  590. rotation: [-1.5708, 0, 0],
  591. width: 20,
  592. height: 20,
  593. mirror: 0.5,
  594. blur: [300, 100],
  595. resolution: 1024,
  596. },
  597. },
  598. Backdrop: {
  599. props: z.object({
  600. ...transformProps,
  601. floor: z.number().nullable(),
  602. segments: z.number().nullable(),
  603. receiveShadow: z.boolean().nullable(),
  604. }),
  605. slots: ["default"],
  606. description:
  607. "Curved studio backdrop for product-style scenes. Set floor for how much bends.",
  608. example: { floor: 0.25, segments: 20, receiveShadow: true },
  609. },
  610. // ===========================================================================
  611. // Crazy / Magic
  612. // ===========================================================================
  613. MeshPortalMaterial: {
  614. props: z.object({
  615. blend: z.number().nullable(),
  616. blur: z.number().nullable(),
  617. resolution: z.number().nullable(),
  618. }),
  619. slots: ["default"],
  620. description:
  621. "Renders children into a portal surface (like a window to another world). Use as a child of a Mesh.",
  622. example: { blend: 0 },
  623. },
  624. HtmlLabel: {
  625. props: z.object({
  626. ...transformProps,
  627. text: z.string(),
  628. transform: z.boolean().nullable(),
  629. distanceFactor: z.number().nullable(),
  630. color: z.string().nullable(),
  631. fontSize: z.number().nullable(),
  632. center: z.boolean().nullable(),
  633. }),
  634. description: "Renders actual HTML/DOM text floating in the 3D scene.",
  635. example: { text: "Hello World", transform: true, distanceFactor: 10 },
  636. },
  637. // ===========================================================================
  638. // Post-Processing
  639. // ===========================================================================
  640. EffectComposer: {
  641. props: z.object({
  642. enabled: z.boolean().nullable(),
  643. multisampling: z.number().nullable(),
  644. }),
  645. slots: ["default"],
  646. description:
  647. "Wrapper for post-processing effects. Add effects as children.",
  648. example: { multisampling: 8 },
  649. },
  650. Bloom: {
  651. props: z.object({
  652. intensity: z.number().nullable(),
  653. luminanceThreshold: z.number().nullable(),
  654. luminanceSmoothing: z.number().nullable(),
  655. mipmapBlur: z.boolean().nullable(),
  656. }),
  657. description: "Bloom post-processing effect. Makes emissive materials glow.",
  658. example: { intensity: 1.5, luminanceThreshold: 0.1, mipmapBlur: true },
  659. },
  660. Glitch: {
  661. props: z.object({
  662. delay: z.tuple([z.number(), z.number()]).nullable(),
  663. duration: z.tuple([z.number(), z.number()]).nullable(),
  664. strength: z.tuple([z.number(), z.number()]).nullable(),
  665. active: z.boolean().nullable(),
  666. ratio: z.number().nullable(),
  667. }),
  668. description: "Cyberpunk glitch post-processing effect.",
  669. example: { active: true },
  670. },
  671. Vignette: {
  672. props: z.object({
  673. offset: z.number().nullable(),
  674. darkness: z.number().nullable(),
  675. }),
  676. description: "Vignette post-processing effect (darkened corners).",
  677. example: { offset: 0.5, darkness: 0.5 },
  678. },
  679. // ===========================================================================
  680. // Animation Wrappers
  681. // ===========================================================================
  682. WarpTunnel: {
  683. props: z.object({
  684. ...transformProps,
  685. ringCount: z.number().nullable(),
  686. radius: z.number().nullable(),
  687. length: z.number().nullable(),
  688. speed: z.number().nullable(),
  689. tubeRadius: z.number().nullable(),
  690. color1: z.string().nullable(),
  691. color2: z.string().nullable(),
  692. }),
  693. description:
  694. "Animated neon tunnel flythrough. Rings rush toward the camera creating a hyperspace warp effect.",
  695. example: {
  696. speed: 8,
  697. ringCount: 80,
  698. radius: 3,
  699. color1: "#00ffff",
  700. color2: "#ff00ff",
  701. },
  702. },
  703. Spin: {
  704. props: z.object({
  705. ...transformProps,
  706. speed: z.number().nullable(),
  707. axis: z.enum(["x", "y", "z"]).nullable(),
  708. }),
  709. slots: ["default"],
  710. description:
  711. "Wrapper that continuously spins children around an axis. Speed in radians/second.",
  712. example: { speed: 1, axis: "y" },
  713. },
  714. Orbit: {
  715. props: z.object({
  716. ...transformProps,
  717. speed: z.number().nullable(),
  718. radius: z.number().nullable(),
  719. tilt: z.number().nullable(),
  720. }),
  721. slots: ["default"],
  722. description:
  723. "Wrapper that orbits children around the Y axis at a given radius and speed.",
  724. example: { speed: 1, radius: 3, tilt: 0.5 },
  725. },
  726. Pulse: {
  727. props: z.object({
  728. ...transformProps,
  729. speed: z.number().nullable(),
  730. min: z.number().nullable(),
  731. max: z.number().nullable(),
  732. }),
  733. slots: ["default"],
  734. description:
  735. "Wrapper that pulses children's scale between min and max values.",
  736. example: { speed: 1, min: 0.8, max: 1.2 },
  737. },
  738. CameraShake: {
  739. props: z.object({
  740. intensity: z.number().nullable(),
  741. maxYaw: z.number().nullable(),
  742. maxPitch: z.number().nullable(),
  743. maxRoll: z.number().nullable(),
  744. }),
  745. description:
  746. "Adds camera shake/vibration. Great for speed, impact, or unease effects.",
  747. example: { intensity: 0.5, maxYaw: 0.1, maxPitch: 0.1, maxRoll: 0.1 },
  748. },
  749. // ===========================================================================
  750. // Camera / Controls
  751. // ===========================================================================
  752. PerspectiveCamera: {
  753. props: z.object({
  754. ...transformProps,
  755. fov: z.number().nullable(),
  756. near: z.number().nullable(),
  757. far: z.number().nullable(),
  758. makeDefault: z.boolean().nullable(),
  759. }),
  760. description:
  761. "Perspective camera. Set makeDefault to use as the scene's main camera.",
  762. example: {
  763. position: [5, 5, 5],
  764. fov: 50,
  765. makeDefault: true,
  766. },
  767. },
  768. OrbitControls: {
  769. props: z.object({
  770. enableDamping: z.boolean().nullable(),
  771. dampingFactor: z.number().nullable(),
  772. enableZoom: z.boolean().nullable(),
  773. enablePan: z.boolean().nullable(),
  774. enableRotate: z.boolean().nullable(),
  775. minDistance: z.number().nullable(),
  776. maxDistance: z.number().nullable(),
  777. minPolarAngle: z.number().nullable(),
  778. maxPolarAngle: z.number().nullable(),
  779. autoRotate: z.boolean().nullable(),
  780. autoRotateSpeed: z.number().nullable(),
  781. target: z.tuple([z.number(), z.number(), z.number()]).nullable(),
  782. }),
  783. description:
  784. "Orbit camera controls. Allows the user to rotate, zoom, and pan around the scene.",
  785. example: { enableDamping: true, autoRotate: false },
  786. },
  787. // ===========================================================================
  788. // Gaussian Splatting
  789. // ===========================================================================
  790. GaussianSplat: {
  791. props: z.object({
  792. src: z.string(),
  793. ...transformProps,
  794. ...shadowProps,
  795. alphaHash: z.boolean().nullable(),
  796. toneMapped: z.boolean().nullable(),
  797. visible: z.boolean().nullable(),
  798. }),
  799. description:
  800. "Loads and renders a .splat or .ply gaussian splat file inside an R3F scene. Composable with all other 3D components (lights, models, primitives). Uses drei's Splat loader.",
  801. example: {
  802. src: "https://huggingface.co/datasets/dylanebert/3dgs/resolve/main/bonsai/bonsai-7k.splat",
  803. position: [0, 0, 0],
  804. },
  805. },
  806. };
  807. // =============================================================================
  808. // Types
  809. // =============================================================================
  810. export type ComponentDefinition = {
  811. props: z.ZodType;
  812. slots?: string[];
  813. description: string;
  814. example?: Record<string, unknown>;
  815. };
  816. /**
  817. * Infer the props type for a Three component by name.
  818. *
  819. * @example
  820. * ```ts
  821. * type BoxProps = ThreeProps<"Box">;
  822. * ```
  823. */
  824. export type ThreeProps<K extends keyof typeof threeComponentDefinitions> =
  825. z.output<(typeof threeComponentDefinitions)[K]["props"]>;