aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/Physics/BulletSPlugin
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/Physics/BulletSPlugin')
-rw-r--r--OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs4
-rwxr-xr-xOpenSim/Region/Physics/BulletSPlugin/BSMaterials.cs3
-rwxr-xr-xOpenSim/Region/Physics/BulletSPlugin/BSParam.cs2
-rwxr-xr-xOpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs88
-rwxr-xr-xOpenSim/Region/Physics/BulletSPlugin/BSTerrainMesh.cs10
5 files changed, 64 insertions, 43 deletions
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs
index e208d3a..90c2d9c 100644
--- a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs
@@ -479,7 +479,7 @@ public sealed class BSCharacter : BSPhysObject
479 // The character is out of the known/simulated area. 479 // The character is out of the known/simulated area.
480 // Force the avatar position to be within known. ScenePresence will use the position 480 // Force the avatar position to be within known. ScenePresence will use the position
481 // plus the velocity to decide if the avatar is moving out of the region. 481 // plus the velocity to decide if the avatar is moving out of the region.
482 RawPosition = PhysicsScene.TerrainManager.ClampPositionIntoKnownTerrain(RawPosition); 482 RawPosition = PhysicsScene.TerrainManager.ClampPositionIntoKnownTerrain(RawPosition);
483 DetailLog("{0},BSCharacter.PositionSanityCheck,notWithinKnownTerrain,clampedPos={1}", LocalID, RawPosition); 483 DetailLog("{0},BSCharacter.PositionSanityCheck,notWithinKnownTerrain,clampedPos={1}", LocalID, RawPosition);
484 return true; 484 return true;
485 } 485 }
@@ -898,7 +898,7 @@ public sealed class BSCharacter : BSPhysObject
898 // Do some sanity checking for the avatar. Make sure it's above ground and inbounds. 898 // Do some sanity checking for the avatar. Make sure it's above ground and inbounds.
899 if (PositionSanityCheck(true)) 899 if (PositionSanityCheck(true))
900 { 900 {
901 DetailLog("{0},BSCharacter.UpdateProperties,updatePosForSanity,pos={1}", LocalID, _position); 901 DetailLog("{0},BSCharacter.UpdateProperties,updatePosForSanity,pos={1}", LocalID, _position);
902 entprop.Position = _position; 902 entprop.Position = _position;
903 } 903 }
904 904
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSMaterials.cs b/OpenSim/Region/Physics/BulletSPlugin/BSMaterials.cs
index 92d62ff..ee77d6e 100755
--- a/OpenSim/Region/Physics/BulletSPlugin/BSMaterials.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSMaterials.cs
@@ -180,11 +180,14 @@ public static class BSMaterials
180 // Use reflection to set the value in the attribute structure. 180 // Use reflection to set the value in the attribute structure.
181 private static void SetAttributeValue(int matType, string attribName, float val) 181 private static void SetAttributeValue(int matType, string attribName, float val)
182 { 182 {
183 // Get the current attribute values for this material
183 MaterialAttributes thisAttrib = Attributes[matType]; 184 MaterialAttributes thisAttrib = Attributes[matType];
185 // Find the field for the passed attribute name (eg, find field named 'friction')
184 FieldInfo fieldInfo = thisAttrib.GetType().GetField(attribName.ToLower()); 186 FieldInfo fieldInfo = thisAttrib.GetType().GetField(attribName.ToLower());
185 if (fieldInfo != null) 187 if (fieldInfo != null)
186 { 188 {
187 fieldInfo.SetValue(thisAttrib, val); 189 fieldInfo.SetValue(thisAttrib, val);
190 // Copy new attributes back to array -- since MaterialAttributes is 'struct', passed by value, not reference.
188 Attributes[matType] = thisAttrib; 191 Attributes[matType] = thisAttrib;
189 } 192 }
190 } 193 }
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSParam.cs b/OpenSim/Region/Physics/BulletSPlugin/BSParam.cs
index f3454c8..385ed9e 100755
--- a/OpenSim/Region/Physics/BulletSPlugin/BSParam.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSParam.cs
@@ -482,7 +482,7 @@ public static class BSParam
482 (s) => { return TerrainImplementation; }, 482 (s) => { return TerrainImplementation; },
483 (s,v) => { TerrainImplementation = v; } ), 483 (s,v) => { TerrainImplementation = v; } ),
484 new ParameterDefn<int>("TerrainMeshMagnification", "Number of times the 256x256 heightmap is multiplied to create the terrain mesh" , 484 new ParameterDefn<int>("TerrainMeshMagnification", "Number of times the 256x256 heightmap is multiplied to create the terrain mesh" ,
485 3, 485 2,
486 (s) => { return TerrainMeshMagnification; }, 486 (s) => { return TerrainMeshMagnification; },
487 (s,v) => { TerrainMeshMagnification = v; } ), 487 (s,v) => { TerrainMeshMagnification = v; } ),
488 new ParameterDefn<float>("TerrainFriction", "Factor to reduce movement against terrain surface" , 488 new ParameterDefn<float>("TerrainFriction", "Factor to reduce movement against terrain surface" ,
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs
index a60946d..b2fb835 100755
--- a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs
@@ -132,6 +132,7 @@ public sealed class BSTerrainManager : IDisposable
132 // safe to call Bullet in real time. We hope no one is moving prims around yet. 132 // safe to call Bullet in real time. We hope no one is moving prims around yet.
133 public void CreateInitialGroundPlaneAndTerrain() 133 public void CreateInitialGroundPlaneAndTerrain()
134 { 134 {
135 DetailLog("{0},BSTerrainManager.CreateInitialGroundPlaneAndTerrain,region={1}", BSScene.DetailLogZero, PhysicsScene.RegionName);
135 // The ground plane is here to catch things that are trying to drop to negative infinity 136 // The ground plane is here to catch things that are trying to drop to negative infinity
136 BulletShape groundPlaneShape = PhysicsScene.PE.CreateGroundPlaneShape(BSScene.GROUNDPLANE_ID, 1f, BSParam.TerrainCollisionMargin); 137 BulletShape groundPlaneShape = PhysicsScene.PE.CreateGroundPlaneShape(BSScene.GROUNDPLANE_ID, 1f, BSParam.TerrainCollisionMargin);
137 m_groundPlane = PhysicsScene.PE.CreateBodyWithDefaultMotionState(groundPlaneShape, 138 m_groundPlane = PhysicsScene.PE.CreateBodyWithDefaultMotionState(groundPlaneShape,
@@ -145,14 +146,18 @@ public sealed class BSTerrainManager : IDisposable
145 m_groundPlane.collisionType = CollisionType.Groundplane; 146 m_groundPlane.collisionType = CollisionType.Groundplane;
146 m_groundPlane.ApplyCollisionMask(PhysicsScene); 147 m_groundPlane.ApplyCollisionMask(PhysicsScene);
147 148
148 // Build an initial terrain and put it in the world. This quickly gets replaced by the real region terrain.
149 BSTerrainPhys initialTerrain = new BSTerrainHeightmap(PhysicsScene, Vector3.Zero, BSScene.TERRAIN_ID, DefaultRegionSize); 149 BSTerrainPhys initialTerrain = new BSTerrainHeightmap(PhysicsScene, Vector3.Zero, BSScene.TERRAIN_ID, DefaultRegionSize);
150 m_terrains.Add(Vector3.Zero, initialTerrain); 150 lock (m_terrains)
151 {
152 // Build an initial terrain and put it in the world. This quickly gets replaced by the real region terrain.
153 m_terrains.Add(Vector3.Zero, initialTerrain);
154 }
151 } 155 }
152 156
153 // Release all the terrain structures we might have allocated 157 // Release all the terrain structures we might have allocated
154 public void ReleaseGroundPlaneAndTerrain() 158 public void ReleaseGroundPlaneAndTerrain()
155 { 159 {
160 DetailLog("{0},BSTerrainManager.ReleaseGroundPlaneAndTerrain,region={1}", BSScene.DetailLogZero, PhysicsScene.RegionName);
156 if (m_groundPlane.HasPhysicalBody) 161 if (m_groundPlane.HasPhysicalBody)
157 { 162 {
158 if (PhysicsScene.PE.RemoveObjectFromWorld(PhysicsScene.World, m_groundPlane)) 163 if (PhysicsScene.PE.RemoveObjectFromWorld(PhysicsScene.World, m_groundPlane))
@@ -193,11 +198,16 @@ public sealed class BSTerrainManager : IDisposable
193 // the terrain is added to our parent 198 // the terrain is added to our parent
194 if (MegaRegionParentPhysicsScene is BSScene) 199 if (MegaRegionParentPhysicsScene is BSScene)
195 { 200 {
196 DetailLog("{0},SetTerrain.ToParent,offset={1},worldMax={2}", 201 DetailLog("{0},SetTerrain.ToParent,offset={1},worldMax={2}", BSScene.DetailLogZero, m_worldOffset, m_worldMax);
197 BSScene.DetailLogZero, m_worldOffset, m_worldMax); 202 // This looks really odd but this region is passing its terrain to its mega-region root region
198 ((BSScene)MegaRegionParentPhysicsScene).TerrainManager.UpdateTerrain( 203 // and the creation of the terrain must happen on the root region's taint thread and not
199 BSScene.CHILDTERRAIN_ID, localHeightMap, 204 // my taint thread.
200 m_worldOffset, m_worldOffset + DefaultRegionSize, true); 205 ((BSScene)MegaRegionParentPhysicsScene).PostTaintObject("TerrainManager.SetTerrain.Mega-" + m_worldOffset.ToString(), 0, delegate()
206 {
207 ((BSScene)MegaRegionParentPhysicsScene).TerrainManager.UpdateTerrain(
208 BSScene.CHILDTERRAIN_ID, localHeightMap,
209 m_worldOffset, m_worldOffset + DefaultRegionSize, true /* inTaintTime */);
210 });
201 } 211 }
202 } 212 }
203 else 213 else
@@ -206,16 +216,16 @@ public sealed class BSTerrainManager : IDisposable
206 DetailLog("{0},SetTerrain.Existing", BSScene.DetailLogZero); 216 DetailLog("{0},SetTerrain.Existing", BSScene.DetailLogZero);
207 217
208 UpdateTerrain(BSScene.TERRAIN_ID, localHeightMap, 218 UpdateTerrain(BSScene.TERRAIN_ID, localHeightMap,
209 m_worldOffset, m_worldOffset + DefaultRegionSize, true); 219 m_worldOffset, m_worldOffset + DefaultRegionSize, true /* inTaintTime */);
210 } 220 }
211 }); 221 });
212 } 222 }
213 223
214 // If called with no mapInfo for the terrain, this will create a new mapInfo and terrain 224 // If called for terrain has has not been previously allocated, a new terrain will be built
215 // based on the passed information. The 'id' should be either the terrain id or 225 // based on the passed information. The 'id' should be either the terrain id or
216 // BSScene.CHILDTERRAIN_ID. If the latter, a new child terrain ID will be allocated and used. 226 // BSScene.CHILDTERRAIN_ID. If the latter, a new child terrain ID will be allocated and used.
217 // The latter feature is for creating child terrains for mega-regions. 227 // The latter feature is for creating child terrains for mega-regions.
218 // If called with a mapInfo in m_heightMaps and there is an existing terrain body, a new 228 // If there is an existing terrain body, a new
219 // terrain shape is created and added to the body. 229 // terrain shape is created and added to the body.
220 // This call is most often used to update the heightMap and parameters of the terrain. 230 // This call is most often used to update the heightMap and parameters of the terrain.
221 // (The above does suggest that some simplification/refactoring is in order.) 231 // (The above does suggest that some simplification/refactoring is in order.)
@@ -223,8 +233,8 @@ public sealed class BSTerrainManager : IDisposable
223 private void UpdateTerrain(uint id, float[] heightMap, 233 private void UpdateTerrain(uint id, float[] heightMap,
224 Vector3 minCoords, Vector3 maxCoords, bool inTaintTime) 234 Vector3 minCoords, Vector3 maxCoords, bool inTaintTime)
225 { 235 {
226 DetailLog("{0},BSTerrainManager.UpdateTerrain,call,minC={1},maxC={2},inTaintTime={3}", 236 DetailLog("{0},BSTerrainManager.UpdateTerrain,call,id={1},minC={2},maxC={3},inTaintTime={4}",
227 BSScene.DetailLogZero, minCoords, maxCoords, inTaintTime); 237 BSScene.DetailLogZero, id, minCoords, maxCoords, inTaintTime);
228 238
229 // Find high and low points of passed heightmap. 239 // Find high and low points of passed heightmap.
230 // The min and max passed in is usually the area objects can be in (maximum 240 // The min and max passed in is usually the area objects can be in (maximum
@@ -253,7 +263,7 @@ public sealed class BSTerrainManager : IDisposable
253 if (m_terrains.TryGetValue(terrainRegionBase, out terrainPhys)) 263 if (m_terrains.TryGetValue(terrainRegionBase, out terrainPhys))
254 { 264 {
255 // There is already a terrain in this spot. Free the old and build the new. 265 // There is already a terrain in this spot. Free the old and build the new.
256 DetailLog("{0},UpdateTerrain:UpdateExisting,call,id={1},base={2},minC={3},maxC={4}", 266 DetailLog("{0},BSTErrainManager.UpdateTerrain:UpdateExisting,call,id={1},base={2},minC={3},maxC={4}",
257 BSScene.DetailLogZero, id, terrainRegionBase, minCoords, minCoords); 267 BSScene.DetailLogZero, id, terrainRegionBase, minCoords, minCoords);
258 268
259 // Remove old terrain from the collection 269 // Remove old terrain from the collection
@@ -292,7 +302,7 @@ public sealed class BSTerrainManager : IDisposable
292 if (newTerrainID >= BSScene.CHILDTERRAIN_ID) 302 if (newTerrainID >= BSScene.CHILDTERRAIN_ID)
293 newTerrainID = ++m_terrainCount; 303 newTerrainID = ++m_terrainCount;
294 304
295 DetailLog("{0},UpdateTerrain:NewTerrain,taint,newID={1},minCoord={2},maxCoord={3}", 305 DetailLog("{0},BSTerrainManager.UpdateTerrain:NewTerrain,taint,newID={1},minCoord={2},maxCoord={3}",
296 BSScene.DetailLogZero, newTerrainID, minCoords, minCoords); 306 BSScene.DetailLogZero, newTerrainID, minCoords, minCoords);
297 BSTerrainPhys newTerrainPhys = BuildPhysicalTerrain(terrainRegionBase, id, heightMap, minCoords, maxCoords); 307 BSTerrainPhys newTerrainPhys = BuildPhysicalTerrain(terrainRegionBase, id, heightMap, minCoords, maxCoords);
298 m_terrains.Add(terrainRegionBase, newTerrainPhys); 308 m_terrains.Add(terrainRegionBase, newTerrainPhys);
@@ -343,37 +353,35 @@ public sealed class BSTerrainManager : IDisposable
343 { 353 {
344 Vector3 ret = pPos; 354 Vector3 ret = pPos;
345 355
356 // First, base addresses are never negative so correct for that possible problem.
357 if (ret.X < 0f || ret.Y < 0f)
358 {
359 ret.X = Util.Clamp<float>(ret.X, 0f, 1000000f);
360 ret.Y = Util.Clamp<float>(ret.Y, 0f, 1000000f);
361 DetailLog("{0},BSTerrainManager.ClampPositionToKnownTerrain,zeroingNegXorY,oldPos={1},newPos={2}",
362 BSScene.DetailLogZero, pPos, ret);
363 }
364
346 // Can't do this function if we don't know about any terrain. 365 // Can't do this function if we don't know about any terrain.
347 if (m_terrains.Count == 0) 366 if (m_terrains.Count == 0)
348 return ret; 367 return ret;
349 368
350 int loopPrevention = 5; 369 int loopPrevention = 10;
351 Vector3 terrainBaseXYZ; 370 Vector3 terrainBaseXYZ;
352 BSTerrainPhys physTerrain; 371 BSTerrainPhys physTerrain;
353 while (!GetTerrainPhysicalAtXYZ(ret, out physTerrain, out terrainBaseXYZ)) 372 while (!GetTerrainPhysicalAtXYZ(ret, out physTerrain, out terrainBaseXYZ))
354 { 373 {
355 // The passed position is not within a known terrain area. 374 // The passed position is not within a known terrain area.
375 // NOTE that GetTerrainPhysicalAtXYZ will set 'terrainBaseXYZ' to the base of the unfound region.
356 376
357 // First, base addresses are never negative so correct for that possible problem. 377 // Must be off the top of a region. Find an adjacent region to move into.
358 if (ret.X < 0f || ret.Y < 0f) 378 Vector3 adjacentTerrainBase = FindAdjacentTerrainBase(terrainBaseXYZ);
359 { 379
360 if (ret.X < 0f) 380 ret.X = Math.Min(ret.X, adjacentTerrainBase.X + (ret.X % DefaultRegionSize.X));
361 ret.X = 0f; 381 ret.Y = Math.Min(ret.Y, adjacentTerrainBase.Y + (ret.X % DefaultRegionSize.Y));
362 if (ret.Y < 0f) 382 DetailLog("{0},BSTerrainManager.ClampPositionToKnownTerrain,findingAdjacentRegion,adjacentRegBase={1},oldPos={2},newPos={3}",
363 ret.Y = 0f; 383 BSScene.DetailLogZero, adjacentTerrainBase, pPos, ret);
364 DetailLog("{0},BSTerrainManager.ClampPositionToKnownTerrain,zeroingNegXorY,oldPos={1},newPos={2}",
365 BSScene.DetailLogZero, pPos, ret);
366 }
367 else
368 {
369 // Must be off the top of a region. Find an adjacent region to move into.
370 Vector3 adjacentTerrainBase = FindAdjacentTerrainBase(terrainBaseXYZ);
371 384
372 ret.X = Math.Min(ret.X, adjacentTerrainBase.X + DefaultRegionSize.X);
373 ret.Y = Math.Min(ret.Y, adjacentTerrainBase.Y + DefaultRegionSize.Y);
374 DetailLog("{0},BSTerrainManager.ClampPositionToKnownTerrain,findingAdjacentRegion,adjacentRegBase={1},oldPos={2},newPos={3}",
375 BSScene.DetailLogZero, adjacentTerrainBase, pPos, ret);
376 }
377 if (loopPrevention-- < 0f) 385 if (loopPrevention-- < 0f)
378 { 386 {
379 // The 'while' is a little dangerous so this prevents looping forever if the 387 // The 'while' is a little dangerous so this prevents looping forever if the
@@ -383,6 +391,7 @@ public sealed class BSTerrainManager : IDisposable
383 break; 391 break;
384 } 392 }
385 } 393 }
394
386 return ret; 395 return ret;
387 } 396 }
388 397
@@ -479,11 +488,20 @@ public sealed class BSTerrainManager : IDisposable
479 private Vector3 FindAdjacentTerrainBase(Vector3 pTerrainBase) 488 private Vector3 FindAdjacentTerrainBase(Vector3 pTerrainBase)
480 { 489 {
481 Vector3 ret = pTerrainBase; 490 Vector3 ret = pTerrainBase;
491
492 // Can't do this function if we don't know about any terrain.
493 if (m_terrains.Count == 0)
494 return ret;
495
496 // Just some sanity
497 ret.X = Util.Clamp<float>(ret.X, 0f, 1000000f);
498 ret.Y = Util.Clamp<float>(ret.Y, 0f, 1000000f);
482 ret.Z = 0f; 499 ret.Z = 0f;
500
483 lock (m_terrains) 501 lock (m_terrains)
484 { 502 {
485 // Once down to the <0,0> region, we have to be done. 503 // Once down to the <0,0> region, we have to be done.
486 while (ret.X > 0f && ret.Y > 0f) 504 while (ret.X > 0f || ret.Y > 0f)
487 { 505 {
488 if (ret.X > 0f) 506 if (ret.X > 0f)
489 { 507 {
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainMesh.cs b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainMesh.cs
index a9cd8a1..2ce1513 100755
--- a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainMesh.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainMesh.cs
@@ -98,20 +98,20 @@ public sealed class BSTerrainMesh : BSTerrainPhys
98 if (!meshCreationSuccess) 98 if (!meshCreationSuccess)
99 { 99 {
100 // DISASTER!! 100 // DISASTER!!
101 PhysicsScene.DetailLog("{0},BSTerrainMesh.create,failedConversionOfHeightmap", ID); 101 PhysicsScene.DetailLog("{0},BSTerrainMesh.create,failedConversionOfHeightmap,id={1}", BSScene.DetailLogZero, ID);
102 PhysicsScene.Logger.ErrorFormat("{0} Failed conversion of heightmap to mesh! base={1}", LogHeader, TerrainBase); 102 PhysicsScene.Logger.ErrorFormat("{0} Failed conversion of heightmap to mesh! base={1}", LogHeader, TerrainBase);
103 // Something is very messed up and a crash is in our future. 103 // Something is very messed up and a crash is in our future.
104 return; 104 return;
105 } 105 }
106 106
107 PhysicsScene.DetailLog("{0},BSTerrainMesh.create,meshed,indices={1},indSz={2},vertices={3},vertSz={4}", 107 PhysicsScene.DetailLog("{0},BSTerrainMesh.create,meshed,id={1},indices={2},indSz={3},vertices={4},vertSz={5}",
108 ID, indicesCount, indices.Length, verticesCount, vertices.Length); 108 BSScene.DetailLogZero, ID, indicesCount, indices.Length, verticesCount, vertices.Length);
109 109
110 m_terrainShape = PhysicsScene.PE.CreateMeshShape(PhysicsScene.World, indicesCount, indices, verticesCount, vertices); 110 m_terrainShape = PhysicsScene.PE.CreateMeshShape(PhysicsScene.World, indicesCount, indices, verticesCount, vertices);
111 if (!m_terrainShape.HasPhysicalShape) 111 if (!m_terrainShape.HasPhysicalShape)
112 { 112 {
113 // DISASTER!! 113 // DISASTER!!
114 PhysicsScene.DetailLog("{0},BSTerrainMesh.create,failedCreationOfShape", ID); 114 PhysicsScene.DetailLog("{0},BSTerrainMesh.create,failedCreationOfShape,id={1}", BSScene.DetailLogZero, ID);
115 PhysicsScene.Logger.ErrorFormat("{0} Failed creation of terrain mesh! base={1}", LogHeader, TerrainBase); 115 PhysicsScene.Logger.ErrorFormat("{0} Failed creation of terrain mesh! base={1}", LogHeader, TerrainBase);
116 // Something is very messed up and a crash is in our future. 116 // Something is very messed up and a crash is in our future.
117 return; 117 return;
@@ -151,7 +151,7 @@ public sealed class BSTerrainMesh : BSTerrainPhys
151 151
152 if (BSParam.UseSingleSidedMeshes) 152 if (BSParam.UseSingleSidedMeshes)
153 { 153 {
154 PhysicsScene.DetailLog("{0},BSTerrainMesh.settingCustomMaterial", id); 154 PhysicsScene.DetailLog("{0},BSTerrainMesh.settingCustomMaterial,id={1}", BSScene.DetailLogZero, id);
155 PhysicsScene.PE.AddToCollisionFlags(m_terrainBody, CollisionFlags.CF_CUSTOM_MATERIAL_CALLBACK); 155 PhysicsScene.PE.AddToCollisionFlags(m_terrainBody, CollisionFlags.CF_CUSTOM_MATERIAL_CALLBACK);
156 } 156 }
157 157