// 同一 Scene3D 在透视/正交视口中展示;相机控制独立,网格业务变换由两边共享。 // 三维内容与普通 Endor 二维控件一起排版,viewport 有限尺寸不限制场景世界坐标。 // 保存左侧视口和共享立方体;动作可改相机位置或真实网格变换。 class SceneActions { // view 只保存左透视视口,cube 保存两个视口共同引用的网格,两个字段的修改传播范围不同。 view: Viewport3D cube: Mesh3D // 把左相机放到 z=6 正面,保持相机既有 target。 function front(self: SceneActions): Unit { // front.onClick 以零参绑定方法调用,按绝对坐标设置左相机,不重新构造 Scene3D 或清除网格旋转。 self.view.camera.x = 0 self.view.camera.y = 0 self.view.camera.z = 6 } // 把同一相机移到侧上方,右侧正交相机保持原设置。 function side(self: SceneActions): Unit { // side.onClick 调用;只设置位置三个分量,保留既有 target 和投影参数。 self.view.camera.x = 6 self.view.camera.y = 2 self.view.camera.z = 0 } // 修改共享 cube 的欧拉旋转,因此两个视口都看到物体变化。 // rotate.onClick 调用,15 是本次绕 Y 轴增加的角度,重复点击累加,不把旋转每次复位到 15。 function rotate(self: SceneActions): Unit { self.cube.rotate(0, 15, 0) } // 示范 Event.hit.mesh 的真实托管网格材质写入;此文件尚未把 select 注册到视口。 // 这是供后续命中事件注册的入口,须由提供 hit.mesh 的网格命中事件调用;本文件的三个按钮均不调用它。 function select(self: SceneActions, event: Event): Unit { event.hit.mesh.color = "#6B9B81" } } // 先构造并变换两个 Box3D,再由 Group3D 组成场景节点;几何与相机是不同对象。 cube = Box3D(1.6, 1.6, 1.6, "#6687AB") cube.rotationY = 25 cube.rotationX = 15 small = Box3D(0.7, 0.7, 0.7, "#DBA36E") small.x = 1.8 small.z = -0.8 group = Group3D(cube, small) // group 保留两个网格的变换层级,两视口复用下面同一个 scene,不分别创建立方体副本。 // 薄盒作地面,方向光与 ambient 一起影响材质颜色,不是二维背景矩形。 floor = Box3D(6, 0.08, 4, "#D8DEE6") floor.y = -1.1 light = DirectionalLight(-1, -2, -3, "#FFFFFF", 0.9) scene = Scene3D(group, floor, light) scene.ambient = 0.35 // 左视口使用默认透视相机,右视口显式换成 OrthographicCamera 并设置可见高度。 perspective = Viewport3D(480, 360, scene) perspective.camera.z = 6 orthographic = Viewport3D(320, 360, scene) orthographic.camera = OrthographicCamera() // height 表示正交相机可见世界高度,不是右视口 360 的逻辑像素高度。 orthographic.camera.height = 5 orthographic.camera.x = 4 orthographic.camera.y = 2 orthographic.camera.z = 5 // 按钮绑定零参数方法;页面文字说明原生默认控制器的交互,代码可继续替换 controller。 actions = SceneActions(perspective, cube) front = Button("正面") front.onClick(actions.front) side = Button("侧面") side.onClick(actions.side) rotate = Button("物体旋转 +15°") rotate.onClick(actions.rotate) page = Page() page.title = "场景、视口和摄像机" page.render(Column(24, 16, TextBlock("同一个 3D 场景,两个摄像机", 2), Row(0, 16, perspective, orthographic), Row(0, 12, front, side, rotate), TextBlock("左键拖动旋转 · 中键或空格拖动平移 · 滚轮缩放\n摄像机和控制器都是可编程对象;2D 控件由 Endor 自己排版。")))