aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/Framework/Scenes/ScenePresence.cs
diff options
context:
space:
mode:
authorMelanie2012-12-18 09:48:12 +0000
committerMelanie2012-12-18 09:48:12 +0000
commit79bdf464d3f123f8e195fa57c497e546b7d9dfcc (patch)
treebf6123b15d1ced7ec72ce871e4801d6645bb23f4 /OpenSim/Region/Framework/Scenes/ScenePresence.cs
parentMerge branch 'master' into careminster (diff)
parentFix locking for good (diff)
downloadopensim-SC_OLD-79bdf464d3f123f8e195fa57c497e546b7d9dfcc.zip
opensim-SC_OLD-79bdf464d3f123f8e195fa57c497e546b7d9dfcc.tar.gz
opensim-SC_OLD-79bdf464d3f123f8e195fa57c497e546b7d9dfcc.tar.bz2
opensim-SC_OLD-79bdf464d3f123f8e195fa57c497e546b7d9dfcc.tar.xz
Merge branch 'avination' into careminster
Diffstat (limited to 'OpenSim/Region/Framework/Scenes/ScenePresence.cs')
-rw-r--r--OpenSim/Region/Framework/Scenes/ScenePresence.cs303
1 files changed, 120 insertions, 183 deletions
diff --git a/OpenSim/Region/Framework/Scenes/ScenePresence.cs b/OpenSim/Region/Framework/Scenes/ScenePresence.cs
index 1250a11..041eac2 100644
--- a/OpenSim/Region/Framework/Scenes/ScenePresence.cs
+++ b/OpenSim/Region/Framework/Scenes/ScenePresence.cs
@@ -140,6 +140,8 @@ namespace OpenSim.Region.Framework.Scenes
140 private Vector3 m_lastPosition; 140 private Vector3 m_lastPosition;
141 private Quaternion m_lastRotation; 141 private Quaternion m_lastRotation;
142 private Vector3 m_lastVelocity; 142 private Vector3 m_lastVelocity;
143 private Vector3 m_lastSize = new Vector3(0.45f,0.6f,1.9f);
144
143 145
144 private Vector3? m_forceToApply; 146 private Vector3? m_forceToApply;
145 private int m_userFlags; 147 private int m_userFlags;
@@ -560,7 +562,24 @@ namespace OpenSim.Region.Framework.Scenes
560// Scene.RegionInfo.RegionName, Name, m_velocity); 562// Scene.RegionInfo.RegionName, Name, m_velocity);
561 } 563 }
562 } 564 }
565/*
566 public override Vector3 AngularVelocity
567 {
568 get
569 {
570 if (PhysicsActor != null)
571 {
572 m_rotationalvelocity = PhysicsActor.RotationalVelocity;
563 573
574 // m_log.DebugFormat(
575 // "[SCENE PRESENCE]: Set velocity {0} for {1} in {2} via getting Velocity!",
576 // m_velocity, Name, Scene.RegionInfo.RegionName);
577 }
578
579 return m_rotationalvelocity;
580 }
581 }
582*/
564 private Quaternion m_bodyRot = Quaternion.Identity; 583 private Quaternion m_bodyRot = Quaternion.Identity;
565 584
566 public Quaternion Rotation 585 public Quaternion Rotation
@@ -569,7 +588,18 @@ namespace OpenSim.Region.Framework.Scenes
569 set 588 set
570 { 589 {
571 m_bodyRot = value; 590 m_bodyRot = value;
572// m_log.DebugFormat("[SCENE PRESENCE]: Body rot for {0} set to {1}", Name, m_bodyRot); 591 // m_log.DebugFormat("[SCENE PRESENCE]: Body rot for {0} set to {1}", Name, m_bodyRot);
592 if (PhysicsActor != null)
593 {
594 try
595 {
596 PhysicsActor.Orientation = value;
597 }
598 catch (Exception e)
599 {
600 m_log.Error("[SCENE PRESENCE]: Orientation " + e.Message);
601 }
602 }
573 } 603 }
574 } 604 }
575 605
@@ -1247,6 +1277,13 @@ namespace OpenSim.Region.Framework.Scenes
1247 PhysicsActor.Size = new Vector3(0.45f, 0.6f, height); 1277 PhysicsActor.Size = new Vector3(0.45f, 0.6f, height);
1248 } 1278 }
1249 1279
1280 public void SetSize(Vector3 size, float feetoffset)
1281 {
1282 if (PhysicsActor != null && !IsChildAgent)
1283 PhysicsActor.setAvatarSize(size, feetoffset);
1284
1285 }
1286
1250 /// <summary> 1287 /// <summary>
1251 /// Complete Avatar's movement into the region. 1288 /// Complete Avatar's movement into the region.
1252 /// </summary> 1289 /// </summary>
@@ -2064,12 +2101,29 @@ namespace OpenSim.Region.Framework.Scenes
2064 } 2101 }
2065 else 2102 else
2066 { 2103 {
2067 if (Util.GetDistanceTo(AbsolutePosition, pos) <= 10) 2104// if (Util.GetDistanceTo(AbsolutePosition, pos) <= 10)
2068 { 2105// {
2069// m_log.DebugFormat( 2106// m_log.DebugFormat(
2070// "[SCENE PRESENCE]: Sitting {0} on {1} {2} because sit target is unset and within 10m", 2107// "[SCENE PRESENCE]: Sitting {0} on {1} {2} because sit target is unset and within 10m",
2071// Name, part.Name, part.LocalId); 2108// Name, part.Name, part.LocalId);
2072 2109
2110 if (m_scene.PhysicsScene != null &&
2111 part.PhysActor != null &&
2112 Util.GetDistanceTo(AbsolutePosition, pos) <= 30)
2113 {
2114
2115 Vector3 camdif = CameraPosition - part.AbsolutePosition;
2116 camdif.Normalize();
2117
2118// m_log.InfoFormat("sit {0} {1}", offset.ToString(), camdif.ToString());
2119
2120 if (m_scene.PhysicsScene.SitAvatar(part.PhysActor, AbsolutePosition, CameraPosition, offset, new Vector3(0.35f, 0, 0.65f), PhysicsSitResponse) != 0)
2121 return;
2122 }
2123
2124 if (Util.GetDistanceTo(AbsolutePosition, pos) <= 10)
2125 {
2126
2073 AbsolutePosition = pos + new Vector3(0.0f, 0.0f, m_sitAvatarHeight); 2127 AbsolutePosition = pos + new Vector3(0.0f, 0.0f, m_sitAvatarHeight);
2074 canSit = true; 2128 canSit = true;
2075 } 2129 }
@@ -2142,197 +2196,54 @@ namespace OpenSim.Region.Framework.Scenes
2142 SendSitResponse(targetID, offset, Quaternion.Identity); 2196 SendSitResponse(targetID, offset, Quaternion.Identity);
2143 } 2197 }
2144 2198
2145 /* 2199 public void PhysicsSitResponse(int status, uint partID, Vector3 offset, Quaternion Orientation)
2146 public void SitRayCastAvatarPosition(SceneObjectPart part)
2147 { 2200 {
2148 Vector3 EndRayCastPosition = part.AbsolutePosition + m_requestedSitOffset;
2149 Vector3 StartRayCastPosition = AbsolutePosition;
2150 Vector3 direction = Vector3.Normalize(EndRayCastPosition - StartRayCastPosition);
2151 float distance = Vector3.Distance(EndRayCastPosition, StartRayCastPosition);
2152 m_scene.PhysicsScene.RaycastWorld(StartRayCastPosition, direction, distance, SitRayCastAvatarPositionResponse);
2153 }
2154 2201
2155 public void SitRayCastAvatarPositionResponse(bool hitYN, Vector3 collisionPoint, uint localid, float pdistance, Vector3 normal) 2202 if (status < 0)
2156 {
2157 SceneObjectPart part = FindNextAvailableSitTarget(m_requestedSitTargetUUID);
2158 if (part != null)
2159 {
2160 if (hitYN)
2161 {
2162 if (collisionPoint.ApproxEquals(m_requestedSitOffset + part.AbsolutePosition, 0.2f))
2163 {
2164 SitRaycastFindEdge(collisionPoint, normal);
2165 m_log.DebugFormat("[SIT]: Raycast Avatar Position succeeded at point: {0}, normal:{1}", collisionPoint, normal);
2166 }
2167 else
2168 {
2169 SitRayCastAvatarPositionCameraZ(part);
2170 }
2171 }
2172 else
2173 {
2174 SitRayCastAvatarPositionCameraZ(part);
2175 }
2176 }
2177 else
2178 { 2203 {
2179 ControllingClient.SendAlertMessage("Sit position no longer exists"); 2204 ControllingClient.SendAlertMessage("Sit position no longer exists");
2180 m_requestedSitTargetUUID = UUID.Zero; 2205 return;
2181 m_requestedSitTargetID = 0;
2182 m_requestedSitOffset = Vector3.Zero;
2183 } 2206 }
2184 2207
2185 } 2208 if (status == 0)
2186 2209 return;
2187 public void SitRayCastAvatarPositionCameraZ(SceneObjectPart part)
2188 {
2189 // Next, try to raycast from the camera Z position
2190 Vector3 EndRayCastPosition = part.AbsolutePosition + m_requestedSitOffset;
2191 Vector3 StartRayCastPosition = AbsolutePosition; StartRayCastPosition.Z = CameraPosition.Z;
2192 Vector3 direction = Vector3.Normalize(EndRayCastPosition - StartRayCastPosition);
2193 float distance = Vector3.Distance(EndRayCastPosition, StartRayCastPosition);
2194 m_scene.PhysicsScene.RaycastWorld(StartRayCastPosition, direction, distance, SitRayCastAvatarPositionCameraZResponse);
2195 }
2196 2210
2197 public void SitRayCastAvatarPositionCameraZResponse(bool hitYN, Vector3 collisionPoint, uint localid, float pdistance, Vector3 normal) 2211 SceneObjectPart part = m_scene.GetSceneObjectPart(partID);
2198 { 2212 if (part == null || part.ParentGroup.IsAttachment)
2199 SceneObjectPart part = FindNextAvailableSitTarget(m_requestedSitTargetUUID);
2200 if (part != null)
2201 {
2202 if (hitYN)
2203 {
2204 if (collisionPoint.ApproxEquals(m_requestedSitOffset + part.AbsolutePosition, 0.2f))
2205 {
2206 SitRaycastFindEdge(collisionPoint, normal);
2207 m_log.DebugFormat("[SIT]: Raycast Avatar Position + CameraZ succeeded at point: {0}, normal:{1}", collisionPoint, normal);
2208 }
2209 else
2210 {
2211 SitRayCastCameraPosition(part);
2212 }
2213 }
2214 else
2215 {
2216 SitRayCastCameraPosition(part);
2217 }
2218 }
2219 else
2220 { 2213 {
2221 ControllingClient.SendAlertMessage("Sit position no longer exists"); 2214 return;
2222 m_requestedSitTargetUUID = UUID.Zero;
2223 m_requestedSitTargetID = 0;
2224 m_requestedSitOffset = Vector3.Zero;
2225 } 2215 }
2226 2216
2227 } 2217// m_log.InfoFormat("physsit {0} {1}", offset.ToString(),Orientation.ToString());
2228 2218
2229 public void SitRayCastCameraPosition(SceneObjectPart part) 2219 part.AddSittingAvatar(UUID);
2230 {
2231 // Next, try to raycast from the camera position
2232 Vector3 EndRayCastPosition = part.AbsolutePosition + m_requestedSitOffset;
2233 Vector3 StartRayCastPosition = CameraPosition;
2234 Vector3 direction = Vector3.Normalize(EndRayCastPosition - StartRayCastPosition);
2235 float distance = Vector3.Distance(EndRayCastPosition, StartRayCastPosition);
2236 m_scene.PhysicsScene.RaycastWorld(StartRayCastPosition, direction, distance, SitRayCastCameraPositionResponse);
2237 }
2238 2220
2239 public void SitRayCastCameraPositionResponse(bool hitYN, Vector3 collisionPoint, uint localid, float pdistance, Vector3 normal) 2221 Vector3 cameraAtOffset = part.GetCameraAtOffset();
2240 { 2222 Vector3 cameraEyeOffset = part.GetCameraEyeOffset();
2241 SceneObjectPart part = FindNextAvailableSitTarget(m_requestedSitTargetUUID); 2223 bool forceMouselook = part.GetForceMouselook();
2242 if (part != null)
2243 {
2244 if (hitYN)
2245 {
2246 if (collisionPoint.ApproxEquals(m_requestedSitOffset + part.AbsolutePosition, 0.2f))
2247 {
2248 SitRaycastFindEdge(collisionPoint, normal);
2249 m_log.DebugFormat("[SIT]: Raycast Camera Position succeeded at point: {0}, normal:{1}", collisionPoint, normal);
2250 }
2251 else
2252 {
2253 SitRayHorizontal(part);
2254 }
2255 }
2256 else
2257 {
2258 SitRayHorizontal(part);
2259 }
2260 }
2261 else
2262 {
2263 ControllingClient.SendAlertMessage("Sit position no longer exists");
2264 m_requestedSitTargetUUID = UUID.Zero;
2265 m_requestedSitTargetID = 0;
2266 m_requestedSitOffset = Vector3.Zero;
2267 }
2268 2224
2269 } 2225 ControllingClient.SendSitResponse(
2226 part.UUID, offset, Orientation, false, cameraAtOffset, cameraEyeOffset, forceMouselook);
2270 2227
2271 public void SitRayHorizontal(SceneObjectPart part) 2228 part.ParentGroup.TriggerScriptChangedEvent(Changed.LINK);
2272 {
2273 // Next, try to raycast from the avatar position to fwd
2274 Vector3 EndRayCastPosition = part.AbsolutePosition + m_requestedSitOffset;
2275 Vector3 StartRayCastPosition = CameraPosition;
2276 Vector3 direction = Vector3.Normalize(EndRayCastPosition - StartRayCastPosition);
2277 float distance = Vector3.Distance(EndRayCastPosition, StartRayCastPosition);
2278 m_scene.PhysicsScene.RaycastWorld(StartRayCastPosition, direction, distance, SitRayCastHorizontalResponse);
2279 }
2280 2229
2281 public void SitRayCastHorizontalResponse(bool hitYN, Vector3 collisionPoint, uint localid, float pdistance, Vector3 normal) 2230 // assuming no autopilot in use
2282 { 2231 Velocity = Vector3.Zero;
2283 SceneObjectPart part = FindNextAvailableSitTarget(m_requestedSitTargetUUID); 2232 RemoveFromPhysicalScene();
2284 if (part != null)
2285 {
2286 if (hitYN)
2287 {
2288 if (collisionPoint.ApproxEquals(m_requestedSitOffset + part.AbsolutePosition, 0.2f))
2289 {
2290 SitRaycastFindEdge(collisionPoint, normal);
2291 m_log.DebugFormat("[SIT]: Raycast Horizontal Position succeeded at point: {0}, normal:{1}", collisionPoint, normal);
2292 // Next, try to raycast from the camera position
2293 Vector3 EndRayCastPosition = part.AbsolutePosition + m_requestedSitOffset;
2294 Vector3 StartRayCastPosition = CameraPosition;
2295 Vector3 direction = Vector3.Normalize(EndRayCastPosition - StartRayCastPosition);
2296 float distance = Vector3.Distance(EndRayCastPosition, StartRayCastPosition);
2297 //m_scene.PhysicsScene.RaycastWorld(StartRayCastPosition, direction, distance, SitRayCastResponseAvatarPosition);
2298 }
2299 else
2300 {
2301 ControllingClient.SendAlertMessage("Sit position not accessable.");
2302 m_requestedSitTargetUUID = UUID.Zero;
2303 m_requestedSitTargetID = 0;
2304 m_requestedSitOffset = Vector3.Zero;
2305 }
2306 }
2307 else
2308 {
2309 ControllingClient.SendAlertMessage("Sit position not accessable.");
2310 m_requestedSitTargetUUID = UUID.Zero;
2311 m_requestedSitTargetID = 0;
2312 m_requestedSitOffset = Vector3.Zero;
2313 }
2314 }
2315 else
2316 {
2317 ControllingClient.SendAlertMessage("Sit position no longer exists");
2318 m_requestedSitTargetUUID = UUID.Zero;
2319 m_requestedSitTargetID = 0;
2320 m_requestedSitOffset = Vector3.Zero;
2321 }
2322 2233
2323 } 2234 Rotation = Orientation;
2235 m_pos = offset;
2324 2236
2325 private void SitRaycastFindEdge(Vector3 collisionPoint, Vector3 collisionNormal) 2237 m_requestedSitTargetID = 0; // invalidate the viewer sit comand for now
2326 { 2238 part.ParentGroup.AddAvatar(UUID);
2327 int i = 0;
2328 //throw new NotImplementedException();
2329 //m_requestedSitTargetUUID = UUID.Zero;
2330 //m_requestedSitTargetID = 0;
2331 //m_requestedSitOffset = Vector3.Zero;
2332 2239
2333 SendSitResponse(ControllingClient, m_requestedSitTargetUUID, collisionPoint - m_requestedSitOffset, Quaternion.Identity); 2240 ParentPart = part;
2241 ParentID = part.LocalId;
2242
2243 Animator.TrySetMovementAnimation("SIT");
2244 SendAvatarDataToAllAgents();
2334 } 2245 }
2335 */ 2246
2336 2247
2337 public void HandleAgentSit(IClientAPI remoteClient, UUID agentID) 2248 public void HandleAgentSit(IClientAPI remoteClient, UUID agentID)
2338 { 2249 {
@@ -2531,9 +2442,13 @@ namespace OpenSim.Region.Framework.Scenes
2531 // NOTE: Velocity is not the same as m_velocity. Velocity will attempt to 2442 // NOTE: Velocity is not the same as m_velocity. Velocity will attempt to
2532 // grab the latest PhysicsActor velocity, whereas m_velocity is often 2443 // grab the latest PhysicsActor velocity, whereas m_velocity is often
2533 // storing a requested force instead of an actual traveling velocity 2444 // storing a requested force instead of an actual traveling velocity
2445 if (Appearance.AvatarSize != m_lastSize)
2446 {
2447 m_lastSize = Appearance.AvatarSize;
2448 SendAvatarDataToAllAgents();
2449 }
2534 2450
2535 // Throw away duplicate or insignificant updates 2451 else if (!Rotation.ApproxEquals(m_lastRotation, ROTATION_TOLERANCE) ||
2536 if (!Rotation.ApproxEquals(m_lastRotation, ROTATION_TOLERANCE) ||
2537 !Velocity.ApproxEquals(m_lastVelocity, VELOCITY_TOLERANCE) || 2452 !Velocity.ApproxEquals(m_lastVelocity, VELOCITY_TOLERANCE) ||
2538 !m_pos.ApproxEquals(m_lastPosition, POSITION_TOLERANCE)) 2453 !m_pos.ApproxEquals(m_lastPosition, POSITION_TOLERANCE))
2539 { 2454 {
@@ -2831,6 +2746,8 @@ namespace OpenSim.Region.Framework.Scenes
2831 2746
2832 avatar.ControllingClient.SendAppearance( 2747 avatar.ControllingClient.SendAppearance(
2833 UUID, Appearance.VisualParams, Appearance.Texture.GetBytes()); 2748 UUID, Appearance.VisualParams, Appearance.Texture.GetBytes());
2749
2750
2834 } 2751 }
2835 2752
2836 #endregion 2753 #endregion
@@ -3403,15 +3320,22 @@ namespace OpenSim.Region.Framework.Scenes
3403 } 3320 }
3404 3321
3405 if (Appearance.AvatarHeight == 0) 3322 if (Appearance.AvatarHeight == 0)
3406 Appearance.SetHeight(); 3323// Appearance.SetHeight();
3324 Appearance.SetSize(new Vector3(0.45f,0.6f,1.9f));
3407 3325
3408 PhysicsScene scene = m_scene.PhysicsScene; 3326 PhysicsScene scene = m_scene.PhysicsScene;
3409 3327
3410 Vector3 pVec = AbsolutePosition; 3328 Vector3 pVec = AbsolutePosition;
3411 3329
3330/*
3331 PhysicsActor = scene.AddAvatar(
3332 LocalId, Firstname + "." + Lastname, pVec,
3333 new Vector3(0.45f, 0.6f, Appearance.AvatarHeight), isFlying);
3334*/
3335
3412 PhysicsActor = scene.AddAvatar( 3336 PhysicsActor = scene.AddAvatar(
3413 LocalId, Firstname + "." + Lastname, pVec, 3337 LocalId, Firstname + "." + Lastname, pVec,
3414 new Vector3(0f, 0f, Appearance.AvatarHeight), isFlying); 3338 Appearance.AvatarBoxSize,Appearance.AvatarFeetOffset, isFlying);
3415 3339
3416 //PhysicsActor.OnRequestTerseUpdate += SendTerseUpdateToAllClients; 3340 //PhysicsActor.OnRequestTerseUpdate += SendTerseUpdateToAllClients;
3417 PhysicsActor.OnCollisionUpdate += PhysicsCollisionUpdate; 3341 PhysicsActor.OnCollisionUpdate += PhysicsCollisionUpdate;
@@ -3430,6 +3354,7 @@ namespace OpenSim.Region.Framework.Scenes
3430 ControllingClient.SendAgentAlertMessage("Physics is having a problem with your avatar. You may not be able to move until you relog.", true); 3354 ControllingClient.SendAgentAlertMessage("Physics is having a problem with your avatar. You may not be able to move until you relog.", true);
3431 } 3355 }
3432 3356
3357
3433 /// <summary> 3358 /// <summary>
3434 /// Event called by the physics plugin to tell the avatar about a collision. 3359 /// Event called by the physics plugin to tell the avatar about a collision.
3435 /// </summary> 3360 /// </summary>
@@ -3459,7 +3384,6 @@ namespace OpenSim.Region.Framework.Scenes
3459 CollisionEventUpdate collisionData = (CollisionEventUpdate)e; 3384 CollisionEventUpdate collisionData = (CollisionEventUpdate)e;
3460 Dictionary<uint, ContactPoint> coldata = collisionData.m_objCollisionList; 3385 Dictionary<uint, ContactPoint> coldata = collisionData.m_objCollisionList;
3461 3386
3462 CollisionPlane = Vector4.UnitW;
3463 3387
3464// // No collisions at all means we may be flying. Update always 3388// // No collisions at all means we may be flying. Update always
3465// // to make falling work 3389// // to make falling work
@@ -3471,6 +3395,7 @@ namespace OpenSim.Region.Framework.Scenes
3471 3395
3472 if (coldata.Count != 0) 3396 if (coldata.Count != 0)
3473 { 3397 {
3398/*
3474 switch (Animator.CurrentMovementAnimation) 3399 switch (Animator.CurrentMovementAnimation)
3475 { 3400 {
3476 case "STAND": 3401 case "STAND":
@@ -3479,24 +3404,36 @@ namespace OpenSim.Region.Framework.Scenes
3479 case "CROUCH": 3404 case "CROUCH":
3480 case "CROUCHWALK": 3405 case "CROUCHWALK":
3481 { 3406 {
3407 */
3482 ContactPoint lowest; 3408 ContactPoint lowest;
3483 lowest.SurfaceNormal = Vector3.Zero; 3409 lowest.SurfaceNormal = Vector3.Zero;
3484 lowest.Position = Vector3.Zero; 3410 lowest.Position = Vector3.Zero;
3485 lowest.Position.Z = Single.NaN; 3411 lowest.Position.Z = float.MaxValue;
3486 3412
3487 foreach (ContactPoint contact in coldata.Values) 3413 foreach (ContactPoint contact in coldata.Values)
3488 { 3414 {
3489 if (Single.IsNaN(lowest.Position.Z) || contact.Position.Z < lowest.Position.Z) 3415
3416 if (contact.CharacterFeet && contact.Position.Z < lowest.Position.Z)
3490 { 3417 {
3491 lowest = contact; 3418 lowest = contact;
3492 } 3419 }
3493 } 3420 }
3494 3421
3495 CollisionPlane = new Vector4(-lowest.SurfaceNormal, -Vector3.Dot(lowest.Position, lowest.SurfaceNormal)); 3422 if (lowest.Position.Z != float.MaxValue)
3423 {
3424 lowest.SurfaceNormal = -lowest.SurfaceNormal;
3425 CollisionPlane = new Vector4(lowest.SurfaceNormal, Vector3.Dot(lowest.Position, lowest.SurfaceNormal));
3426 }
3427 else
3428 CollisionPlane = Vector4.UnitW;
3429/*
3496 } 3430 }
3497 break; 3431 break;
3498 } 3432 }
3433*/
3499 } 3434 }
3435 else
3436 CollisionPlane = Vector4.UnitW;
3500 3437
3501 RaiseCollisionScriptEvents(coldata); 3438 RaiseCollisionScriptEvents(coldata);
3502 3439