diff options
author | Kitto Flora | 2010-12-29 21:13:49 +0000 |
---|---|---|
committer | Kitto Flora | 2010-12-29 21:13:49 +0000 |
commit | 64209c9be1cc8c88bdf8f5c9cb3c1ddebd8f23d2 (patch) | |
tree | 53a29d84be011e5f587aff3283a0ae55f3ebf50b | |
parent | Fixing AbsolutePosition to correct llSensor in vehicles (diff) | |
download | opensim-SC_OLD-64209c9be1cc8c88bdf8f5c9cb3c1ddebd8f23d2.zip opensim-SC_OLD-64209c9be1cc8c88bdf8f5c9cb3c1ddebd8f23d2.tar.gz opensim-SC_OLD-64209c9be1cc8c88bdf8f5c9cb3c1ddebd8f23d2.tar.bz2 opensim-SC_OLD-64209c9be1cc8c88bdf8f5c9cb3c1ddebd8f23d2.tar.xz |
Fix av/prim eject problem.
Diffstat (limited to '')
-rw-r--r-- | OpenSim/Region/Physics/ChOdePlugin/OdePlugin.cs | 105 |
1 files changed, 60 insertions, 45 deletions
diff --git a/OpenSim/Region/Physics/ChOdePlugin/OdePlugin.cs b/OpenSim/Region/Physics/ChOdePlugin/OdePlugin.cs index 756c005..e7455be 100644 --- a/OpenSim/Region/Physics/ChOdePlugin/OdePlugin.cs +++ b/OpenSim/Region/Physics/ChOdePlugin/OdePlugin.cs | |||
@@ -496,7 +496,7 @@ namespace OpenSim.Region.Physics.OdePlugin | |||
496 | contact.surface.mu = 255.0f; | 496 | contact.surface.mu = 255.0f; |
497 | contact.surface.bounce = 0.0f; | 497 | contact.surface.bounce = 0.0f; |
498 | contact.surface.soft_cfm = 0.0f; | 498 | contact.surface.soft_cfm = 0.0f; |
499 | contact.surface.soft_erp = 1.00f; // If this is too small static Av will fall through a sloping prim. | 499 | contact.surface.soft_erp = 0.30f; // If this is too small static Av will fall through a sloping prim. 1.0 prevents fall-thru |
500 | 500 | ||
501 | // Terrain contact friction and Bounce | 501 | // Terrain contact friction and Bounce |
502 | // This is the *non* moving version. Use this when an avatar | 502 | // This is the *non* moving version. Use this when an avatar |
@@ -523,9 +523,9 @@ namespace OpenSim.Region.Physics.OdePlugin | |||
523 | // Use this when an avatar comes in contact with a prim | 523 | // Use this when an avatar comes in contact with a prim |
524 | AvatarMovementprimContact.surface.mode = d.ContactFlags.SoftCFM | d.ContactFlags.SoftERP | d.ContactFlags.Bounce; | 524 | AvatarMovementprimContact.surface.mode = d.ContactFlags.SoftCFM | d.ContactFlags.SoftERP | d.ContactFlags.Bounce; |
525 | AvatarMovementprimContact.surface.mu = 255.0f; | 525 | AvatarMovementprimContact.surface.mu = 255.0f; |
526 | AvatarMovementprimContact.surface.bounce = 0.01f; | 526 | AvatarMovementprimContact.surface.bounce = 0.0f; |
527 | AvatarMovementprimContact.surface.soft_cfm = 0.0f; // if this is 0.01 then prims become phantom to Avs! | 527 | AvatarMovementprimContact.surface.soft_cfm = 0.0f; // if this is 0.01 then prims become phantom to Avs! |
528 | AvatarMovementprimContact.surface.soft_erp = 0.001f; | 528 | AvatarMovementprimContact.surface.soft_erp = 0.3f; |
529 | 529 | ||
530 | // Terrain contact friction bounce and various error correcting calculations | 530 | // Terrain contact friction bounce and various error correcting calculations |
531 | // Use this when an avatar is in contact with the terrain and moving. | 531 | // Use this when an avatar is in contact with the terrain and moving. |
@@ -559,11 +559,11 @@ namespace OpenSim.Region.Physics.OdePlugin | |||
559 | */ | 559 | */ |
560 | 560 | ||
561 | m_materialContacts = new d.Contact[7,2]; | 561 | m_materialContacts = new d.Contact[7,2]; |
562 | 562 | // V 1 = Sliding; 0 = static or fell onto | |
563 | m_materialContacts[(int)Material.Stone, 0] = new d.Contact(); | 563 | m_materialContacts[(int)Material.Stone, 0] = new d.Contact(); |
564 | m_materialContacts[(int)Material.Stone, 0].surface.mode = d.ContactFlags.SoftCFM | d.ContactFlags.SoftERP | d.ContactFlags.Bounce; | 564 | m_materialContacts[(int)Material.Stone, 0].surface.mode = d.ContactFlags.SoftCFM | d.ContactFlags.SoftERP | d.ContactFlags.Bounce; |
565 | m_materialContacts[(int)Material.Stone, 0].surface.mu = 1.8f; | 565 | m_materialContacts[(int)Material.Stone, 0].surface.mu = 1.8f; // friction, 1 = slippery, 255 = no slip |
566 | m_materialContacts[(int)Material.Stone, 0].surface.bounce = 0.0f; | 566 | m_materialContacts[(int)Material.Stone, 0].surface.bounce = 0.0f; |
567 | m_materialContacts[(int)Material.Stone, 0].surface.soft_cfm = 0.0f; | 567 | m_materialContacts[(int)Material.Stone, 0].surface.soft_cfm = 0.0f; |
568 | m_materialContacts[(int)Material.Stone, 0].surface.soft_erp = 0.50f; | 568 | m_materialContacts[(int)Material.Stone, 0].surface.soft_erp = 0.50f; |
569 | 569 | ||
@@ -588,27 +588,19 @@ namespace OpenSim.Region.Physics.OdePlugin | |||
588 | m_materialContacts[(int)Material.Metal, 1].surface.soft_cfm = 0.0f; | 588 | m_materialContacts[(int)Material.Metal, 1].surface.soft_cfm = 0.0f; |
589 | m_materialContacts[(int)Material.Metal, 1].surface.soft_erp = 0.50f; | 589 | m_materialContacts[(int)Material.Metal, 1].surface.soft_erp = 0.50f; |
590 | 590 | ||
591 | /* | ||
592 | flags : d.ContactFlags.SoftCFM | d.ContactFlags.SoftERP | d.ContactFlags.Bounce | ||
593 | private float nmAvatarObjectContactFriction = 250f; | ||
594 | private float nmAvatarObjectContactBounce = 0.1f; | ||
595 | |||
596 | private float mAvatarObjectContactFriction = 75f; | ||
597 | private float mAvatarObjectContactBounce = 0.1f; | ||
598 | */ | ||
599 | m_materialContacts[(int)Material.Glass, 0] = new d.Contact(); | 591 | m_materialContacts[(int)Material.Glass, 0] = new d.Contact(); |
600 | m_materialContacts[(int)Material.Glass, 0].surface.mode = d.ContactFlags.SoftCFM | d.ContactFlags.SoftERP | d.ContactFlags.Bounce; | 592 | m_materialContacts[(int)Material.Glass, 0].surface.mode = d.ContactFlags.SoftCFM | d.ContactFlags.SoftERP | d.ContactFlags.Bounce; |
601 | m_materialContacts[(int)Material.Glass, 0].surface.mu = 1f; | 593 | m_materialContacts[(int)Material.Glass, 0].surface.mu = 1f; |
602 | m_materialContacts[(int)Material.Glass, 0].surface.bounce = 0.1f; | 594 | m_materialContacts[(int)Material.Glass, 0].surface.bounce = 0.0f; |
603 | m_materialContacts[(int)Material.Glass, 0].surface.soft_cfm = 0.0f; | 595 | m_materialContacts[(int)Material.Glass, 0].surface.soft_cfm = 0.01f; |
604 | m_materialContacts[(int)Material.Glass, 0].surface.soft_erp = 0.50f; | 596 | m_materialContacts[(int)Material.Glass, 0].surface.soft_erp = 0.50f; |
605 | 597 | ||
606 | m_materialContacts[(int)Material.Glass, 1] = new d.Contact(); | 598 | m_materialContacts[(int)Material.Glass, 1] = new d.Contact(); |
607 | m_materialContacts[(int)Material.Glass, 1].surface.mode = d.ContactFlags.SoftCFM | d.ContactFlags.SoftERP | d.ContactFlags.Bounce; | 599 | m_materialContacts[(int)Material.Glass, 1].surface.mode = d.ContactFlags.SoftCFM | d.ContactFlags.SoftERP | d.ContactFlags.Bounce; |
608 | m_materialContacts[(int)Material.Glass, 1].surface.mu = 1f; | 600 | m_materialContacts[(int)Material.Glass, 1].surface.mu = 1f; |
609 | m_materialContacts[(int)Material.Glass, 1].surface.bounce = 0.1f; | 601 | m_materialContacts[(int)Material.Glass, 1].surface.bounce = 0.0f; |
610 | m_materialContacts[(int)Material.Glass, 1].surface.soft_cfm = 0.0f; | 602 | m_materialContacts[(int)Material.Glass, 1].surface.soft_cfm = 0.0f; |
611 | m_materialContacts[(int)Material.Glass, 1].surface.soft_erp = 0.50f; | 603 | m_materialContacts[(int)Material.Glass, 1].surface.soft_erp = 0.30f; |
612 | 604 | ||
613 | m_materialContacts[(int)Material.Wood, 0] = new d.Contact(); | 605 | m_materialContacts[(int)Material.Wood, 0] = new d.Contact(); |
614 | m_materialContacts[(int)Material.Wood, 0].surface.mode = d.ContactFlags.SoftCFM | d.ContactFlags.SoftERP | d.ContactFlags.Bounce; | 606 | m_materialContacts[(int)Material.Wood, 0].surface.mode = d.ContactFlags.SoftCFM | d.ContactFlags.SoftERP | d.ContactFlags.Bounce; |
@@ -827,6 +819,11 @@ namespace OpenSim.Region.Physics.OdePlugin | |||
827 | p2 = PANull; | 819 | p2 = PANull; |
828 | } | 820 | } |
829 | 821 | ||
822 | if((p1 is OdePrim ) && (p2 is OdePrim)){ | ||
823 | OdePrim t1 = (OdePrim)p1; | ||
824 | OdePrim t2 = (OdePrim)p2; | ||
825 | Console.WriteLine("Collision {0} {1}", t1.m_primName, t2.m_primName); | ||
826 | } | ||
830 | ContactPoint maxDepthContact = new ContactPoint(); | 827 | ContactPoint maxDepthContact = new ContactPoint(); |
831 | if (p1.CollisionScore + count >= float.MaxValue) | 828 | if (p1.CollisionScore + count >= float.MaxValue) |
832 | p1.CollisionScore = 0; | 829 | p1.CollisionScore = 0; |
@@ -835,7 +832,6 @@ namespace OpenSim.Region.Physics.OdePlugin | |||
835 | if (p2.CollisionScore + count >= float.MaxValue) | 832 | if (p2.CollisionScore + count >= float.MaxValue) |
836 | p2.CollisionScore = 0; | 833 | p2.CollisionScore = 0; |
837 | p2.CollisionScore += count; | 834 | p2.CollisionScore += count; |
838 | |||
839 | for (int i = 0; i < count; i++) | 835 | for (int i = 0; i < count; i++) |
840 | { | 836 | { |
841 | d.ContactGeom curContact = contacts[i]; | 837 | d.ContactGeom curContact = contacts[i]; |
@@ -863,18 +859,13 @@ namespace OpenSim.Region.Physics.OdePlugin | |||
863 | //#@ if ((p2.Position.Z - curContact.pos.Z) > (p2.Size.Z * 0.6f)) | 859 | //#@ if ((p2.Position.Z - curContact.pos.Z) > (p2.Size.Z * 0.6f)) |
864 | //#@ p2.IsColliding = true; | 860 | //#@ p2.IsColliding = true; |
865 | if ((p2.Position.Z - curContact.pos.Z) > (p2.Size.Z * 0.6f)){ //## | 861 | if ((p2.Position.Z - curContact.pos.Z) > (p2.Size.Z * 0.6f)){ //## |
866 | //Console.WriteLine("AvColl 1 {0} - {1} - {2} - {3}", //## | ||
867 | // curContact.pos.Z, p2.Position.Z, (p2.Position.Z - curContact.pos.Z), (p2.Size.Z * 0.6f)); //## | ||
868 | p2.IsColliding = true; //## | 862 | p2.IsColliding = true; //## |
869 | }else{ | 863 | }else{ |
870 | //Console.WriteLine("AvColl 2 {0} - {1} - {2} - {3}", //## | ||
871 | // curContact.pos.Z, p2.Position.Z, (p2.Position.Z - curContact.pos.Z), (p2.Size.Z * 0.6f)); //## | ||
872 | 864 | ||
873 | } //## | 865 | } //## |
874 | } | 866 | } |
875 | else | 867 | else |
876 | { | 868 | { |
877 | //Console.WriteLine("AvColl 3 {0}", p2.PhysicsActorType); //## | ||
878 | p2.IsColliding = true; | 869 | p2.IsColliding = true; |
879 | } | 870 | } |
880 | 871 | ||
@@ -1075,6 +1066,10 @@ namespace OpenSim.Region.Physics.OdePlugin | |||
1075 | 1066 | ||
1076 | if (!skipThisContact) | 1067 | if (!skipThisContact) |
1077 | { | 1068 | { |
1069 | // Add contact joints with materials params---------------------------------- | ||
1070 | int material = (int) Material.Wood; | ||
1071 | int movintYN = 0; // 1 = Sliding; 0 = static or fell onto | ||
1072 | |||
1078 | // If we're colliding against terrain | 1073 | // If we're colliding against terrain |
1079 | if (name1 == "Terrain" || name2 == "Terrain") | 1074 | if (name1 == "Terrain" || name2 == "Terrain") |
1080 | { | 1075 | { |
@@ -1082,7 +1077,7 @@ namespace OpenSim.Region.Physics.OdePlugin | |||
1082 | if ((p2.PhysicsActorType == (int) ActorTypes.Agent) && | 1077 | if ((p2.PhysicsActorType == (int) ActorTypes.Agent) && |
1083 | (Math.Abs(p2.Velocity.X) > 0.01f || Math.Abs(p2.Velocity.Y) > 0.01f)) | 1078 | (Math.Abs(p2.Velocity.X) > 0.01f || Math.Abs(p2.Velocity.Y) > 0.01f)) |
1084 | { | 1079 | { |
1085 | // Use the movement terrain contact | 1080 | //$ Av walk/run on terrain (not falling) Use the Avatar movement terrain contact |
1086 | AvatarMovementTerrainContact.geom = curContact; | 1081 | AvatarMovementTerrainContact.geom = curContact; |
1087 | _perloopContact.Add(curContact); | 1082 | _perloopContact.Add(curContact); |
1088 | if (m_global_contactcount < maxContactsbeforedeath) | 1083 | if (m_global_contactcount < maxContactsbeforedeath) |
@@ -1095,7 +1090,7 @@ namespace OpenSim.Region.Physics.OdePlugin | |||
1095 | { | 1090 | { |
1096 | if (p2.PhysicsActorType == (int)ActorTypes.Agent) | 1091 | if (p2.PhysicsActorType == (int)ActorTypes.Agent) |
1097 | { | 1092 | { |
1098 | // Use the non moving terrain contact | 1093 | //$ Av standing on terrain, Use the non moving Avata terrain contact |
1099 | TerrainContact.geom = curContact; | 1094 | TerrainContact.geom = curContact; |
1100 | _perloopContact.Add(curContact); | 1095 | _perloopContact.Add(curContact); |
1101 | if (m_global_contactcount < maxContactsbeforedeath) | 1096 | if (m_global_contactcount < maxContactsbeforedeath) |
@@ -1108,10 +1103,8 @@ namespace OpenSim.Region.Physics.OdePlugin | |||
1108 | { | 1103 | { |
1109 | if (p2.PhysicsActorType == (int)ActorTypes.Prim && p1.PhysicsActorType == (int)ActorTypes.Prim) | 1104 | if (p2.PhysicsActorType == (int)ActorTypes.Prim && p1.PhysicsActorType == (int)ActorTypes.Prim) |
1110 | { | 1105 | { |
1111 | // prim prim contact | 1106 | //& THIS NEVER HAPPENS prim prim contact //kf Huh? In terrain contact? |
1112 | // int pj294950 = 0; | 1107 | // int pj294950 = 0; |
1113 | int movintYN = 0; | ||
1114 | int material = (int) Material.Wood; | ||
1115 | // prim terrain contact | 1108 | // prim terrain contact |
1116 | if (Math.Abs(p2.Velocity.X) > 0.01f || Math.Abs(p2.Velocity.Y) > 0.01f) | 1109 | if (Math.Abs(p2.Velocity.X) > 0.01f || Math.Abs(p2.Velocity.Y) > 0.01f) |
1117 | { | 1110 | { |
@@ -1120,7 +1113,6 @@ namespace OpenSim.Region.Physics.OdePlugin | |||
1120 | 1113 | ||
1121 | if (p2 is OdePrim) | 1114 | if (p2 is OdePrim) |
1122 | material = ((OdePrim)p2).m_material; | 1115 | material = ((OdePrim)p2).m_material; |
1123 | |||
1124 | //m_log.DebugFormat("Material: {0}", material); | 1116 | //m_log.DebugFormat("Material: {0}", material); |
1125 | m_materialContacts[material, movintYN].geom = curContact; | 1117 | m_materialContacts[material, movintYN].geom = curContact; |
1126 | _perloopContact.Add(curContact); | 1118 | _perloopContact.Add(curContact); |
@@ -1135,16 +1127,12 @@ namespace OpenSim.Region.Physics.OdePlugin | |||
1135 | } | 1127 | } |
1136 | else | 1128 | else |
1137 | { | 1129 | { |
1138 | 1130 | //$ prim on terrain contact | |
1139 | int movintYN = 0; | ||
1140 | // prim terrain contact | ||
1141 | if (Math.Abs(p2.Velocity.X) > 0.01f || Math.Abs(p2.Velocity.Y) > 0.01f) | 1131 | if (Math.Abs(p2.Velocity.X) > 0.01f || Math.Abs(p2.Velocity.Y) > 0.01f) |
1142 | { | 1132 | { |
1143 | movintYN = 1; | 1133 | movintYN = 1; |
1144 | } | 1134 | } |
1145 | 1135 | ||
1146 | int material = (int)Material.Wood; | ||
1147 | |||
1148 | if (p2 is OdePrim) | 1136 | if (p2 is OdePrim) |
1149 | material = ((OdePrim)p2).m_material; | 1137 | material = ((OdePrim)p2).m_material; |
1150 | //m_log.DebugFormat("Material: {0}", material); | 1138 | //m_log.DebugFormat("Material: {0}", material); |
@@ -1167,6 +1155,7 @@ namespace OpenSim.Region.Physics.OdePlugin | |||
1167 | } | 1155 | } |
1168 | else if (name1 == "Water" || name2 == "Water") | 1156 | else if (name1 == "Water" || name2 == "Water") |
1169 | { | 1157 | { |
1158 | //$ This never happens! | ||
1170 | /* | 1159 | /* |
1171 | if ((p2.PhysicsActorType == (int) ActorTypes.Prim)) | 1160 | if ((p2.PhysicsActorType == (int) ActorTypes.Prim)) |
1172 | { | 1161 | { |
@@ -1194,10 +1183,12 @@ namespace OpenSim.Region.Physics.OdePlugin | |||
1194 | } | 1183 | } |
1195 | else | 1184 | else |
1196 | { | 1185 | { |
1197 | // we're colliding with prim or avatar | 1186 | |
1187 | // no terrain and no water, we're colliding with prim or avatar | ||
1198 | // check if we're moving | 1188 | // check if we're moving |
1199 | if ((p2.PhysicsActorType == (int)ActorTypes.Agent)) | 1189 | if ((p2.PhysicsActorType == (int)ActorTypes.Agent)) |
1200 | { | 1190 | { |
1191 | //$ Avatar on Prim or other Avatar | ||
1201 | if ((Math.Abs(p2.Velocity.X) > 0.01f || Math.Abs(p2.Velocity.Y) > 0.01f)) | 1192 | if ((Math.Abs(p2.Velocity.X) > 0.01f || Math.Abs(p2.Velocity.Y) > 0.01f)) |
1202 | { | 1193 | { |
1203 | // Use the AV Movement / prim contact | 1194 | // Use the AV Movement / prim contact |
@@ -1217,28 +1208,52 @@ namespace OpenSim.Region.Physics.OdePlugin | |||
1217 | 1208 | ||
1218 | if (m_global_contactcount < maxContactsbeforedeath) | 1209 | if (m_global_contactcount < maxContactsbeforedeath) |
1219 | { | 1210 | { |
1211 | if (curContact.depth > 0.2) | ||
1212 | { // embedded, eject slowly | ||
1213 | contact.surface.soft_erp = 0.1f; | ||
1214 | contact.surface.soft_cfm = 0.1f; | ||
1215 | } | ||
1216 | else | ||
1217 | { // keep on the surface | ||
1218 | contact.surface.soft_erp = 0.3f; | ||
1219 | contact.surface.soft_cfm = 0.0f; | ||
1220 | } | ||
1220 | joint = d.JointCreateContact(world, contactgroup, ref contact); | 1221 | joint = d.JointCreateContact(world, contactgroup, ref contact); |
1221 | m_global_contactcount++; | 1222 | m_global_contactcount++; |
1223 | /* | ||
1224 | Console.WriteLine("Prim | Av collision 2 mode={0} mu={1} bounce={2} bv={3} erp={4} cfm={5} mot={6} depth={7}", | ||
1225 | contact.surface.mode, | ||
1226 | contact.surface.mu, | ||
1227 | contact.surface.bounce, | ||
1228 | contact.surface.bounce_vel, | ||
1229 | contact.surface.soft_erp, | ||
1230 | contact.surface.soft_cfm, | ||
1231 | contact.surface.motion1, | ||
1232 | curContact.depth); | ||
1233 | */ | ||
1222 | } | 1234 | } |
1223 | } | 1235 | } |
1224 | } | 1236 | } |
1225 | else if (p2.PhysicsActorType == (int)ActorTypes.Prim) | 1237 | else if (p2.PhysicsActorType == (int)ActorTypes.Prim) |
1226 | { | 1238 | { |
1239 | //$ Prim on Prim | ||
1227 | //p1.PhysicsActorType | 1240 | //p1.PhysicsActorType |
1228 | int material = (int)Material.Wood; | ||
1229 | 1241 | ||
1230 | if (p2 is OdePrim) | 1242 | if (p2 is OdePrim) material = ((OdePrim)p2).m_material; |
1231 | material = ((OdePrim)p2).m_material; | ||
1232 | |||
1233 | //m_log.DebugFormat("Material: {0}", material); | 1243 | //m_log.DebugFormat("Material: {0}", material); |
1234 | m_materialContacts[material, 0].geom = curContact; | 1244 | |
1245 | if (Math.Abs(p2.Velocity.X) > 0.01f || Math.Abs(p2.Velocity.Y) > 0.01f) | ||
1246 | { | ||
1247 | movintYN = 1; | ||
1248 | } | ||
1249 | |||
1250 | m_materialContacts[material, movintYN].geom = curContact; | ||
1235 | _perloopContact.Add(curContact); | 1251 | _perloopContact.Add(curContact); |
1236 | 1252 | ||
1237 | if (m_global_contactcount < maxContactsbeforedeath) | 1253 | if (m_global_contactcount < maxContactsbeforedeath) |
1238 | { | 1254 | { |
1239 | joint = d.JointCreateContact(world, contactgroup, ref m_materialContacts[material, 0]); | 1255 | joint = d.JointCreateContact(world, contactgroup, ref m_materialContacts[material, movintYN]); |
1240 | m_global_contactcount++; | 1256 | m_global_contactcount++; |
1241 | |||
1242 | } | 1257 | } |
1243 | } | 1258 | } |
1244 | } | 1259 | } |
@@ -1261,8 +1276,8 @@ namespace OpenSim.Region.Physics.OdePlugin | |||
1261 | } | 1276 | } |
1262 | //m_log.Debug(count.ToString()); | 1277 | //m_log.Debug(count.ToString()); |
1263 | //m_log.Debug("near: A collision was detected between {1} and {2}", 0, name1, name2); | 1278 | //m_log.Debug("near: A collision was detected between {1} and {2}", 0, name1, name2); |
1264 | } | 1279 | } // end for i.. loop |
1265 | } | 1280 | } // end near |
1266 | 1281 | ||
1267 | private bool checkDupe(d.ContactGeom contactGeom, int atype) | 1282 | private bool checkDupe(d.ContactGeom contactGeom, int atype) |
1268 | { | 1283 | { |