/ContentSystem/Rendering/GeometryData.cs
C# | 1805 lines | 1008 code | 155 blank | 642 comment | 63 complexity | 69a8189ae9f3a714d289af3d194777ae MD5 | raw file
Large files files are truncated, but you can click here to view the full file
1using System; 2using System.Collections.Generic; 3using System.IO; 4using Delta.ContentSystem.Rendering.Helpers; 5using Delta.Engine.Dynamic; 6using Delta.Utilities; 7using Delta.Utilities.Datatypes; 8using Delta.Utilities.Graphics; 9using Delta.Utilities.Helpers; 10using NUnit.Framework; 11 12namespace Delta.ContentSystem.Rendering 13{ 14 /// <summary> 15 /// Geometry data class, just contains vertices and indices for rendering. 16 /// Geometry meshes are highly dependant on the underlying type for the 17 /// vertices. Some helper methods can be used to generate simple meshes 18 /// (boxes, spheres, etc.). Data is stored in binary format for quick access 19 /// and the used material data is also stored internally in here. Please note 20 /// that rendering a level or a complex model usually consists of multiple 21 /// meshes, a geometry mesh is just the lowest underlying 3D type in the 22 /// engine! Meshes are even used for dynamic vertex buffers, lines and all 2D 23 /// UI drawing, but those meshes are usually not saved out to content files. 24 /// <para /> 25 /// While this is not directly a content type, it is pretty much what defines 26 /// a mesh, which is used in many other content types as well (Models, Levels 27 /// and also important for MeshAnimations, which are linked up in Models 28 /// too). You can of course also use this class for your own dynamically 29 /// created data (e.g. landscape rendering, BSP trees, or any other type of 30 /// rendering) 31 /// <para /> 32 /// Note: Geometry data can get quite big and while we need this class and 33 /// all the internal data for dynamic geometry data that needs to be updated 34 /// from time to time, it is not needed for static geometry data after the 35 /// internal data structure has been created on the graphic card (vertex 36 /// and index buffer objects). To save memory we can dispose this object 37 /// and just in case it is needed again, we need to notify the owner to 38 /// make sure that the data is reloaded again. 39 /// </summary> 40 /// <remarks> 41 /// Vertex data. Note: Filling data into this is up to 5 times slower than 42 /// directly filling a struct array. For setting this once or even loading 43 /// from a file, this class is perfectly fine (loading from a file is even 44 /// about 2 times faster than using struct arrays). However, for dynamic 45 /// vertex buffers it is a good optimization to have a native struct 46 /// representing the data and setting that instead with SetData (e.g. see 47 /// the DrawManager implementation for line rendering using ColoredVertex). 48 /// Please note that even with the slower speed of GeometryData it is still 49 /// a very good idea to use for our engine because we want to use vertex 50 /// and index buffers, which is only possible with a generic byte data 51 /// stream and thus much faster as converting data types or not using 52 /// vertex buffers. So rendering is as quick as it gets, especially for 53 /// static geometry data (which is what we will use 99% of the time) and 54 /// dynamic geometry is also rendered very quickly (in fact as fast as 55 /// possible on each platform), just setting the data is a little slow on 56 /// the CPU because of all the reader/writer stuff involved here. 57 /// </remarks> 58 public class GeometryData : ISaveLoadBinary, IDisposable 59 { 60 #region Constants 61 /// <summary> 62 /// Version number for this MeshData. If this goes above 1, we need 63 /// to support loading older versions as well. Saving is always going 64 /// to be the latest version (this one). 65 /// </summary> 66 private const int VersionNumber = 2; 67 #endregion 68 69 #region CreateCube (Static) 70 /// <summary> 71 /// Generate a cube mesh with the same size in all dimensions. Box meshes 72 /// have their pivot point at Vector.Zero (to make them easier to place). 73 /// Note: The GeometryHelper in Delta.Rendering.Models has many more 74 /// Create methods to create spheres, cones, cylinders, etc. 75 /// </summary> 76 /// <param name="vertexFormat">The vertex format for this geometry</param> 77 /// <param name="size">Size of the box in all dimensions</param> 78 /// <param name="setColor">Vertex color during creation.</param> 79 /// <returns> 80 /// Geometry Data containing all vertices and indices for this box 81 /// </returns> 82 public static GeometryData CreateCube(VertexFormat vertexFormat, 83 float size, Color setColor) 84 { 85 return CreateBox(vertexFormat, size, size, size, false, setColor); 86 } 87 88 /// <summary> 89 /// Generate a cube mesh with the same size in all dimensions. Box meshes 90 /// have their pivot point at Vector.Zero (to make them easier to place). 91 /// Note: The GeometryHelper in Delta.Rendering.Models has many more 92 /// Create methods to create spheres, cones, cylinders, etc. 93 /// </summary> 94 /// <param name="vertexFormat">The vertex format for this geometry</param> 95 /// <param name="size">Size of the box in all dimensions</param> 96 /// <returns> 97 /// Geometry Data containing all vertices and indices for this box 98 /// </returns> 99 public static GeometryData CreateCube(VertexFormat vertexFormat, float size) 100 { 101 return CreateBox(vertexFormat, size, size, size, false, Color.White); 102 } 103 #endregion 104 105 #region CreateBox (Static) 106 /// <summary> 107 /// Generates a box mesh with the given sizes and a material. Box meshes 108 /// have their pivot point at Vector.Zero (to make them easier to place). 109 /// </summary> 110 /// <param name="vertexFormat">The vertex format.</param> 111 /// <param name="width">The width in the X dimension</param> 112 /// <param name="depth">The depth in the Y dimension</param> 113 /// <param name="height">The height in the Z dimension</param> 114 /// <param name="invertWinding">Whether to invert indices order</param> 115 /// <param name="setColor">Vertex color during creation.</param> 116 /// <returns> 117 /// Geometry Data containing all vertices and indices for this box 118 /// </returns> 119 public static GeometryData CreateBox(VertexFormat vertexFormat, 120 float width, float depth, float height, bool invertWinding, Color setColor) 121 { 122 return CreateBox(vertexFormat, width, depth, height, invertWinding, 123 "<GeneratedBox(" + width + ", " + depth + ", " + height + ")>", 124 setColor); 125 } 126 127 /// <summary> 128 /// Generates a box mesh with a given pivot point height (usually used 129 /// to generate the box at Vector.Zero, see other overloads). 130 /// </summary> 131 /// <param name="vertexFormat">The vertex format.</param> 132 /// <param name="width">The width in the X dimension</param> 133 /// <param name="depth">The depth in the Y dimension</param> 134 /// <param name="height">The height in the Z dimension</param> 135 /// <param name="invertWinding">Whether to invert indices order</param> 136 /// <param name="meshName">Name of the mesh.</param> 137 /// <param name="setColor">Vertex color during creation.</param> 138 /// <returns> 139 /// Geometry Data containing all vertices and indices for this box 140 /// </returns> 141 public static GeometryData CreateBox(VertexFormat vertexFormat, 142 float width, float depth, float height, bool invertWinding, 143 string meshName, Color setColor) 144 { 145 // The box has 8 positions, but we need 24 vertices (4 for each of the 146 // 6 sides, each side has its own texture coordinates, see ordering) 147 // Texture coordinates begin at the left upper corner (0, 0) 148 GeometryData geometry = new GeometryData( 149 meshName, 24, vertexFormat, 36, true, false); 150 151 Vector cubeHalfSize = new Vector(width, height, depth) / 2; 152 // Create each face in turn. 153 int vertexIndex = 0; 154 155 // front side 156 geometry.SetVertexData(vertexIndex++, 157 new Vector(-cubeHalfSize.X, -cubeHalfSize.Y, cubeHalfSize.Z), 158 Point.UnitY, setColor, Vector.UnitZ, Vector.UnitZ); 159 160 geometry.SetVertexData(vertexIndex++, 161 new Vector(cubeHalfSize.X, -cubeHalfSize.Y, cubeHalfSize.Z), 162 Point.One, setColor, Vector.UnitZ, Vector.UnitZ); 163 164 geometry.SetVertexData(vertexIndex++, 165 new Vector(cubeHalfSize.X, cubeHalfSize.Y, cubeHalfSize.Z), 166 Point.UnitX, setColor, Vector.UnitZ, Vector.UnitZ); 167 168 geometry.SetVertexData(vertexIndex++, 169 new Vector(-cubeHalfSize.X, cubeHalfSize.Y, cubeHalfSize.Z), 170 Point.Zero, setColor, Vector.UnitZ, Vector.UnitZ); 171 172 // back side 173 geometry.SetVertexData(vertexIndex++, 174 new Vector(cubeHalfSize.X, -cubeHalfSize.Y, -cubeHalfSize.Z), 175 Point.UnitY, setColor, -Vector.UnitZ, -Vector.UnitZ); 176 177 geometry.SetVertexData(vertexIndex++, 178 new Vector(-cubeHalfSize.X, -cubeHalfSize.Y, -cubeHalfSize.Z), 179 Point.One, setColor, -Vector.UnitZ, -Vector.UnitZ); 180 181 geometry.SetVertexData(vertexIndex++, 182 new Vector(-cubeHalfSize.X, cubeHalfSize.Y, -cubeHalfSize.Z), 183 Point.UnitX, setColor, -Vector.UnitZ, -Vector.UnitZ); 184 185 geometry.SetVertexData(vertexIndex++, 186 new Vector(cubeHalfSize.X, cubeHalfSize.Y, -cubeHalfSize.Z), 187 Point.Zero, setColor, -Vector.UnitZ, -Vector.UnitZ); 188 189 // left side 190 geometry.SetVertexData(vertexIndex++, 191 new Vector(-cubeHalfSize.X, -cubeHalfSize.Y, -cubeHalfSize.Z), 192 Point.UnitY, setColor, -Vector.UnitX, -Vector.UnitX); 193 194 geometry.SetVertexData(vertexIndex++, 195 new Vector(-cubeHalfSize.X, -cubeHalfSize.Y, cubeHalfSize.Z), 196 Point.One, setColor, -Vector.UnitX, -Vector.UnitX); 197 198 geometry.SetVertexData(vertexIndex++, 199 new Vector(-cubeHalfSize.X, cubeHalfSize.Y, cubeHalfSize.Z), 200 Point.UnitX, setColor, -Vector.UnitX, -Vector.UnitX); 201 202 geometry.SetVertexData(vertexIndex++, 203 new Vector(-cubeHalfSize.X, cubeHalfSize.Y, -cubeHalfSize.Z), 204 Point.Zero, setColor, -Vector.UnitX, -Vector.UnitX); 205 206 // right side 207 geometry.SetVertexData(vertexIndex++, 208 new Vector(cubeHalfSize.X, -cubeHalfSize.Y, cubeHalfSize.Z), 209 Point.UnitY, setColor, Vector.UnitX, Vector.UnitX); 210 211 geometry.SetVertexData(vertexIndex++, 212 new Vector(cubeHalfSize.X, -cubeHalfSize.Y, -cubeHalfSize.Z), 213 Point.One, setColor, Vector.UnitX, Vector.UnitX); 214 215 geometry.SetVertexData(vertexIndex++, 216 new Vector(cubeHalfSize.X, cubeHalfSize.Y, -cubeHalfSize.Z), 217 Point.UnitX, setColor, Vector.UnitX, Vector.UnitX); 218 219 geometry.SetVertexData(vertexIndex++, 220 new Vector(cubeHalfSize.X, cubeHalfSize.Y, cubeHalfSize.Z), 221 Point.Zero, setColor, Vector.UnitX, Vector.UnitX); 222 223 // up side 224 geometry.SetVertexData(vertexIndex++, 225 new Vector(-cubeHalfSize.X, cubeHalfSize.Y, cubeHalfSize.Z), 226 Point.UnitY, setColor, Vector.UnitY, Vector.UnitY); 227 228 geometry.SetVertexData(vertexIndex++, 229 new Vector(cubeHalfSize.X, cubeHalfSize.Y, cubeHalfSize.Z), 230 Point.One, setColor, Vector.UnitY, Vector.UnitY); 231 232 geometry.SetVertexData(vertexIndex++, 233 new Vector(cubeHalfSize.X, cubeHalfSize.Y, -cubeHalfSize.Z), 234 Point.UnitX, setColor, Vector.UnitY, Vector.UnitY); 235 236 geometry.SetVertexData(vertexIndex++, 237 new Vector(-cubeHalfSize.X, cubeHalfSize.Y, -cubeHalfSize.Z), 238 Point.Zero, setColor, Vector.UnitY, Vector.UnitY); 239 240 // down side 241 geometry.SetVertexData(vertexIndex++, 242 new Vector(-cubeHalfSize.X, -cubeHalfSize.Y, -cubeHalfSize.Z), 243 Point.UnitY, setColor, -Vector.UnitY, -Vector.UnitY); 244 245 geometry.SetVertexData(vertexIndex++, 246 new Vector(cubeHalfSize.X, -cubeHalfSize.Y, -cubeHalfSize.Z), 247 Point.One, setColor, -Vector.UnitY, -Vector.UnitY); 248 249 geometry.SetVertexData(vertexIndex++, 250 new Vector(cubeHalfSize.X, -cubeHalfSize.Y, cubeHalfSize.Z), 251 Point.UnitX, setColor, -Vector.UnitY, -Vector.UnitY); 252 253 geometry.SetVertexData(vertexIndex++, 254 new Vector(-cubeHalfSize.X, -cubeHalfSize.Y, cubeHalfSize.Z), 255 Point.Zero, setColor, -Vector.UnitY, -Vector.UnitY); 256 257 if (invertWinding) 258 { 259 geometry.Indices = new ushort[] 260 { 261 // front 262 2, 1, 0, 263 3, 2, 0, 264 // back 265 6, 5, 4, 266 7, 6, 4, 267 // left 268 10, 9, 8, 269 11, 10, 8, 270 // right 271 14, 13, 12, 272 15, 14, 12, 273 // up 274 18, 17, 16, 275 19, 18, 16, 276 // down 277 22, 21, 20, 278 23, 22, 20 279 }; 280 } 281 else 282 { 283 geometry.Indices = new ushort[] 284 { 285 // front 286 0, 1, 2, 287 0, 2, 3, 288 // back 289 4, 5, 6, 290 4, 6, 7, 291 // left 292 8, 9, 10, 293 8, 10, 11, 294 // right 295 12, 13, 14, 296 12, 14, 15, 297 // up 298 16, 17, 18, 299 16, 18, 19, 300 // down 301 20, 21, 22, 302 20, 22, 23 303 }; 304 } 305 306 return geometry; 307 } 308 #endregion 309 310 #region CreateSphere (Static) 311 /// <summary> 312 /// Creates a sphere as an GeometryData object with the given parameters. 313 /// </summary> 314 /// <param name="vertexFormat">The vertex format to use.</param> 315 /// <param name="radius">The radius for the sphere from the center</param> 316 /// <param name="setColor"> 317 /// Color for the new sphere in case the vertex format supports colored 318 /// vertices (otherwise ignored). 319 /// </param> 320 /// <param name="invertWinding"> 321 /// Whether to invert winding order (usually false, except you want to be 322 /// inside the sphere). 323 /// </param> 324 /// <returns>The geometry data of the specified sphere.</returns> 325 public static GeometryData CreateSphere(VertexFormat vertexFormat, 326 float radius, Color setColor, bool invertWinding = false) 327 { 328 // Set up the subdivision in which we build the geometry of the sphere. 329 // The tessellation indicates in how many segments the sphere is 330 // interpolated, smaller spheres have less tessellation, bigger spheres 331 // use more tessellation, but we keep it between 4 and 32. 332 int tessellation = (int)(MathHelper.Sqrt(radius) * 6.0f); 333 tessellation = MathHelper.Clamp(tessellation, 6, 32); 334 // Make multiple of 3 for better fitting texturing 335 tessellation = (tessellation / 3) * 3; 336 int verticalSegments = tessellation; 337 // Add one horizontal segment to fit around a circle, good for UVs 338 int horizontalSegments = tessellation * 2 + 1; 339 340 // Create rings of vertices at progressively higher latitudes. 341 GeometryData geometry = new GeometryData( 342 "<GeneratedSphere(" + radius + ")>", 343 verticalSegments * horizontalSegments, 344 vertexFormat, 6 * (verticalSegments - 1) * horizontalSegments, 345 true, false); 346 for (int index = 0; index < verticalSegments; index++) 347 { 348 // Lets begin with the latitude and divide each segment. As we are 349 // using circular surface, we will split each position along the 350 // vertical axis with the cosine. That is every position in the 351 // vertical segment creates a ring with a maximum width stated by the 352 // cosine (at the top width=0, in the medium reaches the maximum 353 // width = 1, and at the bottom width=0). 354 float latitude = index * 180.0f / (verticalSegments - 1) - 90.0f; 355 float dy = MathHelper.Sin(latitude); 356 float dxz = MathHelper.Cos(latitude); 357 358 // Create a single ring of vertices at this latitude. 359 for (int j = 0; j < horizontalSegments; j++) 360 { 361 // Next step is tessellation along horizontal axis in which we just 362 // simple indicates the position of each vertex in the ring with the 363 // previously established width along the surface of the sphere 364 float longitude = j * 360.0f / (horizontalSegments - 1); 365 366 float dx = MathHelper.Cos(longitude) * dxz; 367 float dz = MathHelper.Sin(longitude) * dxz; 368 369 // finally we got the correct position 370 Vector normal = new Vector(dx, dy, dz); 371 372 // and we assign the corresponding U,V coordinate of the texture 373 // in a way that for each circle of the sphere it contains a line 374 // of the texture image 375 geometry.SetVertexData( 376 index * horizontalSegments + j, 377 normal * radius, 378 new Point(j / (float)(horizontalSegments - 1), 379 index / (float)(verticalSegments - 1)), setColor, normal, 380 Vector.Cross(normal, Vector.UnitZ)); 381 } 382 } 383 384 // Create a fan connecting the bottom vertex to the bottom latitude ring 385 // and finally set up the indices connecting each vertex. 386 // Fill the sphere body with triangles joining each pair of rings. 387 int num = 0; 388 for (int index = 0; index < verticalSegments - 1; index++) 389 { 390 for (int j = 0; j < horizontalSegments; j++) 391 { 392 int nextI = (index + 1); 393 int nextJ = (j + 1) % horizontalSegments; 394 395 if (invertWinding) 396 { 397 geometry.Indices[num++] = 398 (ushort)(index * horizontalSegments + nextJ); 399 geometry.Indices[num++] = 400 (ushort)(nextI * horizontalSegments + j); 401 geometry.Indices[num++] = 402 (ushort)(index * horizontalSegments + j); 403 404 geometry.Indices[num++] = 405 (ushort)(nextI * horizontalSegments + nextJ); 406 geometry.Indices[num++] = 407 (ushort)(nextI * horizontalSegments + j); 408 geometry.Indices[num++] = 409 (ushort)(index * horizontalSegments + nextJ); 410 } 411 else 412 { 413 geometry.Indices[num++] = 414 (ushort)(index * horizontalSegments + j); 415 geometry.Indices[num++] = 416 (ushort)(nextI * horizontalSegments + j); 417 geometry.Indices[num++] = 418 (ushort)(index * horizontalSegments + nextJ); 419 420 geometry.Indices[num++] = 421 (ushort)(index * horizontalSegments + nextJ); 422 geometry.Indices[num++] = 423 (ushort)(nextI * horizontalSegments + j); 424 geometry.Indices[num++] = 425 (ushort)(nextI * horizontalSegments + nextJ); 426 } 427 } 428 } 429 430 return geometry; 431 } 432 #endregion 433 434 #region CreatePlane (Static) 435 /// <summary> 436 /// Create plane 437 /// </summary> 438 /// <param name="vertexFormat">The vertex format.</param> 439 /// <param name="setWidth">Width of the set.</param> 440 /// <param name="setHeight">Height of the set.</param> 441 /// <returns>The geometry data of the specified plane.</returns> 442 public static GeometryData CreatePlane(VertexFormat vertexFormat, 443 float setWidth, float setHeight) 444 { 445 return CreatePlane(vertexFormat, setWidth, setHeight, Color.White); 446 } 447 448 /// <summary> 449 /// Creates an XY plane in the specified vertex format (you can use 450 /// one of the predefined vertex formats from VertexData). 451 /// </summary> 452 /// <param name="vertexFormat">The vertex format.</param> 453 /// <param name="setWidth">Set width.</param> 454 /// <param name="setHeight">Set Height.</param> 455 /// <param name="setColor">Set Color.</param> 456 /// <returns>The geometry data of the specified plane.</returns> 457 public static GeometryData CreatePlane(VertexFormat vertexFormat, 458 float setWidth, float setHeight, Color setColor) 459 { 460 float halfWidth = setWidth * 0.5f; 461 float halfDepth = setHeight * 0.5f; 462 float height = 0; 463 464 // We use 4 vertices with 6 indices (0, 1, 2, 2, 1, 3): 465 // 0 - 1 466 // | / | 467 // 2 - 3 468 GeometryData geometry = new GeometryData( 469 "<GeneratedPlane(" + setWidth + "x" + setHeight + ")>", 470 4, vertexFormat, 6, true, false); 471 Vector planeNormal = new Vector(0, 0, 1); 472 Vector planeTangent = new Vector(1, 0, 0); 473 // Front left 474 geometry.SetVertexData(0, 475 new Vector(-halfWidth, halfDepth, height), 476 new Point(0, 0), setColor, planeNormal, planeTangent); 477 // Front right 478 geometry.SetVertexData(1, 479 new Vector(halfWidth, halfDepth, height), 480 new Point(1, 0), setColor, planeNormal, planeTangent); 481 // Rear left 482 geometry.SetVertexData(2, 483 new Vector(-halfWidth, -halfDepth, height), 484 new Point(0, 1), setColor, planeNormal, planeTangent); 485 // Rear right 486 geometry.SetVertexData(3, 487 new Vector(halfWidth, -halfDepth, height), 488 new Point(1, 1), setColor, planeNormal, planeTangent); 489 490 // Top left polygon (Note: Counter-Clockwise is front) 491 geometry.Indices[0] = 0; 492 geometry.Indices[1] = 2; 493 geometry.Indices[2] = 1; 494 // Bottom right polygon 495 geometry.Indices[3] = 2; 496 geometry.Indices[4] = 3; 497 geometry.Indices[5] = 1; 498 499 return geometry; 500 } 501 #endregion 502 503 #region CreateSegmentedPlane (Static) 504 /// <summary> 505 /// Creates an XY plane in the specified vertex format (you can use 506 /// one of the predefined vertex formats from VertexData). 507 /// </summary> 508 /// <param name="vertexFormat">The vertex format.</param> 509 /// <param name="setWidth">Sets Width.</param> 510 /// <param name="setHeight">Sets Height.</param> 511 /// <param name="segments">The segments.</param> 512 /// <param name="uvForEachSegment">Create new uv from 0-1 for each segment. 513 /// Useful for tiling (works even without tileable textures)</param> 514 /// <param name="setColor">Sets Color.</param> 515 public static GeometryData CreateSegmentedPlane(VertexFormat vertexFormat, 516 float setWidth, float setHeight, int segments, bool uvForEachSegment, 517 Color setColor) 518 { 519 if (segments < 1) 520 { 521 throw new InvalidOperationException( 522 "You need at least one segment for CreateSegmentedPlane!"); 523 } 524 525 float halfWidth = setWidth * 0.5f; 526 float halfDepth = setHeight * 0.5f; 527 float height = 0; 528 529 // We use 4 vertices with 6 indices (0, 1, 2, 2, 1, 3): 530 // 0 - 1 531 // | / | 532 // 2 - 3 533 // But duplicate this many times for as many segments in each direction. 534 int totalSegments = segments * segments; 535 int segmentStride = segments + 1; 536 int totalPoints = 537 uvForEachSegment 538 ? totalSegments * 4 539 : segmentStride * segmentStride; 540 GeometryData geometry = new GeometryData( 541 "<GeneratedSegmentedPlane(" + setWidth + "x" + setHeight + ")>", 542 totalPoints, vertexFormat, 6 * totalSegments, true, false); 543 Vector planeNormal = new Vector(0, 0, 1); 544 Vector planeTangent = new Vector(1, 0, 0); 545 // For each segment + 1, build the points 546 if (uvForEachSegment) 547 { 548 // For each segment, build up the 2 triangles 549 int index = 0; 550 int pointIndex = 0; 551 for (int ySegment = 0; ySegment < segments; ySegment++) 552 { 553 for (int xSegment = 0; xSegment < segments; xSegment++) 554 { 555 float xFactor = xSegment / (float)segments; 556 float yFactor = ySegment / (float)segments; 557 float xFactor2 = (xSegment + 1) / (float)segments; 558 float yFactor2 = (ySegment + 1) / (float)segments; 559 geometry.SetVertexData(pointIndex + 0, 560 new Vector(-halfWidth + xFactor * setWidth, 561 halfDepth - yFactor * setHeight, height), 562 new Point(0, 0), setColor, planeNormal, planeTangent); 563 geometry.SetVertexData(pointIndex + 1, 564 new Vector(-halfWidth + xFactor2 * setWidth, 565 halfDepth - yFactor * setHeight, height), 566 new Point(1, 0), setColor, planeNormal, planeTangent); 567 geometry.SetVertexData(pointIndex + 2, 568 new Vector(-halfWidth + xFactor * setWidth, 569 halfDepth - yFactor2 * setHeight, height), 570 new Point(0, 1), setColor, planeNormal, planeTangent); 571 geometry.SetVertexData(pointIndex + 3, 572 new Vector(-halfWidth + xFactor2 * setWidth, 573 halfDepth - yFactor2 * setHeight, height), 574 new Point(1, 1), setColor, planeNormal, planeTangent); 575 576 // Top left polygon (Note: Counter-Clockwise is front) 577 geometry.Indices[index++] = //0,0 578 (ushort)(pointIndex + 0); 579 geometry.Indices[index++] = //0,1 580 (ushort)(pointIndex + 2); 581 geometry.Indices[index++] = //1,0 582 (ushort)(pointIndex + 1); 583 // Bottom right polygon 584 geometry.Indices[index++] = //0,1 585 (ushort)(pointIndex + 2); 586 geometry.Indices[index++] = //1,1 587 (ushort)(pointIndex + 3); 588 geometry.Indices[index++] = //1,0 589 (ushort)(pointIndex + 1); 590 591 pointIndex += 4; 592 } 593 } 594 } 595 else 596 { 597 int pointIndex = 0; 598 for (int ySegment = 0; ySegment < segments + 1; ySegment++) 599 { 600 for (int xSegment = 0; xSegment < segments + 1; xSegment++) 601 { 602 float xFactor = xSegment / (float)segments; 603 float yFactor = ySegment / (float)segments; 604 geometry.SetVertexData(pointIndex++, 605 new Vector(-halfWidth + xFactor * setWidth, 606 halfDepth - yFactor * setHeight, height), 607 new Point(xFactor, yFactor), 608 setColor, planeNormal, planeTangent); 609 } 610 } 611 612 // For each segment, build up the 2 triangles 613 int index = 0; 614 for (int ySegment = 0; ySegment < segments; ySegment++) 615 { 616 for (int xSegment = 0; xSegment < segments; xSegment++) 617 { 618 // Top left polygon (Note: Counter-Clockwise is front) 619 geometry.Indices[index++] = //0,0 620 (ushort)(xSegment + 0 + (ySegment + 0) * segmentStride); 621 geometry.Indices[index++] = //0,1 622 (ushort)(xSegment + 0 + (ySegment + 1) * segmentStride); 623 geometry.Indices[index++] = //1,0 624 (ushort)(xSegment + 1 + (ySegment + 0) * segmentStride); 625 // Bottom right polygon 626 geometry.Indices[index++] = //0,1 627 (ushort)(xSegment + 0 + (ySegment + 1) * segmentStride); 628 geometry.Indices[index++] = //1,1 629 (ushort)(xSegment + 1 + (ySegment + 1) * segmentStride); 630 geometry.Indices[index++] = //1,0 631 (ushort)(xSegment + 1 + (ySegment + 0) * segmentStride); 632 } 633 } 634 } 635 636 return geometry; 637 } 638 639 /// <summary> 640 /// Create segmented plane. 641 /// </summary> 642 /// <param name="vertexFormat">The vertex format.</param> 643 /// <param name="setWidth">Width of the set.</param> 644 /// <param name="setHeight">Height of the set.</param> 645 /// <param name="segments">The segments.</param> 646 /// <returns></returns> 647 public static GeometryData CreateSegmentedPlane(VertexFormat vertexFormat, 648 float setWidth, float setHeight, int segments) 649 { 650 return CreateSegmentedPlane(vertexFormat, setWidth, setHeight, segments, 651 false, Color.White); 652 } 653 #endregion 654 655 #region CreateCapsule (Static) 656 /// <summary> 657 /// Creates a capsule geometry in the specified vertex format (you can use 658 /// one of the predefined vertex formats from VertexData). 659 /// </summary> 660 /// <param name="vertexFormat">The vertex format.</param> 661 /// <param name="setDiameter">Diameter of the capsule</param> 662 /// <param name="setLength">Length of the capsule</param> 663 /// <returns>The geometry data of the specified capsule.</returns> 664 public static GeometryData CreateCapsule(VertexFormat vertexFormat, 665 float setDiameter, float setLength) 666 { 667 return CreateCapsule(vertexFormat, setDiameter, setLength, 668 Color.White); 669 } 670 671 /// <summary> 672 /// Creates a capsule geometry in the specified vertex format (you can use 673 /// one of the predefined vertex formats from VertexData). 674 /// </summary> 675 /// <param name="vertexFormat">The vertex format.</param> 676 /// <param name="setDiameter">Diameter of the capsule</param> 677 /// <param name="setLength">Length of the capsule</param> 678 /// <param name="setColor">Color for the capsule</param> 679 /// <returns> 680 /// Geometry Data with the capsule, using tessellation of 12. 681 /// </returns> 682 public static GeometryData CreateCapsule(VertexFormat vertexFormat, 683 float setDiameter, float setLength, Color setColor) 684 { 685 int tessellation = 12; 686 687 int verticalSegments = tessellation; 688 int horizontalSegments = tessellation * 2; 689 690 float radius = setDiameter / 2; 691 692 int vertexCount = horizontalSegments * (verticalSegments - 1) + 2; 693 694 GeometryData geometry = new GeometryData( 695 "<GeneratedSegmentedPlane(" + setDiameter + "x" + setLength + ")>", 696 vertexCount, vertexFormat, 6 * vertexCount, true, false); 697 698 Vector down = -Vector.UnitZ; 699 700 // For each segment + 1, build the points 701 int pointIndex = 0; 702 // Start with a single vertex at the bottom of the sphere. 703 geometry.SetVertexData(pointIndex++, 704 down * radius + down * 0.5f * setLength, 705 Point.Zero, setColor, down, down); 706 707 // Create rings of vertices at progressively higher latitudes. 708 for (int i = 0; i < verticalSegments - 1; i++) 709 { 710 float latitude = ((i + 1) * MathHelper.Pi / 711 verticalSegments) - MathHelper.PiHalf; 712 float dy = (float)Math.Sin(latitude); 713 float dxz = (float)Math.Cos(latitude); 714 715 bool bla = false; 716 717 if (i > (verticalSegments - 2) / 2) 718 { 719 bla = true; 720 } 721 722 // Create a single ring of vertices at this latitude. 723 for (int j = 0; j < horizontalSegments; j++) 724 { 725 float longitude = j * MathHelper.PiDouble / horizontalSegments; 726 727 float dx = (float)Math.Cos(longitude) * dxz; 728 float dz = (float)Math.Sin(longitude) * dxz; 729 730 Vector normal = new Vector(dx, dy, dz); 731 Vector position = normal * radius; 732 733 if (bla) 734 { 735 position += Vector.UnitZ * 0.5f * setLength; 736 } 737 else 738 { 739 position += down * 0.5f * setLength; 740 } 741 742 geometry.SetVertexData(pointIndex++, 743 position, 744 new Point(1.0f, 0.0f), setColor, normal, normal); 745 } 746 } 747 748 // Finish with a single vertex at the top of the sphere. 749 geometry.SetVertexData(pointIndex++, 750 Vector.UnitZ * radius + Vector.UnitZ * 0.5f * setLength, 751 new Point(0.0f, 1.0f), setColor, Vector.UnitZ, Vector.UnitZ); 752 753 int index = 0; 754 // Create a fan connecting the bottom vertex to the bottom latitude ring. 755 for (int i = 0; i < horizontalSegments; i++) 756 { 757 geometry.Indices[index++] = (ushort)(1 + i); 758 geometry.Indices[index++] = (ushort)(1 + (i + 1) % horizontalSegments); 759 geometry.Indices[index++] = 0; 760 } 761 762 // Fill the sphere body with triangles joining each pair of latitude rings. 763 for (int i = 0; i < verticalSegments - 2; i++) 764 { 765 for (int j = 0; j < horizontalSegments; j++) 766 { 767 int nextI = i + 1; 768 int nextJ = (j + 1) % horizontalSegments; 769 770 geometry.Indices[index++] = 771 (ushort)(1 + nextI * horizontalSegments + j); 772 geometry.Indices[index++] = 773 (ushort)(1 + i * horizontalSegments + nextJ); 774 geometry.Indices[index++] = 775 (ushort)(1 + i * horizontalSegments + j); 776 777 geometry.Indices[index++] = 778 (ushort)(1 + nextI * horizontalSegments + j); 779 geometry.Indices[index++] = 780 (ushort)(1 + nextI * horizontalSegments + nextJ); 781 geometry.Indices[index++] = 782 (ushort)(1 + i * horizontalSegments + nextJ); 783 } 784 } 785 786 // Create a fan connecting the top vertex to the top latitude ring. 787 for (int i = 0; i < horizontalSegments; i++) 788 { 789 geometry.Indices[index++] = 790 (ushort)(vertexCount - 2 - i); 791 geometry.Indices[index++] = 792 (ushort)(vertexCount - 2 - (i + 1) % horizontalSegments); 793 geometry.Indices[index++] = 794 (ushort)(vertexCount - 1); 795 } 796 797 return geometry; 798 } 799 #endregion 800 801 #region Create (Static) 802 /// <summary> 803 /// Helper method to create a geometry out of position vectors, indices 804 /// and a color for each position. Will use VertexFormat.Position3DColor. 805 /// </summary> 806 /// <param name="positions">3D positions for the new geometry.</param> 807 /// <param name="colors"> 808 /// Colors for each of the positions, must match the length of the 809 /// positions list. 810 /// </param> 811 /// <param name="indices"> 812 /// Indices to connect the triangles, must be at least 3 for one triangle 813 /// or a multiple of 3 for more. 814 /// </param> 815 /// <returns> 816 /// A newly created GeometryData, which can be passed to MeshData to be 817 /// used for rendering. 818 /// </returns> 819 public static GeometryData Create(List<Vector> positions, 820 List<Color> colors, List<ushort> indices) 821 { 822 if (colors.Count != positions.Count) 823 { 824 throw new ArgumentException( 825 "The colors list length=" + colors.Count + " must match the " + 826 "positions list length=" + positions.Count); 827 } 828 if (indices.Count < 3 || 829 indices.Count % 3 != 0) 830 { 831 throw new ArgumentException( 832 "The indices list length=" + indices.Count + " must be at least 3 " + 833 "for one triangle or a multiple of 3 for more."); 834 } 835 836 // And finally create the geometry out of the custom positions 837 GeometryData geometry = new GeometryData( 838 "<CustomColoredGeometry>", positions.Count, 839 VertexFormat.Position3DColor, indices.Count, true, false); 840 841 // Add all vertices from the positions 842 for (int num = 0; num < positions.Count; num++) 843 { 844 // Note: Only position and color is used here, rest is ignored. 845 geometry.SetVertexData(num, positions[num], Point.Zero, colors[num], 846 Vector.UnitZ, Vector.UnitZ); 847 } 848 // Set the indices 849 geometry.Indices = indices.ToArray(); 850 851 return geometry; 852 } 853 #endregion 854 855 #region Create (Static) 856 /// <summary> 857 /// Helper method to create a geometry out of position vectors, indices 858 /// and a uv for each position. Will use VertexFormat.Position3DTextured. 859 /// </summary> 860 /// <param name="positions">3D positions for the new geometry.</param> 861 /// <param name="uvs"> 862 /// Texture uvs for each of the positions, must match the length of the 863 /// positions list. 864 /// </param> 865 /// <param name="indices"> 866 /// Indices to connect the triangles, must be at least 3 for one triangle 867 /// or a multiple of 3 for more. 868 /// </param> 869 /// <returns> 870 /// A newly created GeometryData, which can be passed to MeshData to be 871 /// used for rendering. 872 /// </returns> 873 public static GeometryData Create(List<Vector> positions, List<Point> uvs, 874 List<ushort> indices) 875 { 876 if (uvs.Count != positions.Count) 877 { 878 throw new ArgumentException( 879 "The uvs list length=" + uvs.Count + " must match the " + 880 "positions list length=" + positions.Count); 881 } 882 if (indices.Count < 3 || 883 indices.Count % 3 != 0) 884 { 885 throw new ArgumentException( 886 "The indices list length=" + indices.Count + " must be at least 3 " + 887 "for one triangle or a multiple of 3 for more."); 888 } 889 890 // And finally create the geometry out of the custom positions 891 GeometryData geometry = new GeometryData( 892 "<CustomColoredGeometry>", positions.Count, 893 VertexFormat.Position3DTextured, indices.Count, true, false); 894 895 // Add all vertices from the positions 896 for (int num = 0; num < positions.Count; num++) 897 { 898 // Note: Only position and uv is used here, rest is ignored. 899 geometry.SetVertexData(num, positions[num], uvs[num], Color.White, 900 Vector.UnitZ, Vector.UnitZ); 901 } 902 // Set the indices 903 geometry.Indices = indices.ToArray(); 904 905 return geometry; 906 } 907 #endregion 908 909 #region DefaultBox (Static) 910 /// <summary> 911 /// Default box (created on demand) with the size (1, 1). Usually using 912 /// the default material and used to display fallback content if loading 913 /// failed (e.g. if mesh content file was not found). 914 /// </summary> 915 public static GeometryData DefaultBox 916 { 917 get 918 { 919 if (defaultBoxMesh == null) 920 { 921 defaultBoxMesh = CreateCube(VertexFormat.Position3DTextured, 1); 922 } 923 return defaultBoxMesh; 924 } 925 } 926 #endregion 927 928 #region Name (Public) 929 /// <summary> 930 /// Name of the geometry, useful for debugging or identify warnings. 931 /// </summary> 932 public string Name 933 { 934 get; 935 private set; 936 } 937 #endregion 938 939 #region writer (Public) 940 /// <summary> 941 /// Writer to directly manipulate the vertex data (use Seek and Write). 942 /// You can also just define or use an existing vertex struct and use 943 /// SetData to store it into this GeometryData. 944 /// </summary> 945 public BinaryWriter writer; 946 #endregion 947 948 #region reader (Public) 949 /// <summary> 950 /// Reader to get data back. For debugging you could also use the Get 951 /// methods defined below (GetPosition, etc.), but they are slower. 952 /// </summary> 953 public BinaryReader reader; 954 #endregion 955 956 #region MaxNumberOfVertices (Public) 957 /// <summary> 958 /// Number of vertices this VertexData currently can hold. We usually only 959 /// increase this if the buffer needs to get bigger (e.g. via SetData or 960 /// IncreaseVertices). Normally this should be set once in the constructor 961 /// however (for static meshes we know beforehand)! This value is usually 962 /// the same as NumberOfUsedVertices, but for dynamic vertex data (e.g. 963 /// in DrawManager for lines or in MaterialManager for 2D UI vertices) 964 /// the number of used vertices can be lower and increase over time! 965 /// </summary> 966 public int MaxNumberOfVertices 967 { 968 get; 969 private set; 970 } 971 #endregion 972 973 #region NumberOfUsedVertices (Public) 974 /// <summary> 975 /// Number of used vertices, must always be the same (for static meshes) 976 /// or lower than MaxNumberOfVertices. For dynamic vertex data this can 977 /// go up to more than MaxNumberOfVertices can handle, then just call 978 /// IncreaseMaxVertices to increase MaxNumberOfVertices! 979 /// </summary> 980 public int NumberOfUsedVertices 981 { 982 get 983 { 984 return numberOfUsedVerticesInternal; 985 } 986 } 987 #endregion 988 989 #region Format (Public) 990 /// <summary> 991 /// Vertex element format as specified in the constructor. Everything 992 /// else here depends on it, so changing this is not possible, create 993 /// a new VertexData instance to convert vertex formats. 994 /// </summary> 995 public VertexFormat Format 996 { 997 get; 998 private set; 999 } 1000 #endregion 1001 1002 #region VertexDataLengthInBytes (Public) 1003 /// <summary> 1004 /// Vertex data length for each of the vertices in bytes. 1005 /// </summary> 1006 public int VertexDataLengthInBytes 1007 { 1008 get; 1009 private set; 1010 } 1011 #endregion 1012 1013 #region Indices (Public) 1014 /// <summary> 1015 /// And all the indices that link into the vertices. For 2D, UI or 1016 /// effect billboards these indices can be pre-calculated (e.g. 1017 /// 0, 1, 2, 0, 2, 3) and be used with whatever vertices are used. 1018 /// </summary> 1019 public ushort[] Indices; 1020 #endregion 1021 1022 #region NumberOfUsedIndices (Public) 1023 /// <summary> 1024 /// Number of used indices, only used for dynamic vertex and index data. 1025 /// Note: Not in Indices because that struct only contains the indices 1026 /// arrays as unions. 1027 /// </summary> 1028 public int NumberOfUsedIndices; 1029 #endregion 1030 1031 1032 #region Bones (Public) 1033 /// <summary> 1034 /// Flat list of bones, the first bone is always the root bone, all 1035 /// children can be accessed from here. The main reason for having a flat 1036 /// list is easy access to all bones for showing bone previous and of 1037 /// course to quickly access all animation matrices. 1038 /// </summary> 1039 public BoneData[] Bones = new BoneData[0]; 1040 #endregion 1041 1042 #region IsStatic (Public) 1043 /// <summary> 1044 /// Is the data in this Mesh static or dynamic? Only dynamic data can 1045 /// be changed at runtime and will force a dynamic vertex buffer update. 1046 /// Always true for loaded content, sometimes false for generated content. 1047 /// </summary> 1048 public bool IsStatic 1049 { 1050 get; 1051 private set; 1052 } 1053 #endregion 1054 1055 #region IsStaticAndLoadedFromContent (Public) 1056 /// <summary> 1057 /// For static geometry we can get rid of the vertices and indices as we 1058 /// have them now on the GPU. This saves memory and the content can be 1059 /// reloaded at any time too. 1060 /// </summary> 1061 public bool IsStaticAndLoadedFromContent 1062 { 1063 get 1064 { 1065 return IsStatic && 1066 reloadContent != null; 1067 } 1068 } 1069 #endregion 1070 1071 #region HasDataChanged (Public) 1072 /// <summary> 1073 /// This value is used to check if the data has changed for dynamic 1074 /// geometry objects. Only if this is true we actually need to update the 1075 /// native objects on the GPU (VBO and IBO), which is slow and should be 1076 /// avoided if nothing has changed (e.g. no new lines were added). 1077 /// </summary> 1078 public bool HasDataChanged = true; 1079 #endregion 1080 1081 #region IsLineData (Public) 1082 /// <summary> 1083 /// Is line data in the vertex stream? Only used for dynamic mesh data 1084 /// for the DrawManager. Only used without indices. 1085 /// </summary> 1086 public bool IsLineData 1087 { 1088 get; 1089 private set; 1090 } 1091 #endregion 1092 1093 #region IsSky (Public) 1094 /// <summary> 1095 /// Is sky 1096 /// </summary> 1097 public bool IsSky 1098 { 1099 get; 1100 private set; 1101 } 1102 #endregion 1103 1104 #region PolygonCount (Public) 1105 /// <summary> 1106 /// Gets the number of polygons for this mesh (indices / 3) 1107 /// </summary> 1108 public int PolygonCount 1109 { 1110 get 1111 { 1112 return Indices.Length / 3; 1113 } 1114 } 1115 #endregion 1116 1117 #region BoundingBox (Public) 1118 /// <summary> 1119 /// Get bounding box of all vertices (in whatever space they are). Only 1120 /// calculated once and also used for the BoundingSphere calculation. 1121 /// </summary> 1122 public BoundingBox BoundingBox 1123 { 1124 get 1125 { 1126 // Was this bounding box initialized before? Note: Normally it is 1127 // already initialized at creation time for all processed content (from 1128 // the content-pipeline), only code-generated won't be initialized. 1129 if (IsStatic && 1130 (boundingBox.Max.X != 0 || 1131 boundingBox.Max.Y != 0 || 1132 boundingBox.Max.Z != 0)) 1133 { 1134 // Then just return it. 1135 return boundingBox; 1136 } 1137 1138 // Else just get all vertex positions to compute it 1139 Vector[] positions = new Vector[numberOfUsedVerticesInternal]; 1140 for (int index = 0; index < numberOfUsedVerticesInternal; index++) 1141 { 1142 positions[index] = GetPosition(index); 1143 } 1144 1145 // before we return it 1146 boundingBox = BoundingBox.Create(positions); 1147 return boundingBox; 1148 } 1149 } 1150 #endregion 1151 1152 #region BoundingSphere (Public) 1153 /// <summary> 1154 /// Bounding radius 1155 /// </summary> 1156 public BoundingSphere BoundingSphere 1157 { 1158 get 1159 { 1160 // Only calculate bounding radius once. 1161 if (IsStatic == false || 1162 sphere.Radius == 0.0f) 1163 { 1164 sphere = BoundingBox.ToBoundingSphere(); 1165 } 1166 return sphere; 1167 } 1168 } 1169 #endregion 1170 1171 #region CheckFirstPlaneId (Public) 1172 /// <summary> 1173 /// The ID of the plane that the box was outside last in the 1174 /// frustum check. Most of the time the frustum check will fail at the 1175 /// same plane again so checking that one first makes it faster. 1176 /// </summary> 1177 public byte CheckFirstPlaneId 1178 { 1179 get; 1180 set; 1181 } 1182 #endregion 1183 1184 #region Private 1185 1186 #region defaultBoxMesh (Private) 1187 /// <summary> 1188 /// Cached default box mesh data. 1189 /// </summary> 1190 private static GeometryData defaultBoxMesh; 1191 #endregion 1192 1193 #region numberOfUsedVerticesInternal (Private) 1194 /// <summary> 1195 /// Optimization to make the NumberOfUsedVertices property as fast as 1196 /// possible, especially in debug mode and when profiling the property 1197 /// set slows us down. 1198 /// </summary> 1199 private int numberOfUsedVerticesInternal; 1200 #endregion 1201 1202 #region boundingBox (Private) 1203 /// <summary> 1204 /// Box 1205 /// </summary> 1206 private BoundingBox boundingBox; 1207 #endregion 1208 1209 #region sphere (Private) 1210 /// <summary> 1211 /// Sphere 1212 /// </summary> 1213 private BoundingSphere sphere = new BoundingSphere(Vector.Zero, 0.0f); 1214 #endregion 1215 1216 #region stream (Private) 1217 /// <summary> 1218 /// Data stream for all the data. 1219 /// </summary> 1220 private MemoryStream stream; 1221 #endregion 1222 1223 #region reloadContent (Private) 1224 /// <summary> 1225 /// Helper delegate to reload this geometry content after it has been 1226 /// disposed. Usually used for static geometry, which can be disposed 1227 /// after the GPU VBO and IBO have been created to save memory. 1228 /// </summary> 1229 private readonly RunDelegate reloadContent; 1230 #endregion 1231 1232 #endregion 1233 1234 #region Constructors 1235 /// <summary> 1236 /// Create geometry data with a predefined number of vertices. This way all 1237 /// the vertex data is already allocated and future writes to this vertex 1238 /// data are fast. Use the writer directly to write up to 2000 vertices, 1239 /// for more use the SetData method below. 1240 /// Note: When rendering this is always send to the native vertex buffer 1241 /// (e.g. in Xna DynamicVertexBuffer is used). The index buffer is not used 1242 /// if setNumberOfIndices is 0, else you must fill the indices yourself 1243 /// after filling in the vertices. 1244 /// </summary> 1245 /// <param name="setName"> 1246 /// Set name for this geometry for identification. Should start with > 1247 /// for dynamically created geometry to not confuse it with loaded gometry 1248 /// from content, which uses content names. 1249 /// </param> 1250 /// <param name="setMaxNumberOfVertices"> 1251 /// Set maximum number of vertices to be used, can be increased if the 1252 /// geometry is dynamic (not static). 1253 /// </param> 1254 /// <param name="setVertexFormat"> 1255 /// Set vertex format (usually from a shader). 1256 /// </param> 1257 /// <param name="setNumberOfIndices">Set number of indices</param> 1258 /// <param name="setIsStaticData"> 1259 /// Is static data? Faster to render because does not need to be modified. 1260 /// </param> 1261 /// <param name="setIsLineData"> 1262 /// Set if line data is used in this vertex stream. Only used for dynamic 1263 /// mesh data for the DrawManager. Only used without indices. 1264 /// </param> 1265 public GeometryData(string setName, int setMaxNumberOfVertices, 1266 VertexFormat setVertexFormat, int setNumberOfIndices, 1267 bool setIsStaticData, bool setIsLineData) 1268 : this(setName, setMaxNumberOfVertices, setVertexFormat, 1269 setNumberOfIndices, setIsStaticData, setIsLineData, 1270 new BoundingBox(Vector.Zero, Vector.Zero)) 1271 { 1272 } 1273 1274 /// <summary> 1275 /// Create geometry data with a predefined number of vertices. This way all 1276 /// the vertex data is already allocated and future writes to this vertex 1277 /// data are fast. Use the writer directly to write up to 2000 vertices, 1278 /// for more use the SetData method below. 1279 /// Note: When rendering this is always send to the native vertex buffer 1280 /// (e.g. in Xna DynamicVertexBuffer is used). The index buffer is not used 1281 /// if setNumberOfIndices is 0, else you must fill the indices yourself 1282 /// after filling in the vertices. 1283 /// </summary> 1284 /// <param name="setName"> 1285 /// Set name for this geometry for identification. Should start with > 1286 /// for dynamically created geometry to not confuse it with loaded gometry 1287 /// from content, which uses content names. 1288 /// </param> 1289 /// <param name="setMaxNumberOfVertices"> 1290 /// Set maximum number of vertices to be used, can be increased if the 1291 /// geometry is dynamic (not static). 1292 /// </param> 1293 /// <param name="setVertexFormat"> 1294 /// Set vertex format (usually from a shader). 1295 /// </param> 1296 /// <param name="setNumberOfIndices">Set number of indices</param> 1297 /// <param name="setIsStaticData"> 1298 /// Is static data? Faster to render because does not need to be modified. 1299 /// </param> 1300 /// <param name="setIsLineData"> 1301 /// Set if line data is used in this vertex stream. Only used for dynamic 1302 /// mesh data for the DrawManager. Only used without indices. 1303 /// </param> 1304 /// <param name="setBoundingBox"> 1305 /// Set bounding box with given data, useful for physics and displaying 1306 /// the boundings in debug mode. For content this is usually calculated 1307 /// already in the content system, for generated geometry it will not be 1308 /// set usually and will be calculated automatically when first needed. 1309 /// </param> 1310 public GeometryData(string setName, int setMaxNumberOfVertices, 1311 VertexFormat setVertexFormat, int setNumberOfIndices, 1312 bool setIsStaticData, bool setIsLineData, BoundingBox setBoundingBox) 1313 { 1314 Name = setName; 1315 IsStatic = setIsStaticData; 1316 IsLineData = setIsLineData; 1317 // Both the Skydome model and the generated sky box contain "Sky" 1318 IsSky = Name.Contains("Sky"); 1319 1320 Format = setVertexFormat; 1321 VertexDataLengthInBytes = Format.LengthInBytes; 1322 MaxNumberOfVertices = setMaxNumberOfVertices; 1323 // Dynamic vertex data is empty by default 1324 numberOfUsedVerticesInternal = 1325 setIsStaticData 1326 ? MaxNumberOfVertices 1327 : 0; 1328 1329 stream = new MemoryStream(MaxNumberOfVertices * VertexDataLengthInBytes); 1330 stream.SetLength(stream.Capacity); 1331 writer = new BinaryWriter(stream); 1332 reader = new BinaryReader(stream); 1333 1334 Indices = new ushort[setNumberOfIndices]; 1335 NumberOfUsedIndices = 1336 setIsStaticData 1337 ? setNumberOfIndices 1338 : 0; 1339 1340 boundingBox = setBoundingBox; 1341 } 1342 1343 /// <summary> 1344 /// Create geometry data from a binary stream (e.g. as part of ModelData). 1345 /// This constructor requires the binary stream data and a way to reload 1346 /// the content when Dispose was called. This is used for static geometry 1347 /// to save memory and this constructor is always used for static geometry. 1348 /// </summary> 1349 /// <param name="dataReader"> 1350 /// The data reader which contains the stored content data internally. 1351 /// </param> 1352 /// <param name="setReloadContent"> 1353 /// The delegate which contains the code for reloading the content. 1354 /// </param> 1355 public GeometryData(BinaryReader dataReader, RunDelegate setReloadContent) 1356 { 1357 // Loaded data is always static 1358 IsStatic = true; 1359 IsLineData = false; 1360 reloadContent = setReloadContent; 1361 // All data is filled in Load 1362 Load(dataReader); 1363 // Both the Skydome model and the generated sky box contain "Sky" 1364 IsSky = Name.Contains("Sky"); 1365 } 1366 #endregion 1367 1368 #region IDisposable Members 1369 /// <summary> 1370 /// Dispose all internal data (vertices and indices). The lists are still 1371 /// valid, just empty. This is mainly used for static geometry data, which 1372 /// can be disposed after the native objects have been created. 1373 /// </summary> 1374 public void Dispose() 1375 { 1376 if (reader != null) 1377 { 1378 reader.Dispose(); 1379 reader = null; 1380 } 1381 if (writer != null) 1382 { 1383 writer.Dispose(); 1384 writer = null; 1385 } 1386 if (stream != null) 1387 { 1388 stream.Dispose(); 1389 stream = null; 1390 } 1391 Indices = new ushort[0]; 1392 numberOfUsedVerticesInternal = 0; 1393 // Note: Do not touch NumberOfUsedIndices or MaxNumberOfVertices as 1394 // these are still used even for disposed geometry data objects to 1395 // perform different re…
Large files files are truncated, but you can click here to view the full file