diff options
author | Dahlia Trimble | 2008-10-19 09:04:25 +0000 |
---|---|---|
committer | Dahlia Trimble | 2008-10-19 09:04:25 +0000 |
commit | b6396bc9a73ee8dbb2cef8708f7876e21a448f0c (patch) | |
tree | b4105613e29909c0cc3afbbc78cbd318509c0292 | |
parent | * Null check before UseCircuitCode Check or the server crashes on packet loss! (diff) | |
download | opensim-SC_OLD-b6396bc9a73ee8dbb2cef8708f7876e21a448f0c.zip opensim-SC_OLD-b6396bc9a73ee8dbb2cef8708f7876e21a448f0c.tar.gz opensim-SC_OLD-b6396bc9a73ee8dbb2cef8708f7876e21a448f0c.tar.bz2 opensim-SC_OLD-b6396bc9a73ee8dbb2cef8708f7876e21a448f0c.tar.xz |
More progress towards implementing vertex normals - not complete yet.
Diffstat (limited to '')
-rw-r--r-- | OpenSim/Region/Physics/Meshing/PrimMesher.cs | 487 |
1 files changed, 371 insertions, 116 deletions
diff --git a/OpenSim/Region/Physics/Meshing/PrimMesher.cs b/OpenSim/Region/Physics/Meshing/PrimMesher.cs index 98c7b5b..4814c4a 100644 --- a/OpenSim/Region/Physics/Meshing/PrimMesher.cs +++ b/OpenSim/Region/Physics/Meshing/PrimMesher.cs | |||
@@ -192,8 +192,23 @@ namespace PrimMesher | |||
192 | } | 192 | } |
193 | } | 193 | } |
194 | 194 | ||
195 | public struct UVCoord | ||
196 | { | ||
197 | public float U; | ||
198 | public float V; | ||
199 | |||
200 | |||
201 | public UVCoord(float u, float v) | ||
202 | { | ||
203 | this.U = u; | ||
204 | this.V = v; | ||
205 | } | ||
206 | } | ||
207 | |||
195 | public struct Face | 208 | public struct Face |
196 | { | 209 | { |
210 | public int primFace; | ||
211 | |||
197 | // vertices | 212 | // vertices |
198 | public int v1; | 213 | public int v1; |
199 | public int v2; | 214 | public int v2; |
@@ -204,8 +219,15 @@ namespace PrimMesher | |||
204 | public int n2; | 219 | public int n2; |
205 | public int n3; | 220 | public int n3; |
206 | 221 | ||
222 | //// UVs | ||
223 | //public int uv1; | ||
224 | //public int uv2; | ||
225 | //public int uv3; | ||
226 | |||
207 | public Face(int v1, int v2, int v3) | 227 | public Face(int v1, int v2, int v3) |
208 | { | 228 | { |
229 | primFace = 0; | ||
230 | |||
209 | this.v1 = v1; | 231 | this.v1 = v1; |
210 | this.v2 = v2; | 232 | this.v2 = v2; |
211 | this.v3 = v3; | 233 | this.v3 = v3; |
@@ -213,10 +235,16 @@ namespace PrimMesher | |||
213 | this.n1 = 0; | 235 | this.n1 = 0; |
214 | this.n2 = 0; | 236 | this.n2 = 0; |
215 | this.n3 = 0; | 237 | this.n3 = 0; |
238 | |||
239 | //this.uv1 = 0; | ||
240 | //this.uv2 = 0; | ||
241 | //this.uv3 = 0; | ||
216 | } | 242 | } |
217 | 243 | ||
218 | public Face(int v1, int v2, int v3, int n1, int n2, int n3) | 244 | public Face(int v1, int v2, int v3, int n1, int n2, int n3) |
219 | { | 245 | { |
246 | primFace = 0; | ||
247 | |||
220 | this.v1 = v1; | 248 | this.v1 = v1; |
221 | this.v2 = v2; | 249 | this.v2 = v2; |
222 | this.v3 = v3; | 250 | this.v3 = v3; |
@@ -224,6 +252,85 @@ namespace PrimMesher | |||
224 | this.n1 = n1; | 252 | this.n1 = n1; |
225 | this.n2 = n2; | 253 | this.n2 = n2; |
226 | this.n3 = n3; | 254 | this.n3 = n3; |
255 | |||
256 | //this.uv1 = 0; | ||
257 | //this.uv2 = 0; | ||
258 | //this.uv3 = 0; | ||
259 | } | ||
260 | } | ||
261 | |||
262 | public struct ViewerFace | ||
263 | { | ||
264 | public int primFaceNumber; | ||
265 | |||
266 | public Coord v1; | ||
267 | public Coord v2; | ||
268 | public Coord v3; | ||
269 | |||
270 | public Coord n1; | ||
271 | public Coord n2; | ||
272 | public Coord n3; | ||
273 | |||
274 | public UVCoord uv1; | ||
275 | public UVCoord uv2; | ||
276 | public UVCoord uv3; | ||
277 | |||
278 | public ViewerFace(int primFaceNumber) | ||
279 | { | ||
280 | this.primFaceNumber = primFaceNumber; | ||
281 | |||
282 | this.v1 = new Coord(); | ||
283 | this.v2 = new Coord(); | ||
284 | this.v3 = new Coord(); | ||
285 | |||
286 | this.n1 = new Coord(); | ||
287 | this.n2 = new Coord(); | ||
288 | this.n3 = new Coord(); | ||
289 | |||
290 | this.uv1 = new UVCoord(); | ||
291 | this.uv2 = new UVCoord(); | ||
292 | this.uv3 = new UVCoord(); | ||
293 | } | ||
294 | |||
295 | public void Scale(float x, float y, float z) | ||
296 | { | ||
297 | this.v1.X *= x; | ||
298 | this.v1.Y *= y; | ||
299 | this.v1.Z *= z; | ||
300 | |||
301 | this.v2.X *= x; | ||
302 | this.v2.Y *= y; | ||
303 | this.v2.Z *= z; | ||
304 | |||
305 | this.v3.X *= x; | ||
306 | this.v3.Y *= y; | ||
307 | this.v3.Z *= z; | ||
308 | } | ||
309 | |||
310 | public void AddRot(Quat q) | ||
311 | { | ||
312 | this.v1 *= q; | ||
313 | this.v2 *= q; | ||
314 | this.v3 *= q; | ||
315 | |||
316 | this.n1 *= q; | ||
317 | this.n2 *= q; | ||
318 | this.n3 *= q; | ||
319 | } | ||
320 | |||
321 | public void CalcSurfaceNormal() | ||
322 | { | ||
323 | |||
324 | Coord edge1 = new Coord(this.n2.X - this.n1.X, this.n2.Y - this.n1.Y, this.n2.Z - this.n1.Z); | ||
325 | Coord edge2 = new Coord(this.n3.X - this.n1.X, this.n3.Y - this.n1.Y, this.n3.Z - this.n1.Z); | ||
326 | |||
327 | //Coord normal = Coord.Cross(edge1, edge2).Normalize(); | ||
328 | |||
329 | //this.n1 = normal; | ||
330 | //this.n2 = normal; | ||
331 | //this.n3 = normal; | ||
332 | |||
333 | this.n1 = this.n2 = this.n3 = Coord.Cross(edge1, edge2).Normalize(); | ||
227 | } | 334 | } |
228 | } | 335 | } |
229 | 336 | ||
@@ -415,7 +522,13 @@ namespace PrimMesher | |||
415 | 522 | ||
416 | internal List<Coord> coords; | 523 | internal List<Coord> coords; |
417 | internal List<Face> faces; | 524 | internal List<Face> faces; |
418 | internal List<Coord> edgeNormals; | 525 | internal List<Coord> vertexNormals; |
526 | internal Coord faceNormal = new Coord(0.0f, 0.0f, 1.0f); | ||
527 | internal Coord cutNormal1 = new Coord(); | ||
528 | internal Coord cutNormal2 = new Coord(); | ||
529 | |||
530 | internal int numOuterVerts = 0; | ||
531 | internal int numHollowVerts = 0; | ||
419 | 532 | ||
420 | internal bool calcVertexNormals = false; | 533 | internal bool calcVertexNormals = false; |
421 | 534 | ||
@@ -423,7 +536,7 @@ namespace PrimMesher | |||
423 | { | 536 | { |
424 | this.coords = new List<Coord>(); | 537 | this.coords = new List<Coord>(); |
425 | this.faces = new List<Face>(); | 538 | this.faces = new List<Face>(); |
426 | this.edgeNormals = new List<Coord>(); | 539 | this.vertexNormals = new List<Coord>(); |
427 | } | 540 | } |
428 | 541 | ||
429 | public Profile(int sides, float profileStart, float profileEnd, float hollow, int hollowSides, bool createFaces, bool calcVertexNormals) | 542 | public Profile(int sides, float profileStart, float profileEnd, float hollow, int hollowSides, bool createFaces, bool calcVertexNormals) |
@@ -431,13 +544,15 @@ namespace PrimMesher | |||
431 | this.calcVertexNormals = calcVertexNormals; | 544 | this.calcVertexNormals = calcVertexNormals; |
432 | this.coords = new List<Coord>(); | 545 | this.coords = new List<Coord>(); |
433 | this.faces = new List<Face>(); | 546 | this.faces = new List<Face>(); |
434 | this.edgeNormals = new List<Coord>(); | 547 | this.vertexNormals = new List<Coord>(); |
435 | Coord center = new Coord(0.0f, 0.0f, 0.0f); | 548 | Coord center = new Coord(0.0f, 0.0f, 0.0f); |
436 | 549 | ||
437 | List<Coord> hollowCoords = new List<Coord>(); | 550 | List<Coord> hollowCoords = new List<Coord>(); |
438 | List<Coord> hollowNormals = new List<Coord>(); | 551 | List<Coord> hollowNormals = new List<Coord>(); |
439 | 552 | ||
553 | //Quat rot180 = new Quat(new Coord(0.0f, 0.0f, 1.0f), (float)Math.PI); ; | ||
440 | bool hasHollow = (hollow > 0.0f); | 554 | bool hasHollow = (hollow > 0.0f); |
555 | |||
441 | bool hasProfileCut = (profileStart > 0.0f || profileEnd < 1.0f); | 556 | bool hasProfileCut = (profileStart > 0.0f || profileEnd < 1.0f); |
442 | 557 | ||
443 | AngleList angles = new AngleList(); | 558 | AngleList angles = new AngleList(); |
@@ -463,6 +578,8 @@ namespace PrimMesher | |||
463 | return; | 578 | return; |
464 | } | 579 | } |
465 | 580 | ||
581 | this.numOuterVerts = angles.angles.Count; | ||
582 | |||
466 | // flag to create as few triangles as possible for 3 or 4 side profile | 583 | // flag to create as few triangles as possible for 3 or 4 side profile |
467 | bool simpleFace = (sides < 5 && !(hasHollow || hasProfileCut)); | 584 | bool simpleFace = (sides < 5 && !(hasHollow || hasProfileCut)); |
468 | 585 | ||
@@ -480,12 +597,13 @@ namespace PrimMesher | |||
480 | return; | 597 | return; |
481 | } | 598 | } |
482 | } | 599 | } |
600 | this.numHollowVerts = hollowAngles.angles.Count; | ||
483 | } | 601 | } |
484 | else if (!simpleFace) | 602 | else if (!simpleFace) |
485 | { | 603 | { |
486 | this.coords.Add(center); | 604 | this.coords.Add(center); |
487 | if (this.calcVertexNormals && sides > 4) | 605 | if (this.calcVertexNormals)// && sides > 4) |
488 | this.edgeNormals.Add(new Coord(0.0f, 0.0f, 1.0f)); | 606 | this.vertexNormals.Add(new Coord(0.0f, 0.0f, 1.0f)); |
489 | } | 607 | } |
490 | 608 | ||
491 | float z = 0.0f; | 609 | float z = 0.0f; |
@@ -504,12 +622,18 @@ namespace PrimMesher | |||
504 | 622 | ||
505 | hollowCoords.Add(newVert); | 623 | hollowCoords.Add(newVert); |
506 | if (this.calcVertexNormals) | 624 | if (this.calcVertexNormals) |
625 | { | ||
626 | //Coord hollowNormal = new Coord(angle.X, angle.Y, 0.0f); | ||
627 | //hollowNormal *= rot180; | ||
628 | //hollowNormals.Add(hollowNormal); | ||
507 | hollowNormals.Add(new Coord(-angle.X, -angle.Y, 0.0f)); | 629 | hollowNormals.Add(new Coord(-angle.X, -angle.Y, 0.0f)); |
630 | } | ||
508 | } | 631 | } |
509 | } | 632 | } |
510 | 633 | ||
511 | int index = 0; | 634 | int index = 0; |
512 | int numAngles = angles.angles.Count; | 635 | int numAngles = angles.angles.Count; |
636 | |||
513 | for (int i = 0; i < numAngles; i++) | 637 | for (int i = 0; i < numAngles; i++) |
514 | { | 638 | { |
515 | angle = angles.angles[i]; | 639 | angle = angles.angles[i]; |
@@ -518,7 +642,7 @@ namespace PrimMesher | |||
518 | newVert.Z = z; | 642 | newVert.Z = z; |
519 | this.coords.Add(newVert); | 643 | this.coords.Add(newVert); |
520 | if (this.calcVertexNormals) | 644 | if (this.calcVertexNormals) |
521 | this.edgeNormals.Add(new Coord(angle.X, angle.Y, 0.0f)); | 645 | this.vertexNormals.Add(new Coord(angle.X, angle.Y, 0.0f)); |
522 | 646 | ||
523 | if (hollow > 0.0f) | 647 | if (hollow > 0.0f) |
524 | { | 648 | { |
@@ -529,7 +653,12 @@ namespace PrimMesher | |||
529 | newVert.Z = z; | 653 | newVert.Z = z; |
530 | hollowCoords.Add(newVert); | 654 | hollowCoords.Add(newVert); |
531 | if (this.calcVertexNormals) | 655 | if (this.calcVertexNormals) |
656 | { | ||
657 | //Coord hollowNormal = new Coord(angle.X, angle.Y, 0.0f); | ||
658 | //hollowNormal *= rot180; | ||
659 | //hollowNormals.Add(hollowNormal); | ||
532 | hollowNormals.Add(new Coord(-angle.X, -angle.Y, 0.0f)); | 660 | hollowNormals.Add(new Coord(-angle.X, -angle.Y, 0.0f)); |
661 | } | ||
533 | } | 662 | } |
534 | } | 663 | } |
535 | else if (!simpleFace && createFaces && angle.angle > 0.0001f) | 664 | else if (!simpleFace && createFaces && angle.angle > 0.0001f) |
@@ -538,12 +667,7 @@ namespace PrimMesher | |||
538 | newFace.v1 = 0; | 667 | newFace.v1 = 0; |
539 | newFace.v2 = index; | 668 | newFace.v2 = index; |
540 | newFace.v3 = index + 1; | 669 | newFace.v3 = index + 1; |
541 | //if (this.calcVertexNormals) | 670 | |
542 | //{ | ||
543 | // newFace.n1 = newFace.v1; | ||
544 | // newFace.n2 = newFace.v2; | ||
545 | // newFace.n3 = newFace.v3; | ||
546 | //} | ||
547 | this.faces.Add(newFace); | 671 | this.faces.Add(newFace); |
548 | } | 672 | } |
549 | index += 1; | 673 | index += 1; |
@@ -570,23 +694,11 @@ namespace PrimMesher | |||
570 | newFace.v1 = coordIndex; | 694 | newFace.v1 = coordIndex; |
571 | newFace.v2 = coordIndex + 1; | 695 | newFace.v2 = coordIndex + 1; |
572 | newFace.v3 = numTotalVerts - coordIndex - 1; | 696 | newFace.v3 = numTotalVerts - coordIndex - 1; |
573 | //if (this.calcVertexNormals) | ||
574 | //{ | ||
575 | // newFace.n1 = newFace.v1; | ||
576 | // newFace.n2 = newFace.v2; | ||
577 | // newFace.n3 = newFace.v3; | ||
578 | //} | ||
579 | this.faces.Add(newFace); | 697 | this.faces.Add(newFace); |
580 | 698 | ||
581 | newFace.v1 = coordIndex + 1; | 699 | newFace.v1 = coordIndex + 1; |
582 | newFace.v2 = numTotalVerts - coordIndex - 2; | 700 | newFace.v2 = numTotalVerts - coordIndex - 2; |
583 | newFace.v3 = numTotalVerts - coordIndex - 1; | 701 | newFace.v3 = numTotalVerts - coordIndex - 1; |
584 | //if (this.calcVertexNormals) | ||
585 | //{ | ||
586 | // newFace.n1 = newFace.v1; | ||
587 | // newFace.n2 = newFace.v2; | ||
588 | // newFace.n3 = newFace.v3; | ||
589 | //} | ||
590 | this.faces.Add(newFace); | 702 | this.faces.Add(newFace); |
591 | } | 703 | } |
592 | } | 704 | } |
@@ -605,12 +717,6 @@ namespace PrimMesher | |||
605 | newFace.v1 = numTotalVerts - i - 1; | 717 | newFace.v1 = numTotalVerts - i - 1; |
606 | newFace.v2 = j; | 718 | newFace.v2 = j; |
607 | newFace.v3 = j + 1; | 719 | newFace.v3 = j + 1; |
608 | //if (this.calcVertexNormals) | ||
609 | //{ | ||
610 | // newFace.n1 = newFace.v1; | ||
611 | // newFace.n2 = newFace.v2; | ||
612 | // newFace.n3 = newFace.v3; | ||
613 | //} | ||
614 | 720 | ||
615 | this.faces.Add(newFace); | 721 | this.faces.Add(newFace); |
616 | j += 1; | 722 | j += 1; |
@@ -619,12 +725,6 @@ namespace PrimMesher | |||
619 | newFace.v1 = j; | 725 | newFace.v1 = j; |
620 | newFace.v2 = numTotalVerts - i - 2; | 726 | newFace.v2 = numTotalVerts - i - 2; |
621 | newFace.v3 = numTotalVerts - i - 1; | 727 | newFace.v3 = numTotalVerts - i - 1; |
622 | //if (this.calcVertexNormals) | ||
623 | //{ | ||
624 | // newFace.n1 = newFace.v1; | ||
625 | // newFace.n2 = newFace.v2; | ||
626 | // newFace.n3 = newFace.v3; | ||
627 | //} | ||
628 | 728 | ||
629 | this.faces.Add(newFace); | 729 | this.faces.Add(newFace); |
630 | } | 730 | } |
@@ -642,12 +742,6 @@ namespace PrimMesher | |||
642 | newFace.v1 = i; | 742 | newFace.v1 = i; |
643 | newFace.v2 = numTotalVerts - j - 2; | 743 | newFace.v2 = numTotalVerts - j - 2; |
644 | newFace.v3 = numTotalVerts - j - 1; | 744 | newFace.v3 = numTotalVerts - j - 1; |
645 | //if (this.calcVertexNormals) | ||
646 | //{ | ||
647 | // newFace.n1 = newFace.v1; | ||
648 | // newFace.n2 = newFace.v2; | ||
649 | // newFace.n3 = newFace.v3; | ||
650 | //} | ||
651 | 745 | ||
652 | this.faces.Add(newFace); | 746 | this.faces.Add(newFace); |
653 | j += 1; | 747 | j += 1; |
@@ -656,12 +750,6 @@ namespace PrimMesher | |||
656 | newFace.v1 = numTotalVerts - j - 1; | 750 | newFace.v1 = numTotalVerts - j - 1; |
657 | newFace.v2 = i; | 751 | newFace.v2 = i; |
658 | newFace.v3 = i + 1; | 752 | newFace.v3 = i + 1; |
659 | //if (this.calcVertexNormals) | ||
660 | //{ | ||
661 | // newFace.n1 = newFace.v1; | ||
662 | // newFace.n2 = newFace.v2; | ||
663 | // newFace.n3 = newFace.v3; | ||
664 | //} | ||
665 | 753 | ||
666 | this.faces.Add(newFace); | 754 | this.faces.Add(newFace); |
667 | } | 755 | } |
@@ -671,10 +759,7 @@ namespace PrimMesher | |||
671 | 759 | ||
672 | this.coords.AddRange(hollowCoords); | 760 | this.coords.AddRange(hollowCoords); |
673 | if (this.calcVertexNormals) | 761 | if (this.calcVertexNormals) |
674 | this.edgeNormals.AddRange(hollowNormals); | 762 | this.vertexNormals.AddRange(hollowNormals); |
675 | |||
676 | hollowCoords = null; | ||
677 | hollowNormals = null; | ||
678 | } | 763 | } |
679 | 764 | ||
680 | if (simpleFace && createFaces) | 765 | if (simpleFace && createFaces) |
@@ -688,6 +773,33 @@ namespace PrimMesher | |||
688 | } | 773 | } |
689 | } | 774 | } |
690 | 775 | ||
776 | if (calcVertexNormals && hasProfileCut) | ||
777 | { | ||
778 | if (hasHollow) | ||
779 | { | ||
780 | this.cutNormal1.X = -this.vertexNormals[0].Y - this.vertexNormals[this.vertexNormals.Count - 1].Y; | ||
781 | this.cutNormal1.Y = this.vertexNormals[0].X - this.vertexNormals[this.vertexNormals.Count - 1].X; | ||
782 | |||
783 | int lastOuterVertIndex = this.numOuterVerts - 1; | ||
784 | this.cutNormal2.X = -this.vertexNormals[lastOuterVertIndex].Y - this.vertexNormals[lastOuterVertIndex + 1].Y; | ||
785 | this.cutNormal2.Y = this.vertexNormals[lastOuterVertIndex].X - this.vertexNormals[lastOuterVertIndex + 1].X; | ||
786 | } | ||
787 | else | ||
788 | { | ||
789 | this.cutNormal1.X = this.vertexNormals[1].Y; | ||
790 | this.cutNormal1.Y = -this.vertexNormals[1].X; | ||
791 | |||
792 | this.cutNormal2.X = -this.vertexNormals[this.vertexNormals.Count - 1].Y; | ||
793 | this.cutNormal2.Y = this.vertexNormals[this.vertexNormals.Count - 1].X; | ||
794 | |||
795 | } | ||
796 | this.cutNormal1.Normalize(); | ||
797 | this.cutNormal2.Normalize(); | ||
798 | } | ||
799 | |||
800 | hollowCoords = null; | ||
801 | hollowNormals = null; | ||
802 | |||
691 | } | 803 | } |
692 | 804 | ||
693 | public Profile Clone() | 805 | public Profile Clone() |
@@ -702,7 +814,15 @@ namespace PrimMesher | |||
702 | clone.coords.AddRange(this.coords); | 814 | clone.coords.AddRange(this.coords); |
703 | if (needFaces) | 815 | if (needFaces) |
704 | clone.faces.AddRange(this.faces); | 816 | clone.faces.AddRange(this.faces); |
705 | clone.edgeNormals.AddRange(this.edgeNormals); | 817 | if ((clone.calcVertexNormals = this.calcVertexNormals) == true) |
818 | { | ||
819 | clone.vertexNormals.AddRange(this.vertexNormals); | ||
820 | clone.faceNormal = this.faceNormal; | ||
821 | clone.cutNormal1 = this.cutNormal1; | ||
822 | clone.cutNormal2 = this.cutNormal2; | ||
823 | } | ||
824 | clone.numOuterVerts = this.numOuterVerts; | ||
825 | clone.numHollowVerts = this.numHollowVerts; | ||
706 | 826 | ||
707 | return clone; | 827 | return clone; |
708 | } | 828 | } |
@@ -745,16 +865,23 @@ namespace PrimMesher | |||
745 | this.coords[i] = c; | 865 | this.coords[i] = c; |
746 | } | 866 | } |
747 | 867 | ||
748 | int numNormals = this.edgeNormals.Count; | 868 | if (this.calcVertexNormals) |
749 | for (i = 0; i < numNormals; i++) | ||
750 | { | 869 | { |
751 | c = this.edgeNormals[i]; | 870 | int numNormals = this.vertexNormals.Count; |
752 | Coord n = new Coord(c.X, c.Y, c.Z) * q; | 871 | for (i = 0; i < numNormals; i++) |
872 | { | ||
873 | c = this.vertexNormals[i]; | ||
874 | Coord n = new Coord(c.X, c.Y, c.Z) * q; | ||
753 | 875 | ||
754 | c.X = n.X; | 876 | c.X = n.X; |
755 | c.Y = n.Y; | 877 | c.Y = n.Y; |
756 | c.Z = n.Z; | 878 | c.Z = n.Z; |
757 | this.edgeNormals[i] = c; | 879 | this.vertexNormals[i] = c; |
880 | } | ||
881 | |||
882 | this.faceNormal *= q; | ||
883 | this.cutNormal1 *= q; | ||
884 | this.cutNormal2 *= q; | ||
758 | } | 885 | } |
759 | } | 886 | } |
760 | 887 | ||
@@ -794,14 +921,18 @@ namespace PrimMesher | |||
794 | 921 | ||
795 | if (this.calcVertexNormals) | 922 | if (this.calcVertexNormals) |
796 | { | 923 | { |
797 | int normalCount = this.edgeNormals.Count; | 924 | int normalCount = this.vertexNormals.Count; |
798 | if (normalCount > 0) | 925 | if (normalCount > 0) |
799 | { | 926 | { |
800 | Coord n = this.edgeNormals[normalCount - 1]; | 927 | Coord n = this.vertexNormals[normalCount - 1]; |
801 | n.Z = -n.Z; | 928 | n.Z = -n.Z; |
802 | this.edgeNormals[normalCount - 1] = n; | 929 | this.vertexNormals[normalCount - 1] = n; |
803 | } | 930 | } |
804 | } | 931 | } |
932 | |||
933 | this.faceNormal.X = -this.faceNormal.X; | ||
934 | this.faceNormal.Y = -this.faceNormal.Y; | ||
935 | this.faceNormal.Z = -this.faceNormal.Z; | ||
805 | } | 936 | } |
806 | 937 | ||
807 | public void AddValue2FaceVertexIndices(int num) | 938 | public void AddValue2FaceVertexIndices(int num) |
@@ -866,11 +997,13 @@ namespace PrimMesher | |||
866 | public List<Coord> normals; | 997 | public List<Coord> normals; |
867 | public List<Face> faces; | 998 | public List<Face> faces; |
868 | 999 | ||
869 | public int sides = 4; | 1000 | public List<ViewerFace> viewerFaces; |
870 | public int hollowSides = 4; | 1001 | |
871 | public float profileStart = 0.0f; | 1002 | private int sides = 4; |
872 | public float profileEnd = 1.0f; | 1003 | private int hollowSides = 4; |
873 | public float hollow = 0.0f; | 1004 | private float profileStart = 0.0f; |
1005 | private float profileEnd = 1.0f; | ||
1006 | private float hollow = 0.0f; | ||
874 | public int twistBegin = 0; | 1007 | public int twistBegin = 0; |
875 | public int twistEnd = 0; | 1008 | public int twistEnd = 0; |
876 | public float topShearX = 0.0f; | 1009 | public float topShearX = 0.0f; |
@@ -888,10 +1021,10 @@ namespace PrimMesher | |||
888 | public float revolutions = 1.0f; | 1021 | public float revolutions = 1.0f; |
889 | public int stepsPerRevolution = 24; | 1022 | public int stepsPerRevolution = 24; |
890 | 1023 | ||
1024 | private bool hasProfileCut = false; | ||
891 | public bool calcVertexNormals = false; | 1025 | public bool calcVertexNormals = false; |
892 | private bool normalsProcessed = false; | 1026 | private bool normalsProcessed = false; |
893 | 1027 | public bool viewerMode = false; | |
894 | //private List<Coord> edgeNormals; | ||
895 | 1028 | ||
896 | public string ParamsToDisplayString() | 1029 | public string ParamsToDisplayString() |
897 | { | 1030 | { |
@@ -949,6 +1082,8 @@ namespace PrimMesher | |||
949 | this.hollow = 1.0f; | 1082 | this.hollow = 1.0f; |
950 | if (hollow < 0.0f) | 1083 | if (hollow < 0.0f) |
951 | this.hollow = 0.0f; | 1084 | this.hollow = 0.0f; |
1085 | |||
1086 | this.hasProfileCut = (this.profileStart > 0.0f || this.profileEnd < 1.0f); | ||
952 | } | 1087 | } |
953 | 1088 | ||
954 | public void ExtrudeLinear() | 1089 | public void ExtrudeLinear() |
@@ -956,6 +1091,12 @@ namespace PrimMesher | |||
956 | this.coords = new List<Coord>(); | 1091 | this.coords = new List<Coord>(); |
957 | this.faces = new List<Face>(); | 1092 | this.faces = new List<Face>(); |
958 | 1093 | ||
1094 | if (this.viewerMode) | ||
1095 | { | ||
1096 | this.viewerFaces = new List<ViewerFace>(); | ||
1097 | this.calcVertexNormals = true; | ||
1098 | } | ||
1099 | |||
959 | if (this.calcVertexNormals) | 1100 | if (this.calcVertexNormals) |
960 | this.normals = new List<Coord>(); | 1101 | this.normals = new List<Coord>(); |
961 | 1102 | ||
@@ -964,18 +1105,15 @@ namespace PrimMesher | |||
964 | 1105 | ||
965 | float length = this.pathCutEnd - this.pathCutBegin; | 1106 | float length = this.pathCutEnd - this.pathCutBegin; |
966 | normalsProcessed = false; | 1107 | normalsProcessed = false; |
967 | //if (this.calcVertexNormals) | ||
968 | // this.edgeNormals = new List<Coord>(); | ||
969 | 1108 | ||
970 | #if VIEWER | 1109 | if (this.viewerMode && this.sides == 3) |
971 | if (this.sides == 3) | ||
972 | { | 1110 | { |
973 | // prisms don't taper well so add some vertical resolution | 1111 | // prisms don't taper well so add some vertical resolution |
974 | // other prims may benefit from this but just do prisms for now | 1112 | // other prims may benefit from this but just do prisms for now |
975 | if (Math.Abs(this.taperX) > 0.01 || Math.Abs(this.taperY) > 0.01) | 1113 | if (Math.Abs(this.taperX) > 0.01 || Math.Abs(this.taperY) > 0.01) |
976 | steps = (int)(steps * 4.5 * length); | 1114 | steps = (int)(steps * 4.5 * length); |
977 | } | 1115 | } |
978 | #endif | 1116 | |
979 | 1117 | ||
980 | float twistBegin = this.twistBegin / 360.0f * twoPi; | 1118 | float twistBegin = this.twistBegin / 360.0f * twoPi; |
981 | float twistEnd = this.twistEnd / 360.0f * twoPi; | 1119 | float twistEnd = this.twistEnd / 360.0f * twoPi; |
@@ -1021,15 +1159,14 @@ namespace PrimMesher | |||
1021 | else if (this.sides == 24 && this.hollowSides == 4) | 1159 | else if (this.sides == 24 && this.hollowSides == 4) |
1022 | hollow *= 1.414f; | 1160 | hollow *= 1.414f; |
1023 | 1161 | ||
1024 | bool hasProfileCut = false; | ||
1025 | if (profileStart > 0.0f || profileEnd < 1.0f) | ||
1026 | hasProfileCut = true; | ||
1027 | |||
1028 | Profile profile = new Profile(this.sides, this.profileStart, this.profileEnd, hollow, this.hollowSides, true, calcVertexNormals); | 1162 | Profile profile = new Profile(this.sides, this.profileStart, this.profileEnd, hollow, this.hollowSides, true, calcVertexNormals); |
1029 | 1163 | ||
1030 | if (initialProfileRot != 0.0f) | 1164 | if (initialProfileRot != 0.0f) |
1031 | profile.AddRot(new Quat(new Coord(0.0f, 0.0f, 1.0f), initialProfileRot)); | 1165 | profile.AddRot(new Quat(new Coord(0.0f, 0.0f, 1.0f), initialProfileRot)); |
1032 | 1166 | ||
1167 | Coord lastCutNormal1 = new Coord(); | ||
1168 | Coord lastCutNormal2 = new Coord(); | ||
1169 | |||
1033 | bool done = false; | 1170 | bool done = false; |
1034 | while (!done) | 1171 | while (!done) |
1035 | { | 1172 | { |
@@ -1057,8 +1194,31 @@ namespace PrimMesher | |||
1057 | newLayer.AddPos(xOffset, yOffset, zOffset); | 1194 | newLayer.AddPos(xOffset, yOffset, zOffset); |
1058 | 1195 | ||
1059 | if (step == 0) | 1196 | if (step == 0) |
1197 | { | ||
1060 | newLayer.FlipNormals(); | 1198 | newLayer.FlipNormals(); |
1061 | 1199 | ||
1200 | // add the top faces to the viewerFaces list here | ||
1201 | if (this.viewerMode) | ||
1202 | { | ||
1203 | Coord faceNormal = newLayer.faceNormal; | ||
1204 | ViewerFace newViewerFace = new ViewerFace(); | ||
1205 | newViewerFace.primFaceNumber = 0; | ||
1206 | foreach (Face face in newLayer.faces) | ||
1207 | { | ||
1208 | newViewerFace.v1 = newLayer.coords[face.v1]; | ||
1209 | newViewerFace.v2 = newLayer.coords[face.v2]; | ||
1210 | newViewerFace.v3 = newLayer.coords[face.v3]; | ||
1211 | |||
1212 | newViewerFace.n1 = faceNormal; | ||
1213 | newViewerFace.n2 = faceNormal; | ||
1214 | newViewerFace.n3 = faceNormal; | ||
1215 | |||
1216 | this.viewerFaces.Add(newViewerFace); | ||
1217 | } | ||
1218 | |||
1219 | } | ||
1220 | } | ||
1221 | |||
1062 | // append this layer | 1222 | // append this layer |
1063 | 1223 | ||
1064 | int coordsLen = this.coords.Count; | 1224 | int coordsLen = this.coords.Count; |
@@ -1069,7 +1229,7 @@ namespace PrimMesher | |||
1069 | if (this.calcVertexNormals) | 1229 | if (this.calcVertexNormals) |
1070 | { | 1230 | { |
1071 | newLayer.AddValue2FaceNormalIndices(this.normals.Count); | 1231 | newLayer.AddValue2FaceNormalIndices(this.normals.Count); |
1072 | this.normals.AddRange(newLayer.edgeNormals); | 1232 | this.normals.AddRange(newLayer.vertexNormals); |
1073 | } | 1233 | } |
1074 | 1234 | ||
1075 | if (percentOfPath <= this.pathCutBegin || percentOfPath >= this.pathCutEnd) | 1235 | if (percentOfPath <= this.pathCutBegin || percentOfPath >= this.pathCutEnd) |
@@ -1085,61 +1245,112 @@ namespace PrimMesher | |||
1085 | int startVert = coordsLen + 1; | 1245 | int startVert = coordsLen + 1; |
1086 | int endVert = this.coords.Count - 1; | 1246 | int endVert = this.coords.Count - 1; |
1087 | 1247 | ||
1088 | if (sides < 5 || hasProfileCut || hollow > 0.0f) | 1248 | if (sides < 5 || this.hasProfileCut || hollow > 0.0f) |
1089 | startVert--; | 1249 | startVert--; |
1090 | 1250 | ||
1091 | for (int i = startVert; i < endVert; i++) | 1251 | for (int i = startVert; i < endVert; i++) |
1092 | { | 1252 | { |
1253 | int whichVert = i - startVert; | ||
1254 | |||
1093 | newFace.v1 = i; | 1255 | newFace.v1 = i; |
1094 | newFace.v2 = i - numVerts; | 1256 | newFace.v2 = i - numVerts; |
1095 | newFace.v3 = i - numVerts + 1; | 1257 | newFace.v3 = i - numVerts + 1; |
1096 | if (this.calcVertexNormals) | ||
1097 | { | ||
1098 | newFace.n1 = newFace.v1; | ||
1099 | newFace.n2 = newFace.v2; | ||
1100 | newFace.n3 = newFace.v3; | ||
1101 | } | ||
1102 | this.faces.Add(newFace); | 1258 | this.faces.Add(newFace); |
1103 | 1259 | ||
1104 | newFace.v2 = i - numVerts + 1; | 1260 | newFace.v2 = i - numVerts + 1; |
1105 | newFace.v3 = i + 1; | 1261 | newFace.v3 = i + 1; |
1106 | if (this.calcVertexNormals) | 1262 | this.faces.Add(newFace); |
1263 | |||
1264 | if (this.viewerMode) | ||
1107 | { | 1265 | { |
1108 | newFace.n2 = newFace.v2; | 1266 | // add the side faces to the list of viewerFaces here |
1109 | newFace.n3 = newFace.v3; | 1267 | ViewerFace newViewerFace1 = new ViewerFace(); |
1268 | ViewerFace newViewerFace2 = new ViewerFace(); | ||
1269 | |||
1270 | newViewerFace1.v1 = this.coords[i]; | ||
1271 | newViewerFace1.v2 = this.coords[i - numVerts]; | ||
1272 | newViewerFace1.v3 = this.coords[i - numVerts + 1]; | ||
1273 | |||
1274 | newViewerFace2.v1 = this.coords[i]; | ||
1275 | newViewerFace2.v2 = this.coords[i - numVerts + 1]; | ||
1276 | newViewerFace2.v3 = this.coords[i + 1]; | ||
1277 | |||
1278 | if (whichVert == newLayer.numOuterVerts - 1 && hasProfileCut) | ||
1279 | { // start profile cut faces | ||
1280 | |||
1281 | newViewerFace1.n2 = newViewerFace1.n1 = lastCutNormal1; | ||
1282 | newViewerFace1.n3 = newLayer.cutNormal1; | ||
1283 | |||
1284 | newViewerFace2.n3 = newViewerFace2.n1 = newLayer.cutNormal1; | ||
1285 | newViewerFace2.n2 = lastCutNormal1; | ||
1286 | } | ||
1287 | |||
1288 | else // periphery faces | ||
1289 | { | ||
1290 | if ((sides < 5 && whichVert < newLayer.numOuterVerts) || (hollowSides < 5 && whichVert >= newLayer.numOuterVerts)) | ||
1291 | { | ||
1292 | newViewerFace1.CalcSurfaceNormal(); | ||
1293 | newViewerFace2.CalcSurfaceNormal(); | ||
1294 | } | ||
1295 | else | ||
1296 | { | ||
1297 | newViewerFace1.n1 = this.normals[i]; | ||
1298 | newViewerFace1.n2 = this.normals[i - numVerts]; | ||
1299 | newViewerFace1.n3 = this.normals[i - numVerts + 1]; | ||
1300 | |||
1301 | newViewerFace2.n1 = this.normals[i]; | ||
1302 | newViewerFace2.n2 = this.normals[i - numVerts + 1]; | ||
1303 | newViewerFace2.n3 = this.normals[i + 1]; | ||
1304 | } | ||
1305 | } | ||
1306 | |||
1307 | this.viewerFaces.Add(newViewerFace1); | ||
1308 | this.viewerFaces.Add(newViewerFace2); | ||
1309 | |||
1110 | } | 1310 | } |
1111 | this.faces.Add(newFace); | ||
1112 | } | 1311 | } |
1113 | 1312 | ||
1114 | if (hasProfileCut) | 1313 | if (this.hasProfileCut) |
1115 | { | 1314 | { // add the end cut face to the list of viewerFaces here |
1315 | // the first cut face was filled in the above loop | ||
1116 | newFace.v1 = coordsLen - 1; | 1316 | newFace.v1 = coordsLen - 1; |
1117 | newFace.v2 = coordsLen - numVerts; | 1317 | newFace.v2 = coordsLen - numVerts; |
1118 | newFace.v3 = coordsLen; | 1318 | newFace.v3 = coordsLen; |
1119 | if (this.calcVertexNormals) | ||
1120 | { | ||
1121 | Coord n1 = SurfaceNormal(newFace); | ||
1122 | this.normals.Add(n1); | ||
1123 | |||
1124 | newFace.n1 = newFace.n2 = newFace.n3 = this.normals.Count - 1; | ||
1125 | } | ||
1126 | this.faces.Add(newFace); | 1319 | this.faces.Add(newFace); |
1127 | 1320 | ||
1128 | newFace.v1 = coordsLen + numVerts - 1; | 1321 | newFace.v1 = coordsLen + numVerts - 1; |
1129 | newFace.v2 = coordsLen - 1; | 1322 | newFace.v2 = coordsLen - 1; |
1130 | newFace.v3 = coordsLen; | 1323 | newFace.v3 = coordsLen; |
1131 | if (this.calcVertexNormals) | 1324 | this.faces.Add(newFace); |
1325 | |||
1326 | if (this.viewerMode) | ||
1132 | { | 1327 | { |
1133 | Coord n1 = SurfaceNormal(newFace); | 1328 | ViewerFace newViewerFace = new ViewerFace(); |
1134 | this.normals.Add(n1); | 1329 | newViewerFace.v1 = this.coords[coordsLen - 1]; // last vert in prior layer |
1330 | newViewerFace.v2 = this.coords[coordsLen - numVerts]; // first vert in prior layer | ||
1331 | newViewerFace.v3 = this.coords[coordsLen]; // first vert in new layer | ||
1332 | |||
1333 | newViewerFace.n2 = newViewerFace.n1 = lastCutNormal2; | ||
1334 | newViewerFace.n3 = newLayer.cutNormal2; | ||
1135 | 1335 | ||
1136 | newFace.n1 = newFace.n2 = newFace.n3 = this.normals.Count - 1; | 1336 | this.viewerFaces.Add(newViewerFace); |
1337 | |||
1338 | newViewerFace.v1 = this.coords[coordsLen + numVerts - 1]; // last vert in new layer | ||
1339 | newViewerFace.v2 = this.coords[coordsLen - 1]; // last vert in prior layer | ||
1340 | newViewerFace.v3 = this.coords[coordsLen]; // first vert in new layer | ||
1341 | |||
1342 | newViewerFace.n3 = newViewerFace.n1 = newLayer.cutNormal2; | ||
1343 | newViewerFace.n2 = lastCutNormal2; | ||
1344 | |||
1345 | this.viewerFaces.Add(newViewerFace); | ||
1137 | } | 1346 | } |
1138 | this.faces.Add(newFace); | ||
1139 | } | 1347 | } |
1140 | 1348 | ||
1141 | } | 1349 | } |
1142 | 1350 | ||
1351 | lastCutNormal1 = newLayer.cutNormal1; | ||
1352 | lastCutNormal2 = newLayer.cutNormal2; | ||
1353 | |||
1143 | // calc the step for the next iteration of the loop | 1354 | // calc the step for the next iteration of the loop |
1144 | 1355 | ||
1145 | if (step < steps) | 1356 | if (step < steps) |
@@ -1153,10 +1364,27 @@ namespace PrimMesher | |||
1153 | done = true; | 1364 | done = true; |
1154 | } | 1365 | } |
1155 | else done = true; | 1366 | else done = true; |
1156 | } | ||
1157 | 1367 | ||
1158 | //if (calcVertexNormals && sides < 5 && twistBegin == 0.0f && twistEnd == 0.0f) | 1368 | if (done && viewerMode) |
1159 | // this.CalcNormals(); | 1369 | { |
1370 | // add the bottom faces to the viewerFaces list here | ||
1371 | Coord faceNormal = newLayer.faceNormal; | ||
1372 | ViewerFace newViewerFace = new ViewerFace(); | ||
1373 | newViewerFace.primFaceNumber = 0; | ||
1374 | foreach (Face face in newLayer.faces) | ||
1375 | { | ||
1376 | newViewerFace.v1 = newLayer.coords[face.v1 - coordsLen]; | ||
1377 | newViewerFace.v2 = newLayer.coords[face.v2 - coordsLen]; | ||
1378 | newViewerFace.v3 = newLayer.coords[face.v3 - coordsLen]; | ||
1379 | |||
1380 | newViewerFace.n1 = faceNormal; | ||
1381 | newViewerFace.n2 = faceNormal; | ||
1382 | newViewerFace.n3 = faceNormal; | ||
1383 | |||
1384 | this.viewerFaces.Add(newViewerFace); | ||
1385 | } | ||
1386 | } | ||
1387 | } | ||
1160 | } | 1388 | } |
1161 | 1389 | ||
1162 | public void ExtrudeCircular() | 1390 | public void ExtrudeCircular() |
@@ -1164,6 +1392,12 @@ namespace PrimMesher | |||
1164 | this.coords = new List<Coord>(); | 1392 | this.coords = new List<Coord>(); |
1165 | this.faces = new List<Face>(); | 1393 | this.faces = new List<Face>(); |
1166 | 1394 | ||
1395 | if (this.viewerMode) | ||
1396 | { | ||
1397 | this.viewerFaces = new List<ViewerFace>(); | ||
1398 | this.calcVertexNormals = true; | ||
1399 | } | ||
1400 | |||
1167 | if (this.calcVertexNormals) | 1401 | if (this.calcVertexNormals) |
1168 | this.normals = new List<Coord>(); | 1402 | this.normals = new List<Coord>(); |
1169 | 1403 | ||
@@ -1242,10 +1476,6 @@ namespace PrimMesher | |||
1242 | } | 1476 | } |
1243 | } | 1477 | } |
1244 | 1478 | ||
1245 | bool hasProfileCut = false; | ||
1246 | if (profileStart > 0.0f || profileEnd < 1.0f) | ||
1247 | hasProfileCut = true; | ||
1248 | |||
1249 | bool needEndFaces = false; | 1479 | bool needEndFaces = false; |
1250 | if (this.pathCutBegin != 0.0 || this.pathCutEnd != 1.0) | 1480 | if (this.pathCutBegin != 0.0 || this.pathCutEnd != 1.0) |
1251 | needEndFaces = true; | 1481 | needEndFaces = true; |
@@ -1327,7 +1557,7 @@ namespace PrimMesher | |||
1327 | if (this.calcVertexNormals) | 1557 | if (this.calcVertexNormals) |
1328 | { | 1558 | { |
1329 | newLayer.AddValue2FaceNormalIndices(this.normals.Count); | 1559 | newLayer.AddValue2FaceNormalIndices(this.normals.Count); |
1330 | this.normals.AddRange(newLayer.edgeNormals); | 1560 | this.normals.AddRange(newLayer.vertexNormals); |
1331 | } | 1561 | } |
1332 | 1562 | ||
1333 | if (isEndLayer) | 1563 | if (isEndLayer) |
@@ -1342,7 +1572,7 @@ namespace PrimMesher | |||
1342 | int startVert = coordsLen + 1; | 1572 | int startVert = coordsLen + 1; |
1343 | int endVert = this.coords.Count - 1; | 1573 | int endVert = this.coords.Count - 1; |
1344 | 1574 | ||
1345 | if (sides < 5 || hasProfileCut || hollow > 0.0f) | 1575 | if (sides < 5 || this.hasProfileCut || hollow > 0.0f) |
1346 | startVert--; | 1576 | startVert--; |
1347 | 1577 | ||
1348 | for (int i = startVert; i < endVert; i++) | 1578 | for (int i = startVert; i < endVert; i++) |
@@ -1356,9 +1586,33 @@ namespace PrimMesher | |||
1356 | newFace.v2 = i - numVerts + 1; | 1586 | newFace.v2 = i - numVerts + 1; |
1357 | newFace.v3 = i + 1; | 1587 | newFace.v3 = i + 1; |
1358 | this.faces.Add(newFace); | 1588 | this.faces.Add(newFace); |
1589 | |||
1590 | if (this.viewerMode) | ||
1591 | { | ||
1592 | // add the side faces to the list of viewerFaces here | ||
1593 | ViewerFace newViewerFace = new ViewerFace(); | ||
1594 | newViewerFace.v1 = this.coords[i]; | ||
1595 | newViewerFace.v2 = this.coords[i - numVerts]; | ||
1596 | newViewerFace.v3 = this.coords[i - numVerts + 1]; | ||
1597 | |||
1598 | newViewerFace.n1 = this.normals[i]; | ||
1599 | newViewerFace.n2 = this.normals[i - numVerts]; | ||
1600 | newViewerFace.n3 = this.normals[i - numVerts + 1]; | ||
1601 | |||
1602 | this.viewerFaces.Add(newViewerFace); | ||
1603 | |||
1604 | newViewerFace.v2 = this.coords[i - numVerts + 1]; | ||
1605 | newViewerFace.v3 = this.coords[i + 1]; | ||
1606 | |||
1607 | newViewerFace.n2 = this.normals[i - numVerts + 1]; | ||
1608 | newViewerFace.n3 = this.normals[i + 1]; | ||
1609 | |||
1610 | this.viewerFaces.Add(newViewerFace); | ||
1611 | |||
1612 | } | ||
1359 | } | 1613 | } |
1360 | 1614 | ||
1361 | if (hasProfileCut) | 1615 | if (this.hasProfileCut) |
1362 | { | 1616 | { |
1363 | newFace.v1 = coordsLen - 1; | 1617 | newFace.v1 = coordsLen - 1; |
1364 | newFace.v2 = coordsLen - numVerts; | 1618 | newFace.v2 = coordsLen - numVerts; |
@@ -1405,6 +1659,7 @@ namespace PrimMesher | |||
1405 | 1659 | ||
1406 | public Coord SurfaceNormal(int faceIndex) | 1660 | public Coord SurfaceNormal(int faceIndex) |
1407 | { | 1661 | { |
1662 | int numFaces = faces.Count; | ||
1408 | if (faceIndex < 0 || faceIndex >= faces.Count) | 1663 | if (faceIndex < 0 || faceIndex >= faces.Count) |
1409 | throw new Exception("faceIndex out of range"); | 1664 | throw new Exception("faceIndex out of range"); |
1410 | 1665 | ||