public Tile(TileDescriptor descriptor) { _descriptor = descriptor; if (descriptor.SpriteInfo != null) { _collisionArea = new Rect(descriptor.SpriteInfo.Transform.Position.X, descriptor.SpriteInfo.Transform.Position.Y, Settings.TileSize, Settings.TileSize); } else { _collisionArea = new Rect(descriptor.Position.X, descriptor.Position.Y, Settings.TileSize, Settings.TileSize); } _heartbeatListener = new NPCHeartbeatListener(); }
public void AddTile(TileDescriptor tile, Stream data) { string filepath = GetFilePath(tile); string dir = Path.GetDirectoryName(filepath); if (!Directory.Exists(dir)) { Directory.CreateDirectory(dir); } var file = File.Create(filepath); data.CopyTo(file); file.Close(); }
public void AddTile(TileDescriptor tile, Stream data) { MemoryStream memStr = new MemoryStream(); data.CopyTo(memStr); if (_cache[_subCache].ContainsKey(tile)) { _cache[_subCache][tile].Dispose(); _cache[_subCache][tile] = memStr; } else { _cache[_subCache].Add(tile, memStr); } }
public Stream GetTile(TileDescriptor tile) { MemoryStream memStr = new MemoryStream(); if (!HasTile(tile)) { return(null); } else { _cache[_subCache][tile].Position = 0; _cache[_subCache][tile].CopyTo(memStr); } return(memStr); }
/// <summary> /// Deal with collisions between an object and a level (based on Lab 2) /// </summary> /// <param name="currentLevel"></param> /// <param name="obj"></param> private void UpdateObjectLevelCollisions(Level currentLevel, CollidableObject obj) { Vector2 tileDimensions = new Vector2(currentLevel.tileSize.X, currentLevel.tileSize.Y); Rectangle playerBounds = obj.BoundingBox; //Determination of neighbouring tile indices from platformer demo (lab 2) int left = (int)Math.Floor(playerBounds.Left / tileDimensions.X); int right = (int)Math.Ceiling((playerBounds.Right / tileDimensions.X)); int top = (int)Math.Floor(playerBounds.Top / tileDimensions.Y); int bottom = (int)Math.Ceiling((playerBounds.Bottom / tileDimensions.Y)); //For each neighbouring tile, check whether it collides with the object for (int i = left; i < right; i++) { for (int j = top; j < bottom; j++) { var collisionMode = currentLevel.GetCollisionModeAt(i, j); //Skip collisions with non-colliding tiles if (TileCollisionMode.empty.Equals(collisionMode)) { continue; } var tileBounds = currentLevel.GetBoundsAt(i, j); Vector2 penDepth; if (obj.CheckCollision(tileBounds, out penDepth)) { //If a collision occured, fire relevant event TileDescriptor tileDescriptor = new TileDescriptor(collisionMode, currentLevel.GetWorldPosition(i, j), new Point(i, j), tileBounds); Player player = obj as Player; if (player != null) { GameEventManager.Instance.PlayerCollision(player, tileDescriptor, penDepth); } else { GameEventManager.Instance.NonPlayerCollision(obj, tileDescriptor, penDepth); } } } } }
/// <summary> /// Returns a texture2d of the given tile. /// Loads the Tile Stream from the map provider and turns it into a texture. /// If the texture is not available, it returns null. /// Also takes care of haven onnly MacTileCount tiles in the memory, i.e. removing textures that /// were not used for a long time. /// </summary> /// <param name="desc">The tile descriptor of the tile that shall be loaded.</param> /// <returns>A Texture2D of the given tile or null.</returns> private Texture2D GetTileTexture(TileDescriptor desc) { if (!_tiles.ContainsKey(desc)) { Stream s = _map.GetTile(desc.Zoom, desc.Tile); if (s != null) { // s will be disposed after the tile has been loaded. using (s) { _tiles.Add(desc, Texture2D.FromStream(_device, s)); } // Handle the max tiles in memory // Add the current tile to the last place in the list _loadedTiles.Remove(desc); _loadedTiles.Add(desc); // Now remove as many tiles from the front of the list until // we have only MacLoadedTiles loaded. while (_loadedTiles.Count > MaxLoadedTiles) { var t = _loadedTiles[0]; _loadedTiles.RemoveAt(0); var text = _tiles[t]; text.Dispose(); _tiles.Remove(t); } return(_tiles[desc]); } else { return(null); } } else { return(_tiles[desc]); } }
public WorldDescriptor GetDescriptor() { WorldDescriptor wd = new WorldDescriptor(); wd.Width = this.Width; wd.Height = this.Height; wd.Tiles = new TileDescriptor[Width, Height]; for (int x = 0; x < Width; x++) { for (int y = 0; y < Height; y++) { string tName = Grid[x, y].Name; TileDescriptor td = TileDescriptor.Create(tName); wd.Tiles[x, y] = td; } } return(wd); }
public static World LoadWorld(WorldDescriptor worldDescriptor) { World world = (World)GD.Load <PackedScene>("res://Scenes/World.tscn").Instance(); Grid3D grid = new Grid3D(world, worldDescriptor.Width, worldDescriptor.Height); world.AddChild(grid); for (int y = 0; y < worldDescriptor.Height; y++) { for (int x = 0; x < worldDescriptor.Width; x++) { TileDescriptor tileD = worldDescriptor.Tiles[x, y]; Tile tileT = CreateTile(tileD); grid.SetTile(tileT, x, y, false); } } return(world); }
public Tile(Layer layer, TileDescriptor <SpriteInfo> tileData) : this(tileData) { this.Layer = layer; if (this.Attribute != null) { this.Attribute.ActionHandler = TileAttributeActionHandlerFactory.Create(this.Attribute); } if (this.Sprite != null) { _collisionArea = new Rect(this.Sprite.Transform.Position.X, this.Sprite.Transform.Position.Y, Settings.TileSize, Settings.TileSize); } else { _collisionArea = new Rect(this.Position.X, this.Position.Y, Settings.TileSize, Settings.TileSize); } this.Attribute?.ActionHandler?.OnInitalize(new TileAttributeArgs(this.Attribute, this)); }
private static Bitmap ConvertTileToBitmap(TileDescriptor tileDesc, byte[] tmem, ushort[][] palettes) { if (Math.Floor(tileDesc.sHi - tileDesc.sLo) != tileDesc.sHi - tileDesc.sLo) { throw new Exception(); } if (Math.Floor(tileDesc.tHi - tileDesc.tLo) != tileDesc.tHi - tileDesc.tLo) { throw new Exception(); } // Break into lines int startPtrBytes = tileDesc.tmemAddressInWords * 8; int width = (int)(tileDesc.sHi - tileDesc.sLo + 1); int height = (int)(tileDesc.tHi - tileDesc.tLo + 1); int bytesPerLine = tileDesc.wordsPerLine * 8; byte[] data = tmem.Subsection(startPtrBytes, bytesPerLine * height); ushort[]? palette = tileDesc.palette == 0 ? null : palettes[tileDesc.palette - 1]; return(Texels.ConvertToBitmap(data, tileDesc.format, tileDesc.bitSize, width, height, bytesPerLine, true, true, palette)); }
public bool HasTile(TileDescriptor tile) { return(_cache[_subCache].ContainsKey(tile)); }
public List <TileDrawInfo> GetDrawTiles() { List <TileDrawInfo> info = new List <TileDrawInfo>(); // 1. Get center point MapCoordinates MapVector centerMapVector = LatLonToMapPoint(Position); // 2. Calculate offset from center point to topleft point in map coordinates int topLeftXOffset = (int)((ViewBounds.Width / 2.0) / CoordinateScale); int topLeftYOffset = (int)((ViewBounds.Height / 2.0) / CoordinateScale); // 3. Calculate the top left point in map coordinates MapVector topLeftMapVector = new MapVector() { // real a mod b: // a mod b = (a % b + b) % b // https://de.wikipedia.org/wiki/Division_mit_Rest#Modulo X = (((centerMapVector.X - topLeftXOffset) % MapCoordinatesWidth) + MapCoordinatesWidth) % MapCoordinatesWidth, Y = (((centerMapVector.Y - topLeftYOffset) % MapCoordinatesHeight) + MapCoordinatesHeight) % MapCoordinatesHeight }; // 4. Calculate Tile X/Y directly from Map Coordiantes MapVector tileCoord = new MapVector() { X = (int)Math.Floor(topLeftMapVector.X / (Provider.TileSize / CoordinateScale)), Y = (int)Math.Floor(topLeftMapVector.Y / (Provider.TileSize / CoordinateScale)) }; // 5. Calculate the Top-Left point of the top-left tile //PointXY tileMapVector = LatLonToMapPoint(Provider.GetPointForTile(Zoom, tileCoord)); MapVector tileMapVector = new MapVector() { X = (int)(tileCoord.X * (Provider.TileSize / CoordinateScale)), Y = (int)(tileCoord.Y * (Provider.TileSize / CoordinateScale)) }; // 6. Get the offset of the top-left-point of the top-left-tile to the top-left point of the map // So we know if it is outside of the viewable port. MapVector offset = (tileMapVector - topLeftMapVector) * CoordinateScale; // 7. Create a rectangle for the top-left tile MapRectangle imgRect = new MapRectangle(offset.X, offset.Y, Provider.TileSize, Provider.TileSize); // 8. The top-left tile TileDescriptor descriptor = new TileDescriptor(Zoom, tileCoord); // 9. Now iterate over all viewable tiles and add them to the list // Handling the source and destination rectangles is done by special functions. int currentTileY = descriptor.Tile.Y; int currentTileX = descriptor.Tile.X; int currentRectY = imgRect.Y; int currentRectX = imgRect.X; int tileCount = Provider.GetTileCount(Zoom); while (currentRectX < ViewBounds.Width) { while (currentRectY < ViewBounds.Height) { TileDescriptor desc = new TileDescriptor(descriptor.Zoom, currentTileX, currentTileY); // 10. Create rectangle of the "full" tile, might lap over the borders of the viewport, i.e. // having negative offsets here. MapRectangle cRect = new MapRectangle(currentRectX, currentRectY, Provider.TileSize, Provider.TileSize); // 11. Get the part of the tile that is being rendered. MapRectangle tileSrcRect = GetSourceRectangle(cRect); // 12. Get the position and the size in the viewport where the tile will be rendered. MapRectangle viewDstRect = GetDestRectangle(cRect); TileDrawInfo i = new TileDrawInfo(); i.Tile = new TileDescriptor(Zoom, currentTileX, currentTileY); i.SourceRectangle = tileSrcRect; i.DestinationRectangle = viewDstRect; info.Add(i); currentRectY += Provider.TileSize; currentTileY = (currentTileY + 1) % tileCount; } currentRectY = imgRect.Y; currentTileY = descriptor.Tile.Y; currentTileX = (currentTileX + 1) % tileCount; currentRectX += Provider.TileSize; } return(info); }
public Tile(Vector position) { _descriptor = new TileDescriptor(position); _collisionArea = new Rect(position.X, position.Y, Settings.TileSize, Settings.TileSize); _heartbeatListener = new NPCHeartbeatListener(); }
public static RDPState ExecuteCommands(UVTXFile uvtx, out StringBuilder cmdsString) { RDPState rdpState = new RDPState(); cmdsString = new StringBuilder(); foreach (byte[] commandBytes in uvtx.displayListCommands) { byte[] bytes = commandBytes; string operationDesc; switch ((Fast3DEX2Opcode)bytes[0]) { case Fast3DEX2Opcode.G_TEXTURE: { ulong word = bytes.ReadUInt64(0); byte mipMap = (byte)(word.Bits(43, 3) + 1); byte tileDescIndex = (byte)word.Bits(40, 3); bool on = word.Bit(33); float scaleFactorS = bytes.ReadUInt16(4) / (float)0x10000; float scaleFactorT = bytes.ReadUInt16(6) / (float)0x10000; rdpState.tileToUseWhenTexturing = tileDescIndex; rdpState.maxMipMapLevels = mipMap; rdpState.texturingEnabled = on; rdpState.textureScaleS = scaleFactorS; rdpState.textureScaleT = scaleFactorT; operationDesc = "G_TEXTURE (Set RSP texture state)"; operationDesc += $": tile {tileDescIndex} scale=<{scaleFactorS}, {scaleFactorT}>; mm={mipMap} on={on}"; break; } case Fast3DEX2Opcode.G_SetOtherMode_H: { // TODO: actually implement this? operationDesc = "G_SetOtherMode_H (Set Other Modes Hi)"; int length = bytes[3] + 1; int shift = 32 - length - bytes[2]; string str = otherModeHShiftToStr[shift]; byte val = (byte)bytes.ReadUInt64(0).Bits(shift, length); // TODO?: https://wiki.cloudmodding.com/oot/F3DZEX#RDP_Other_Modes.2C_Higher_Half string valStr = Convert.ToString(val, 2).PadLeft(length, '0'); operationDesc += $": {str} = {valStr}"; break; } case Fast3DEX2Opcode.G_SETTILESIZE: { ulong word = bytes.ReadUInt64(0); ushort sLoRaw = (ushort)word.Bits(44, 12); ushort tLoRaw = (ushort)word.Bits(32, 12); byte tileDescIndex = (byte)word.Bits(24, 3); ushort sHiRaw = (ushort)word.Bits(12, 12); ushort tHiRaw = (ushort)word.Bits(0, 12); TileDescriptor t = rdpState.tileDescriptors[tileDescIndex]; t.sLo = sLoRaw / 4.0f; t.tLo = tLoRaw / 4.0f; t.sHi = sHiRaw / 4.0f; t.tHi = tHiRaw / 4.0f; float visWidth = (t.sHi - t.sLo) + 1; float visHeight = (t.tHi - t.tLo) + 1; operationDesc = "G_SETTILESIZE (Set texture coords and size)"; operationDesc += $": tile {tileDescIndex} lo=({t.sLo}, {t.tLo}) hi=({t.sHi}, {t.tHi}) [[{visWidth}, {visHeight}]]"; break; } case Fast3DEX2Opcode.G_LOADBLOCK: { ulong word = bytes.ReadUInt64(0); ushort sLo = (ushort)word.Bits(44, 12); ushort tLo = (ushort)word.Bits(32, 12); byte tileDescIndex = (byte)word.Bits(24, 3); ushort sHi = (ushort)word.Bits(12, 12); ushort dxt = (ushort)word.Bits(0, 12); if (dxt != 0) { throw new Exception(); } if (sLo != 0 || tLo != 0) { throw new Exception(); } if (rdpState.nextDRAMAddrForLoad == null || rdpState.bitSizeOfNextDataForLoad == null) { throw new Exception(); } rdpState.tileDescriptors[tileDescIndex].sLo = sLo; rdpState.tileDescriptors[tileDescIndex].tLo = tLo; rdpState.tileDescriptors[tileDescIndex].sHi = sHi; rdpState.tileDescriptors[tileDescIndex].tHi = dxt; // Not 100% sure this is the correct behavior int dataStart = (int)rdpState.nextDRAMAddrForLoad; int dataLengthBytes = (sHi + 1) * Texels.BitSizeToNumBytes((BitSize)rdpState.bitSizeOfNextDataForLoad); int destPtr = rdpState.tileDescriptors[tileDescIndex].tmemAddressInWords * 8; // I'm assuming this is the correct behavior because if I don't do this a lot of textures have a notch at the top right // (Also it would make sense given that interleaving and addresses are all done on 64-bit words dataLengthBytes = (int)Math.Ceiling(dataLengthBytes / 8f) * 8; // Note: technically this inaccurate, we shouldn't clamp. But the instructions read beyond the file and IDK why, // it doesn't seem to serve any purpose so I assume it's a bug (or I don't understand something about how the RSP works) Array.Copy(uvtx.texelData, dataStart, rdpState.tmem, destPtr, Math.Min(uvtx.texelData.Length - dataStart, dataLengthBytes)); operationDesc = "G_LOADBLOCK (Load data into TMEM (uses params set in SETTIMG))"; operationDesc += $": tile {tileDescIndex} sLo={sLo} tLo={tLo} sHi={sHi} dxt={dxt}"; break; } case Fast3DEX2Opcode.G_SETTILE: { ulong word = bytes.ReadUInt64(0); TileDescriptor t = new TileDescriptor { format = (ColorFormat)word.Bits(53, 3), bitSize = (BitSize)word.Bits(51, 2), wordsPerLine = (ushort)word.Bits(41, 9), tmemAddressInWords = (ushort)word.Bits(32, 9), palette = (byte)word.Bits(20, 4), clampEnableT = word.Bit(19), mirrorEnableT = word.Bit(18), maskT = (byte)word.Bits(14, 4), shiftT = (byte)word.Bits(10, 4), clampEnableS = word.Bit(9), mirrorEnableS = word.Bit(8), maskS = (byte)word.Bits(4, 4), shiftS = (byte)word.Bits(0, 4), }; byte tileDescIndex = (byte)word.Bits(24, 3); rdpState.tileDescriptors[tileDescIndex] = t; operationDesc = "G_SETTILE (Set texture properties)"; operationDesc += $": tile {tileDescIndex} fmt={t.bitSize}-bit {t.format} wordsPerLine={t.wordsPerLine} addrWords={t.tmemAddressInWords} palette={t.palette}" + $" s(clmp={t.clampEnableS} mirr={t.mirrorEnableS} mask={t.maskS} shift={t.shiftS}) t(clmp={t.clampEnableT} mirr={t.mirrorEnableT} mask={t.maskT} shift={t.shiftT})"; break; } case Fast3DEX2Opcode.G_SETPRIMCOLOR: { float minLODLevel = bytes[2] / 0x100f; float LODfrac = bytes[3] / 0x100f; byte r = bytes[4]; byte g = bytes[5]; byte b = bytes[6]; byte a = bytes[7]; rdpState.colorCombinerSettings.primR = r; rdpState.colorCombinerSettings.primG = g; rdpState.colorCombinerSettings.primB = b; rdpState.colorCombinerSettings.primA = a; rdpState.colorCombinerSettings.minLODLevel = minLODLevel; rdpState.colorCombinerSettings.LODfrac = LODfrac; operationDesc = "G_SETPRIMCOLOR (Set color combiner primitive color + LOD)"; operationDesc += $": rgba({r}, {g}, {b}, {a}) minLOD={minLODLevel} LODfrac={LODfrac}"; break; } case Fast3DEX2Opcode.G_SETENVCOLOR: { byte r = bytes[4]; byte g = bytes[5]; byte b = bytes[6]; byte a = bytes[7]; rdpState.colorCombinerSettings.envR = r; rdpState.colorCombinerSettings.envG = g; rdpState.colorCombinerSettings.envB = b; rdpState.colorCombinerSettings.envA = a; operationDesc = "G_SETENVCOLOR (Set color combiner environment color)"; operationDesc += $": rgba({r}, {g}, {b}, {a})"; break; } case Fast3DEX2Opcode.G_SETCOMBINE: operationDesc = "G_SETCOMBINE (Set color combiner algorithm)"; break; case Fast3DEX2Opcode.G_SETTIMG: { ulong word = bytes.ReadUInt64(0); ColorFormat format = (ColorFormat)word.Bits(53, 3); BitSize bitSize = (BitSize)word.Bits(51, 2); uint dramAddress = (uint)word.Bits(0, 25); rdpState.nextDRAMAddrForLoad = dramAddress; rdpState.bitSizeOfNextDataForLoad = bitSize; operationDesc = "G_SETTIMG (Set pointer to data to load + size of data)"; operationDesc += $": DRAM 0x{dramAddress:X8}; fmt={bitSize}-bit {format}"; break; } case Fast3DEX2Opcode.G_RDPLOADSYNC: operationDesc = "G_RDPLOADSYNC (Wait for texture load)"; break; case Fast3DEX2Opcode.G_RDPTILESYNC: operationDesc = "G_RDPTILESYNC (Wait for rendering + update tile descriptor attributes)"; break; case Fast3DEX2Opcode.G_ENDDL: operationDesc = "G_ENDDL (End display list)"; break; default: throw new InvalidOperationException(); } string bytesStr = String.Join(" ", bytes.Select(b => b.ToString("X2"))); cmdsString.AppendLine(bytesStr + " | " + operationDesc); } return(rdpState); }
private string GetFilePath(TileDescriptor tile) { return(Path.Combine(_cacheDir, SubCache, tile.Zoom.ToString(), tile.Tile.X.ToString(), $"{tile.Tile.Y}.png")); }
public bool HasTile(TileDescriptor tile) { return(File.Exists(GetFilePath(tile))); }