diff options
author | Dahlia Trimble | 2008-11-05 10:22:41 +0000 |
---|---|---|
committer | Dahlia Trimble | 2008-11-05 10:22:41 +0000 |
commit | 5fffc04ae67381cebc1f20f062873ee889151ac6 (patch) | |
tree | 439909d1db7f35cc0de81dbcde6490b540dffa43 /OpenSim/Region | |
parent | Mantis#2557. Thank you kindly, Diva for a patch that: (diff) | |
download | opensim-SC-5fffc04ae67381cebc1f20f062873ee889151ac6.zip opensim-SC-5fffc04ae67381cebc1f20f062873ee889151ac6.tar.gz opensim-SC-5fffc04ae67381cebc1f20f062873ee889151ac6.tar.bz2 opensim-SC-5fffc04ae67381cebc1f20f062873ee889151ac6.tar.xz |
Add more vertex normals and UV coordinates. Sync with primmesher.dll forge project.
Diffstat (limited to 'OpenSim/Region')
-rw-r--r-- | OpenSim/Region/Physics/Meshing/PrimMesher.cs | 172 |
1 files changed, 123 insertions, 49 deletions
diff --git a/OpenSim/Region/Physics/Meshing/PrimMesher.cs b/OpenSim/Region/Physics/Meshing/PrimMesher.cs index 741010d..72da5cf 100644 --- a/OpenSim/Region/Physics/Meshing/PrimMesher.cs +++ b/OpenSim/Region/Physics/Meshing/PrimMesher.cs | |||
@@ -121,6 +121,15 @@ namespace PrimMesher | |||
121 | return (float)Math.Sqrt(this.X * this.X + this.Y * this.Y + this.Z * this.Z); | 121 | return (float)Math.Sqrt(this.X * this.X + this.Y * this.Y + this.Z * this.Z); |
122 | } | 122 | } |
123 | 123 | ||
124 | public Coord Invert() | ||
125 | { | ||
126 | this.X = -this.X; | ||
127 | this.Y = -this.Y; | ||
128 | this.Z = -this.Z; | ||
129 | |||
130 | return this; | ||
131 | } | ||
132 | |||
124 | public Coord Normalize() | 133 | public Coord Normalize() |
125 | { | 134 | { |
126 | const float MAG_THRESHOLD = 0.0000001f; | 135 | const float MAG_THRESHOLD = 0.0000001f; |
@@ -369,6 +378,14 @@ namespace PrimMesher | |||
369 | new Angle(1.0f, 1.0f, 0.0f) | 378 | new Angle(1.0f, 1.0f, 0.0f) |
370 | }; | 379 | }; |
371 | 380 | ||
381 | private Coord[] normals3 = | ||
382 | { | ||
383 | new Coord(0.25f, 0.4330127019f, 0.0f).Normalize(), | ||
384 | new Coord(-0.5f, 0.0f, 0.0f).Normalize(), | ||
385 | new Coord(0.25f, -0.4330127019f, 0.0f).Normalize(), | ||
386 | new Coord(0.25f, 0.4330127019f, 0.0f).Normalize() | ||
387 | }; | ||
388 | |||
372 | private Angle[] angles4 = | 389 | private Angle[] angles4 = |
373 | { | 390 | { |
374 | new Angle(0.0f, 1.0f, 0.0f), | 391 | new Angle(0.0f, 1.0f, 0.0f), |
@@ -378,6 +395,15 @@ namespace PrimMesher | |||
378 | new Angle(1.0f, 1.0f, 0.0f) | 395 | new Angle(1.0f, 1.0f, 0.0f) |
379 | }; | 396 | }; |
380 | 397 | ||
398 | private Coord[] normals4 = | ||
399 | { | ||
400 | new Coord(0.5f, 0.5f, 0.0f).Normalize(), | ||
401 | new Coord(-0.5f, 0.5f, 0.0f).Normalize(), | ||
402 | new Coord(-0.5f, -0.5f, 0.0f).Normalize(), | ||
403 | new Coord(0.5f, -0.5f, 0.0f).Normalize(), | ||
404 | new Coord(0.5f, 0.5f, 0.0f).Normalize() | ||
405 | }; | ||
406 | |||
381 | private Angle[] angles24 = | 407 | private Angle[] angles24 = |
382 | { | 408 | { |
383 | new Angle(0.0f, 1.0f, 0.0f), | 409 | new Angle(0.0f, 1.0f, 0.0f), |
@@ -427,10 +453,13 @@ namespace PrimMesher | |||
427 | } | 453 | } |
428 | 454 | ||
429 | internal List<Angle> angles; | 455 | internal List<Angle> angles; |
456 | internal List<Coord> normals; | ||
430 | 457 | ||
431 | internal void makeAngles(int sides, float startAngle, float stopAngle) | 458 | internal void makeAngles(int sides, float startAngle, float stopAngle) |
432 | { | 459 | { |
433 | angles = new List<Angle>(); | 460 | angles = new List<Angle>(); |
461 | normals = new List<Coord>(); | ||
462 | |||
434 | double twoPi = System.Math.PI * 2.0; | 463 | double twoPi = System.Math.PI * 2.0; |
435 | float twoPiInv = 1.0f / (float)twoPi; | 464 | float twoPiInv = 1.0f / (float)twoPi; |
436 | 465 | ||
@@ -459,7 +488,13 @@ namespace PrimMesher | |||
459 | endAngleIndex++; | 488 | endAngleIndex++; |
460 | 489 | ||
461 | for (int angleIndex = startAngleIndex; angleIndex < endAngleIndex + 1; angleIndex++) | 490 | for (int angleIndex = startAngleIndex; angleIndex < endAngleIndex + 1; angleIndex++) |
491 | { | ||
462 | angles.Add(sourceAngles[angleIndex]); | 492 | angles.Add(sourceAngles[angleIndex]); |
493 | if (sides == 3) | ||
494 | normals.Add(normals3[angleIndex]); | ||
495 | else if (sides == 4) | ||
496 | normals.Add(normals4[angleIndex]); | ||
497 | } | ||
463 | 498 | ||
464 | if (startAngle > 0.0f) | 499 | if (startAngle > 0.0f) |
465 | angles[0] = interpolatePoints(startAngle, angles[0], angles[1]); | 500 | angles[0] = interpolatePoints(startAngle, angles[0], angles[1]); |
@@ -639,8 +674,12 @@ namespace PrimMesher | |||
639 | hollowCoords.Add(newVert); | 674 | hollowCoords.Add(newVert); |
640 | if (this.calcVertexNormals) | 675 | if (this.calcVertexNormals) |
641 | { | 676 | { |
642 | hollowNormals.Add(new Coord(-angle.X, -angle.Y, 0.0f)); | 677 | if (sides < 5) |
643 | hollowUs.Add(angle.angle); | 678 | hollowNormals.Add(hollowAngles.normals[i].Invert()); |
679 | else | ||
680 | hollowNormals.Add(new Coord(-angle.X, -angle.Y, 0.0f)); | ||
681 | |||
682 | hollowUs.Add(angle.angle * hollow); | ||
644 | } | 683 | } |
645 | } | 684 | } |
646 | } | 685 | } |
@@ -650,6 +689,7 @@ namespace PrimMesher | |||
650 | 689 | ||
651 | for (int i = 0; i < numAngles; i++) | 690 | for (int i = 0; i < numAngles; i++) |
652 | { | 691 | { |
692 | int iNext = i == numAngles ? i + 1 : 0; | ||
653 | angle = angles.angles[i]; | 693 | angle = angles.angles[i]; |
654 | newVert.X = angle.X * xScale; | 694 | newVert.X = angle.X * xScale; |
655 | newVert.Y = angle.Y * yScale; | 695 | newVert.Y = angle.Y * yScale; |
@@ -657,14 +697,18 @@ namespace PrimMesher | |||
657 | this.coords.Add(newVert); | 697 | this.coords.Add(newVert); |
658 | if (this.calcVertexNormals) | 698 | if (this.calcVertexNormals) |
659 | { | 699 | { |
660 | this.vertexNormals.Add(new Coord(angle.X, angle.Y, 0.0f)); | 700 | |
661 | if (sides < 5) | 701 | if (sides < 5) |
662 | { | 702 | { |
703 | this.vertexNormals.Add(angles.normals[i]); | ||
663 | float u = angle.angle; | 704 | float u = angle.angle; |
664 | this.us.Add(u); | 705 | this.us.Add(u); |
665 | } | 706 | } |
666 | else | 707 | else |
708 | { | ||
709 | this.vertexNormals.Add(new Coord(angle.X, angle.Y, 0.0f)); | ||
667 | this.us.Add(angle.angle); | 710 | this.us.Add(angle.angle); |
711 | } | ||
668 | } | 712 | } |
669 | 713 | ||
670 | if (hollow > 0.0f) | 714 | if (hollow > 0.0f) |
@@ -677,8 +721,15 @@ namespace PrimMesher | |||
677 | hollowCoords.Add(newVert); | 721 | hollowCoords.Add(newVert); |
678 | if (this.calcVertexNormals) | 722 | if (this.calcVertexNormals) |
679 | { | 723 | { |
680 | hollowNormals.Add(new Coord(-angle.X, -angle.Y, 0.0f)); | 724 | if (sides < 5) |
681 | hollowUs.Add(angle.angle); | 725 | { |
726 | hollowNormals.Add(angles.normals[i].Invert()); | ||
727 | } | ||
728 | |||
729 | else | ||
730 | hollowNormals.Add(new Coord(-angle.X, -angle.Y, 0.0f)); | ||
731 | |||
732 | hollowUs.Add(angle.angle * hollow); | ||
682 | } | 733 | } |
683 | } | 734 | } |
684 | } | 735 | } |
@@ -805,20 +856,22 @@ namespace PrimMesher | |||
805 | { | 856 | { |
806 | if (hasHollow) | 857 | if (hasHollow) |
807 | { | 858 | { |
808 | this.cutNormal1.X = -this.vertexNormals[0].Y - this.vertexNormals[this.vertexNormals.Count - 1].Y; | ||
809 | this.cutNormal1.Y = this.vertexNormals[0].X - this.vertexNormals[this.vertexNormals.Count - 1].X; | ||
810 | |||
811 | int lastOuterVertIndex = this.numOuterVerts - 1; | 859 | int lastOuterVertIndex = this.numOuterVerts - 1; |
812 | this.cutNormal2.X = -this.vertexNormals[lastOuterVertIndex].Y - this.vertexNormals[lastOuterVertIndex + 1].Y; | 860 | |
813 | this.cutNormal2.Y = this.vertexNormals[lastOuterVertIndex].X - this.vertexNormals[lastOuterVertIndex + 1].X; | 861 | this.cutNormal1.X = this.coords[0].Y - this.coords[this.coords.Count - 1].Y; |
862 | this.cutNormal1.Y = -(this.coords[0].X - this.coords[this.coords.Count - 1].X); | ||
863 | |||
864 | this.cutNormal2.X = this.coords[lastOuterVertIndex + 1].Y - this.coords[lastOuterVertIndex].Y; | ||
865 | this.cutNormal2.Y = -(this.coords[lastOuterVertIndex + 1].X - this.coords[lastOuterVertIndex].X); | ||
814 | } | 866 | } |
867 | |||
815 | else | 868 | else |
816 | { | 869 | { |
817 | this.cutNormal1.X = this.vertexNormals[1].Y; | 870 | this.cutNormal1.X = this.vertexNormals[1].Y; |
818 | this.cutNormal1.Y = -this.vertexNormals[1].X; | 871 | this.cutNormal1.Y = -this.vertexNormals[1].X; |
819 | 872 | ||
820 | this.cutNormal2.X = -this.vertexNormals[this.vertexNormals.Count - 1].Y; | 873 | this.cutNormal2.X = -this.vertexNormals[this.vertexNormals.Count - 2].Y; |
821 | this.cutNormal2.Y = this.vertexNormals[this.vertexNormals.Count - 1].X; | 874 | this.cutNormal2.Y = this.vertexNormals[this.vertexNormals.Count - 2].X; |
822 | 875 | ||
823 | } | 876 | } |
824 | this.cutNormal1.Normalize(); | 877 | this.cutNormal1.Normalize(); |
@@ -837,7 +890,7 @@ namespace PrimMesher | |||
837 | { | 890 | { |
838 | this.faceUVs = new List<UVCoord>(); | 891 | this.faceUVs = new List<UVCoord>(); |
839 | foreach (Coord c in this.coords) | 892 | foreach (Coord c in this.coords) |
840 | this.faceUVs.Add(new UVCoord(0.5f + c.X, 0.5f - c.Y)); | 893 | this.faceUVs.Add(new UVCoord(1.0f - (0.5f + c.X), 1.0f - (0.5f - c.Y))); |
841 | } | 894 | } |
842 | 895 | ||
843 | public Profile Clone() | 896 | public Profile Clone() |
@@ -963,7 +1016,7 @@ namespace PrimMesher | |||
963 | for (i = 0; i < numfaceUVs; i++) | 1016 | for (i = 0; i < numfaceUVs; i++) |
964 | { | 1017 | { |
965 | UVCoord uv = this.faceUVs[i]; | 1018 | UVCoord uv = this.faceUVs[i]; |
966 | uv.U = 1.0f - uv.U; | 1019 | uv.V = 1.0f - uv.V; |
967 | this.faceUVs[i] = uv; | 1020 | this.faceUVs[i] = uv; |
968 | } | 1021 | } |
969 | } | 1022 | } |
@@ -1250,8 +1303,7 @@ namespace PrimMesher | |||
1250 | if (this.viewerMode) | 1303 | if (this.viewerMode) |
1251 | { | 1304 | { |
1252 | Coord faceNormal = newLayer.faceNormal; | 1305 | Coord faceNormal = newLayer.faceNormal; |
1253 | ViewerFace newViewerFace = new ViewerFace(); | 1306 | ViewerFace newViewerFace = new ViewerFace(0); |
1254 | newViewerFace.primFaceNumber = 0; | ||
1255 | foreach (Face face in newLayer.faces) | 1307 | foreach (Face face in newLayer.faces) |
1256 | { | 1308 | { |
1257 | newViewerFace.v1 = newLayer.coords[face.v1]; | 1309 | newViewerFace.v1 = newLayer.coords[face.v1]; |
@@ -1325,29 +1377,30 @@ namespace PrimMesher | |||
1325 | primFaceNum = 2; | 1377 | primFaceNum = 2; |
1326 | ViewerFace newViewerFace1 = new ViewerFace(primFaceNum); | 1378 | ViewerFace newViewerFace1 = new ViewerFace(primFaceNum); |
1327 | ViewerFace newViewerFace2 = new ViewerFace(primFaceNum); | 1379 | ViewerFace newViewerFace2 = new ViewerFace(primFaceNum); |
1380 | |||
1328 | float u1 = newLayer.us[whichVert]; | 1381 | float u1 = newLayer.us[whichVert]; |
1329 | float u2 = 1.0f; | 1382 | float u2 = 1.0f; |
1330 | if (whichVert < newLayer.us.Count - 1) | 1383 | if (whichVert < newLayer.us.Count - 1) |
1331 | u2 = newLayer.us[whichVert + 1]; | 1384 | u2 = newLayer.us[whichVert + 1]; |
1332 | int whichOuterVert = (hasProfileCut && hasHollow) ? whichVert - 1 : whichVert; | 1385 | |
1333 | if (sides < 5 && whichOuterVert < sides) | 1386 | if (whichVert == cut1Vert || whichVert == cut2Vert) |
1334 | { | 1387 | { |
1388 | u1 = 0.0f; | ||
1389 | u2 = 1.0f; | ||
1390 | } | ||
1391 | else if (sides < 5) | ||
1392 | { // boxes and prisms have one texture face per side of the prim, so the U values have to be scaled | ||
1393 | // to reflect the entire texture width | ||
1335 | u1 *= sides; | 1394 | u1 *= sides; |
1336 | u2 *= sides; | 1395 | u2 *= sides; |
1337 | u1 -= whichOuterVert; | 1396 | u2 -= (int)u1; |
1338 | u2 -= whichOuterVert; | 1397 | u1 -= (int)u1; |
1339 | if (u2 < 0.1f) | 1398 | if (u2 < 0.1f) |
1340 | u2 = 1.0f; | 1399 | u2 = 1.0f; |
1341 | 1400 | ||
1342 | newViewerFace2.primFaceNumber = newViewerFace1.primFaceNumber = whichVert + 1; | 1401 | newViewerFace2.primFaceNumber = newViewerFace1.primFaceNumber = whichVert + 1; |
1343 | } | 1402 | } |
1344 | 1403 | ||
1345 | if (whichVert == cut1Vert || whichVert == cut2Vert) | ||
1346 | { | ||
1347 | u1 = 0.0f; | ||
1348 | u2 = 1.0f; | ||
1349 | } | ||
1350 | |||
1351 | newViewerFace1.uv1.U = u1; | 1404 | newViewerFace1.uv1.U = u1; |
1352 | newViewerFace1.uv2.U = u1; | 1405 | newViewerFace1.uv2.U = u1; |
1353 | newViewerFace1.uv3.U = u2; | 1406 | newViewerFace1.uv3.U = u2; |
@@ -1375,27 +1428,24 @@ namespace PrimMesher | |||
1375 | // profile cut faces | 1428 | // profile cut faces |
1376 | if (whichVert == cut1Vert) | 1429 | if (whichVert == cut1Vert) |
1377 | { | 1430 | { |
1431 | newViewerFace1.n1 = newLayer.cutNormal1; | ||
1432 | newViewerFace1.n2 = newViewerFace1.n3 = lastCutNormal1; | ||
1378 | 1433 | ||
1379 | newViewerFace1.n2 = newViewerFace1.n1 = lastCutNormal1; | 1434 | newViewerFace2.n1 = newViewerFace2.n3 = newLayer.cutNormal1; |
1380 | newViewerFace1.n3 = newLayer.cutNormal1; | ||
1381 | |||
1382 | newViewerFace2.n3 = newViewerFace2.n1 = newLayer.cutNormal1; | ||
1383 | newViewerFace2.n2 = lastCutNormal1; | 1435 | newViewerFace2.n2 = lastCutNormal1; |
1384 | } | 1436 | } |
1385 | else if (whichVert == cut2Vert) | 1437 | else if (whichVert == cut2Vert) |
1386 | { | 1438 | { |
1439 | newViewerFace1.n1 = newLayer.cutNormal2; | ||
1440 | newViewerFace1.n2 = newViewerFace1.n3 = lastCutNormal2; | ||
1387 | 1441 | ||
1388 | newViewerFace1.n2 = newViewerFace1.n1 = lastCutNormal2; | 1442 | newViewerFace2.n1 = newViewerFace2.n3 = newLayer.cutNormal2; |
1389 | newViewerFace1.n3 = newLayer.cutNormal2; | ||
1390 | |||
1391 | newViewerFace2.n3 = newViewerFace2.n1 = newLayer.cutNormal2; | ||
1392 | newViewerFace2.n2 = lastCutNormal2; | 1443 | newViewerFace2.n2 = lastCutNormal2; |
1393 | } | 1444 | } |
1394 | 1445 | ||
1395 | else // outer and hollow faces | 1446 | else // outer and hollow faces |
1396 | { | 1447 | { |
1397 | //if ((sides < 5 && whichVert < newLayer.numOuterVerts) || (hollowSides < 5 && whichVert >= newLayer.numOuterVerts)) | 1448 | if ((sides < 5 && whichVert < newLayer.numOuterVerts) || (hollowSides < 5 && whichVert >= newLayer.numOuterVerts)) |
1398 | if (sides < 5) | ||
1399 | { | 1449 | { |
1400 | newViewerFace1.CalcSurfaceNormal(); | 1450 | newViewerFace1.CalcSurfaceNormal(); |
1401 | newViewerFace2.CalcSurfaceNormal(); | 1451 | newViewerFace2.CalcSurfaceNormal(); |
@@ -1727,6 +1777,18 @@ namespace PrimMesher | |||
1727 | u1 = 0.0f; | 1777 | u1 = 0.0f; |
1728 | u2 = 1.0f; | 1778 | u2 = 1.0f; |
1729 | } | 1779 | } |
1780 | else if (sides < 5) | ||
1781 | { // boxes and prisms have one texture face per side of the prim, so the U values have to be scaled | ||
1782 | // to reflect the entire texture width | ||
1783 | u1 *= sides; | ||
1784 | u2 *= sides; | ||
1785 | u2 -= (int)u1; | ||
1786 | u1 -= (int)u1; | ||
1787 | if (u2 < 0.1f) | ||
1788 | u2 = 1.0f; | ||
1789 | |||
1790 | newViewerFace2.primFaceNumber = newViewerFace1.primFaceNumber = whichVert + 1; | ||
1791 | } | ||
1730 | 1792 | ||
1731 | newViewerFace1.uv1.U = u1; | 1793 | newViewerFace1.uv1.U = u1; |
1732 | newViewerFace1.uv2.U = u1; | 1794 | newViewerFace1.uv2.U = u1; |
@@ -1752,32 +1814,44 @@ namespace PrimMesher | |||
1752 | newViewerFace2.v2 = this.coords[iNext - numVerts]; | 1814 | newViewerFace2.v2 = this.coords[iNext - numVerts]; |
1753 | newViewerFace2.v3 = this.coords[iNext]; | 1815 | newViewerFace2.v3 = this.coords[iNext]; |
1754 | 1816 | ||
1817 | // profile cut faces | ||
1755 | if (whichVert == cut1Vert) | 1818 | if (whichVert == cut1Vert) |
1756 | { | 1819 | { |
1820 | newViewerFace1.n1 = newLayer.cutNormal1; | ||
1821 | newViewerFace1.n2 = newViewerFace1.n3 = lastCutNormal1; | ||
1757 | 1822 | ||
1758 | newViewerFace1.n2 = newViewerFace1.n1 = lastCutNormal1; | 1823 | newViewerFace2.n1 = newViewerFace2.n3 = newLayer.cutNormal1; |
1759 | newViewerFace1.n3 = newLayer.cutNormal1; | ||
1760 | |||
1761 | newViewerFace2.n3 = newViewerFace2.n1 = newLayer.cutNormal1; | ||
1762 | newViewerFace2.n2 = lastCutNormal1; | 1824 | newViewerFace2.n2 = lastCutNormal1; |
1763 | } | 1825 | } |
1764 | else if (whichVert == cut2Vert) | 1826 | else if (whichVert == cut2Vert) |
1765 | { | 1827 | { |
1828 | newViewerFace1.n1 = newLayer.cutNormal2; | ||
1829 | newViewerFace1.n2 = newViewerFace1.n3 = lastCutNormal2; | ||
1766 | 1830 | ||
1767 | newViewerFace1.n2 = newViewerFace1.n1 = lastCutNormal2; | 1831 | newViewerFace2.n1 = newViewerFace2.n3 = newLayer.cutNormal2; |
1768 | newViewerFace1.n3 = newLayer.cutNormal2; | ||
1769 | |||
1770 | newViewerFace2.n3 = newViewerFace2.n1 = newLayer.cutNormal2; | ||
1771 | newViewerFace2.n2 = lastCutNormal2; | 1832 | newViewerFace2.n2 = lastCutNormal2; |
1772 | } | 1833 | } |
1773 | |||
1774 | else // periphery faces | 1834 | else // periphery faces |
1775 | { | 1835 | { |
1776 | //if ((sides < 5 && whichVert < newLayer.numOuterVerts) || (hollowSides < 5 && whichVert >= newLayer.numOuterVerts)) | 1836 | if (sides < 5 && whichVert < newLayer.numOuterVerts) |
1777 | if (sides < 5) | ||
1778 | { | 1837 | { |
1779 | newViewerFace1.CalcSurfaceNormal(); | 1838 | newViewerFace1.n1 = this.normals[i]; |
1780 | newViewerFace2.CalcSurfaceNormal(); | 1839 | newViewerFace1.n2 = this.normals[i - numVerts]; |
1840 | newViewerFace1.n3 = this.normals[i - numVerts]; | ||
1841 | |||
1842 | newViewerFace2.n1 = this.normals[i]; | ||
1843 | newViewerFace2.n2 = this.normals[i - numVerts]; | ||
1844 | newViewerFace2.n3 = this.normals[i]; | ||
1845 | } | ||
1846 | else if (hollowSides < 5 && whichVert >= newLayer.numOuterVerts) | ||
1847 | { | ||
1848 | newViewerFace1.n1 = this.normals[iNext]; | ||
1849 | newViewerFace1.n2 = this.normals[iNext - numVerts]; | ||
1850 | newViewerFace1.n3 = this.normals[iNext - numVerts]; | ||
1851 | |||
1852 | newViewerFace2.n1 = this.normals[iNext]; | ||
1853 | newViewerFace2.n2 = this.normals[iNext - numVerts]; | ||
1854 | newViewerFace2.n3 = this.normals[iNext]; | ||
1781 | } | 1855 | } |
1782 | else | 1856 | else |
1783 | { | 1857 | { |