Example #1
0
        /// <summary>
        /// The connections in the set.
        /// </summary>
        /// <param name="index">
        /// The index of the connection. [Limits: 0 &lt;= value &lt; <see cref="Count"/>].
        /// </param>
        /// <returns>The connection.</returns>
        public OffMeshConnection this[int index]
        {
            get
            {
                // Not using parameterized constructor.  Don't need validation cost.
                OffMeshConnection result = new OffMeshConnection();

                result.start     = mVerts[index * 2 + 0];
                result.end       = mVerts[index * 2 + 1];
                result.radius    = mRadii[index];
                result.direction = mDirs[index];
                result.flags     = mFlags[index];
                result.area      = mAreas[index];
                result.userId    = mUserIds[index];

                return(result);
            }

            set
            {
                // Must be validated.
                // Let exceptions be thrown.

                mRadii[index]   = MathUtil.ClampToPositiveNonZero(value.radius);
                mDirs[index]    = (byte)(value.IsBiDirectional ? 1 : 0);
                mAreas[index]   = NMGen.ClampArea(value.area);
                mFlags[index]   = value.flags;
                mUserIds[index] = value.userId;

                // Keep verts last.
                mVerts[index * 2 + 0] = value.start;
                mVerts[index * 2 + 1] = value.end;
            }
        }
Example #2
0
        public static void CombineMeshes(Queue <CombineInstance> items
                                         , byte area
                                         , InputGeometryCompiler compiler)
        {
            const int MaxTris = 65000;

            List <CombineInstance> combineInstancesPart = new List <CombineInstance>();

            byte[] areas = NMGen.CreateAreaBuffer(MaxTris, area);

            while (items.Count != 0)
            {
                int vertCount = 0;

                while (items.Count > 0 &&
                       (vertCount + items.Peek().mesh.vertexCount < MaxTris))
                {
                    vertCount += items.Peek().mesh.vertexCount;
                    combineInstancesPart.Add(items.Dequeue());
                }

                Mesh meshPart = new Mesh();

                meshPart.CombineMeshes(combineInstancesPart.ToArray(), true, true);

                UnityEngine.Vector3[] vs  = meshPart.vertices;
                Vector3[]             vvs = VectorHelper.ToVector3Array(ref vs);
                compiler.AddTriangles(vvs, meshPart.vertexCount
                                      , meshPart.triangles, areas, meshPart.triangles.Length / 3);

                Object.DestroyImmediate(meshPart);

                combineInstancesPart.Clear();
            }
        }
Example #3
0
        /// <summary>
        /// Creates a new tile set.
        /// </summary>
        /// <remarks>
        /// <para>
        /// The bounds is normally based on the desired origin of the navigation mesh
        /// and the maximum bounds of the input geometry.
        /// </para>
        /// </remarks>
        /// <param name="boundsMin">The minimum AABB bounds of the set.</param>
        /// <param name="boundsMax">The maximum AABB counds of the set.</param>
        /// <param name="config">The shared NMGen configuration.</param>
        /// <param name="geom">The input geometry.</param>
        /// <returns>A new tile set, or null on error.</returns>
        public static TileSetDefinition Create(Vector3 boundsMin, Vector3 boundsMax
                                               , NMGenParams config
                                               , InputGeometry geom)
        {
            if (config == null || !config.IsValid() ||
                !TriangleMesh.IsBoundsValid(boundsMin, boundsMax) ||
                geom == null ||
                config.tileSize <= 0)
            {
                return(null);
            }

            int w;
            int d;

            NMGen.DeriveSizeOfTileGrid(boundsMin, boundsMax
                                       , config.XZCellSize
                                       , config.tileSize
                                       , out w, out d);

            if (w < 1 || d < 1)
            {
                return(null);
            }

            return(new TileSetDefinition(w, d, boundsMin, boundsMax, config, geom));
        }
Example #4
0
        /// <summary>
        /// Creates a builder.
        /// </summary>
        /// <remarks>
        /// <para>
        /// No validation is performed and the builder will use the parameters directly
        /// during the build.
        /// </para>
        /// <para>
        /// Builders created using this method are not guarenteed to produce a usable result.
        /// </para>
        /// <para>
        /// It is the responsibility of the caller to ensure thread safely if
        /// <paramref name="isThreadSafe"/> is set to true.
        /// </para>
        /// <para>
        /// <b>Warning:</b> If walkable slope if greather than zero then the builder will
        /// apply <see cref="NMGen.ClearUnwalkableTriangles"/> directly to the areas parameter.
        /// </para>
        /// </remarks>
        /// <param name="mesh">The triangle mesh to use for the build.</param>
        /// <param name="areas">The triangle areas. (Null not permitted.)</param>
        /// <param name="walkableSlope">The walkable slope.
        /// (See <see cref="NMGenParams.WalkableSlope"/>)</param>
        /// <param name="isThreadSafe">True if the builder can run safely on its own thread.</param>
        /// <returns>A builder, or null on error.</returns>
        public static InputGeometryBuilder UnsafeCreate(TriangleMesh mesh
                                                        , byte[] areas
                                                        , float walkableSlope
                                                        , bool isThreadSafe)
        {
            if (mesh == null || areas == null || mesh.triCount < 0)
            {
                return(null);
            }

            walkableSlope = System.Math.Min(NMGen.MaxAllowedSlope, walkableSlope);

            if (walkableSlope > 0)
            {
                BuildContext context = new BuildContext();
                if (!NMGen.ClearUnwalkableTriangles(context, mesh, walkableSlope, areas))
                {
                    return(null);
                }
            }

            ChunkyTriMeshBuilder builder = ChunkyTriMeshBuilder.Create(mesh, areas, 32768);

            if (builder == null)
            {
                return(null);
            }

            Vector3 bmin;
            Vector3 bmax;

            mesh.GetBounds(out bmin, out bmax);

            return(new InputGeometryBuilder(builder, bmin, bmax, isThreadSafe));
        }
Example #5
0
        /// <summary>
        /// Creates a thread-safe, fully validated builder.
        /// </summary>
        /// <remarks>
        /// <para>
        /// The input mesh and area parameters are fully validated.
        /// </para>
        /// <para>
        /// Will return null if there are zero triangles.
        /// </para>
        /// <para>
        /// All triangleswill default to <see cref="NMGen.MaxArea"/> if the
        /// <paramref name="areas"/> parameter is null.
        /// </para>
        /// <para>
        /// If walkable slope if greather than zero then the builder will apply
        /// <see cref="NMGen.ClearUnwalkableTriangles"/> to the areas.
        /// </para>
        /// </remarks>
        /// <param name="mesh">The triangle mesh to use for the build.</param>
        /// <param name="areas">The triangle areas. (Null permitted.)</param>
        /// <param name="walkableSlope">The walkable slope.
        /// (See <see cref="NMGenParams.WalkableSlope"/>)</param>
        /// <returns>A thread-safe, fully validated builder. Or null on error.</returns>
        public static InputGeometryBuilder Create(TriangleMesh mesh
                                                  , byte[] areas
                                                  , float walkableSlope)
        {
            if (!IsValid(mesh, areas))
            {
                return(null);
            }

            TriangleMesh lmesh = new TriangleMesh(mesh.vertCount, mesh.triCount);

            lmesh.triCount  = mesh.triCount;
            lmesh.vertCount = mesh.vertCount;

            System.Array.Copy(mesh.verts, 0, lmesh.verts, 0, lmesh.verts.Length);
            System.Array.Copy(mesh.tris, 0, lmesh.tris, 0, lmesh.tris.Length);

            byte[] lareas;
            if (areas == null)
            {
                lareas = NMGen.CreateDefaultAreaBuffer(mesh.triCount);
            }
            else
            {
                lareas = new byte[mesh.triCount];
                System.Array.Copy(areas, 0, lareas, 0, lareas.Length);
            }

            return(UnsafeCreate(lmesh, lareas, walkableSlope, true));
        }
        /// <summary>
        /// Adds an arbitrary group of triangles.
        /// </summary>
        /// <remarks>
        /// <para>
        /// All triangles will default to <see cref="NMGen.MaxArea"/> if the
        /// <paramref name="areas"/> parameter is null.
        /// </para>
        /// </remarks>
        /// <param name="verts">
        /// The triangle vertices. [Length: >= <paramref name="vertCount"/>]
        /// </param>
        /// <param name="vertCount">The number of vertices. [Length: >= 3]</param>
        /// <param name="tris">
        /// The triangles. [(vertAIndex, vertBIndex, vertCIndex) * triCount]
        /// [Length: >= 3 * <paramref name="triCount"/>]
        /// </param>
        /// <param name="areas">
        /// The triangle areas. (Optional) [Length: >= <paramref name="triCount"/>]
        /// </param>
        /// <param name="triCount">The number of triangles. [Limit: > 0]</param>
        /// <returns>True if the triangles were successfully added.</returns>
        public bool AddTriangles(Vector3[] verts, int vertCount
                                 , int[] tris, byte[] areas, int triCount)
        {
            if (triCount < 1 || vertCount < 3 ||
                verts == null || verts.Length < vertCount ||
                tris == null || tris.Length < triCount * 3 ||
                areas != null && areas.Length < triCount)
            {
                return(false);
            }

            if (areas == null)
            {
                areas = NMGen.CreateDefaultAreaBuffer(triCount);
            }

            int iVertOffset = mVerts.Count;

            if (vertCount == verts.Length)
            {
                mVerts.AddRange(verts);
            }
            else
            {
                mVerts.Capacity += vertCount;

                for (int p = 0; p < vertCount; p++)
                {
                    mVerts.Add(verts[p]);
                }
            }

            int length = triCount * 3;

            mTris.Capacity += length;

            for (int p = 0; p < length; p++)
            {
                mTris.Add(tris[p] + iVertOffset);
            }

            if (areas.Length == triCount)
            {
                mAreas.AddRange(areas);
            }
            else
            {
                mAreas.Capacity += triCount;

                for (int i = 0; i < triCount; i++)
                {
                    mAreas.Add(areas[i]);
                }
            }

            return(true);
        }
Example #7
0
    private void Compile(InputBuildContext context)
    {
        context.info.compilerCount++;

        InputGeometryCompiler compiler = context.geomCompiler;
        List <Component>      items    = context.components;
        List <byte>           areas    = context.areas;

        for (int i = 0; i < items.Count; i++)
        {
            Component item = items[i];

            if (item is Terrain)
            {
                Terrain terrain = (Terrain)item;

                if (terrain.terrainData != terrainData)
                {
                    continue;
                }

                TriangleMesh mesh   = TerrainUtil.TriangulateSurface(terrain, mResolution);
                byte[]       lareas = NMGen.CreateAreaBuffer(mesh.triCount, areas[i]);

                if (compiler.AddTriangles(mesh, lareas))
                {
                    string msg = string.Format("Compiled the {0} terrain surface. Triangles: {1}"
                                               , terrain.name, mesh.triCount);

                    context.Log(msg, this);
                }
                else
                {
                    string msg =
                        string.Format("Compiler rejected mesh for the {0} terrain.", terrain.name);

                    context.LogError(msg, this);

                    return;
                }

                if (includeTrees)
                {
                    int before = compiler.TriCount;

                    TerrainUtil.TriangluateTrees(terrain, areas[i], compiler);

                    string msg = string.Format("Compiled the {0} terrain trees. Triangles: {1}"
                                               , terrain.name, compiler.TriCount - before);

                    context.Log(msg, this);
                }

                break;
            }
        }
    }
Example #8
0
 /// <summary>
 /// Add a connection.
 /// </summary>
 /// <remarks>
 /// <para>
 /// The connection fields are auto-clamped to valid values.
 /// </para>
 /// </remarks>
 /// <param name="connection">The connection to add.</param>
 public void Add(OffMeshConnection connection)
 {
     // Must be validated.
     mVerts.Add(connection.start);
     mVerts.Add(connection.end);
     mRadii.Add(MathUtil.ClampToPositiveNonZero(connection.radius));
     mDirs.Add((byte)(connection.IsBiDirectional ? 1 : 0));
     mAreas.Add(NMGen.ClampArea(connection.area));
     mFlags.Add(connection.flags);
     mUserIds.Add(connection.userId);
 }
Example #9
0
 /// <summary>
 /// Add a connection.
 /// </summary>
 /// <remarks>
 /// <para>
 /// All values are auto-clamped to valid values.
 /// </para>
 /// </remarks>
 /// <param name="start">The connection start point.</param>
 /// <param name="end">The connection end point.</param>
 /// <param name="radius">The radius of the connection vertices.</param>
 /// <param name="isBidirectional">True if the connection can be traversed in both
 /// directions. (Start to end, end to start.)</param>
 /// <param name="area">The connection area id.</param>
 /// <param name="flags">The connection flags.</param>
 /// <param name="userId">The connection user id.</param>
 public void Add(Vector3 start, Vector3 end, float radius
                 , bool isBidirectional, byte area, ushort flags, uint userId)
 {
     mVerts.Add(start);
     mVerts.Add(end);
     mRadii.Add(System.Math.Max(MathUtil.Epsilon, radius));
     mDirs.Add((byte)(isBidirectional ? 1 : 0));
     mAreas.Add(NMGen.ClampArea(area));
     mFlags.Add(flags);
     mUserIds.Add(userId);
 }
Example #10
0
        /// <summary>
        /// Creates a new mapper.
        /// </summary>
        /// <remarks>
        /// <para>
        /// Will return null if parameters are invalid. Must have at least one valid area/flag map.
        /// </para>
        /// </remarks>
        /// <param name="name">The processor name.</param>
        /// <param name="priority">The processor priority.</param>
        /// <param name="areas">The areas check for.</param>
        /// <param name="flags">The flags to apply.</param>
        /// <returns>A new marker, or null on error.</returns>
        public static AreaFlagMapper Create(string name, int priority, byte[] areas, ushort[] flags)
        {
            if (areas == null || flags == null || areas.Length != flags.Length ||
                !NMGen.IsValidAreaBuffer(areas, areas.Length))
            {
                return(null);
            }

            return(new AreaFlagMapper(name, priority
                                      , (byte[])areas.Clone(), (ushort[])flags.Clone()));
        }
Example #11
0
        /// <summary>
        /// Validates the mesh and areas.
        /// </summary>
        /// <remarks>
        /// <para>
        /// The <paramref name="areas"/> parameter is validated only if it is non-null.
        /// </para>
        /// </remarks>
        /// <param name="mesh">The mesh to validate.</param>
        /// <param name="areas">The area to validate. (If non-null.)</param>
        /// <returns>True if the structure and content of the parameters are considered valid.
        /// </returns>
        public static bool IsValid(TriangleMesh mesh, byte[] areas)
        {
            if (mesh == null || mesh.triCount == 0 || !TriangleMesh.IsValid(mesh, true))
            {
                return(false);
            }

            if (areas != null && (areas.Length != mesh.triCount ||
                                  !NMGen.IsValidAreaBuffer(areas, mesh.triCount)))
            {
                return(false);
            }

            return(true);
        }
        private void BuildHeightfield()
        {
            int width;
            int depth;

            NMGen.DeriveSizeOfCellGrid(mTileConfig.BoundsMin
                                       , mTileConfig.BoundsMax
                                       , mConfig.XZCellSize
                                       , out width
                                       , out depth);

            Heightfield hf = Heightfield.Create(width, depth
                                                , mTileConfig.BoundsMin, mTileConfig.BoundsMax
                                                , mConfig.XZCellSize, mConfig.YCellSize);


            hf.AddTriangles(mBuildContext
                            , mGeometry.Mesh
                            , mTileConfig.boundsMin
                            , mTileConfig.boundsMax
                            , mConfig.WalkableStep); // Merge for any spans less than step.


            if (hf.GetSpanCount() < 1)
            {
                FinalizeNoResult("Complete at heightfield build. No spans.");
                return;
            }


            mBuildContext.Heightfield = hf;

            if (PostProcess() && PostHeightfieldCheck())
            {
                mBuildContext.Log("Voxelized triangles. Span count: " + hf.GetSpanCount(), this);
                mState = NMGenState.CompactFieldBuild;
            }
        }
Example #13
0
    internal bool InitializeBuild(BuildContext context, bool fromTarget)
    {
        Navmesh navmesh = null;

        if (fromTarget)
        {
            if (!CanLoadFromTarget(context, true))
            {
                return(false);
            }

            navmesh = BuildTarget.GetNavmesh();

            SetConfigFromTargetIntern(navmesh);
        }

        mIsDirty = true;

        // Note: If loading from the target, the tile size was already validated.
        // So it won't trigger this adjustment.
        if (mConfig.TileSize != 0 &&
            mConfig.TileSize < MinAllowedTileSize)
        {
            string msg = string.Format("Tile size too small. Reverting tile size from"
                                       + " {0} to 0 (non-tiled). Minimum tile size is {1}"
                                       , mConfig.TileSize, MinAllowedTileSize);

            context.LogWarning(msg, this);

            mConfig.TileSize = 0;
        }

        if (mConfig.TileSize == 0)
        {
            mBuildData.Resize(1, 1);
        }
        else
        {
            // Need to check to see if the the build is truly tiled.

            int w;
            int d = 0;

            if (navmesh == null)
            {
                NMGen.DeriveSizeOfTileGrid(mBoundsMin, mBoundsMax
                                           , mConfig.XZCellSize, mConfig.TileSize
                                           , out w, out d);
            }
            else
            {
                // Existing navmesh will always be tiled.
                w = 2;
            }

            if (w > 1 || d > 1)
            {
                mTileSet = TileSetDefinition.Create(mBoundsMin, mBoundsMax
                                                    , mConfig.GetConfig()
                                                    , mInputGeom);

                if (mTileSet == null)
                {
                    context.LogError("Create tile build definition: Unexpected error."
                                     + " Invalid input data or configuration."
                                     , this);

                    return(false);
                }

                mBuildData.Resize(mTileSet.Width, mTileSet.Depth);
            }
            else
            {
                // Not really tiled.
                mBuildData.Resize(1, 1);
            }
        }

        if (navmesh != null)
        {
            // Need to load the tiles from existing navmesh.

            NavmeshTileExtract[] tiles;
            NavmeshParams        meshConfig;

            NavStatus status = Navmesh.ExtractTileData(navmesh.GetSerializedMesh()
                                                       , out tiles
                                                       , out meshConfig);

            if ((status & NavStatus.Failure) != 0)
            {
                context.LogError("Could not extract the tile data from the target's"
                                 + " navigation mesh. (Can't initialize from build target.)"
                                 , this);
                return(false);
            }

            foreach (NavmeshTileExtract tile in tiles)
            {
                int polyCount = tile.header.polyCount;

                if (polyCount == 0)
                {
                    continue;
                }

                int tx = tile.header.tileX;
                int tz = tile.header.tileZ;

                if (tx >= mBuildData.Width || tz >= mBuildData.Depth)
                {
                    // Shouldn't happen.  Probably indicates in internal error
                    // of some type.

                    string msg = string.Format("The existing navigation mesh"
                                               + " contains a tile outside the expected range. Ignoring"
                                               + " the tile. (Tile: [{0},{1}])"
                                               , tx, tz);

                    context.LogWarning(msg, this);

                    continue;
                }

                mBuildData.SetAsBaked(tx, tz, tile.data, polyCount);
            }
        }

        // Note: Dirty state was set earlier in the code.

        return(true);
    }
Example #14
0
 /// <summary>
 /// Constructor
 /// </summary>
 /// <param name="name">The processor name.</param>
 /// <param name="priority">The processor priority.</param>
 /// <param name="area">The area to apply.</param>
 public AreaMarker(string name, int priority, byte area)
     : base(name, priority)
 {
     mArea = NMGen.ClampArea(area);
 }