public override Rectangle GetBounds(GameObject obj) { if (Vxl == null || Hva == null) return Rectangle.Empty; var bounds = VxlRenderer.GetBounds(obj, Vxl, Hva, Props); bounds.Offset(obj.Tile.Dx * TileWidth / 2, (obj.Tile.Dy - obj.Tile.Z) * TileHeight / 2); bounds.Offset(Props.GetOffset(obj)); return bounds; }
public override Rectangle GetBounds(GameObject obj) { if (InvisibleInGame || Shp == null) return Rectangle.Empty; var bounds = ShpRenderer.GetBounds(obj, Shp, Props); bounds.Offset(obj.Tile.Dx * TileWidth / 2, (obj.Tile.Dy - obj.Tile.Z) * TileHeight / 2); bounds.Offset(Props.GetOffset(obj)); return bounds; }
public override void Draw(GameObject obj, DrawingSurface ds, bool shadows = true) { if (obj == null || TsEntry == null) return; var tmpFile = TsEntry.GetTmpFile(obj as MapTile); if (tmpFile != null) TmpRenderer.Draw((MapTile)obj, tmpFile, ds); // todo: tile shadows (TS) }
public Drawable GetDrawable(GameObject o) { if (o is NamedObject) return GetDrawable((o as NamedObject).Name); if (o is NumberedObject) { int idx = Math.Max(0, (o as NumberedObject).Number); if (idx >= 0 && idx < _drawables.Count) return GetDrawable(idx); } return null; }
public override void Draw(GameObject obj, DrawingSurface ds, bool omitShadow = false) { if (TransLucency == 0) base.Draw(obj, ds, omitShadow); else { Logger.Debug("Drawing object {0} with {1}% translucency", obj, TransLucency); ShpRenderer.Draw(Shp, obj, this, Props, ds, TransLucency); } if (Props.HasShadow && !omitShadow) ShpRenderer.DrawShadow(obj, Shp, Props, ds); }
public override void Draw(GameObject obj, DrawingSurface ds, bool shadows = true) { Size onBridgeOffset = Size.Empty; if (obj is OwnableObject && (obj as OwnableObject).OnBridge) onBridgeOffset = new Size(0, -4 * TileHeight / 2); foreach (var drawable in SubDrawables) { drawable.Props.Offset += onBridgeOffset; drawable.Draw(obj, ds, shadows); drawable.Props.Offset -= onBridgeOffset; } }
public static Rectangle GetBounds(GameObject obj, ShpFile shp, DrawProperties props) { shp.Initialize(); int frameIndex = DecideFrameIndex(props.FrameDecider(obj), shp.NumImages); var offset = new Point(-shp.Width / 2, -shp.Height / 2); Size size = new Size(0, 0); var img = shp.GetImage(frameIndex); if (img != null) { offset.Offset(img.X, img.Y); size = new Size(img.Width, img.Height); } return new Rectangle(offset, size); }
private void ExamineObjects(GameObject obj, GameObject obj2) { if (obj == obj2) return; var front = GetFrontBlock(obj, obj2); if (front == obj && !_hist.Contains(obj2)) AddDependency(obj, obj2, "obj in front"); else if (front == obj2) { // obj2 is in front of obj, so so obj cannot have been drawn yet // Debug.Assert(!_hist.Contains(obj2), "obj drawn before all its dependencies were found"); AddDependency(obj2, obj, "obj2 in front"); } }
private unsafe void BlitVoxelToSurface(DrawingSurface ds, DrawingSurface vxl_ds, GameObject obj, DrawProperties props) { Point d = new Point(obj.Tile.Dx * TileWidth / 2, (obj.Tile.Dy - obj.Tile.Z) * TileHeight / 2); d.Offset(props.GetOffset(obj)); d.Offset(-vxl_ds.BitmapData.Width / 2, -vxl_ds.BitmapData.Height / 2); // rows inverted! var w_low = (byte*)ds.BitmapData.Scan0; byte* w_high = w_low + ds.BitmapData.Stride * ds.BitmapData.Height; var zBuffer = ds.GetZBuffer(); var shadowBufVxl = vxl_ds.GetShadows(); var shadowBuf = ds.GetShadows(); // int rowsTouched = 0; // short firstRowTouched = short.MaxValue; for (int y = 0; y < vxl_ds.Height; y++) { byte* src_row = (byte*)vxl_ds.BitmapData.Scan0 + vxl_ds.BitmapData.Stride * (vxl_ds.Height - y - 1); byte* dst_row = ((byte*)ds.BitmapData.Scan0 + (d.Y + y) * ds.BitmapData.Stride + d.X * 3); int zIdx = (d.Y + y) * ds.Width + d.X; if (dst_row < w_low || dst_row >= w_high) continue; for (int x = 0; x < vxl_ds.Width; x++) { // only non-transparent pixels if (*(src_row + x * 4 + 3) > 0) { *(dst_row + x * 3) = *(src_row + x * 4); *(dst_row + x * 3 + 1) = *(src_row + x * 4 + 1); *(dst_row + x * 3 + 2) = *(src_row + x * 4 + 2); // if (y < firstRowTouched) // firstRowTouched = (short)y; short zBufVal = (short)((obj.Tile.Rx + obj.Tile.Ry + obj.Tile.Z) * TileHeight / 2); if (zBufVal >= zBuffer[zIdx]) zBuffer[zIdx] = zBufVal; } // or shadows else if (shadowBufVxl[x + y * vxl_ds.Height]) { int shadIdx = (d.Y + y) * ds.Width + d.X + x; if (!shadowBuf[shadIdx]) { *(dst_row + x * 3) /= 2; *(dst_row + x * 3 + 1) /= 2; *(dst_row + x * 3 + 2) /= 2; shadowBuf[shadIdx] = true; } } zIdx++; } } }
public override Rectangle GetBounds(GameObject obj) { Rectangle bounds = Rectangle.Empty; var parts = new List<Drawable>(); parts.AddRange(SubDrawables); foreach (var d in parts) { var db = d.GetBounds(obj); if (db == Rectangle.Empty) continue; if (bounds == Rectangle.Empty) bounds = db; else bounds = Rectangle.Union(bounds, db); } Point onBridgeOffset = Point.Empty; if (obj is OwnableObject && (obj as OwnableObject).OnBridge) onBridgeOffset = new Point(0, -4 * TileHeight / 2); bounds.Offset(onBridgeOffset); return bounds; }
internal void ExamineNeighbourhood(GameObject obj) { // Debug.WriteLine("Examining neighhourhood of " + obj); // Debug.Assert(!_hist.Contains(obj), "examining neighbourhood for an object that's already in the draw list"); var objBB = obj.GetBounds(); var tileTL = _map.GetTileScreen(objBB.Location, true, false); var tileBR = _map.GetTileScreen(objBB.Location + objBB.Size); for (int y = tileTL.Dy - 3; y <= tileBR.Dy + 3; y++) { for (int x = tileTL.Dx - 3; x <= tileBR.Dx + 3; x += 2) { if ((x + (y + obj.TopTile.Dy)) < 0 || y < 0) continue; var tile2 = _map[x + (y + obj.TopTile.Dy)%2, y/2]; if (tile2 == null) continue; // Debug.WriteLine("neighhourhood tile " + tile2 + " of obj " + obj + " at " + obj.Tile); ExamineObjects(obj, tile2); foreach (var obj2 in tile2.AllObjects) ExamineObjects(obj, obj2); } } }
public void AddObject(GameObject obj) { _allObjects.Add(obj); obj.Tile = this; }
public void RemoveObject(GameObject obj, bool silent = false) { if (!silent) Logger.Warn("Removing unknown object {0} from tile {1}", obj, this); bool removed = _allObjects.Remove(obj); if (!removed) Logger.Warn("Failed to reomve objects {0} from tile {1}", obj, this); }
public static Rectangle GetBounds(GameObject obj, VxlFile vxl, HvaFile hva, DrawProperties props) { vxl.Initialize(); hva.Initialize(); float direction = (obj is OwnableObject) ? (obj as OwnableObject).Direction : 0; float objectRotation = 45f - direction / 256f * 360f; // convert game rotation to world degrees var world = Matrix4.CreateRotationX(MathHelper.DegreesToRadians(60)); world = Matrix4.CreateRotationZ(MathHelper.DegreesToRadians(objectRotation)) * world; // object facing world = Matrix4.Scale(0.25f, 0.25f, 0.25f) * world; // art.ini TurretOffset value positions some voxel parts over our x-axis world = Matrix4.CreateTranslation(0.18f * props.TurretVoxelOffset, 0, 0) * world; var camera = Matrix4.CreatePerspectiveFieldOfView(MathHelper.DegreesToRadians(30), 1f, 1, 100); world = world * camera; Rectangle ret = Rectangle.Empty; foreach (var section in vxl.Sections) { var frameRot = hva.LoadGLMatrix(section.Index); frameRot.M41 *= section.HVAMultiplier * section.ScaleX; frameRot.M42 *= section.HVAMultiplier * section.ScaleY; frameRot.M43 *= section.HVAMultiplier * section.ScaleZ; var minbounds = new Vector3(section.MinBounds); if (props.HasShadow) minbounds.Z = -100; var frameTransl = Matrix4.CreateTranslation(minbounds); var frame = frameTransl * frameRot * world; // floor rect of the bounding box Vector3 floorTopLeft = new Vector3(0, 0, 0); Vector3 floorTopRight = new Vector3(section.SpanX, 0, 0); Vector3 floorBottomRight = new Vector3(section.SpanX, section.SpanY, 0); Vector3 floorBottomLeft = new Vector3(0, section.SpanY, 0); // ceil rect of the bounding box Vector3 ceilTopLeft = new Vector3(0, 0, section.SpanZ); Vector3 ceilTopRight = new Vector3(section.SpanX, 0, section.SpanZ); Vector3 ceilBottomRight = new Vector3(section.SpanX, section.SpanY, section.SpanZ); Vector3 ceilBottomLeft = new Vector3(0, section.SpanY, section.SpanZ); // apply transformations floorTopLeft = Vector3.Transform(floorTopLeft, frame); floorTopRight = Vector3.Transform(floorTopRight, frame); floorBottomRight = Vector3.Transform(floorBottomRight, frame); floorBottomLeft = Vector3.Transform(floorBottomLeft, frame); ceilTopLeft = Vector3.Transform(ceilTopLeft, frame); ceilTopRight = Vector3.Transform(ceilTopRight, frame); ceilBottomRight = Vector3.Transform(ceilBottomRight, frame); ceilBottomLeft = Vector3.Transform(ceilBottomLeft, frame); int FminX = (int)Math.Floor(Math.Min(Math.Min(Math.Min(floorTopLeft.X, floorTopRight.X), floorBottomRight.X), floorBottomLeft.X)); int FmaxX = (int)Math.Ceiling(Math.Max(Math.Max(Math.Max(floorTopLeft.X, floorTopRight.X), floorBottomRight.X), floorBottomLeft.X)); int FminY = (int)Math.Floor(Math.Min(Math.Min(Math.Min(floorTopLeft.Y, floorTopRight.Y), floorBottomRight.Y), floorBottomLeft.Y)); int FmaxY = (int)Math.Ceiling(Math.Max(Math.Max(Math.Max(floorTopLeft.Y, floorTopRight.Y), floorBottomRight.Y), floorBottomLeft.Y)); int TminX = (int)Math.Floor(Math.Min(Math.Min(Math.Min(ceilTopLeft.X, ceilTopRight.X), ceilBottomRight.X), ceilBottomLeft.X)); int TmaxX = (int)Math.Ceiling(Math.Max(Math.Max(Math.Max(ceilTopLeft.X, ceilTopRight.X), ceilBottomRight.X), ceilBottomLeft.X)); int TminY = (int)Math.Floor(Math.Min(Math.Min(Math.Min(ceilTopLeft.Y, ceilTopRight.Y), ceilBottomRight.Y), ceilBottomLeft.Y)); int TmaxY = (int)Math.Ceiling(Math.Max(Math.Max(Math.Max(ceilTopLeft.Y, ceilTopRight.Y), ceilBottomRight.Y), ceilBottomLeft.Y)); int minX = Math.Min(FminX, TminX); int maxX = Math.Max(FmaxX, TmaxX); int minY = Math.Min(FminY, TminY); int maxY = Math.Max(FmaxY, TmaxY); ret = Rectangle.Union(ret, Rectangle.FromLTRB(minX, minY, maxX, maxY)); } // return new Rectangle(-ret.Width / 2, -ret.Height / 2, ret.Width, ret.Height); return ret; }
public override void Draw(GameObject obj, DrawingSurface ds, bool shadow = true) { ShpRenderer.DrawAlpha(obj, Shp, Props, ds); }
private void MarkDependencies(GameObject nowSatisfied) { var satisfiedQueue = new List<GameObject> { nowSatisfied }; while (satisfiedQueue.Count > 0) { var mark = satisfiedQueue.Last(); satisfiedQueue.RemoveAt(satisfiedQueue.Count - 1); // move the tile we're marking from the graph to the history _graph.Remove(mark); _hist.Add(mark); _histOrdered.Add(mark); // Debug.WriteLine("Inserting object " + mark + "@" + mark.Tile + " to hist"); var prune = from entry in _graph where entry.Value.Remove(mark) && entry.Value.Count == 0 select entry.Key; // prune newly satisfied foreach (var obj in prune.ToList()) { _graph.Remove(obj); satisfiedQueue.Add(obj); } } }
public DrawingSurface Render(VxlFile vxl, HvaFile hva, GameObject obj, DrawProperties props) { if (!_isInit) Initialize(); if (!_canRender) { Logger.Warn("Not rendering {0} because no OpenGL context could be obtained", vxl.FileName); return null; } Logger.Debug("Rendering voxel {0}", vxl.FileName); vxl.Initialize(); hva.Initialize(); GL.Viewport(0, 0, _surface.BitmapData.Width, _surface.BitmapData.Height); GL.Clear(ClearBufferMask.DepthBufferBit | ClearBufferMask.ColorBufferBit); // RA2 uses dimetric projection with camera elevated 30° off the ground GL.MatrixMode(MatrixMode.Projection); var persp = Matrix4.CreatePerspectiveFieldOfView(MathHelper.DegreesToRadians(30), _surface.BitmapData.Width / (float)_surface.BitmapData.Height, 1, _surface.BitmapData.Height); GL.LoadMatrix(ref persp); GL.MatrixMode(MatrixMode.Modelview); GL.LoadIdentity(); var lookat = Matrix4.LookAt(0, 0, -10, 0, 0, 0, 0, 1, 0); GL.MultMatrix(ref lookat); var trans = Matrix4.CreateTranslation(0, 0, 10); GL.MultMatrix(ref trans); // align and zoom var world = Matrix4.CreateRotationX(MathHelper.DegreesToRadians(60)); world = Matrix4.CreateRotationY(MathHelper.DegreesToRadians(180)) * world; world = Matrix4.CreateRotationZ(MathHelper.DegreesToRadians(-45)) * world; world = Matrix4.Scale(0.028f, 0.028f, 0.028f) * world; GL.MultMatrix(ref world); // DrawAxes(); // determine tilt vectors Matrix4 tilt; int tiltPitch, tiltYaw; if (true) { int ramp = (obj.Tile.Drawable as TileDrawable).GetTileImage(obj.Tile).RampType; if (ramp == 0 || ramp >= 17) { tiltPitch = tiltYaw = 0; } else if (ramp <= 4) { // screen-diagonal facings (perpendicular to axes) tiltPitch = 25; tiltYaw = -90 * ramp; } else { // world-diagonal facings (perpendicular to screen) tiltPitch = 25; tiltYaw = 225 - 90 * ((ramp - 1) % 4); } tilt = Matrix4.CreateRotationX(MathHelper.DegreesToRadians(tiltPitch)); tilt *= Matrix4.CreateRotationZ(MathHelper.DegreesToRadians(tiltYaw)); /*// show tilt direction GL.Color3(Color.Black); GL.Begin(BeginMode.Lines); GL.Vertex3(Vector3.Zero); var tiltVec = Vector3.UnitZ; tiltVec = Vector3.Transform(tiltVec, tilt); tiltVec = Vector3.Multiply(tiltVec, 1000f); GL.Vertex3(tiltVec); GL.End();*/ } /*// draw slope normals GL.LineWidth(2); var colors = new[] { Color.Red, Color.Green, Color.Blue, Color.Yellow, Color.Orange, Color.Black, Color.Purple, Color.SlateBlue, Color.DimGray, Color.White, Color.Teal, Color.Tan }; for (int i = 0; i < 8; i++) { GL.Color3(colors[i]); const float roll = 25f; float syaw = 45f * i; var slopeNormal = Vector3.UnitZ; slopeNormal = Vector3.Transform(slopeNormal, Matrix4.CreateRotationX(MathHelper.DegreesToRadians(roll))); slopeNormal = Vector3.Transform(slopeNormal, Matrix4.CreateRotationZ(MathHelper.DegreesToRadians(syaw))); GL.Begin(BeginMode.Lines); GL.Vertex3(0, 0, 0); GL.Vertex3(Vector3.Multiply(slopeNormal, 1000f)); GL.End(); }*/ // object rotation around Z float direction = (obj is OwnableObject) ? (obj as OwnableObject).Direction : 0; float objectRotation = 90 - direction / 256f * 360f - tiltYaw; // convert game rotation to world degrees Matrix4 @object = Matrix4.CreateRotationZ(MathHelper.DegreesToRadians(objectRotation)) * tilt; // object facing // art.ini TurretOffset value positions some voxel parts over our x-axis @object = Matrix4.CreateTranslation(0.18f * props.TurretVoxelOffset, 0, 0) * @object; GL.MultMatrix(ref @object); // DrawAxes(); float pitch = MathHelper.DegreesToRadians(210); float yaw = MathHelper.DegreesToRadians(120); /*// helps to find good pitch/yaw // direction of light vector given by pitch & yaw for (int i = 0; i < 360; i += 30) { for (int j = 0; j < 360; j += 30) { GL.Color3(colors[i / 30]); var shadowTransform2 = Matrix4.CreateRotationZ(MathHelper.DegreesToRadians(i)) * Matrix4.CreateRotationY(MathHelper.DegreesToRadians(j)); GL.LineWidth(2); GL.Begin(BeginMode.Lines); GL.Vertex3(0, 0, 0); GL.Vertex3(Vector3.Multiply(ExtractRotationVector(ToOpenGL(Matrix4.Invert(world * shadowTransform2))), 100f)); GL.End(); } }*/ var shadowTransform = Matrix4.CreateRotationZ(pitch) * Matrix4.CreateRotationY(yaw); // clear shadowbuf var shadBuf = _surface.GetShadows(); Array.Clear(shadBuf, 0, shadBuf.Length); foreach (var section in vxl.Sections) { GL.PushMatrix(); var frameRot = hva.LoadGLMatrix(section.Index); frameRot.M41 *= section.HVAMultiplier * section.ScaleX; frameRot.M42 *= section.HVAMultiplier * section.ScaleY; frameRot.M43 *= section.HVAMultiplier * section.ScaleZ; var frameTransl = Matrix4.CreateTranslation(section.MinBounds); var frame = frameTransl * frameRot; GL.MultMatrix(ref frame); var shadowScale = Matrix4.Scale(0.5f); //var shadowTilt = null; var shadowToScreen = frameTransl * shadowScale * frameRot * (@object * world) * trans * lookat; // undo world transformations on light direction var lightDirection = ExtractRotationVector(ToOpenGL(Matrix4.Invert((@object * world) * frame * shadowTransform))); // draw line in direction light comes from /*GL.Color3(Color.Red); GL.LineWidth(4f); GL.Begin(BeginMode.Lines); GL.Vertex3(0, 0, 0); GL.Vertex3(Vector3.Multiply(lightDirection, 100f)); GL.End();*/ GL.Begin(BeginMode.Quads); for (uint x = 0; x != section.SizeX; x++) { for (uint y = 0; y != section.SizeY; y++) { foreach (VxlFile.Voxel vx in section.Spans[x, y].Voxels) { Color color = obj.Palette.Colors[vx.ColorIndex]; Vector3 normal = section.GetNormal(vx.NormalIndex); // shader function taken from https://github.com/OpenRA/OpenRA/blob/bleed/cg/vxl.fx // thanks to pchote for a LOT of help getting it right Vector3 colorMult = Vector3.Add(Ambient, Diffuse * Math.Max(Vector3.Dot(normal, lightDirection), 0f)); GL.Color3( (byte)Math.Min(255, color.R * colorMult.X), (byte)Math.Min(255, color.G * colorMult.Y), (byte)Math.Min(255, color.B * colorMult.Z)); Vector3 vxlPos = Vector3.Multiply(new Vector3(x, y, vx.Z), section.Scale); RenderVoxel(vxlPos); var shadpos = new Vector3(x, y, 0); var screenPos = Vector3.Transform(shadpos, shadowToScreen); screenPos = Vector3.Transform(screenPos, persp); screenPos.X /= screenPos.Z; screenPos.Y /= screenPos.Z; screenPos.X = (screenPos.X + 1) * _surface.Width / 2; screenPos.Y = (screenPos.Y + 1) * _surface.Height / 2; if (0 <= screenPos.X && screenPos.X < _surface.Width && 0 <= screenPos.Y && screenPos.Y < _surface.Height) shadBuf[(int)screenPos.X + (_surface.Height - 1 - (int)screenPos.Y) * _surface.Width] = true; /* draw line in normal direction if (r.Next(100) == 4) { float m = Math.Max(Vector3.Dot(normal, lightDirection), 0f); GL.Color3(m, m, m); GL.LineWidth(1); GL.Begin(BeginMode.Lines); GL.Vertex3(new Vector3(x, y, vx.Z)); GL.Vertex3(new Vector3(x, y, vx.Z) + Vector3.Multiply(normal, 100f)); GL.End(); }*/ } } } GL.End(); GL.PopMatrix(); } // read pixels back to surface GL.ReadPixels(0, 0, _surface.BitmapData.Width, _surface.BitmapData.Height, OpenTK.Graphics.OpenGL.PixelFormat.Bgra, PixelType.UnsignedByte, _surface.BitmapData.Scan0); return _surface; }
unsafe public static void Draw(ShpFile shp, GameObject obj, Drawable dr, DrawProperties props, DrawingSurface ds, int transLucency = 0) { shp.Initialize(); int frameIndex = props.FrameDecider(obj); Palette p = props.PaletteOverride ?? obj.Palette; frameIndex = DecideFrameIndex(frameIndex, shp.NumImages); if (frameIndex >= shp.Images.Count) return; var img = shp.GetImage(frameIndex); var imgData = img.GetImageData(); if (imgData == null || img.Width * img.Height != imgData.Length) return; Point offset = props.GetOffset(obj); offset.X += obj.Tile.Dx * Drawable.TileWidth / 2 - shp.Width / 2 + img.X; offset.Y += (obj.Tile.Dy - obj.Tile.Z) * Drawable.TileHeight / 2 - shp.Height / 2 + img.Y; Logger.Trace("Drawing SHP file {0} (Frame {1}) at ({2},{3})", shp.FileName, frameIndex, offset.X, offset.Y); int stride = ds.BitmapData.Stride; var heightBuffer = ds.GetHeightBuffer(); var zBuffer = ds.GetZBuffer(); var w_low = (byte*)ds.BitmapData.Scan0; byte* w_high = (byte*)ds.BitmapData.Scan0 + stride * ds.BitmapData.Height; byte* w = (byte*)ds.BitmapData.Scan0 + offset.X * 3 + stride * offset.Y; // clip to 25-50-75-100 transLucency = (transLucency / 25) * 25; float a = transLucency / 100f; float b = 1 - a; int rIdx = 0; // image pixel index int zIdx = offset.X + offset.Y * ds.Width; // z-buffer pixel index short hBufVal = (short)(obj.Tile.Z * Drawable.TileHeight / 2); short zOffset = (short)((obj.BottomTile.Rx + obj.BottomTile.Ry) * Drawable.TileHeight / 2); if (!dr.Flat) hBufVal += shp.Height; for (int y = 0; y < img.Height; y++) { if (offset.Y + y < 0) { w += stride; rIdx += img.Width; zIdx += ds.Width; continue; // out of bounds } for (int x = 0; x < img.Width; x++) { byte paletteValue = imgData[rIdx]; short zshapeOffset = obj is StructureObject ? (GetBuildingZ(x, y, shp, img, obj)) : (short)0; if (paletteValue != 0) { short zBufVal = zOffset; if (dr.Flat) zBufVal += (short)(y - img.Height); else if (dr.IsBuildingPart) { // nonflat building zBufVal += zshapeOffset; } else zBufVal += img.Height; if (w_low <= w && w < w_high /*&& zBufVal >= zBuffer[zIdx]*/) { if (transLucency != 0) { *(w + 0) = (byte)(a * *(w + 0) + b * p.Colors[paletteValue].B); *(w + 1) = (byte)(a * *(w + 1) + b * p.Colors[paletteValue].G); *(w + 2) = (byte)(a * *(w + 2) + b * p.Colors[paletteValue].R); } else { *(w + 0) = p.Colors[paletteValue].B; *(w + 1) = p.Colors[paletteValue].G; *(w + 2) = p.Colors[paletteValue].R; //var pal = Theater.Active.GetPalettes().UnitPalette.Colors; //*(w + 0) = pal[zshapeOffset].R; //*(w + 1) = pal[zshapeOffset].G; //*(w + 2) = pal[zshapeOffset].B; } zBuffer[zIdx] = zBufVal; heightBuffer[zIdx] = hBufVal; } } //else { // *(w + 0) = 0; // *(w + 1) = 0; // *(w + 2) = 255; //} // Up to the next pixel rIdx++; zIdx++; w += 3; } w += stride - 3 * img.Width; zIdx += ds.Width - img.Width; } }
public GameCollection GetObjectCollection(GameObject o) { if (o is InfantryObject) return _infantryTypes; else if (o is UnitObject) return _vehicleTypes; else if (o is AircraftObject) return _aircraftTypes; else if (o is StructureObject) { if (_buildingTypes.HasObject(o)) return _buildingTypes; else return _overlayTypes; } else if (o is OverlayObject) return _overlayTypes; else if (o is TerrainObject) return _terrainTypes; else if (o is SmudgeObject) return _smudgeTypes; else if (o is AnimationObject) return _animations; else if (o is MapTile) return _tileTypes; else return null; }
public bool HasObject(GameObject o) { return GetDrawable(o) != null; }
private void AddDependency(GameObject obj, GameObject dependency, string reason = "") { HashSet<GameObject> list; if (!_graph.TryGetValue(obj, out list)) _graph[obj] = list = new HashSet<GameObject> { obj.BottomTile, obj.TopTile }; if (dependency != null) { bool added = list.Add(dependency); // if (added) Debug.WriteLine("dependency (" + obj + "@" + obj.Tile + " --> " + dependency + "@" + dependency.Tile + ") added because " + reason); } }
public Point GetShadowOffset(GameObject obj) { var ret = Offset; if (ShadowOffsetHack != null) ret.Offset(ShadowOffsetHack(obj)); return ret; }
public Hexagon GetIsoBoundingBox(GameObject obj) { if (obj is MapTile) return new Hexagon { xMin = obj.Tile.Rx, xMax = obj.Tile.Rx, yMin = obj.Tile.Ry, yMax = obj.Tile.Ry, zMin = obj.Tile.Z, zMax = obj.Tile.Z, }; else if (obj is OwnableObject) { var oObj = obj as OwnableObject; return new Hexagon { xMin = obj.TopTile.Rx, xMax = obj.BottomTile.Rx, yMin = obj.TopTile.Ry, yMax = obj.BottomTile.Ry, zMin = obj.Tile.Z + (oObj.OnBridge ? 4 : 0), zMax = obj.Tile.Z + (oObj.OnBridge ? 4 : 0), }; } else return new Hexagon { xMin = obj.TopTile.Rx, xMax = obj.BottomTile.Rx, yMin = obj.TopTile.Ry, yMax = obj.BottomTile.Ry, zMin = obj.Tile.Z + obj.Drawable.TileElevation, zMax = obj.Tile.Z + obj.Drawable.TileElevation, }; }
private object GetFrontBlock(GameObject objA, GameObject objB) { // magic, don't touch. // any kind of randomness or antisymmetry in this function // will lead to cyclic dependencies in the drawing order graph, // resulting in neither object every being drawn. // tiles never overlap if (objA is MapTile && objB is MapTile) return null; var boxA = objA.GetBounds(); var boxB = objB.GetBounds(); if (!boxA.IntersectsWith(boxB)) return null; var hexA = GetIsoBoundingBox(objA); var hexB = GetIsoBoundingBox(objB); var sepAxis = Hexagon.GetSeparationAxis(hexA, hexB); // tiles can only be in front based on z-axis separation if ((objA is MapTile) ^ (objB is MapTile) && sepAxis != Axis.Z) return (objA is MapTile) ? objB : objA; // flat stuff always loses if (objA.Drawable.Flat ^ objB.Drawable.Flat) { if (sepAxis != Axis.Z) return (objA.Drawable.Flat) ? objB : objA; } switch (sepAxis) { case Axis.X: if (hexA.xMin > hexB.xMax) return objA; else if (hexB.xMin > hexA.xMax) return objB; break; case Axis.Y: if (hexA.yMin > hexB.yMax) return objA; else if (hexB.yMin > hexA.yMax) return objB; break; case Axis.Z: if (hexA.zMin > hexB.zMax) return objA; else if (hexB.zMin > hexA.zMax) return objB; break; } // units on bridges can only be drawn after the bridge if (objA is OverlayObject && SpecialOverlays.IsHighBridge(objA as OverlayObject) && objB is OwnableObject && (objB as OwnableObject).OnBridge) return objB; else if (objB is OverlayObject && SpecialOverlays.IsHighBridge(objB as OverlayObject) && objA is OwnableObject && (objA as OwnableObject).OnBridge) return objA; // no proper separation is possible, if one of both // objects is flat then mark the other one as in front, // otherwise use the one with lowest y if (objA.Drawable.Flat && !objB.Drawable.Flat) return objB; else if (objB.Drawable.Flat && !objA.Drawable.Flat) return objA; // try to make distinction based on object type // tile, smudge, overlay, terrain, unit/building, aircraft var priorities = new Dictionary<Type, int> { {typeof(MapTile), 0}, {typeof(SmudgeObject), 1}, {typeof(OverlayObject), 2}, {typeof(TerrainObject), 3}, {typeof(StructureObject), 3}, {typeof(AnimationObject), 3}, {typeof(UnitObject), 3}, {typeof(InfantryObject), 3}, {typeof(AircraftObject), 4}, }; int prioA = priorities[objA.GetType()]; int prioB = priorities[objB.GetType()]; if (prioA > prioB) return objA; else if (prioA < prioB) return objB; // finally try the minimal y coordinate if (boxA.Bottom > boxB.Bottom) return objA; else if (boxA.Bottom < boxB.Bottom) return objB; // finally if nothing worked up to here, which is very unlikely, // we'll use a tie-breaker that is at least guaranteed to yield // the same result regardless of argument order return objA.Id < objB.Id ? objA : objB; }
private static short GetBuildingZ(int x, int y, ShpFile shp, ShpFile.ShpImage img, GameObject obj) { if (_noBuildingZAvailable) return 0; else if (BuildingZ == null) { if (ModConfig.ActiveConfig.Engine < EngineType.YurisRevenge) BuildingZ = VFS.Open<ShpFile>("buildngz.shp"); else // Yuri's Revenge uses .sha as a file extension for this BuildingZ = VFS.Open<ShpFile>("buildngz.sha"); if (BuildingZ != null) BuildingZ.Initialize(); else _noBuildingZAvailable = true; } var zImg = BuildingZ.GetImage(0); byte[] zData = zImg.GetImageData(); // center x x += zImg.Width / 2 - shp.Width / 2 + img.X; // correct for foundation x -= (obj.Drawable.Foundation.Width - obj.Drawable.Foundation.Height) * 30; // add zshapepointmove x += obj.Drawable.Props.ZShapePointMove.X; // align y on bottom y += zImg.Height - shp.Height; // add zshapepointmove y -= obj.Drawable.Props.ZShapePointMove.Y; x = Math.Min(zImg.Width - 1, Math.Max(0, x)); y = Math.Min(zImg.Height - 1, Math.Max(0, y)); return (short)(-64 + zData[y * zImg.Width + x]); }
public override Rectangle GetBounds(GameObject obj) { var tile = (MapTile)obj; return TmpRenderer.GetBounds(tile, TsEntry.GetTmpFile(tile)); }
unsafe public static void DrawAlpha(GameObject obj, ShpFile shp, DrawProperties props, DrawingSurface ds) { shp.Initialize(); // Change originally implemented by Starkku: Ares supports multiframe AlphaImages, based on frame count // the direction the unit it facing. int frameIndex = props.FrameDecider(obj); var img = shp.GetImage(frameIndex); var imgData = img.GetImageData(); var c_px = (uint)(img.Width * img.Height); if (c_px <= 0 || img.Width < 0 || img.Height < 0 || frameIndex > shp.NumImages) return; Point offset = props.GetOffset(obj); offset.X += obj.Tile.Dx * Drawable.TileWidth / 2; offset.Y += (obj.Tile.Dy - obj.Tile.Z) * Drawable.TileHeight / 2; Logger.Trace("Drawing AlphaImage SHP file {0} (frame {1}) at ({2},{3})", shp.FileName, frameIndex, offset.X, offset.Y); int stride = ds.BitmapData.Stride; var w_low = (byte*)ds.BitmapData.Scan0; byte* w_high = (byte*)ds.BitmapData.Scan0 + stride * ds.BitmapData.Height; int dx = offset.X + Drawable.TileWidth / 2 - shp.Width / 2 + img.X, dy = offset.Y - shp.Height / 2 + img.Y; byte* w = (byte*)ds.BitmapData.Scan0 + dx * 3 + stride * dy; short zOffset = (short)((obj.Tile.Rx + obj.Tile.Ry) * Drawable.TileHeight / 2 - shp.Height / 2 + img.Y); int rIdx = 0; for (int y = 0; y < img.Height; y++) { for (int x = 0; x < img.Width; x++) { if (imgData[rIdx] != 0 && w_low <= w && w < w_high) { float mult = imgData[rIdx] / 127.0f; *(w + 0) = limit(mult, *(w + 0)); *(w + 1) = limit(mult, *(w + 1)); *(w + 2) = limit(mult, *(w + 2)); } // Up to the next pixel rIdx++; w += 3; } w += stride - 3 * img.Width; // ... and if we're no more on the same row, // adjust the writing pointer accordingy } }
public override void DrawBoundingBox(GameObject obj, Graphics gfx) { // meh }
internal void Draw(GameObject obj, DrawingSurface ds) { Logger.Trace("Drawing object {0} @ {1}", obj, obj.Tile); obj.Drawable.Draw(obj, ds); }
/// <summary> /// Applies a lamp to this object's palette if it's in range /// </summary> /// <param name="lamp">The lamp to apply</param> /// <returns>Whether the palette was replaced, meaning it needs to be recalculated</returns> public bool ApplyLamp(GameObject obj, bool ambientOnly = false) { var lamp = this; const double TOLERANCE = 0.001; if (Math.Abs(lamp.LightIntensity) < TOLERANCE) return false; var drawLocation = obj.Tile; double sqX = (lamp.Tile.Rx - drawLocation.Rx) * (lamp.Tile.Rx - drawLocation.Rx); double sqY = (lamp.Tile.Ry - (drawLocation.Ry)) * (lamp.Tile.Ry - (drawLocation.Ry)); double distance = Math.Sqrt(sqX + sqY); // checks whether we're in range if ((0 < lamp.LightVisibility) && (distance < lamp.LightVisibility / 256)) { double lsEffect = (lamp.LightVisibility - 256 * distance) / lamp.LightVisibility; // we don't want to apply lamps to shared palettes, so clone first if (obj.Palette.IsShared) obj.Palette = obj.Palette.Clone(); obj.Palette.ApplyLamp(lamp, lsEffect, ambientOnly); return true; } else return false; }