portal-gallery.ts 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809
  1. import type { Scene } from "./_helpers";
  2. export const portalGallery: Scene = {
  3. name: "Portal Gallery",
  4. description:
  5. "Three framed portals into different worlds -- sunset clouds, starfield, and enchanted forest",
  6. spec: {
  7. root: "scene",
  8. elements: {
  9. scene: {
  10. type: "Group",
  11. props: { position: null, rotation: null, scale: null },
  12. children: [
  13. "cam",
  14. "env",
  15. "ambient",
  16. "directional",
  17. "floor",
  18. "shadows",
  19. "frame1",
  20. "frame2",
  21. "frame3",
  22. "gallery-sparkles",
  23. "post",
  24. "controls",
  25. ],
  26. },
  27. cam: {
  28. type: "PerspectiveCamera",
  29. props: {
  30. position: [0, 2, 7],
  31. rotation: null,
  32. scale: null,
  33. fov: 50,
  34. near: 0.1,
  35. far: 200,
  36. makeDefault: true,
  37. },
  38. children: [],
  39. },
  40. env: {
  41. type: "Environment",
  42. props: {
  43. preset: "lobby",
  44. background: false,
  45. blur: 0,
  46. intensity: 0.4,
  47. },
  48. children: [],
  49. },
  50. ambient: {
  51. type: "AmbientLight",
  52. props: { color: "#e8e0d8", intensity: 0.5 },
  53. children: [],
  54. },
  55. directional: {
  56. type: "DirectionalLight",
  57. props: {
  58. position: [5, 8, 3],
  59. rotation: null,
  60. scale: null,
  61. color: "#ffffff",
  62. intensity: 1,
  63. castShadow: true,
  64. },
  65. children: [],
  66. },
  67. floor: {
  68. type: "ReflectorPlane",
  69. props: {
  70. position: [0, 0, 0],
  71. rotation: [-1.5708, 0, 0],
  72. scale: null,
  73. width: 30,
  74. height: 30,
  75. color: "#2a2a2a",
  76. resolution: 1024,
  77. blur: 400,
  78. mirror: 0.4,
  79. mixBlur: 10,
  80. mixStrength: 1.5,
  81. metalness: 0.5,
  82. roughness: 0.8,
  83. },
  84. children: [],
  85. },
  86. shadows: {
  87. type: "ContactShadows",
  88. props: {
  89. position: [0, 0.01, 0],
  90. rotation: null,
  91. scale: null,
  92. opacity: 0.4,
  93. width: 20,
  94. height: 20,
  95. blur: 2.5,
  96. far: 8,
  97. color: "#000000",
  98. },
  99. children: [],
  100. },
  101. // --- PORTAL 1 (left, sunset world) ---
  102. frame1: {
  103. type: "Group",
  104. props: {
  105. position: [-3.5, 2, -2],
  106. rotation: [0, 0.3, 0],
  107. scale: null,
  108. },
  109. children: [
  110. "frame1-top",
  111. "frame1-bottom",
  112. "frame1-left",
  113. "frame1-right",
  114. "portal1-mesh",
  115. ],
  116. },
  117. "frame1-top": {
  118. type: "Box",
  119. props: {
  120. position: [0, 1.55, 0],
  121. rotation: null,
  122. scale: null,
  123. width: 2.3,
  124. height: 0.15,
  125. depth: 0.15,
  126. material: { color: "#8B7355", metalness: 0.3, roughness: 0.6 },
  127. },
  128. children: [],
  129. },
  130. "frame1-bottom": {
  131. type: "Box",
  132. props: {
  133. position: [0, -1.55, 0],
  134. rotation: null,
  135. scale: null,
  136. width: 2.3,
  137. height: 0.15,
  138. depth: 0.15,
  139. material: { color: "#8B7355", metalness: 0.3, roughness: 0.6 },
  140. },
  141. children: [],
  142. },
  143. "frame1-left": {
  144. type: "Box",
  145. props: {
  146. position: [-1.075, 0, 0],
  147. rotation: null,
  148. scale: null,
  149. width: 0.15,
  150. height: 3.25,
  151. depth: 0.15,
  152. material: { color: "#8B7355", metalness: 0.3, roughness: 0.6 },
  153. },
  154. children: [],
  155. },
  156. "frame1-right": {
  157. type: "Box",
  158. props: {
  159. position: [1.075, 0, 0],
  160. rotation: null,
  161. scale: null,
  162. width: 0.15,
  163. height: 3.25,
  164. depth: 0.15,
  165. material: { color: "#8B7355", metalness: 0.3, roughness: 0.6 },
  166. },
  167. children: [],
  168. },
  169. "portal1-mesh": {
  170. type: "Plane",
  171. props: {
  172. position: null,
  173. rotation: null,
  174. scale: null,
  175. width: 2,
  176. height: 3,
  177. },
  178. children: ["portal1-mat"],
  179. },
  180. "portal1-mat": {
  181. type: "MeshPortalMaterial",
  182. props: { blend: 0, blur: 0.5, resolution: 512 },
  183. children: [
  184. "p1-sky",
  185. "p1-sun-light",
  186. "p1-cloud1",
  187. "p1-cloud2",
  188. "p1-cloud3",
  189. "p1-ground",
  190. ],
  191. },
  192. "p1-sky": {
  193. type: "Sky",
  194. props: {
  195. distance: null,
  196. sunPosition: [100, 10, 50],
  197. inclination: null,
  198. azimuth: null,
  199. rayleigh: 2,
  200. turbidity: 10,
  201. mieCoefficient: 0.005,
  202. mieDirectionalG: null,
  203. },
  204. children: [],
  205. },
  206. "p1-sun-light": {
  207. type: "DirectionalLight",
  208. props: {
  209. position: [5, 5, 3],
  210. rotation: null,
  211. scale: null,
  212. color: "#ffaa55",
  213. intensity: 2,
  214. castShadow: false,
  215. },
  216. children: [],
  217. },
  218. "p1-cloud1": {
  219. type: "Cloud",
  220. props: {
  221. position: [-3, 4, -5],
  222. rotation: null,
  223. scale: null,
  224. seed: 1,
  225. segments: 20,
  226. bounds: [6, 2, 3],
  227. opacity: 0.6,
  228. color: "#ffddaa",
  229. speed: 0.1,
  230. volume: 5,
  231. fade: null,
  232. growth: null,
  233. },
  234. children: [],
  235. },
  236. "p1-cloud2": {
  237. type: "Cloud",
  238. props: {
  239. position: [4, 5, -8],
  240. rotation: null,
  241. scale: null,
  242. seed: 7,
  243. segments: 25,
  244. bounds: [8, 2, 4],
  245. opacity: 0.5,
  246. color: "#ffccaa",
  247. speed: 0.08,
  248. volume: 6,
  249. fade: null,
  250. growth: null,
  251. },
  252. children: [],
  253. },
  254. "p1-cloud3": {
  255. type: "Cloud",
  256. props: {
  257. position: [0, 3.5, -4],
  258. rotation: null,
  259. scale: null,
  260. seed: 14,
  261. segments: 15,
  262. bounds: [5, 1.5, 2.5],
  263. opacity: 0.7,
  264. color: "#ffeedd",
  265. speed: 0.12,
  266. volume: 4,
  267. fade: null,
  268. growth: null,
  269. },
  270. children: [],
  271. },
  272. "p1-ground": {
  273. type: "Plane",
  274. props: {
  275. position: [0, -2, 0],
  276. rotation: [-1.5708, 0, 0],
  277. scale: null,
  278. width: 30,
  279. height: 30,
  280. material: { color: "#886644", roughness: 1 },
  281. },
  282. children: [],
  283. },
  284. // --- PORTAL 2 (center, night/stars world) ---
  285. frame2: {
  286. type: "Group",
  287. props: {
  288. position: [0, 2, -3],
  289. rotation: null,
  290. scale: null,
  291. },
  292. children: [
  293. "frame2-top",
  294. "frame2-bottom",
  295. "frame2-left",
  296. "frame2-right",
  297. "portal2-mesh",
  298. ],
  299. },
  300. "frame2-top": {
  301. type: "Box",
  302. props: {
  303. position: [0, 1.55, 0],
  304. rotation: null,
  305. scale: null,
  306. width: 2.3,
  307. height: 0.15,
  308. depth: 0.15,
  309. material: { color: "#555566", metalness: 0.8, roughness: 0.2 },
  310. },
  311. children: [],
  312. },
  313. "frame2-bottom": {
  314. type: "Box",
  315. props: {
  316. position: [0, -1.55, 0],
  317. rotation: null,
  318. scale: null,
  319. width: 2.3,
  320. height: 0.15,
  321. depth: 0.15,
  322. material: { color: "#555566", metalness: 0.8, roughness: 0.2 },
  323. },
  324. children: [],
  325. },
  326. "frame2-left": {
  327. type: "Box",
  328. props: {
  329. position: [-1.075, 0, 0],
  330. rotation: null,
  331. scale: null,
  332. width: 0.15,
  333. height: 3.25,
  334. depth: 0.15,
  335. material: { color: "#555566", metalness: 0.8, roughness: 0.2 },
  336. },
  337. children: [],
  338. },
  339. "frame2-right": {
  340. type: "Box",
  341. props: {
  342. position: [1.075, 0, 0],
  343. rotation: null,
  344. scale: null,
  345. width: 0.15,
  346. height: 3.25,
  347. depth: 0.15,
  348. material: { color: "#555566", metalness: 0.8, roughness: 0.2 },
  349. },
  350. children: [],
  351. },
  352. "portal2-mesh": {
  353. type: "Plane",
  354. props: {
  355. position: null,
  356. rotation: null,
  357. scale: null,
  358. width: 2,
  359. height: 3,
  360. },
  361. children: ["portal2-mat"],
  362. },
  363. "portal2-mat": {
  364. type: "MeshPortalMaterial",
  365. props: { blend: 0, blur: 0.5, resolution: 512 },
  366. children: [
  367. "p2-stars",
  368. "p2-ambient",
  369. "p2-nebula1",
  370. "p2-nebula2",
  371. "p2-sparkles",
  372. ],
  373. },
  374. "p2-stars": {
  375. type: "Stars",
  376. props: {
  377. radius: 80,
  378. depth: 40,
  379. count: 6000,
  380. factor: 5,
  381. saturation: 0.5,
  382. fade: true,
  383. speed: 0.3,
  384. },
  385. children: [],
  386. },
  387. "p2-ambient": {
  388. type: "AmbientLight",
  389. props: { color: "#111133", intensity: 0.3 },
  390. children: [],
  391. },
  392. "p2-nebula1": {
  393. type: "Sphere",
  394. props: {
  395. position: [3, 2, -8],
  396. rotation: null,
  397. scale: null,
  398. radius: 2,
  399. widthSegments: null,
  400. heightSegments: null,
  401. material: {
  402. color: "#4400aa",
  403. emissive: "#4400aa",
  404. emissiveIntensity: 0.5,
  405. transparent: true,
  406. opacity: 0.3,
  407. },
  408. },
  409. children: [],
  410. },
  411. "p2-nebula2": {
  412. type: "Sphere",
  413. props: {
  414. position: [-4, -1, -10],
  415. rotation: null,
  416. scale: null,
  417. radius: 3,
  418. widthSegments: null,
  419. heightSegments: null,
  420. material: {
  421. color: "#aa0044",
  422. emissive: "#aa0044",
  423. emissiveIntensity: 0.4,
  424. transparent: true,
  425. opacity: 0.25,
  426. },
  427. },
  428. children: [],
  429. },
  430. "p2-sparkles": {
  431. type: "Sparkles",
  432. props: {
  433. position: [0, 0, -5],
  434. rotation: null,
  435. scale: [10, 10, 10],
  436. count: 200,
  437. speed: 0.2,
  438. opacity: 0.8,
  439. color: "#aabbff",
  440. size: 2,
  441. noise: 3,
  442. },
  443. children: [],
  444. },
  445. // --- PORTAL 3 (right, forest/nature world) ---
  446. frame3: {
  447. type: "Group",
  448. props: {
  449. position: [3.5, 2, -2],
  450. rotation: [0, -0.3, 0],
  451. scale: null,
  452. },
  453. children: [
  454. "frame3-top",
  455. "frame3-bottom",
  456. "frame3-left",
  457. "frame3-right",
  458. "portal3-mesh",
  459. ],
  460. },
  461. "frame3-top": {
  462. type: "Box",
  463. props: {
  464. position: [0, 1.55, 0],
  465. rotation: null,
  466. scale: null,
  467. width: 2.3,
  468. height: 0.15,
  469. depth: 0.15,
  470. material: { color: "#6B4423", metalness: 0.1, roughness: 0.8 },
  471. },
  472. children: [],
  473. },
  474. "frame3-bottom": {
  475. type: "Box",
  476. props: {
  477. position: [0, -1.55, 0],
  478. rotation: null,
  479. scale: null,
  480. width: 2.3,
  481. height: 0.15,
  482. depth: 0.15,
  483. material: { color: "#6B4423", metalness: 0.1, roughness: 0.8 },
  484. },
  485. children: [],
  486. },
  487. "frame3-left": {
  488. type: "Box",
  489. props: {
  490. position: [-1.075, 0, 0],
  491. rotation: null,
  492. scale: null,
  493. width: 0.15,
  494. height: 3.25,
  495. depth: 0.15,
  496. material: { color: "#6B4423", metalness: 0.1, roughness: 0.8 },
  497. },
  498. children: [],
  499. },
  500. "frame3-right": {
  501. type: "Box",
  502. props: {
  503. position: [1.075, 0, 0],
  504. rotation: null,
  505. scale: null,
  506. width: 0.15,
  507. height: 3.25,
  508. depth: 0.15,
  509. material: { color: "#6B4423", metalness: 0.1, roughness: 0.8 },
  510. },
  511. children: [],
  512. },
  513. "portal3-mesh": {
  514. type: "Plane",
  515. props: {
  516. position: null,
  517. rotation: null,
  518. scale: null,
  519. width: 2,
  520. height: 3,
  521. },
  522. children: ["portal3-mat"],
  523. },
  524. "portal3-mat": {
  525. type: "MeshPortalMaterial",
  526. props: { blend: 0, blur: 0.5, resolution: 512 },
  527. children: [
  528. "p3-env",
  529. "p3-ambient",
  530. "p3-sun",
  531. "p3-tree1",
  532. "p3-tree2",
  533. "p3-tree3",
  534. "p3-tree4",
  535. "p3-tree5",
  536. "p3-ground",
  537. "p3-fireflies",
  538. ],
  539. },
  540. "p3-env": {
  541. type: "Environment",
  542. props: { preset: "forest", background: false, blur: 0, intensity: 0.8 },
  543. children: [],
  544. },
  545. "p3-ambient": {
  546. type: "AmbientLight",
  547. props: { color: "#334422", intensity: 0.4 },
  548. children: [],
  549. },
  550. "p3-sun": {
  551. type: "DirectionalLight",
  552. props: {
  553. position: [-3, 8, 2],
  554. rotation: null,
  555. scale: null,
  556. color: "#aaffaa",
  557. intensity: 1.5,
  558. castShadow: true,
  559. },
  560. children: [],
  561. },
  562. "p3-tree1": {
  563. type: "Group",
  564. props: { position: [-2, -2, -4], rotation: null, scale: null },
  565. children: ["p3-t1-trunk", "p3-t1-leaves"],
  566. },
  567. "p3-t1-trunk": {
  568. type: "Cylinder",
  569. props: {
  570. position: [0, 0.6, 0],
  571. rotation: null,
  572. scale: null,
  573. radiusTop: 0.08,
  574. radiusBottom: 0.12,
  575. height: 1.2,
  576. radialSegments: 8,
  577. material: { color: "#4a3520", roughness: 0.9 },
  578. },
  579. children: [],
  580. },
  581. "p3-t1-leaves": {
  582. type: "Cone",
  583. props: {
  584. position: [0, 1.8, 0],
  585. rotation: null,
  586. scale: null,
  587. radius: 0.6,
  588. height: 1.5,
  589. radialSegments: 8,
  590. material: { color: "#2d5a1e", roughness: 0.8 },
  591. },
  592. children: [],
  593. },
  594. "p3-tree2": {
  595. type: "Group",
  596. props: { position: [1.5, -2, -6], rotation: null, scale: null },
  597. children: ["p3-t2-trunk", "p3-t2-leaves"],
  598. },
  599. "p3-t2-trunk": {
  600. type: "Cylinder",
  601. props: {
  602. position: [0, 0.8, 0],
  603. rotation: null,
  604. scale: null,
  605. radiusTop: 0.1,
  606. radiusBottom: 0.15,
  607. height: 1.6,
  608. radialSegments: 8,
  609. material: { color: "#3d2b15", roughness: 0.9 },
  610. },
  611. children: [],
  612. },
  613. "p3-t2-leaves": {
  614. type: "Cone",
  615. props: {
  616. position: [0, 2.3, 0],
  617. rotation: null,
  618. scale: null,
  619. radius: 0.8,
  620. height: 2,
  621. radialSegments: 8,
  622. material: { color: "#1e4a10", roughness: 0.8 },
  623. },
  624. children: [],
  625. },
  626. "p3-tree3": {
  627. type: "Group",
  628. props: { position: [-0.5, -2, -3], rotation: null, scale: null },
  629. children: ["p3-t3-trunk", "p3-t3-leaves"],
  630. },
  631. "p3-t3-trunk": {
  632. type: "Cylinder",
  633. props: {
  634. position: [0, 0.5, 0],
  635. rotation: null,
  636. scale: null,
  637. radiusTop: 0.06,
  638. radiusBottom: 0.1,
  639. height: 1,
  640. radialSegments: 8,
  641. material: { color: "#4a3520", roughness: 0.9 },
  642. },
  643. children: [],
  644. },
  645. "p3-t3-leaves": {
  646. type: "Cone",
  647. props: {
  648. position: [0, 1.5, 0],
  649. rotation: null,
  650. scale: null,
  651. radius: 0.5,
  652. height: 1.2,
  653. radialSegments: 8,
  654. material: { color: "#3a6b2a", roughness: 0.8 },
  655. },
  656. children: [],
  657. },
  658. "p3-tree4": {
  659. type: "Group",
  660. props: { position: [3, -2, -8], rotation: null, scale: null },
  661. children: ["p3-t4-trunk", "p3-t4-leaves"],
  662. },
  663. "p3-t4-trunk": {
  664. type: "Cylinder",
  665. props: {
  666. position: [0, 1, 0],
  667. rotation: null,
  668. scale: null,
  669. radiusTop: 0.12,
  670. radiusBottom: 0.18,
  671. height: 2,
  672. radialSegments: 8,
  673. material: { color: "#3d2b15", roughness: 0.9 },
  674. },
  675. children: [],
  676. },
  677. "p3-t4-leaves": {
  678. type: "Cone",
  679. props: {
  680. position: [0, 2.8, 0],
  681. rotation: null,
  682. scale: null,
  683. radius: 1,
  684. height: 2.5,
  685. radialSegments: 8,
  686. material: { color: "#2d5a1e", roughness: 0.8 },
  687. },
  688. children: [],
  689. },
  690. "p3-tree5": {
  691. type: "Group",
  692. props: { position: [-3.5, -2, -7], rotation: null, scale: null },
  693. children: ["p3-t5-trunk", "p3-t5-leaves"],
  694. },
  695. "p3-t5-trunk": {
  696. type: "Cylinder",
  697. props: {
  698. position: [0, 0.7, 0],
  699. rotation: null,
  700. scale: null,
  701. radiusTop: 0.09,
  702. radiusBottom: 0.13,
  703. height: 1.4,
  704. radialSegments: 8,
  705. material: { color: "#4a3520", roughness: 0.9 },
  706. },
  707. children: [],
  708. },
  709. "p3-t5-leaves": {
  710. type: "Cone",
  711. props: {
  712. position: [0, 2, 0],
  713. rotation: null,
  714. scale: null,
  715. radius: 0.7,
  716. height: 1.8,
  717. radialSegments: 8,
  718. material: { color: "#1e4a10", roughness: 0.8 },
  719. },
  720. children: [],
  721. },
  722. "p3-ground": {
  723. type: "Plane",
  724. props: {
  725. position: [0, -2, 0],
  726. rotation: [-1.5708, 0, 0],
  727. scale: null,
  728. width: 30,
  729. height: 30,
  730. material: { color: "#3a5a2a", roughness: 1 },
  731. },
  732. children: [],
  733. },
  734. "p3-fireflies": {
  735. type: "Sparkles",
  736. props: {
  737. position: [0, 0, -5],
  738. rotation: null,
  739. scale: [6, 4, 6],
  740. count: 80,
  741. speed: 0.3,
  742. opacity: 0.7,
  743. color: "#aaff66",
  744. size: 1.5,
  745. noise: 2,
  746. },
  747. children: [],
  748. },
  749. "gallery-sparkles": {
  750. type: "Sparkles",
  751. props: {
  752. position: [0, 3, 0],
  753. rotation: null,
  754. scale: [12, 5, 10],
  755. count: 40,
  756. speed: 0.05,
  757. opacity: 0.2,
  758. color: "#ffffff",
  759. size: 0.3,
  760. noise: null,
  761. },
  762. children: [],
  763. },
  764. post: {
  765. type: "EffectComposer",
  766. props: { enabled: true, multisampling: 8 },
  767. children: ["bloom", "vignette"],
  768. },
  769. bloom: {
  770. type: "Bloom",
  771. props: {
  772. intensity: 0.6,
  773. luminanceThreshold: 0.3,
  774. luminanceSmoothing: null,
  775. mipmapBlur: true,
  776. },
  777. children: [],
  778. },
  779. vignette: {
  780. type: "Vignette",
  781. props: { offset: 0.4, darkness: 0.4 },
  782. children: [],
  783. },
  784. controls: {
  785. type: "OrbitControls",
  786. props: {
  787. enableDamping: true,
  788. dampingFactor: null,
  789. enableZoom: true,
  790. enablePan: null,
  791. enableRotate: true,
  792. minDistance: 4,
  793. maxDistance: 16,
  794. minPolarAngle: null,
  795. maxPolarAngle: null,
  796. autoRotate: true,
  797. autoRotateSpeed: 0.3,
  798. target: [0, 1.8, -2],
  799. },
  800. children: [],
  801. },
  802. },
  803. },
  804. };