11/*
2- * Copyright (c) 2009-2021 jMonkeyEngine
2+ * Copyright (c) 2009-2025 jMonkeyEngine
33 * All rights reserved.
44 *
55 * Redistribution and use in source and binary forms, with or without
4747/**
4848 * An ordinary (single holed) torus.
4949 * <p>
50- * The center is by default the origin.
50+ * The torus is centered at the origin by default, but its position and
51+ * orientation can be transformed.
5152 *
5253 * @author Mark Powell
5354 * @version $Revision: 4131 $, $Date: 2009-03-19 16:15:28 -0400 (Thu, 19 Mar 2009) $
5455 */
5556public class Torus extends Mesh {
5657
58+ /**
59+ * The number of samples around the circular cross-section of the torus.
60+ */
5761 private int circleSamples ;
58-
62+ /**
63+ * The number of samples along the major radius of the torus.
64+ */
5965 private int radialSamples ;
6066 /**
61- * minor radius of the torus
67+ * The minor radius of the torus.
6268 */
6369 private float innerRadius ;
6470 /**
65- * major radius of the torus
71+ * The major radius of the torus.
6672 */
6773 private float outerRadius ;
6874
75+ /**
76+ * Serialization-only constructor. Do not use.
77+ */
6978 public Torus () {
7079 }
7180
7281 /**
73- * Constructs a new Torus. Center is the origin, but the Torus may be
74- * transformed.
75- *
76- * @param circleSamples
77- * The number of samples along the circles.
78- * @param radialSamples
79- * The number of samples along the radial.
80- * @param innerRadius minor radius of the torus
81- * @param outerRadius major radius of the torus
82+ * Constructs a new `Torus` mesh centered at the origin.
83+ *
84+ * @param circleSamples The number of samples around the circular cross-section.
85+ * Higher values result in a smoother tube.
86+ * @param radialSamples The number of samples along the major radius (around the hole).
87+ * Higher values result in a smoother overall torus shape.
88+ * @param innerRadius The minor radius of the torus (radius of the circular cross-section).
89+ * @param outerRadius The major radius of the torus (distance from the center of the hole to the center of the tube).
90+ * @throws IllegalArgumentException if `circleSamples` or `radialSamples` are less than 3,
91+ * or if `innerRadius` or `outerRadius` are negative.
8292 */
83- public Torus (int circleSamples , int radialSamples ,
84- float innerRadius , float outerRadius ) {
93+ public Torus (int circleSamples , int radialSamples , float innerRadius , float outerRadius ) {
8594 super ();
8695 updateGeometry (circleSamples , radialSamples , innerRadius , outerRadius );
8796 }
8897
89- public int getCircleSamples () {
90- return circleSamples ;
91- }
92-
93- public float getInnerRadius () {
94- return innerRadius ;
95- }
96-
97- public float getOuterRadius () {
98- return outerRadius ;
99- }
100-
101- public int getRadialSamples () {
102- return radialSamples ;
103- }
104-
105- @ Override
106- public void read (JmeImporter importer ) throws IOException {
107- super .read (importer );
108- InputCapsule capsule = importer .getCapsule (this );
109- circleSamples = capsule .readInt ("circleSamples" , 0 );
110- radialSamples = capsule .readInt ("radialSamples" , 0 );
111- innerRadius = capsule .readFloat ("innerRadius" , 0 );
112- outerRadius = capsule .readFloat ("outerRadius" , 0 );
113- }
114-
11598 private void setGeometryData () {
11699 // allocate vertices
117100 int vertCount = (circleSamples + 1 ) * (radialSamples + 1 );
118101 FloatBuffer fpb = BufferUtils .createVector3Buffer (vertCount );
119102 setBuffer (Type .Position , 3 , fpb );
120103
121- // allocate normals if requested
104+ // allocate normals
122105 FloatBuffer fnb = BufferUtils .createVector3Buffer (vertCount );
123106 setBuffer (Type .Normal , 3 , fnb );
124107
@@ -129,109 +112,99 @@ private void setGeometryData() {
129112 // generate geometry
130113 float inverseCircleSamples = 1.0f / circleSamples ;
131114 float inverseRadialSamples = 1.0f / radialSamples ;
132- int i = 0 ;
133115 // generate the cylinder itself
134- Vector3f radialAxis = new Vector3f (), torusMiddle = new Vector3f (), tempNormal = new Vector3f ();
135- for (int circleCount = 0 ; circleCount < circleSamples ; circleCount ++) {
116+ Vector3f radialAxis = new Vector3f ();
117+ Vector3f torusMiddle = new Vector3f ();
118+ Vector3f tempNormal = new Vector3f ();
119+
120+ for (int circleCount = 0 ; circleCount <= circleSamples ; circleCount ++) {
136121 // compute center point on torus circle at specified angle
137122 float circleFraction = circleCount * inverseCircleSamples ;
138123 float theta = FastMath .TWO_PI * circleFraction ;
139124 float cosTheta = FastMath .cos (theta );
140125 float sinTheta = FastMath .sin (theta );
126+
127+ // Calculate the center point of the current circular cross-section on the major radius
141128 radialAxis .set (cosTheta , sinTheta , 0 );
142129 radialAxis .mult (outerRadius , torusMiddle );
143130
144131 // compute slice vertices with duplication at end point
145- int iSave = i ;
146- for (int radialCount = 0 ; radialCount < radialSamples ; radialCount ++) {
132+ for (int radialCount = 0 ; radialCount <= radialSamples ; radialCount ++) {
147133 float radialFraction = radialCount * inverseRadialSamples ;
148134 // in [0,1)
149135 float phi = FastMath .TWO_PI * radialFraction ;
150136 float cosPhi = FastMath .cos (phi );
151137 float sinPhi = FastMath .sin (phi );
138+
139+ // Calculate normal vector
152140 tempNormal .set (radialAxis ).multLocal (cosPhi );
153141 tempNormal .z += sinPhi ;
154- fnb .put (tempNormal .x ).put (tempNormal .y ).put (
155- tempNormal . z );
156-
142+ fnb .put (tempNormal .x ).put (tempNormal .y ).put (tempNormal . z );
143+
144+ // Calculate vertex position
157145 tempNormal .multLocal (innerRadius ).addLocal (torusMiddle );
158- fpb .put (tempNormal .x ).put (tempNormal .y ).put (
159- tempNormal .z );
146+ fpb .put (tempNormal .x ).put (tempNormal .y ).put (tempNormal .z );
160147
148+ // Calculate texture coordinates
161149 ftb .put (radialFraction ).put (circleFraction );
162- i ++;
163150 }
164-
165- BufferUtils .copyInternalVector3 (fpb , iSave , i );
166- BufferUtils .copyInternalVector3 (fnb , iSave , i );
167-
168- ftb .put (1.0f ).put (circleFraction );
169-
170- i ++;
171- }
172-
173- // duplicate the cylinder ends to form a torus
174- for (int iR = 0 ; iR <= radialSamples ; iR ++, i ++) {
175- BufferUtils .copyInternalVector3 (fpb , iR , i );
176- BufferUtils .copyInternalVector3 (fnb , iR , i );
177- BufferUtils .copyInternalVector2 (ftb , iR , i );
178- ftb .put (i * 2 + 1 , 1.0f );
179151 }
180152 }
181153
182154 private void setIndexData () {
183- // allocate connectivity
184- int triCount = 2 * circleSamples * radialSamples ;
185-
186- ShortBuffer sib = BufferUtils .createShortBuffer (3 * triCount );
187- setBuffer (Type .Index , 3 , sib );
188-
189- int i ;
190- // generate connectivity
191- int connectionStart = 0 ;
192- int index = 0 ;
193- for (int circleCount = 0 ; circleCount < circleSamples ; circleCount ++) {
194- int i0 = connectionStart ;
195- int i1 = i0 + 1 ;
196- connectionStart += radialSamples + 1 ;
197- int i2 = connectionStart ;
198- int i3 = i2 + 1 ;
199- for (i = 0 ; i < radialSamples ; i ++, index += 6 ) {
200- // if (true) {
201- sib .put ((short )i0 ++);
202- sib .put ((short )i2 );
203- sib .put ((short )i1 );
204- sib .put ((short )i1 ++);
205- sib .put ((short )i2 ++);
206- sib .put ((short )i3 ++);
207-
208- // getIndexBuffer().put(i0++);
209- // getIndexBuffer().put(i2);
210- // getIndexBuffer().put(i1);
211- // getIndexBuffer().put(i1++);
212- // getIndexBuffer().put(i2++);
213- // getIndexBuffer().put(i3++);
214- // } else {
215- // getIndexBuffer().put(i0++);
216- // getIndexBuffer().put(i1);
217- // getIndexBuffer().put(i2);
218- // getIndexBuffer().put(i1++);
219- // getIndexBuffer().put(i3++);
220- // getIndexBuffer().put(i2++);
221- // }
155+ // Each quad forms two triangles, and there are radialSamples * circleSamples quads.
156+ int totalTriangles = 2 * circleSamples * radialSamples ;
157+ ShortBuffer indexBuffer = BufferUtils .createShortBuffer (3 * totalTriangles );
158+ setBuffer (Type .Index , 3 , indexBuffer );
159+
160+ int currentQuadStartIndex = 0 ;
161+ for (int circleIter = 0 ; circleIter < circleSamples ; circleIter ++) {
162+ for (int radialIter = 0 ; radialIter < radialSamples ; radialIter ++) {
163+ int i0 = currentQuadStartIndex + radialIter ;
164+ int i1 = i0 + 1 ;
165+ int i2 = i0 + (radialSamples + 1 );
166+ int i3 = i2 + 1 ;
167+
168+ // First triangle of the quad
169+ indexBuffer .put ((short ) i0 );
170+ indexBuffer .put ((short ) i2 );
171+ indexBuffer .put ((short ) i1 );
172+
173+ // Second triangle of the quad
174+ indexBuffer .put ((short ) i1 );
175+ indexBuffer .put ((short ) i2 );
176+ indexBuffer .put ((short ) i3 );
222177 }
178+ currentQuadStartIndex += (radialSamples + 1 );
223179 }
224180 }
225181
226182 /**
227- * Rebuilds this torus based on a new set of parameters.
228- *
229- * @param circleSamples the number of samples along the circles.
230- * @param radialSamples the number of samples along the radial.
231- * @param innerRadius minor radius of the torus
232- * @param outerRadius major radius of the torus
183+ * Rebuilds the torus mesh based on a new set of parameters.
184+ * This method updates the internal parameters and then regenerates
185+ * the vertex data, index data, updates the bounding volume, and counts.
186+ *
187+ * @param circleSamples The number of samples around the circular cross-section.
188+ * @param radialSamples The number of samples along the major radius.
189+ * @param innerRadius The minor radius of the torus.
190+ * @param outerRadius The major radius of the torus.
191+ * @throws IllegalArgumentException if `circleSamples` or `radialSamples` are less than 3,
192+ * or if `innerRadius` or `outerRadius` are negative.
233193 */
234194 public void updateGeometry (int circleSamples , int radialSamples , float innerRadius , float outerRadius ) {
195+ if (circleSamples < 3 ) {
196+ throw new IllegalArgumentException ("circleSamples must be at least 3." );
197+ }
198+ if (radialSamples < 3 ) {
199+ throw new IllegalArgumentException ("radialSamples must be at least 3." );
200+ }
201+ if (innerRadius < 0 ) {
202+ throw new IllegalArgumentException ("innerRadius cannot be negative." );
203+ }
204+ if (outerRadius < 0 ) {
205+ throw new IllegalArgumentException ("outerRadius cannot be negative." );
206+ }
207+
235208 this .circleSamples = circleSamples ;
236209 this .radialSamples = radialSamples ;
237210 this .innerRadius = innerRadius ;
@@ -242,14 +215,60 @@ public void updateGeometry(int circleSamples, int radialSamples, float innerRadi
242215 updateCounts ();
243216 }
244217
218+ /**
219+ * Returns the number of samples around the circular cross-section of the torus.
220+ *
221+ * @return The number of circle samples.
222+ */
223+ public int getCircleSamples () {
224+ return circleSamples ;
225+ }
226+
227+ /**
228+ * Returns the minor radius of the torus.
229+ *
230+ * @return The inner radius.
231+ */
232+ public float getInnerRadius () {
233+ return innerRadius ;
234+ }
235+
236+ /**
237+ * Returns the major radius of the torus.
238+ *
239+ * @return The outer radius.
240+ */
241+ public float getOuterRadius () {
242+ return outerRadius ;
243+ }
244+
245+ /**
246+ * Returns the number of samples along the major radius of the torus.
247+ *
248+ * @return The number of radial samples.
249+ */
250+ public int getRadialSamples () {
251+ return radialSamples ;
252+ }
253+
254+ @ Override
255+ public void write (JmeExporter ex ) throws IOException {
256+ super .write (ex );
257+ OutputCapsule oc = ex .getCapsule (this );
258+ oc .write (circleSamples , "circleSamples" , 0 );
259+ oc .write (radialSamples , "radialSamples" , 0 );
260+ oc .write (innerRadius , "innerRadius" , 0 );
261+ oc .write (outerRadius , "outerRadius" , 0 );
262+ }
263+
245264 @ Override
246- public void write ( JmeExporter e ) throws IOException {
247- super .write ( e );
248- OutputCapsule capsule = e .getCapsule (this );
249- capsule . write ( circleSamples , "circleSamples" , 0 );
250- capsule . write ( radialSamples , "radialSamples" , 0 );
251- capsule . write ( innerRadius , "innerRadius" , 0 );
252- capsule . write ( outerRadius , "outerRadius" , 0 );
265+ public void read ( JmeImporter im ) throws IOException {
266+ super .read ( im );
267+ InputCapsule ic = im .getCapsule (this );
268+ circleSamples = ic . readInt ( "circleSamples" , 0 );
269+ radialSamples = ic . readInt ( "radialSamples" , 0 );
270+ innerRadius = ic . readFloat ( "innerRadius" , 0 );
271+ outerRadius = ic . readFloat ( "outerRadius" , 0 );
253272 }
254273
255- }
274+ }
0 commit comments