minecraft-renderer 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (187) hide show
  1. package/README.md +297 -0
  2. package/dist/index.html +83 -0
  3. package/dist/static/image/arrow.6f27b59f.png +0 -0
  4. package/dist/static/image/blocksAtlasLatest.7850afa3.png +0 -0
  5. package/dist/static/image/blocksAtlasLegacy.5c76823d.png +0 -0
  6. package/dist/static/image/itemsAtlasLatest.36036f95.png +0 -0
  7. package/dist/static/image/itemsAtlasLegacy.dcb1b58d.png +0 -0
  8. package/dist/static/image/tipped_arrow.6f27b59f.png +0 -0
  9. package/dist/static/js/365.f05233ab.js +8462 -0
  10. package/dist/static/js/365.f05233ab.js.LICENSE.txt +52 -0
  11. package/dist/static/js/async/738.efa27644.js +1 -0
  12. package/dist/static/js/index.092ec5be.js +56 -0
  13. package/dist/static/js/lib-polyfill.98986ac5.js +1 -0
  14. package/dist/static/js/lib-react.5c9129e0.js +2 -0
  15. package/dist/static/js/lib-react.5c9129e0.js.LICENSE.txt +39 -0
  16. package/package.json +104 -0
  17. package/src/assets/destroy_stage_0.png +0 -0
  18. package/src/assets/destroy_stage_1.png +0 -0
  19. package/src/assets/destroy_stage_2.png +0 -0
  20. package/src/assets/destroy_stage_3.png +0 -0
  21. package/src/assets/destroy_stage_4.png +0 -0
  22. package/src/assets/destroy_stage_5.png +0 -0
  23. package/src/assets/destroy_stage_6.png +0 -0
  24. package/src/assets/destroy_stage_7.png +0 -0
  25. package/src/assets/destroy_stage_8.png +0 -0
  26. package/src/assets/destroy_stage_9.png +0 -0
  27. package/src/examples/README.md +146 -0
  28. package/src/examples/appViewerExample.ts +205 -0
  29. package/src/examples/initialMenuStart.ts +161 -0
  30. package/src/graphicsBackend/appViewer.ts +297 -0
  31. package/src/graphicsBackend/config.ts +119 -0
  32. package/src/graphicsBackend/index.ts +10 -0
  33. package/src/graphicsBackend/playerState.ts +61 -0
  34. package/src/graphicsBackend/types.ts +143 -0
  35. package/src/index.ts +97 -0
  36. package/src/lib/DebugGui.ts +190 -0
  37. package/src/lib/animationController.ts +85 -0
  38. package/src/lib/buildSharedConfig.mjs +1 -0
  39. package/src/lib/cameraBobbing.ts +94 -0
  40. package/src/lib/canvas2DOverlay.example.ts +361 -0
  41. package/src/lib/canvas2DOverlay.quickstart.ts +242 -0
  42. package/src/lib/canvas2DOverlay.ts +381 -0
  43. package/src/lib/cleanupDecorator.ts +29 -0
  44. package/src/lib/createPlayerObject.ts +55 -0
  45. package/src/lib/frameTimingCollector.ts +164 -0
  46. package/src/lib/guiRenderer.ts +283 -0
  47. package/src/lib/items.ts +140 -0
  48. package/src/lib/mesherlogReader.ts +131 -0
  49. package/src/lib/moreBlockDataGenerated.json +714 -0
  50. package/src/lib/preflatMap.json +1741 -0
  51. package/src/lib/simpleUtils.ts +40 -0
  52. package/src/lib/smoothSwitcher.ts +168 -0
  53. package/src/lib/spiral.ts +29 -0
  54. package/src/lib/ui/newStats.ts +120 -0
  55. package/src/lib/utils/proxy.ts +23 -0
  56. package/src/lib/utils/skins.ts +63 -0
  57. package/src/lib/utils.ts +76 -0
  58. package/src/lib/workerProxy.ts +342 -0
  59. package/src/lib/worldrendererCommon.ts +1088 -0
  60. package/src/mesher/mesher.ts +253 -0
  61. package/src/mesher/models.ts +769 -0
  62. package/src/mesher/modelsGeometryCommon.ts +142 -0
  63. package/src/mesher/shared.ts +80 -0
  64. package/src/mesher/standaloneRenderer.ts +270 -0
  65. package/src/mesher/test/a.ts +3 -0
  66. package/src/mesher/test/mesherTester.ts +76 -0
  67. package/src/mesher/test/playground.ts +19 -0
  68. package/src/mesher/test/test-perf.ts +74 -0
  69. package/src/mesher/test/tests.test.ts +56 -0
  70. package/src/mesher/world.ts +294 -0
  71. package/src/mesher/worldConstants.ts +1 -0
  72. package/src/modules/index.ts +11 -0
  73. package/src/modules/starfield.ts +313 -0
  74. package/src/modules/types.ts +110 -0
  75. package/src/playerState/playerState.ts +78 -0
  76. package/src/playerState/types.ts +36 -0
  77. package/src/playground/allEntitiesDebug.ts +170 -0
  78. package/src/playground/baseScene.ts +587 -0
  79. package/src/playground/mobileControls.tsx +268 -0
  80. package/src/playground/playground.html +83 -0
  81. package/src/playground/playground.ts +11 -0
  82. package/src/playground/playgroundUi.tsx +140 -0
  83. package/src/playground/reactUtils.ts +71 -0
  84. package/src/playground/scenes/allEntities.ts +13 -0
  85. package/src/playground/scenes/entities.ts +37 -0
  86. package/src/playground/scenes/floorRandom.ts +33 -0
  87. package/src/playground/scenes/frequentUpdates.ts +148 -0
  88. package/src/playground/scenes/geometryExport.ts +142 -0
  89. package/src/playground/scenes/index.ts +12 -0
  90. package/src/playground/scenes/lightingStarfield.ts +40 -0
  91. package/src/playground/scenes/main.ts +313 -0
  92. package/src/playground/scenes/railsCobweb.ts +14 -0
  93. package/src/playground/scenes/rotationIssue.ts +7 -0
  94. package/src/playground/scenes/slabsOptimization.ts +16 -0
  95. package/src/playground/scenes/transparencyIssue.ts +11 -0
  96. package/src/playground/shared.ts +79 -0
  97. package/src/resourcesManager/index.ts +5 -0
  98. package/src/resourcesManager/resourcesManager.ts +314 -0
  99. package/src/shims/minecraftData.ts +41 -0
  100. package/src/sign-renderer/index.html +21 -0
  101. package/src/sign-renderer/index.ts +216 -0
  102. package/src/sign-renderer/noop.js +1 -0
  103. package/src/sign-renderer/playground.ts +38 -0
  104. package/src/sign-renderer/tests.test.ts +69 -0
  105. package/src/sign-renderer/vite.config.ts +10 -0
  106. package/src/three/appShared.ts +75 -0
  107. package/src/three/bannerRenderer.ts +275 -0
  108. package/src/three/cameraShake.ts +120 -0
  109. package/src/three/cinimaticScript.ts +350 -0
  110. package/src/three/documentRenderer.ts +491 -0
  111. package/src/three/entities.ts +1580 -0
  112. package/src/three/entity/EntityMesh.ts +707 -0
  113. package/src/three/entity/animations.js +171 -0
  114. package/src/three/entity/armorModels.json +204 -0
  115. package/src/three/entity/armorModels.ts +36 -0
  116. package/src/three/entity/entities.json +6230 -0
  117. package/src/three/entity/exportedModels.js +38 -0
  118. package/src/three/entity/externalTextures.json +1 -0
  119. package/src/three/entity/models/allay.obj +325 -0
  120. package/src/three/entity/models/arrow.obj +60 -0
  121. package/src/three/entity/models/axolotl.obj +509 -0
  122. package/src/three/entity/models/blaze.obj +601 -0
  123. package/src/three/entity/models/boat.obj +417 -0
  124. package/src/three/entity/models/camel.obj +1061 -0
  125. package/src/three/entity/models/cat.obj +509 -0
  126. package/src/three/entity/models/chicken.obj +371 -0
  127. package/src/three/entity/models/cod.obj +371 -0
  128. package/src/three/entity/models/creeper.obj +279 -0
  129. package/src/three/entity/models/dolphin.obj +371 -0
  130. package/src/three/entity/models/ender_dragon.obj +2993 -0
  131. package/src/three/entity/models/enderman.obj +325 -0
  132. package/src/three/entity/models/endermite.obj +187 -0
  133. package/src/three/entity/models/fox.obj +463 -0
  134. package/src/three/entity/models/frog.obj +739 -0
  135. package/src/three/entity/models/ghast.obj +463 -0
  136. package/src/three/entity/models/goat.obj +601 -0
  137. package/src/three/entity/models/guardian.obj +1015 -0
  138. package/src/three/entity/models/horse.obj +1061 -0
  139. package/src/three/entity/models/llama.obj +509 -0
  140. package/src/three/entity/models/minecart.obj +233 -0
  141. package/src/three/entity/models/parrot.obj +509 -0
  142. package/src/three/entity/models/piglin.obj +739 -0
  143. package/src/three/entity/models/pillager.obj +371 -0
  144. package/src/three/entity/models/rabbit.obj +555 -0
  145. package/src/three/entity/models/sheep.obj +555 -0
  146. package/src/three/entity/models/shulker.obj +141 -0
  147. package/src/three/entity/models/sniffer.obj +693 -0
  148. package/src/three/entity/models/spider.obj +509 -0
  149. package/src/three/entity/models/tadpole.obj +95 -0
  150. package/src/three/entity/models/turtle.obj +371 -0
  151. package/src/three/entity/models/vex.obj +325 -0
  152. package/src/three/entity/models/villager.obj +509 -0
  153. package/src/three/entity/models/warden.obj +463 -0
  154. package/src/three/entity/models/witch.obj +647 -0
  155. package/src/three/entity/models/wolf.obj +509 -0
  156. package/src/three/entity/models/zombie_villager.obj +463 -0
  157. package/src/three/entity/objModels.js +1 -0
  158. package/src/three/fireworks.ts +661 -0
  159. package/src/three/fireworksRenderer.ts +434 -0
  160. package/src/three/globals.d.ts +7 -0
  161. package/src/three/graphicsBackend.ts +274 -0
  162. package/src/three/graphicsBackendOffThread.ts +107 -0
  163. package/src/three/hand.ts +89 -0
  164. package/src/three/holdingBlock.ts +926 -0
  165. package/src/three/index.ts +20 -0
  166. package/src/three/itemMesh.ts +427 -0
  167. package/src/three/modules.d.ts +14 -0
  168. package/src/three/panorama.ts +308 -0
  169. package/src/three/panoramaShared.ts +1 -0
  170. package/src/three/renderSlot.ts +82 -0
  171. package/src/three/skyboxRenderer.ts +406 -0
  172. package/src/three/starField.ts +13 -0
  173. package/src/three/threeJsMedia.ts +731 -0
  174. package/src/three/threeJsMethods.ts +15 -0
  175. package/src/three/threeJsParticles.ts +160 -0
  176. package/src/three/threeJsSound.ts +95 -0
  177. package/src/three/threeJsUtils.ts +90 -0
  178. package/src/three/waypointSprite.ts +435 -0
  179. package/src/three/waypoints.ts +163 -0
  180. package/src/three/world/cursorBlock.ts +172 -0
  181. package/src/three/world/vr.ts +257 -0
  182. package/src/three/worldGeometryExport.ts +259 -0
  183. package/src/three/worldGeometryHandler.ts +279 -0
  184. package/src/three/worldRendererThree.ts +1381 -0
  185. package/src/worldView/index.ts +6 -0
  186. package/src/worldView/types.ts +66 -0
  187. package/src/worldView/worldView.ts +424 -0
@@ -0,0 +1,350 @@
1
+ import * as THREE from 'three'
2
+ import * as tweenJs from '@tweenjs/tween.js'
3
+ import { Vec3 } from 'vec3'
4
+ import { WorldRendererThree } from './worldRendererThree'
5
+
6
+ export interface CinimaticPoint {
7
+ x: number
8
+ y: number
9
+ z: number
10
+ yaw: number
11
+ pitch: number
12
+ duration: number // Time to reach this point from the previous one
13
+ easing?: 'linear' | 'easeIn' | 'easeOut' | 'easeInOut' | 'smoothstep' | 'bounce'
14
+ lookAt?: { x: number, y: number, z: number } // Optional: override rotation to look at this point
15
+ fov?: number // Optional: change field of view
16
+ }
17
+
18
+ export interface CinimaticScript {
19
+ name?: string
20
+ points: CinimaticPoint[]
21
+ loop?: boolean
22
+ onComplete?: () => void
23
+ onPointReached?: (pointIndex: number, point: CinimaticPoint) => void
24
+ }
25
+
26
+ export class CinimaticScriptRunner {
27
+ private isRunning = false
28
+ private currentScript: CinimaticScript | null = null
29
+ private currentPointIndex = 0
30
+ private currentTweens: Array<tweenJs.Tween<any>> = []
31
+ private startTime = 0
32
+ private totalDuration = 0
33
+
34
+ // Camera state
35
+ private currentPosition = { x: 0, y: 0, z: 0 }
36
+ private currentRotation = { yaw: 0, pitch: 0 }
37
+ private currentFov = 75
38
+
39
+ constructor(
40
+ private readonly worldRenderer: WorldRendererThree,
41
+ private readonly updateCamera: (pos: Vec3, yaw: number, pitch: number) => void,
42
+ private readonly updateFov: (fov: number) => void,
43
+ private readonly getInitialState: () => { position: Vec3, yaw: number, pitch: number, fov: number }
44
+ ) { }
45
+
46
+ startScript(script: CinimaticScript): boolean {
47
+ if (this.isRunning) {
48
+ console.warn('Cinematic script already running. Stop current script first.')
49
+ return false
50
+ }
51
+
52
+ if (!script.points || script.points.length === 0) {
53
+ console.warn('Cinematic script has no points')
54
+ return false
55
+ }
56
+
57
+ this.currentScript = script
58
+ this.isRunning = true
59
+ this.currentPointIndex = 0
60
+ this.startTime = performance.now()
61
+
62
+ // Calculate total duration
63
+ this.totalDuration = script.points.reduce((sum, point) => sum + point.duration, 0)
64
+
65
+ // Get initial state
66
+ const initialState = this.getInitialState()
67
+ this.currentPosition = {
68
+ x: initialState.position.x,
69
+ y: initialState.position.y,
70
+ z: initialState.position.z
71
+ }
72
+ this.currentRotation = {
73
+ yaw: initialState.yaw,
74
+ pitch: initialState.pitch
75
+ }
76
+ this.currentFov = initialState.fov
77
+
78
+ console.log(`Starting cinematic script: ${script.name || 'Unnamed'} (${this.totalDuration}ms)`)
79
+
80
+ // Start from first point
81
+ this.moveToPoint(0)
82
+
83
+ return true
84
+ }
85
+
86
+ stopScript(): void {
87
+ if (!this.isRunning) return
88
+
89
+ // Stop all active tweens
90
+ for (const tween of this.currentTweens) tween.stop()
91
+ this.currentTweens = []
92
+
93
+ this.isRunning = false
94
+ this.currentScript = null
95
+ this.currentPointIndex = 0
96
+ }
97
+
98
+ runExampleScripts(index: number) {
99
+ const { cameraObject } = this.worldRenderer
100
+ const playerPos = new Vec3(cameraObject.position.x, cameraObject.position.y, cameraObject.position.z)
101
+
102
+ // Circular flyby around current position
103
+ const circular = CinimaticScriptRunner.createCircularFlyby(playerPos, 30, 20, 15_000)
104
+
105
+ // Spiral descent from high above to current position
106
+ const spiral = CinimaticScriptRunner.createSpiralDescent(
107
+ playerPos.offset(0, 50, 0),
108
+ playerPos,
109
+ 3, // 3 spirals
110
+ 12_000 // 12 seconds
111
+ )
112
+
113
+ // Building tour example
114
+ const buildingTour = CinimaticScriptRunner.createBuildingTour([
115
+ { pos: playerPos.offset(-20, 10, -20), lookAt: playerPos, duration: 3000 },
116
+ { pos: playerPos.offset(20, 15, -20), lookAt: playerPos, duration: 3000 },
117
+ { pos: playerPos.offset(20, 20, 20), lookAt: playerPos, duration: 3000 },
118
+ { pos: playerPos.offset(-20, 25, 20), lookAt: playerPos, duration: 3000 },
119
+ ])
120
+
121
+ const scripts = [circular, spiral, buildingTour]
122
+ this.startScript(scripts[index])
123
+
124
+ return { circular, spiral, buildingTour }
125
+ }
126
+
127
+ private moveToPoint(pointIndex: number): void {
128
+ if (!this.currentScript || pointIndex >= this.currentScript.points.length) {
129
+ this.handleScriptComplete()
130
+ return
131
+ }
132
+
133
+ const point = this.currentScript.points[pointIndex]
134
+ const { duration } = point
135
+
136
+ // Create target state
137
+ const targetPosition = { x: point.x, y: point.y, z: point.z }
138
+ const targetRotation = { yaw: point.yaw, pitch: point.pitch }
139
+ const targetFov = point.fov ?? this.currentFov
140
+
141
+ // Handle lookAt override
142
+ if (point.lookAt) {
143
+ const lookAtVec = new THREE.Vector3(point.lookAt.x, point.lookAt.y, point.lookAt.z)
144
+ const fromVec = new THREE.Vector3(point.x, point.y, point.z)
145
+ const direction = lookAtVec.sub(fromVec).normalize()
146
+
147
+ // Convert direction to yaw/pitch
148
+ targetRotation.yaw = Math.atan2(-direction.x, -direction.z)
149
+ targetRotation.pitch = Math.asin(direction.y)
150
+ }
151
+
152
+ // Get easing function
153
+ const easingFn = this.getEasingFunction(point.easing || 'easeInOut')
154
+
155
+ // Create position tween
156
+ const positionTween = new tweenJs.Tween(this.currentPosition)
157
+ .to(targetPosition, duration)
158
+ .easing(easingFn)
159
+ .onUpdate(() => {
160
+ this.updateCamera(
161
+ new Vec3(this.currentPosition.x, this.currentPosition.y, this.currentPosition.z),
162
+ this.currentRotation.yaw,
163
+ this.currentRotation.pitch
164
+ )
165
+ })
166
+
167
+ // Create rotation tween (handle wrapping)
168
+ const rotationTween = new tweenJs.Tween(this.currentRotation)
169
+ .to(this.wrapRotation(targetRotation), duration)
170
+ .easing(easingFn)
171
+ .onUpdate(() => {
172
+ this.updateCamera(
173
+ new Vec3(this.currentPosition.x, this.currentPosition.y, this.currentPosition.z),
174
+ this.currentRotation.yaw,
175
+ this.currentRotation.pitch
176
+ )
177
+ })
178
+
179
+ // Create FOV tween if needed
180
+ let fovTween: tweenJs.Tween<any> | null = null
181
+ if (Math.abs(targetFov - this.currentFov) > 0.1) {
182
+ const fovState = { fov: this.currentFov }
183
+ fovTween = new tweenJs.Tween(fovState)
184
+ .to({ fov: targetFov }, duration)
185
+ .easing(easingFn)
186
+ .onUpdate(() => {
187
+ this.currentFov = fovState.fov
188
+ this.updateFov(this.currentFov)
189
+ })
190
+ }
191
+
192
+ // Start tweens
193
+ this.currentTweens = [positionTween, rotationTween]
194
+ if (fovTween) this.currentTweens.push(fovTween)
195
+
196
+ positionTween.start()
197
+ rotationTween.start()
198
+ fovTween?.start()
199
+
200
+ // Handle completion
201
+ positionTween.onComplete(() => {
202
+ this.currentPointIndex++
203
+
204
+ // Call point reached callback
205
+ this.currentScript?.onPointReached?.(pointIndex, point)
206
+
207
+ // Move to next point
208
+ if (this.isRunning) {
209
+ this.moveToPoint(this.currentPointIndex)
210
+ }
211
+ })
212
+ }
213
+
214
+ private wrapRotation(target: { yaw: number, pitch: number }): { yaw: number, pitch: number } {
215
+ // Handle yaw wrapping to take shortest path
216
+ let targetYaw = target.yaw
217
+ const yawDiff = targetYaw - this.currentRotation.yaw
218
+
219
+ if (yawDiff > Math.PI) {
220
+ targetYaw -= 2 * Math.PI
221
+ } else if (yawDiff < -Math.PI) {
222
+ targetYaw += 2 * Math.PI
223
+ }
224
+
225
+ // Clamp pitch to avoid gimbal lock
226
+ const targetPitch = Math.max(-Math.PI / 2 + 0.01, Math.min(Math.PI / 2 - 0.01, target.pitch))
227
+
228
+ return { yaw: targetYaw, pitch: targetPitch }
229
+ }
230
+
231
+ private getEasingFunction(easing: string): (t: number) => number {
232
+ switch (easing) {
233
+ case 'linear': return tweenJs.Easing.Linear.None
234
+ case 'easeIn': return tweenJs.Easing.Quadratic.In
235
+ case 'easeOut': return tweenJs.Easing.Quadratic.Out
236
+ case 'easeInOut': return tweenJs.Easing.Quadratic.InOut
237
+ case 'smoothstep': return tweenJs.Easing.Cubic.InOut
238
+ case 'bounce': return tweenJs.Easing.Bounce.Out
239
+ default: return tweenJs.Easing.Quadratic.InOut
240
+ }
241
+ }
242
+
243
+ private handleScriptComplete(): void {
244
+ if (!this.currentScript) return
245
+
246
+ const script = this.currentScript
247
+
248
+ if (script.loop) {
249
+ // Restart from beginning
250
+ this.currentPointIndex = 0
251
+ this.moveToPoint(0)
252
+ } else {
253
+ // Script finished
254
+ console.log(`Cinematic script completed: ${script.name || 'Unnamed'}`)
255
+ script.onComplete?.()
256
+ this.stopScript()
257
+ }
258
+ }
259
+
260
+ // Public getters
261
+ get running(): boolean {
262
+ return this.isRunning
263
+ }
264
+
265
+ get progress(): number {
266
+ if (!this.isRunning || this.totalDuration === 0) return 0
267
+ const elapsed = performance.now() - this.startTime
268
+ return Math.min(elapsed / this.totalDuration, 1)
269
+ }
270
+
271
+ get currentScriptName(): string | undefined {
272
+ return this.currentScript?.name
273
+ }
274
+
275
+ // Static helper methods for creating common scripts
276
+ static createCircularFlyby(center: Vec3, radius: number, height: number, duration: number): CinimaticScript {
277
+ const points: CinimaticPoint[] = []
278
+ const numPoints = 8
279
+
280
+ for (let i = 0; i <= numPoints; i++) {
281
+ const angle = (i / numPoints) * Math.PI * 2
282
+ const x = center.x + Math.cos(angle) * radius
283
+ const z = center.z + Math.sin(angle) * radius
284
+ const y = center.y + height
285
+
286
+ points.push({
287
+ x, y, z,
288
+ yaw: angle + Math.PI / 2, // Look tangent to circle
289
+ pitch: -0.2, // Look slightly down
290
+ duration: duration / numPoints,
291
+ easing: 'smoothstep',
292
+ lookAt: { x: center.x, y: center.y, z: center.z }
293
+ })
294
+ }
295
+
296
+ return {
297
+ name: 'Circular Flyby',
298
+ points,
299
+ loop: false
300
+ }
301
+ }
302
+
303
+ static createSpiralDescent(start: Vec3, end: Vec3, spirals: number, duration: number): CinimaticScript {
304
+ const points: CinimaticPoint[] = []
305
+ const numPoints = spirals * 8
306
+ const radius = 20
307
+
308
+ for (let i = 0; i <= numPoints; i++) {
309
+ const t = i / numPoints
310
+ const angle = t * spirals * Math.PI * 2
311
+
312
+ const x = THREE.MathUtils.lerp(start.x, end.x, t) + Math.cos(angle) * radius * (1 - t)
313
+ const z = THREE.MathUtils.lerp(start.z, end.z, t) + Math.sin(angle) * radius * (1 - t)
314
+ const y = THREE.MathUtils.lerp(start.y, end.y, t)
315
+
316
+ points.push({
317
+ x, y, z,
318
+ yaw: angle + Math.PI / 2,
319
+ pitch: -0.3 * t, // Gradually look more down
320
+ duration: duration / numPoints,
321
+ easing: 'easeInOut'
322
+ })
323
+ }
324
+
325
+ return {
326
+ name: 'Spiral Descent',
327
+ points,
328
+ loop: false
329
+ }
330
+ }
331
+
332
+ static createBuildingTour(waypoints: Array<{ pos: Vec3, lookAt?: Vec3, duration?: number }>): CinimaticScript {
333
+ const points: CinimaticPoint[] = waypoints.map((wp, i) => ({
334
+ x: wp.pos.x,
335
+ y: wp.pos.y,
336
+ z: wp.pos.z,
337
+ yaw: 0, // Will be overridden by lookAt if provided
338
+ pitch: 0,
339
+ duration: wp.duration || 3000,
340
+ easing: 'smoothstep',
341
+ lookAt: wp.lookAt ? { x: wp.lookAt.x, y: wp.lookAt.y, z: wp.lookAt.z } : undefined
342
+ }))
343
+
344
+ return {
345
+ name: 'Building Tour',
346
+ points,
347
+ loop: false
348
+ }
349
+ }
350
+ }