|
1 | 1 | /* |
2 | | - * Copyright (c) 2009-2012 jMonkeyEngine |
| 2 | + * Copyright (c) 2009-2025 jMonkeyEngine |
3 | 3 | * All rights reserved. |
4 | 4 | * |
5 | 5 | * Redistribution and use in source and binary forms, with or without |
|
32 | 32 | package com.jme3.scene.debug; |
33 | 33 |
|
34 | 34 | import com.jme3.math.Vector3f; |
| 35 | +import com.jme3.renderer.Camera; |
| 36 | +import com.jme3.scene.Geometry; |
35 | 37 | import com.jme3.scene.Mesh; |
36 | 38 | import com.jme3.scene.VertexBuffer; |
37 | 39 | import com.jme3.scene.VertexBuffer.Type; |
38 | 40 | import com.jme3.scene.VertexBuffer.Usage; |
| 41 | +import com.jme3.shadow.ShadowUtil; |
39 | 42 | import com.jme3.util.BufferUtils; |
40 | 43 | import java.nio.FloatBuffer; |
41 | 44 |
|
| 45 | +/** |
| 46 | + * A specialized Mesh that renders a camera frustum as a wireframe. |
| 47 | + * This class extends jME3's Mesh and is designed to visually represent |
| 48 | + * the viewing volume of a camera, which can be useful for debugging |
| 49 | + * or visualization purposes. |
| 50 | + * <p> |
| 51 | + * The frustum is defined by eight points: four for the near plane |
| 52 | + * and four for the far plane. These points are connected by lines |
| 53 | + * to form a wireframe cube-like structure. |
| 54 | + */ |
42 | 55 | public class WireFrustum extends Mesh { |
43 | 56 |
|
44 | 57 | /** |
45 | | - * This constructor is for serialization only. Do not use. |
| 58 | + * For Serialization only. Do not use. |
46 | 59 | */ |
47 | 60 | protected WireFrustum() { |
48 | 61 | } |
49 | 62 |
|
50 | | - public WireFrustum(Vector3f[] points){ |
51 | | - initGeom(this, points); |
52 | | - } |
53 | | - |
54 | | - public static Mesh makeFrustum(Vector3f[] points){ |
55 | | - Mesh m = new Mesh(); |
56 | | - initGeom(m, points); |
57 | | - return m; |
| 63 | + /** |
| 64 | + * Constructs a new `WireFrustum` mesh using the specified frustum corner points. |
| 65 | + * The points should represent the 8 corners of the frustum. |
| 66 | + * The expected order of points is typically: |
| 67 | + * 0-3: Near plane (e.g., bottom-left, bottom-right, top-right, top-left) |
| 68 | + * 4-7: Far plane (e.g., bottom-left, bottom-right, top-right, top-left) |
| 69 | + * |
| 70 | + * @param points An array of 8 `Vector3f` objects representing the frustum's corners. |
| 71 | + * If the array is null or does not contain 8 points, an |
| 72 | + * `IllegalArgumentException` will be thrown. |
| 73 | + */ |
| 74 | + public WireFrustum(Vector3f[] points) { |
| 75 | + if (points == null || points.length != 8) { |
| 76 | + throw new IllegalArgumentException("Frustum points array must not be null and must contain 8 points."); |
| 77 | + } |
| 78 | + setGeometryData(points); |
58 | 79 | } |
59 | 80 |
|
60 | | - private static void initGeom(Mesh m, Vector3f[] points) { |
61 | | - if (points != null) |
62 | | - m.setBuffer(Type.Position, 3, BufferUtils.createFloatBuffer(points)); |
| 81 | + /** |
| 82 | + * Initializes the mesh's geometric data, setting up the vertex positions and indices. |
| 83 | + * This method is called during the construction of the `WireFrustum`. |
| 84 | + * |
| 85 | + * @param points The 8 `Vector3f` points defining the frustum's corners. |
| 86 | + */ |
| 87 | + private void setGeometryData(Vector3f[] points) { |
| 88 | + // Set vertex positions |
| 89 | + setBuffer(Type.Position, 3, BufferUtils.createFloatBuffer(points)); |
63 | 90 |
|
64 | | - m.setBuffer(Type.Index, 2, |
| 91 | + // Set indices to draw lines connecting the frustum corners |
| 92 | + // The indices define 12 lines: 4 for near plane, 4 for far plane, and 4 connecting near to far. |
| 93 | + setBuffer(Type.Index, 2, |
65 | 94 | new short[]{ |
| 95 | + // Near plane |
66 | 96 | 0, 1, |
67 | 97 | 1, 2, |
68 | 98 | 2, 3, |
69 | 99 | 3, 0, |
70 | 100 |
|
| 101 | + // Far plane |
71 | 102 | 4, 5, |
72 | 103 | 5, 6, |
73 | 104 | 6, 7, |
74 | 105 | 7, 4, |
75 | 106 |
|
| 107 | + // Connecting lines (near to far) |
76 | 108 | 0, 4, |
77 | 109 | 1, 5, |
78 | 110 | 2, 6, |
79 | 111 | 3, 7, |
80 | 112 | } |
81 | 113 | ); |
82 | | - m.getBuffer(Type.Index).setUsage(Usage.Static); |
83 | | - m.setMode(Mode.Lines); |
| 114 | + getBuffer(Type.Index).setUsage(Usage.Static); |
| 115 | + setMode(Mode.Lines); |
| 116 | + updateBound(); |
84 | 117 | } |
85 | 118 |
|
86 | | - public void update(Vector3f[] points){ |
| 119 | + /** |
| 120 | + * Updates the vertex positions of the existing `WireFrustum` mesh. |
| 121 | + * This is more efficient than creating a new `WireFrustum` instance |
| 122 | + * if only the frustum's position or orientation changes. |
| 123 | + * |
| 124 | + * @param points An array of 8 `Vector3f` objects representing the new frustum's corners. |
| 125 | + * If the array is null or does not contain 8 points, an |
| 126 | + * `IllegalArgumentException` will be thrown. |
| 127 | + */ |
| 128 | + public void update(Vector3f[] points) { |
| 129 | + if (points == null || points.length != 8) { |
| 130 | + throw new IllegalArgumentException("Frustum points array must not be null and must contain 8 points."); |
| 131 | + } |
| 132 | + |
87 | 133 | VertexBuffer vb = getBuffer(Type.Position); |
88 | | - if (vb == null){ |
89 | | - setBuffer(Type.Position, 3, BufferUtils.createFloatBuffer(points)); |
| 134 | + if (vb == null) { |
| 135 | + // If for some reason the position buffer is missing, re-create it. |
| 136 | + // This case should ideally not happen if the object is constructed properly. |
| 137 | + setGeometryData(points); |
90 | 138 | return; |
91 | 139 | } |
92 | 140 |
|
93 | | - FloatBuffer b = BufferUtils.createFloatBuffer(points); |
94 | | - FloatBuffer a = (FloatBuffer) vb.getData(); |
95 | | - b.rewind(); |
96 | | - a.rewind(); |
97 | | - a.put(b); |
98 | | - a.rewind(); |
| 141 | + // Create a new FloatBuffer from the updated points |
| 142 | + FloatBuffer newBuff = BufferUtils.createFloatBuffer(points); |
| 143 | + // Get the existing FloatBuffer from the VertexBuffer |
| 144 | + FloatBuffer currBuff = (FloatBuffer) vb.getData(); |
99 | 145 |
|
100 | | - vb.updateData(a); |
101 | | - |
| 146 | + currBuff.clear(); // Clear |
| 147 | + currBuff.put(newBuff); // Copy |
| 148 | + currBuff.rewind(); // Rewind |
| 149 | + |
| 150 | + // Update the VertexBuffer with the modified FloatBuffer data |
| 151 | + vb.updateData(currBuff); |
| 152 | + |
| 153 | + // Update the mesh's bounding volume to reflect the new vertex positions |
102 | 154 | updateBound(); |
103 | 155 | } |
104 | 156 |
|
| 157 | + /** |
| 158 | + * A static factory method to create a new `WireFrustum` mesh. |
| 159 | + * This method provides a cleaner way to instantiate a `WireFrustum`. |
| 160 | + * |
| 161 | + * @param points An array of 8 `Vector3f` objects representing the frustum's corners. |
| 162 | + * @return A new `WireFrustum` instance. |
| 163 | + */ |
| 164 | + public static Mesh makeFrustum(Vector3f[] points) { |
| 165 | + return new WireFrustum(points); |
| 166 | + } |
| 167 | + |
| 168 | + /** |
| 169 | + * Creates a `Geometry` object representing the wireframe frustum of a given camera. |
| 170 | + * The frustum points are calculated based on the camera's current view settings. |
| 171 | + * The returned `Geometry` can be directly attached to a scene graph. |
| 172 | + * |
| 173 | + * @param camera The `Camera` whose frustum is to be visualized. |
| 174 | + * @return A `Geometry` object containing the `WireFrustum` mesh. |
| 175 | + */ |
| 176 | + public static Geometry makeGeometry(Camera camera) { |
| 177 | + Vector3f[] frustumCorners = new Vector3f[8]; |
| 178 | + for (int i = 0; i < 8; i++) { |
| 179 | + frustumCorners[i] = new Vector3f(); |
| 180 | + } |
| 181 | + |
| 182 | + Camera tempCam = camera.clone(); |
| 183 | + tempCam.setLocation(new Vector3f(0, 0, 0)); |
| 184 | + tempCam.lookAt(Vector3f.UNIT_Z, Vector3f.UNIT_Y); |
| 185 | + ShadowUtil.updateFrustumPoints2(tempCam, frustumCorners); |
| 186 | + |
| 187 | + WireFrustum mesh = new WireFrustum(frustumCorners); |
| 188 | + return new Geometry("Viewing Frustum", mesh); |
| 189 | + } |
| 190 | + |
105 | 191 | } |
0 commit comments