Пример #1
0
        // If called with no mapInfo for the terrain, this will create a new mapInfo and terrain
        //     based on the passed information. The 'id' should be either the terrain id or
        //     BSScene.CHILDTERRAIN_ID. If the latter, a new child terrain ID will be allocated and used.
        //     The latter feature is for creating child terrains for mega-regions.
        // If called with a mapInfo in m_heightMaps but the terrain has no body yet (mapInfo.terrainBody.Ptr == 0)
        //     then a new body and shape is created and the mapInfo is filled.
        //     This call is used for doing the initial terrain creation.
        // If called with a mapInfo in m_heightMaps and there is an existing terrain body, a new
        //     terrain shape is created and added to the body.
        //     This call is most often used to update the heightMap and parameters of the terrain.
        // The 'doNow' boolean says whether to do all the unmanaged activities right now (like when
        //     calling this routine from initialization or taint-time routines) or whether to delay
        //     all the unmanaged activities to taint-time.
        private void UpdateOrCreateTerrain(uint id, float[] heightMap, Vector3 minCoords, Vector3 maxCoords, bool atTaintTime)
        {
            DetailLog("{0},BSTerrainManager.UpdateOrCreateTerrain,call,minC={1},maxC={2},atTaintTime={3}",
                      BSScene.DetailLogZero, minCoords, maxCoords, atTaintTime);

            float   minZ = float.MaxValue;
            float   maxZ = float.MinValue;
            Vector2 terrainRegionBase = new Vector2(minCoords.X, minCoords.Y);

            int heightMapSize = heightMap.Length;

            for (int ii = 0; ii < heightMapSize; ii++)
            {
                float height = heightMap[ii];
                if (height < minZ)
                {
                    minZ = height;
                }
                if (height > maxZ)
                {
                    maxZ = height;
                }
            }

            // The shape of the terrain is from its base to its extents.
            minCoords.Z = minZ;
            maxCoords.Z = maxZ;

            BulletHeightMapInfo mapInfo;

            if (m_heightMaps.TryGetValue(terrainRegionBase, out mapInfo))
            {
                // If this is terrain we know about, it's easy to update

                mapInfo.heightMap = heightMap;
                mapInfo.minCoords = minCoords;
                mapInfo.maxCoords = maxCoords;
                mapInfo.minZ      = minZ;
                mapInfo.maxZ      = maxZ;
                mapInfo.sizeX     = maxCoords.X - minCoords.X;
                mapInfo.sizeY     = maxCoords.Y - minCoords.Y;
                DetailLog("{0},UpdateOrCreateTerrain:UpdateExisting,call,terrainBase={1},minC={2}, maxC={3}, szX={4}, szY={5}",
                          BSScene.DetailLogZero, terrainRegionBase, mapInfo.minCoords, mapInfo.maxCoords, mapInfo.sizeX, mapInfo.sizeY);

                BSScene.TaintCallback rebuildOperation = delegate()
                {
                    if (MegaRegionParentPhysicsScene != null)
                    {
                        // It's possible that Combine() was called after this code was queued.
                        // If we are a child of combined regions, we don't create any terrain for us.
                        DetailLog("{0},UpdateOrCreateTerrain:AmACombineChild,taint", BSScene.DetailLogZero);

                        // Get rid of any terrain that may have been allocated for us.
                        ReleaseGroundPlaneAndTerrain();

                        // I hate doing this, but just bail
                        return;
                    }

                    if (mapInfo.terrainBody.ptr != IntPtr.Zero)
                    {
                        // Updating an existing terrain.
                        DetailLog("{0},UpdateOrCreateTerrain:UpdateExisting,taint,terrainBase={1},minC={2}, maxC={3}, szX={4}, szY={5}",
                                  BSScene.DetailLogZero, terrainRegionBase, mapInfo.minCoords, mapInfo.maxCoords, mapInfo.sizeX, mapInfo.sizeY);

                        // Remove from the dynamics world because we're going to mangle this object
                        BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, mapInfo.terrainBody.ptr);

                        // Get rid of the old terrain
                        BulletSimAPI.DestroyObject2(PhysicsScene.World.ptr, mapInfo.terrainBody.ptr);
                        BulletSimAPI.ReleaseHeightMapInfo2(mapInfo.Ptr);
                        mapInfo.Ptr = IntPtr.Zero;

                        /*
                         * // NOTE: This routine is half here because I can't get the terrain shape replacement
                         * //   to work. In the short term, the above three lines completely delete the old
                         * //   terrain and the code below recreates one from scratch.
                         * // Hopefully the Bullet community will help me out on this one.
                         *
                         * // First, release the old collision shape (there is only one terrain)
                         * BulletSimAPI.DeleteCollisionShape2(m_physicsScene.World.Ptr, mapInfo.terrainShape.Ptr);
                         *
                         * // Fill the existing height map info with the new location and size information
                         * BulletSimAPI.FillHeightMapInfo2(m_physicsScene.World.Ptr, mapInfo.Ptr, mapInfo.ID,
                         *              mapInfo.minCoords, mapInfo.maxCoords, mapInfo.heightMap, TERRAIN_COLLISION_MARGIN);
                         *
                         * // Create a terrain shape based on the new info
                         * mapInfo.terrainShape = new BulletShape(BulletSimAPI.CreateTerrainShape2(mapInfo.Ptr));
                         *
                         * // Stuff the shape into the existing terrain body
                         * BulletSimAPI.SetBodyShape2(m_physicsScene.World.Ptr, mapInfo.terrainBody.Ptr, mapInfo.terrainShape.Ptr);
                         */
                    }
                    // else
                    {
                        // Creating a new terrain.
                        DetailLog("{0},UpdateOrCreateTerrain:CreateNewTerrain,taint,baseX={1},baseY={2},minZ={3},maxZ={4}",
                                  BSScene.DetailLogZero, mapInfo.minCoords.X, mapInfo.minCoords.Y, minZ, maxZ);

                        mapInfo.ID  = id;
                        mapInfo.Ptr = BulletSimAPI.CreateHeightMapInfo2(PhysicsScene.World.ptr, mapInfo.ID,
                                                                        mapInfo.minCoords, mapInfo.maxCoords, mapInfo.heightMap, TERRAIN_COLLISION_MARGIN);

                        // The terrain object initial position is at the center of the object
                        Vector3 centerPos;
                        centerPos.X = minCoords.X + (mapInfo.sizeX / 2f);
                        centerPos.Y = minCoords.Y + (mapInfo.sizeY / 2f);
                        centerPos.Z = minZ + ((maxZ - minZ) / 2f);

                        // Create the terrain shape from the mapInfo
                        mapInfo.terrainShape = new BulletShape(BulletSimAPI.CreateTerrainShape2(mapInfo.Ptr),
                                                               ShapeData.PhysicsShapeType.SHAPE_TERRAIN);

                        mapInfo.terrainBody = new BulletBody(mapInfo.ID,
                                                             BulletSimAPI.CreateBodyWithDefaultMotionState2(mapInfo.terrainShape.ptr,
                                                                                                            id, centerPos, Quaternion.Identity));
                    }

                    // Make sure the entry is in the heightmap table
                    m_heightMaps[terrainRegionBase] = mapInfo;

                    // Set current terrain attributes
                    BulletSimAPI.SetFriction2(mapInfo.terrainBody.ptr, PhysicsScene.Params.terrainFriction);
                    BulletSimAPI.SetHitFraction2(mapInfo.terrainBody.ptr, PhysicsScene.Params.terrainHitFraction);
                    BulletSimAPI.SetRestitution2(mapInfo.terrainBody.ptr, PhysicsScene.Params.terrainRestitution);
                    BulletSimAPI.SetCollisionFlags2(mapInfo.terrainBody.ptr, CollisionFlags.CF_STATIC_OBJECT);

                    BulletSimAPI.SetMassProps2(mapInfo.terrainBody.ptr, 0f, Vector3.Zero);
                    BulletSimAPI.UpdateInertiaTensor2(mapInfo.terrainBody.ptr);

                    // Return the new terrain to the world of physical objects
                    BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, mapInfo.terrainBody.ptr);

                    // redo its bounding box now that it is in the world
                    BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, mapInfo.terrainBody.ptr);

                    BulletSimAPI.SetCollisionFilterMask2(mapInfo.terrainBody.ptr,
                                                         (uint)CollisionFilterGroups.TerrainFilter,
                                                         (uint)CollisionFilterGroups.TerrainMask);

                    // Make sure the new shape is processed.
                    // BulletSimAPI.Activate2(mapInfo.terrainBody.ptr, true);
                    BulletSimAPI.ForceActivationState2(mapInfo.terrainBody.ptr, ActivationState.DISABLE_SIMULATION);

                    m_terrainModified = true;
                };

                // There is the option to do the changes now (we're already in 'taint time'), or
                //     to do the Bullet operations later.
                if (atTaintTime)
                {
                    rebuildOperation();
                }
                else
                {
                    PhysicsScene.TaintedObject("BSScene.UpdateOrCreateTerrain:UpdateExisting", rebuildOperation);
                }
            }
            else
            {
                // We don't know about this terrain so either we are creating a new terrain or
                //    our mega-prim child is giving us a new terrain to add to the phys world

                // if this is a child terrain, calculate a unique terrain id
                uint newTerrainID = id;
                if (newTerrainID >= BSScene.CHILDTERRAIN_ID)
                {
                    newTerrainID = ++m_terrainCount;
                }

                float[] heightMapX = heightMap;
                Vector3 minCoordsX = minCoords;
                Vector3 maxCoordsX = maxCoords;

                DetailLog("{0},UpdateOrCreateTerrain:NewTerrain,call,id={1}, minC={2}, maxC={3}",
                          BSScene.DetailLogZero, newTerrainID, minCoords, minCoords);

                // Code that must happen at taint-time
                BSScene.TaintCallback createOperation = delegate()
                {
                    DetailLog("{0},UpdateOrCreateTerrain:NewTerrain,taint,baseX={1},baseY={2}", BSScene.DetailLogZero, minCoords.X, minCoords.Y);
                    // Create a new mapInfo that will be filled with the new info
                    mapInfo = new BulletHeightMapInfo(id, heightMapX,
                                                      BulletSimAPI.CreateHeightMapInfo2(PhysicsScene.World.ptr, newTerrainID,
                                                                                        minCoordsX, maxCoordsX, heightMapX, TERRAIN_COLLISION_MARGIN));
                    // Put the unfilled heightmap info into the collection of same
                    m_heightMaps.Add(terrainRegionBase, mapInfo);
                    // Build the terrain
                    UpdateOrCreateTerrain(newTerrainID, heightMap, minCoords, maxCoords, true);

                    m_terrainModified = true;
                };

                // If already in taint-time, just call Bullet. Otherwise queue the operations for the safe time.
                if (atTaintTime)
                {
                    createOperation();
                }
                else
                {
                    PhysicsScene.TaintedObject("BSScene.UpdateOrCreateTerrain:NewTerrain", createOperation);
                }
            }
        }