// Copyright 2025-2026 肖其顿 (XIAO QI DUN) // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package ofdgo import ( "fmt" "math" ) const ( U3DRenderSolid U3DRenderMode = iota // 实体 U3DRenderSolidWireframe // 实体线框 U3DRenderWireframe // 线框 U3DRenderShadedWireframe // 着色线框 U3DRenderHiddenWireframe // 隐藏线框 U3DRenderVertices // 顶点 U3DRenderShadedVertices // 着色顶点 U3DRenderBoundingBox // 包围盒线框 U3DRenderBoundingBoxFaces // 包围盒面 U3DRenderBoundingBoxOutline // 包围盒面及线框 U3DRenderIllustration // 插图 U3DRenderSolidOutline // 实体轮廓 U3DRenderShadedIllustration // 着色插图 ) // U3DRenderMode 定义静态几何的呈现方式,边线和顶点使用单像素覆盖 type U3DRenderMode uint8 // u3dPrimitiveKey 区分共享点线的颜色及混合状态,避免透明边点重复叠加 type u3dPrimitiveKey struct { a, b [7]float64 blend uint32 function uint32 reference float32 } // u3dTexturedPrimitiveKey 额外区分纹理坐标及纹理层,不扩大普通边点缓存 type u3dTexturedPrimitiveKey struct { base u3dPrimitiveKey coordinates [2][8][3]float64 layers [8]*U3DTextureLayer opacityOverride bool } // uniquePrimitive 在当前网格内去重着色边点,缓存按条目计入绘制预算 // 入参: a 起点或顶点, b 终点或相同顶点, shader 着色器 // 返回: bool 是否首次绘制, error 预算或取消错误 func (r *u3dRender) uniquePrimitive(a, b u3dVertex, shader U3DShader) (bool, error) { if r.primitives == nil { return true, nil } left := [7]float64{a.point[0], a.point[1], a.point[2], a.color[0], a.color[1], a.color[2], a.color[3]} right := [7]float64{b.point[0], b.point[1], b.point[2], b.color[0], b.color[1], b.color[2], b.color[3]} for i := range left { if left[i] != right[i] { if left[i] > right[i] { a, b = b, a left, right = right, left } break } } key := u3dPrimitiveKey{a: left, b: right, blend: shader.Blend} if shader.Attributes&2 != 0 { key.function, key.reference = shader.AlphaFunction, shader.AlphaReference } if shader.Textures != ([8]*U3DTextureLayer{}) { textured := u3dTexturedPrimitiveKey{base: key, coordinates: [2][8][3]float64{a.texture, b.texture}, layers: shader.Textures, opacityOverride: a.opacityOverride} if _, ok := r.texturedPrimitives[textured]; ok { return false, nil } if err := r.reserve(1, 1024); err != nil { return false, err } if r.texturedPrimitives == nil { r.texturedPrimitives = make(map[u3dTexturedPrimitiveKey]struct{}) } r.texturedPrimitives[textured] = struct{}{} return true, nil } if _, ok := r.primitives[key]; ok { return false, nil } if err := r.reserve(1, 512); err != nil { return false, err } r.primitives[key] = struct{}{} return true, nil } // shaded 判断绘制模式是否使用材质、法线和灯光 // 返回: bool 是否着色 func (mode U3DRenderMode) shaded() bool { return mode == U3DRenderSolid || mode == U3DRenderSolidWireframe || mode == U3DRenderShadedWireframe || mode == U3DRenderShadedVertices || mode == U3DRenderSolidOutline || mode == U3DRenderShadedIllustration } // silhouette 判断绘制模式是否仅描绘轮廓边 // 返回: bool 是否使用轮廓边 func (mode U3DRenderMode) silhouette() bool { return mode >= U3DRenderIllustration && mode <= U3DRenderShadedIllustration } // modeTriangle 根据当前模式绘制原三角面、原始边或顶点,不为裁剪边界生成网格边 // 入参: triangle 相机空间顶点, shader 着色器, pass 绘制轮次 // 返回: error 数值、资源或取消错误 func (r *u3dRender) modeTriangle(triangle [3]u3dVertex, shader U3DShader, pass U3DViewPass) error { if r.depthOnly || r.mode == U3DRenderSolid || r.mode.silhouette() { return r.sectionTriangle(triangle, shader, pass) } if r.mode == U3DRenderSolidWireframe { if err := r.sectionTriangle(triangle, shader, pass); err != nil { return err } } if r.mode != U3DRenderShadedWireframe && r.mode != U3DRenderShadedVertices { for i := range triangle { triangle[i].color = [4]float64{r.auxiliaryColor[0], r.auxiliaryColor[1], r.auxiliaryColor[2], 1} } shader = U3DShader{Blend: 0x606} } normal := u3dTriangleNormal([3]u3dVector{triangle[0].point, triangle[1].point, triangle[2].point}) for i, vertex := range triangle { var err error if r.mode == U3DRenderVertices || r.mode == U3DRenderShadedVertices { fresh, uniqueErr := r.uniquePrimitive(vertex, vertex, shader) if uniqueErr != nil { return uniqueErr } if !fresh { continue } err = r.modePoint(vertex, shader, pass) } else { fresh, uniqueErr := r.uniquePrimitive(vertex, triangle[(i+1)%3], shader) if uniqueErr != nil { return uniqueErr } if !fresh { continue } err = r.modeLine(vertex, triangle[(i+1)%3], normal, triangle[0].point, shader, pass) } if err != nil { return err } } if r.mode != U3DRenderSolidWireframe { for i, section := range r.sections { if section.intersection != nil { if err := r.sectionIntersection(triangle, i); err != nil { return err } } } } return nil } // modeLine 对原始网格边应用剖切与视锥裁剪,再按像素深度绘制 // 入参: a 起点, b 终点, normal 面法线, origin 面上点, shader 着色器, pass 绘制轮次 // 返回: error 数值、资源或取消错误 func (r *u3dRender) modeLine(a, b u3dVertex, normal, origin u3dVector, shader U3DShader, pass U3DViewPass) error { for i, section := range r.sections { if i%256 == 0 { if err := r.ctx.Err(); err != nil { return err } } keep, err := u3dClipLine(&a, &b, section.plane) if err != nil || !keep { return err } } for i, plane := range r.frustumPlanes() { if i == 1 && r.camera.Far == 0 && !r.camera.ClipFar { continue } keep, err := u3dClipLine(&a, &b, plane) if err != nil || !keep { return err } } return r.rasterLine(a, b, normal, origin, shader, pass) } // modePoint 绘制原始顶点,不在裁剪边界补充顶点 // 入参: vertex 顶点, shader 着色器, pass 绘制轮次 // 返回: error 数值、资源或取消错误 func (r *u3dRender) modePoint(vertex u3dVertex, shader U3DShader, pass U3DViewPass) error { if err := r.ctx.Err(); err != nil { return err } for i, section := range r.sections { if i%256 == 0 { if err := r.ctx.Err(); err != nil { return err } } distance := u3dPlaneDistance(section.plane, vertex.point) if !finite(distance) { return fmt.Errorf("invalid U3D point clip distance") } if distance < 0 { return nil } } for i, plane := range r.frustumPlanes() { if i == 1 && r.camera.Far == 0 && !r.camera.ClipFar { continue } distance := u3dPlaneDistance(plane, vertex.point) if !finite(distance) { return fmt.Errorf("invalid U3D point clip distance") } if distance < 0 { return nil } } q := 1.0 if r.camera.Perspective { q = 1 / vertex.point[2] } x := math.Floor(r.viewport[0] + r.viewport[2]/2 + r.camera.ShiftX + vertex.point[0]*r.camera.ScaleX*q) y := math.Floor(r.viewport[1] + r.viewport[3]/2 + r.camera.ShiftY - vertex.point[1]*r.camera.ScaleY*q) if !finite(x) || !finite(y) { return fmt.Errorf("invalid U3D projected vertex") } if x < 0 || y < 0 || x >= float64(r.output.Rect.Dx()) || y >= float64(r.output.Rect.Dy()) { return nil } pixel := int(y)*r.output.Rect.Dx() + int(x) if vertex.point[2] > r.frame[pixel].depth { return nil } textures, err := r.shaderTextures(shader) if err != nil { return err } if err := r.textureColor(textures, &vertex.color, &vertex.texture, vertex.point, vertex.opacityOverride); err != nil { return err } visible, err := u3dFragmentColor(&vertex.color, vertex.point, &shader, &pass) if err != nil || !visible { return err } r.order++ return r.fragment(pixel, vertex.point[2], vertex.color, shader.Blend) } // drawBounds 在模型局部空间计算包围盒,再应用实例变换,不以世界坐标轴重建包围盒 // 入参: instance 场景实例, node 模型节点, mesh 网格, pass 绘制轮次, passIndex 轮次索引 // 返回: error 几何、数值、预算或取消错误 func (r *u3dRender) drawBounds(instance u3dInstance, node *U3DNode, mesh *U3DMesh, pass U3DViewPass, passIndex int) error { minimum := u3dVector{math.Inf(1), math.Inf(1), math.Inf(1)} maximum := u3dVector{math.Inf(-1), math.Inf(-1), math.Inf(-1)} visible := false for i := range len(mesh.Faces) + len(mesh.Lines) + len(mesh.Points) { shading, corners := mesh.primitive(i) shaders := node.Shaders if i >= len(mesh.Faces) { shaders = node.LineShaders } if i >= len(mesh.Faces)+len(mesh.Lines) { shaders = node.PointShaders } if i%256 == 0 { if err := r.ctx.Err(); err != nil { return err } } if uint64(shading) >= uint64(len(shaders)) { visible = visible || passIndex == 0 } else if !visible { for _, name := range shaders[shading] { if r.shaderPass(name)&(1<= uint64(len(mesh.Positions)) { return fmt.Errorf("invalid U3D bounds position index") } for j, value := range mesh.Positions[corner.Position] { v := float64(value) if !finite(v) { return fmt.Errorf("invalid U3D bounds position") } minimum[j], maximum[j] = min(minimum[j], v), max(maximum[j], v) } } } if !visible || math.IsInf(minimum[0], 1) { return nil } opacity := 1.0 if instance.opacity >= 0 { opacity = instance.opacity } if r.opacity >= 0 { opacity *= r.opacity } if r.mode == U3DRenderBoundingBox { opacity = 0 } var vertices [8]u3dVertex for i := range vertices { point := minimum for j := range 3 { if i&(1<