Beispiel #1
0
        void CreateFilter()
        {
            NavTerrain includeFlags = 0;
            NavTerrain excludeFlags = 0;

            if (_sourceUnit.IsTypeId(TypeId.Unit))
            {
                Creature creature = _sourceUnit.ToCreature();
                if (creature.CanWalk())
                {
                    includeFlags |= NavTerrain.Ground;
                }

                if (creature.CanSwim())
                {
                    includeFlags |= (NavTerrain.Water | NavTerrain.Magma | NavTerrain.Slime);
                }
            }
            else
            {
                includeFlags = (NavTerrain.Ground | NavTerrain.Water | NavTerrain.Magma | NavTerrain.Slime);
            }

            _filter.setIncludeFlags((ushort)includeFlags);
            _filter.setExcludeFlags((ushort)excludeFlags);

            UpdateFilter();
        }
Beispiel #2
0
        void UpdateFilter()
        {
            // allow creatures to cheat and use different movement types if they are moved
            // forcefully into terrain they can't normally move in
            if (_sourceUnit.IsInWater() || _sourceUnit.IsUnderWater())
            {
                NavTerrain includedFlags = (NavTerrain)_filter.getIncludeFlags();
                includedFlags |= GetNavTerrain(_sourceUnit.GetPositionX(), _sourceUnit.GetPositionY(), _sourceUnit.GetPositionZ());

                _filter.setIncludeFlags((ushort)includedFlags);
            }
        }
Beispiel #3
0
        /**************************************************************************/
        bool loadMap(uint mapID, uint tileX, uint tileY, MeshData meshData, Spot portion)
        {
            string mapFileName = $"maps/{mapID:D4}_{tileY:D2}_{tileX:D2}.map";

            if (!File.Exists(mapFileName))
            {
                return(false);
            }

            using (BinaryReader reader = new BinaryReader(File.Open(mapFileName, FileMode.Open, FileAccess.Read)))
            {
                map_fileheader fheader = reader.ReadStruct <map_fileheader>();
                if (fheader.versionMagic != SharedConst.MAP_VERSION_MAGIC)
                {
                    Console.WriteLine($"{mapFileName} is the wrong version, please extract new .map files");
                    return(false);
                }

                bool haveTerrain = false;
                bool haveLiquid  = false;

                reader.BaseStream.Seek(fheader.heightMapOffset, SeekOrigin.Begin);
                map_heightHeader hheader = reader.ReadStruct <map_heightHeader>();
                if (hheader.fourcc == 1413957709)
                {
                    haveTerrain = !Convert.ToBoolean(hheader.flags & (uint)MapHeightFlags.NoHeight);
                    haveLiquid  = fheader.liquidMapOffset != 0;// && !m_skipLiquid;
                }

                // no data in this map file
                if (!haveTerrain && !haveLiquid)
                {
                    return(false);
                }

                // data used later
                byte[][][] holes = new byte[16][][];
                for (var i = 0; i < 16; ++i)
                {
                    holes[i] = new byte[16][];
                    for (var x = 0; x < 16; ++x)
                    {
                        holes[i][x] = new byte[8];
                    }
                }
                byte[][] liquid_type = new byte[16][];
                for (var i = 0; i < 16; ++i)
                {
                    liquid_type[i] = new byte[16];
                }

                List <int> ltriangles = new List <int>();
                List <int> ttriangles = new List <int>();

                // terrain data
                if (haveTerrain)
                {
                    float   heightMultiplier;
                    float[] V9       = new float[SharedConst.V9_SIZE_SQ];
                    float[] V8       = new float[SharedConst.V8_SIZE_SQ];
                    int     expected = SharedConst.V9_SIZE_SQ + SharedConst.V8_SIZE_SQ;

                    if (Convert.ToBoolean(hheader.flags & (uint)MapHeightFlags.AsInt8))
                    {
                        byte[] v9    = new byte[SharedConst.V9_SIZE_SQ];
                        byte[] v8    = new byte[SharedConst.V8_SIZE_SQ];
                        int    count = 0;
                        count += reader.Read(v9, 0, SharedConst.V9_SIZE_SQ);
                        count += reader.Read(v8, 0, SharedConst.V8_SIZE_SQ);
                        if (count != expected)
                        {
                            Console.WriteLine($"TerrainBuilder.loadMap: Failed to read some data expected {expected}, read {count}");
                        }

                        heightMultiplier = (hheader.gridMaxHeight - hheader.gridHeight) / 255;

                        for (int i = 0; i < SharedConst.V9_SIZE_SQ; ++i)
                        {
                            V9[i] = (float)v9[i] * heightMultiplier + hheader.gridHeight;
                        }

                        for (int i = 0; i < SharedConst.V8_SIZE_SQ; ++i)
                        {
                            V8[i] = (float)v8[i] * heightMultiplier + hheader.gridHeight;
                        }
                    }
                    else if (Convert.ToBoolean(hheader.flags & (uint)MapHeightFlags.AsInt16))
                    {
                        ushort[] v9 = new ushort[SharedConst.V9_SIZE_SQ];
                        ushort[] v8 = new ushort[SharedConst.V8_SIZE_SQ];

                        for (var i = 0; i < SharedConst.V9_SIZE_SQ; ++i)
                        {
                            v9[i] = reader.ReadUInt16();
                        }

                        for (var i = 0; i < SharedConst.V8_SIZE_SQ; ++i)
                        {
                            v8[i] = reader.ReadUInt16();
                        }

                        heightMultiplier = (hheader.gridMaxHeight - hheader.gridHeight) / 65535;

                        for (int i = 0; i < SharedConst.V9_SIZE_SQ; ++i)
                        {
                            V9[i] = (float)v9[i] * heightMultiplier + hheader.gridHeight;
                        }

                        for (int i = 0; i < SharedConst.V8_SIZE_SQ; ++i)
                        {
                            V8[i] = (float)v8[i] * heightMultiplier + hheader.gridHeight;
                        }
                    }
                    else
                    {
                        for (var i = 0; i < SharedConst.V9_SIZE_SQ; ++i)
                        {
                            V9[i] = reader.ReadSingle();
                        }

                        for (var i = 0; i < SharedConst.V8_SIZE_SQ; ++i)
                        {
                            V8[i] = reader.ReadSingle();
                        }
                    }

                    // hole data
                    if (fheader.holesSize != 0)
                    {
                        reader.BaseStream.Seek(fheader.holesOffset, SeekOrigin.Begin);

                        int readCount = 0;
                        for (var i = 0; i < 16; ++i)
                        {
                            for (var x = 0; x < 16; ++x)
                            {
                                for (var c = 0; c < 8; ++c)
                                {
                                    if (readCount == fheader.holesSize)
                                    {
                                        break;
                                    }

                                    holes[i][x][c] = reader.ReadByte();
                                }
                            }
                        }
                    }

                    int   count1  = meshData.solidVerts.Count / 3;
                    float xoffset = ((float)tileX - 32) * SharedConst.GRID_SIZE;
                    float yoffset = ((float)tileY - 32) * SharedConst.GRID_SIZE;

                    float[] coord = new float[3];

                    for (int i = 0; i < SharedConst.V9_SIZE_SQ; ++i)
                    {
                        getHeightCoord(i, Grid.V9, xoffset, yoffset, ref coord, ref V9);
                        meshData.solidVerts.Add(coord[0]);
                        meshData.solidVerts.Add(coord[2]);
                        meshData.solidVerts.Add(coord[1]);
                    }

                    for (int i = 0; i < SharedConst.V8_SIZE_SQ; ++i)
                    {
                        getHeightCoord(i, Grid.V8, xoffset, yoffset, ref coord, ref V8);
                        meshData.solidVerts.Add(coord[0]);
                        meshData.solidVerts.Add(coord[2]);
                        meshData.solidVerts.Add(coord[1]);
                    }

                    int[] indices = { 0, 0, 0 };
                    int   loopStart = 0, loopEnd = 0, loopInc = 0;
                    getLoopVars(portion, ref loopStart, ref loopEnd, ref loopInc);
                    for (int i = loopStart; i < loopEnd; i += loopInc)
                    {
                        for (Spot j = Spot.Top; j <= Spot.Bottom; j += 1)
                        {
                            getHeightTriangle(i, j, indices);
                            ttriangles.Add(indices[2] + count1);
                            ttriangles.Add(indices[1] + count1);
                            ttriangles.Add(indices[0] + count1);
                        }
                    }
                }

                // liquid data
                if (haveLiquid)
                {
                    reader.BaseStream.Seek(fheader.liquidMapOffset, SeekOrigin.Begin);
                    map_liquidHeader lheader = reader.ReadStruct <map_liquidHeader>();

                    float[] liquid_map = null;
                    if (!Convert.ToBoolean(lheader.flags & 0x0001))
                    {
                        for (var i = 0; i < 16; ++i)
                        {
                            for (var x = 0; x < 16; ++x)
                            {
                                liquid_type[i][x] = reader.ReadByte();
                            }
                        }
                    }

                    if (!Convert.ToBoolean(lheader.flags & 0x0002))
                    {
                        int toRead = lheader.width * lheader.height;
                        liquid_map = new float[toRead];
                        for (var i = 0; i < toRead; ++i)
                        {
                            liquid_map[i] = reader.ReadSingle();
                        }
                    }

                    if (liquid_map != null)
                    {
                        int   count   = meshData.liquidVerts.Count / 3;
                        float xoffset = (tileX - 32) * SharedConst.GRID_SIZE;
                        float yoffset = (tileY - 32) * SharedConst.GRID_SIZE;

                        float[] coord = new float[3];
                        int     row, col;

                        // generate coordinates
                        if (!Convert.ToBoolean(lheader.flags & 0x0002))
                        {
                            int j = 0;
                            for (int i = 0; i < SharedConst.V9_SIZE_SQ; ++i)
                            {
                                row = i / SharedConst.V9_SIZE;
                                col = i % SharedConst.V9_SIZE;

                                if (row < lheader.offsetY || row >= lheader.offsetY + lheader.height ||
                                    col < lheader.offsetX || col >= lheader.offsetX + lheader.width)
                                {
                                    // dummy vert using invalid height
                                    meshData.liquidVerts.Add((xoffset + col * SharedConst.GRID_PART_SIZE) * -1);
                                    meshData.liquidVerts.Add(SharedConst.INVALID_MAP_LIQ_HEIGHT);
                                    meshData.liquidVerts.Add((yoffset + row * SharedConst.GRID_PART_SIZE) * -1);
                                    continue;
                                }

                                getLiquidCoord(i, j, xoffset, yoffset, ref coord, ref liquid_map);
                                meshData.liquidVerts.Add(coord[0]);
                                meshData.liquidVerts.Add(coord[2]);
                                meshData.liquidVerts.Add(coord[1]);
                                j++;
                            }
                        }
                        else
                        {
                            for (int i = 0; i < SharedConst.V9_SIZE_SQ; ++i)
                            {
                                row = i / SharedConst.V9_SIZE;
                                col = i % SharedConst.V9_SIZE;
                                meshData.liquidVerts.Add((xoffset + col * SharedConst.GRID_PART_SIZE) * -1);
                                meshData.liquidVerts.Add(lheader.liquidLevel);
                                meshData.liquidVerts.Add((yoffset + row * SharedConst.GRID_PART_SIZE) * -1);
                            }
                        }


                        int[] indices = { 0, 0, 0 };
                        int   loopStart = 0, loopEnd = 0, loopInc = 0, triInc = Spot.Bottom - Spot.Top;
                        getLoopVars(portion, ref loopStart, ref loopEnd, ref loopInc);

                        // generate triangles
                        for (int i = loopStart; i < loopEnd; i += loopInc)
                        {
                            for (Spot j = Spot.Top; j <= Spot.Bottom; j += triInc)
                            {
                                getHeightTriangle(i, j, indices, true);
                                ltriangles.Add(indices[2] + count);
                                ltriangles.Add(indices[1] + count);
                                ltriangles.Add(indices[0] + count);
                            }
                        }
                    }
                }

                // now that we have gathered the data, we can figure out which parts to keep:
                // liquid above ground, ground above liquid
                int  loopStart1 = 0, loopEnd1 = 0, loopInc1 = 0, tTriCount = 4;
                bool useTerrain, useLiquid;

                float[] lverts = meshData.liquidVerts.ToArray();
                int[]   ltris  = ltriangles.ToArray();

                float[] tverts = meshData.solidVerts.ToArray();
                int[]   ttris  = ttriangles.ToArray();

                if ((ltriangles.Count + ttriangles.Count) == 0)
                {
                    return(false);
                }

                // make a copy of liquid vertices
                // used to pad right-bottom frame due to lost vertex data at extraction
                float[] lverts_copy = null;
                if (meshData.liquidVerts.Count != 0)
                {
                    lverts_copy = new float[meshData.liquidVerts.Count];
                    Array.Copy(lverts, lverts_copy, meshData.liquidVerts.Count);
                }

                getLoopVars(portion, ref loopStart1, ref loopEnd1, ref loopInc1);
                for (int i = loopStart1; i < loopEnd1; i += loopInc1)
                {
                    for (int j = 0; j < 2; ++j)
                    {
                        // default is true, will change to false if needed
                        useTerrain = true;
                        useLiquid  = true;
                        NavTerrain navTerrain = 0;
                        // FIXME: "warning: the address of ‘liquid_type’ will always evaluate as ‘true’"

                        // if there is no liquid, don't use liquid
                        if (meshData.liquidVerts.Count == 0 || ltriangles.Count == 0)
                        {
                            useLiquid = false;
                        }
                        else
                        {
                            LiquidTypeMask liquidType = (LiquidTypeMask)getLiquidType(i, liquid_type);
                            switch (liquidType)
                            {
                            default:
                                useLiquid = false;
                                break;

                            case LiquidTypeMask.Water:
                            case LiquidTypeMask.Ocean:
                                // merge different types of water
                                navTerrain = NavTerrain.Water;
                                break;

                            case LiquidTypeMask.Magma:
                                navTerrain = NavTerrain.Magma;
                                break;

                            case LiquidTypeMask.Slime:
                                navTerrain = NavTerrain.Slime;
                                break;

                            case LiquidTypeMask.DarkWater:
                                // players should not be here, so logically neither should creatures
                                useTerrain = false;
                                useLiquid  = false;
                                break;
                            }
                        }

                        // if there is no terrain, don't use terrain
                        if (ttriangles.Count == 0)
                        {
                            useTerrain = false;
                        }

                        // while extracting ADT data we are losing right-bottom vertices
                        // this code adds fair approximation of lost data
                        if (useLiquid)
                        {
                            float quadHeight = 0;
                            uint  validCount = 0;
                            for (uint idx = 0; idx < 3; idx++)
                            {
                                float h = lverts_copy[ltris[idx] * 3 + 1];
                                if (h != SharedConst.INVALID_MAP_LIQ_HEIGHT && h < SharedConst.INVALID_MAP_LIQ_HEIGHT_MAX)
                                {
                                    quadHeight += h;
                                    validCount++;
                                }
                            }

                            // update vertex height data
                            if (validCount > 0 && validCount < 3)
                            {
                                quadHeight /= validCount;
                                for (uint idx = 0; idx < 3; idx++)
                                {
                                    float h = lverts[ltris[idx] * 3 + 1];
                                    if (h == SharedConst.INVALID_MAP_LIQ_HEIGHT || h > SharedConst.INVALID_MAP_LIQ_HEIGHT_MAX)
                                    {
                                        lverts[ltris[idx] * 3 + 1] = quadHeight;
                                    }
                                }
                            }

                            // no valid vertexes - don't use this poly at all
                            if (validCount == 0)
                            {
                                useLiquid = false;
                            }
                        }

                        // if there is a hole here, don't use the terrain
                        if (useTerrain && fheader.holesSize != 0)
                        {
                            useTerrain = !isHole(i, holes);
                        }

                        // we use only one terrain kind per quad - pick higher one
                        if (useTerrain && useLiquid)
                        {
                            float minLLevel = SharedConst.INVALID_MAP_LIQ_HEIGHT_MAX;
                            float maxLLevel = SharedConst.INVALID_MAP_LIQ_HEIGHT;
                            for (uint x = 0; x < 3; x++)
                            {
                                float h = lverts[ltris[x] * 3 + 1];
                                if (minLLevel > h)
                                {
                                    minLLevel = h;
                                }

                                if (maxLLevel < h)
                                {
                                    maxLLevel = h;
                                }
                            }

                            float maxTLevel = SharedConst.INVALID_MAP_LIQ_HEIGHT;
                            float minTLevel = SharedConst.INVALID_MAP_LIQ_HEIGHT_MAX;
                            for (uint x = 0; x < 6; x++)
                            {
                                float h = tverts[ttris[x] * 3 + 1];
                                if (maxTLevel < h)
                                {
                                    maxTLevel = h;
                                }

                                if (minTLevel > h)
                                {
                                    minTLevel = h;
                                }
                            }

                            // terrain under the liquid?
                            if (minLLevel > maxTLevel)
                            {
                                useTerrain = false;
                            }

                            //liquid under the terrain?
                            if (minTLevel > maxLLevel)
                            {
                                useLiquid = false;
                            }
                        }

                        // store the result
                        if (useLiquid)
                        {
                            meshData.liquidType.Add((byte)navTerrain);
                            for (int k = 0; k < 3; ++k)
                            {
                                meshData.liquidTris.Add(ltris[k]);
                            }
                        }

                        if (useTerrain)
                        {
                            for (int k = 0; k < 3 * tTriCount / 2; ++k)
                            {
                                meshData.solidTris.Add(ttris[k]);
                            }
                        }

                        ltris = ltris.Skip(3).ToArray();
                        ttris = ttris.Skip(3 * tTriCount / 2).ToArray();
                    }
                }
            }
            return(meshData.solidTris.Count != 0 || meshData.liquidTris.Count != 0);
        }
Beispiel #4
0
        /**************************************************************************/
        public bool loadVMap(uint mapID, uint tileX, uint tileY, MeshData meshData)
        {
            var            vmapManager = new VMapManager2();
            VMAPLoadResult result      = vmapManager.loadMap("vmaps", mapID, tileX, tileY);
            bool           retval      = false;

            do
            {
                if (result == 0)
                {
                    break;
                }

                Dictionary <uint, StaticMapTree> instanceTrees;
                vmapManager.getInstanceMapTree(out instanceTrees);

                if (instanceTrees[mapID] == null)
                {
                    break;
                }

                ModelInstance[] models = null;
                uint            count;
                instanceTrees[mapID].getModelInstances(out models, out count);

                if (models == null)
                {
                    break;
                }

                for (uint i = 0; i < count; ++i)
                {
                    ModelInstance instance = models[i];
                    if (instance == null)
                    {
                        continue;
                    }

                    // model instances exist in tree even though there are instances of that model in this tile
                    WorldModel worldModel = instance.getWorldModel();
                    if (worldModel == null)
                    {
                        continue;
                    }

                    // now we have a model to add to the meshdata
                    retval = true;

                    List <GroupModel> groupModels;
                    worldModel.getGroupModels(out groupModels);

                    // all M2s need to have triangle indices reversed
                    bool isM2 = instance.name.Contains(".m2") || instance.name.Contains(".M2");

                    // transform data
                    float   scale    = instance.iScale;
                    Matrix3 rotation = Matrix3.fromEulerAnglesXYZ(MathF.PI * instance.iRot.Z / -180.0f, MathF.PI * instance.iRot.X / -180.0f, MathF.PI * instance.iRot.Y / -180.0f);
                    Vector3 position = instance.iPos;
                    position.X -= 32 * SharedConst.GRID_SIZE;
                    position.Y -= 32 * SharedConst.GRID_SIZE;

                    foreach (var it in groupModels)
                    {
                        List <Vector3>      tempVertices;
                        List <Vector3>      transformedVertices = new List <Vector3>();
                        List <MeshTriangle> tempTriangles;
                        WmoLiquid           liquid = null;

                        it.getMeshData(out tempVertices, out tempTriangles, out liquid);

                        // first handle collision mesh
                        transform(tempVertices.ToArray(), transformedVertices, scale, rotation, position);

                        int offset = meshData.solidVerts.Count / 3;

                        copyVertices(transformedVertices.ToArray(), meshData.solidVerts);
                        copyIndices(tempTriangles, meshData.solidTris, offset, isM2);

                        // now handle liquid data
                        if (liquid != null)
                        {
                            List <Vector3> liqVerts = new List <Vector3>();
                            List <uint>    liqTris = new List <uint>();
                            uint           tilesX, tilesY, vertsX, vertsY;
                            Vector3        corner;
                            liquid.getPosInfo(out tilesX, out tilesY, out corner);
                            vertsX = tilesX + 1;
                            vertsY = tilesY + 1;
                            byte[]     flags = liquid.iFlags;
                            float[]    data  = liquid.iHeight;
                            NavTerrain type  = NavTerrain.Empty;

                            // convert liquid type to NavTerrain
                            switch (liquid.iType & 3)
                            {
                            case 0:
                            case 1:
                                type = NavTerrain.Water;
                                break;

                            case 2:
                                type = NavTerrain.Magma;
                                break;

                            case 3:
                                type = NavTerrain.Slime;
                                break;
                            }

                            // indexing is weird...
                            // after a lot of trial and error, this is what works:
                            // vertex = y*vertsX+x
                            // tile   = x*tilesY+y
                            // flag   = y*tilesY+x

                            Vector3 vert;
                            for (uint x = 0; x < vertsX; ++x)
                            {
                                for (uint y = 0; y < vertsY; ++y)
                                {
                                    vert    = new Vector3(corner.X + x * SharedConst.GRID_PART_SIZE, corner.Y + y * SharedConst.GRID_PART_SIZE, data[y * vertsX + x]);
                                    vert    = vert * rotation * scale + position;
                                    vert.X *= -1.0f;
                                    vert.Y *= -1.0f;
                                    liqVerts.Add(vert);
                                }
                            }

                            uint idx1, idx2, idx3, idx4;
                            uint square;
                            for (uint x = 0; x < tilesX; ++x)
                            {
                                for (uint y = 0; y < tilesY; ++y)
                                {
                                    if ((flags[x + y * tilesX] & 0x0f) != 0x0f)
                                    {
                                        square = x * tilesY + y;
                                        idx1   = square + x;
                                        idx2   = square + 1 + x;
                                        idx3   = square + tilesY + 1 + 1 + x;
                                        idx4   = square + tilesY + 1 + x;

                                        // top triangle
                                        liqTris.Add(idx3);
                                        liqTris.Add(idx2);
                                        liqTris.Add(idx1);
                                        // bottom triangle
                                        liqTris.Add(idx4);
                                        liqTris.Add(idx3);
                                        liqTris.Add(idx1);
                                    }
                                }
                            }

                            int liqOffset = meshData.liquidVerts.Count / 3;
                            for (int x = 0; x < liqVerts.Count; ++x)
                            {
                                meshData.liquidVerts.Add(liqVerts[x].Y);
                                meshData.liquidVerts.Add(liqVerts[x].Z);
                                meshData.liquidVerts.Add(liqVerts[x].X);
                            }

                            for (int x = 0; x < liqTris.Count / 3; ++x)
                            {
                                meshData.liquidTris.Add((int)(liqTris[x * 3 + 1] + liqOffset));
                                meshData.liquidTris.Add((int)(liqTris[x * 3 + 2] + liqOffset));
                                meshData.liquidTris.Add((int)(liqTris[x * 3] + liqOffset));
                                meshData.liquidType.Add((byte)type);
                            }
                        }
                    }
                }
            }while (false);

            vmapManager.unloadMap(mapID, tileX, tileY);

            return(retval);
        }