public override void MakeChunkData(int chunkX, int chunkZ, Block[, ,] arrayToFill, List<EntitySchema> entityDataToFill) { for (int x = 0; x < GameConstants.CHUNK_X_WIDTH; x++) { for (int z = 0; z < GameConstants.CHUNK_Z_LENGTH; z++) { int height = FindHeight(chunkX * GameConstants.CHUNK_X_WIDTH + x, chunkZ * GameConstants.CHUNK_Z_LENGTH + z); for (int y = 0; y < GameConstants.CHUNK_Y_HEIGHT; y++) { Block block; if (y <= height) block = new Block(1); else block = new Block(0); arrayToFill[x, y, z] = block; } if (x == 0 && z == 0) entityDataToFill.Add(new EntitySchema(0, x, height+5, z)); } } }
private static void addBlockData(int chunkX, int chunkZ, Block[, ,] arrayToFill) { for (int x = 0; x < GameConstants.CHUNK_X_WIDTH; x++) { for (int y = 0; y < GameConstants.CHUNK_Y_HEIGHT; y++) { for (int z = 0; z < GameConstants.CHUNK_Z_LENGTH; z++) { Block block; //ground everywhere, and pyramid mountains //that are right on the corners of the chunks if (y == 0) //uniform ground everywhere, but sometimes ice { if ((chunkX & 1) == 0 || (chunkZ & 1) == 0) block = new Block(1); else block = new Block(2); } else if (y == 10 && x == 5 && z == 5) { block = new Block(1); } else if ((chunkX & 1) == 0 && (chunkZ & 1) == 0) //even/even chunk coords, etc. { if (z >= x + y + 5) block = new Block(1); else block = new Block(0); } else if ((chunkX & 1) == 1 && (chunkZ & 1) == 0) { if (z >= (GameConstants.CHUNK_X_WIDTH - x) + y + 5) block = new Block(1); else block = new Block(0); } else if ((chunkX & 1) == 0 && (chunkZ & 1) == 1) { if ((GameConstants.CHUNK_Z_LENGTH - z - 2) >= x + y + 5) block = new Block(1); else block = new Block(0); } else { if ((GameConstants.CHUNK_Z_LENGTH - z - 3) >= (GameConstants.CHUNK_X_WIDTH - x - 1) + y + 5) block = new Block(1); else block = new Block(0); } arrayToFill[x, y, z] = block; } } } }
protected override GeometryPrimitive drawingPrimitive(Block block, bool includeFrontFace, bool includeBackFace, bool includeTopFace, bool includeBottomFace, bool includeLeftFace, bool includeRightFace) { int flag = BlockHandler.ConvertBoolFlagsToInt( includeFrontFace, includeBackFace, includeTopFace, includeBottomFace, includeLeftFace, includeRightFace); return primitives[flag]; }
protected override float friction(Block block) { switch (block.blockID) { case 1: return 20; case 2: return 0.3f; default: throw new ArgumentOutOfRangeException(); } }
protected override bool isPassable(Block block) { switch (block.blockID) { case 0: return true; case 1: case 2: return false; default: throw new ArgumentOutOfRangeException(); } }
protected override GeometryPrimitive drawingPrimitive(Block block, bool includeFrontFace = true, bool includeBackFace = true, bool includeTopFace = true, bool includeBottomFace = true, bool includeLeftFace = true, bool includeRightFace = true) { switch (block.blockID) { case 1: case 2: int flags = ConvertBoolFlagsToInt(includeFrontFace, includeBackFace, includeTopFace, includeBottomFace, includeLeftFace, includeRightFace); return fullSizeBlocks[flags]; default: throw new ArgumentOutOfRangeException(); } }
/// <summary> /// Construct a new collider object for a block. Note the specified coordinates are for /// the block itself (describing its position in space) and not for the thing it's colliding with. /// </summary> /// <param name="block"></param> /// <param name="chunkX"></param> /// <param name="blockX"></param> /// <param name="chunkZ"></param> /// <param name="blockZ"></param> /// <param name="handler"></param> public Collider(Block block, int chunkX, int chunkZ, int blockX, int blockY, int blockZ) { #if DEBUG this.collidedObject = block; #endif this.colliderChunkX = chunkX; this.colliderChunkZ = chunkZ; this.StartingBoundingBox = BlockHandler.PhysicalBlockingBox(block); Vector3 translation = new Vector3(blockX, blockY, blockZ); this.StartingBoundingBox = new BoundingBox( StartingBoundingBox.Min + translation, StartingBoundingBox.Max + translation); this.Friction = BlockHandler.Friction(block); this.FrictionVelocity = BlockHandler.FrictionVelocity(block); }
/// <summary> /// This takes as input a position and a Ray and a maximum distance, then finds /// the first Block impacted by the Ray on this map, if any. Return types are /// through a large number of "out" parameters. Note it will never return the /// cell which contains the start of the Ray. /// /// Note: if successful is FALSE, the returned data will be garbage (since it's /// not easily nullable). So be aware of that. /// </summary> /// <param name="chunkX">The chunk(X) position to start the Ray from. This will be /// set to the chunk(X) position we end on.</param> /// <param name="chunkZ">The chunk(Z) position to start the Ray from. This will be /// set to the chunk(Z) position we end on.</param> /// <param name="lookRay">The Ray to look along.</param> /// <param name="maxDistance">The maximum distance along the ray that collisions /// will be considered. Uses the MAX distance.</param> /// <param name="requireVisible">Whether or not to skip invisible blocks.</param> /// <param name="requireImpassable">Whether or not to skip passable blocks.</param> /// <param name="foundBlock">The block that was actually found.</param> /// <param name="blockPosition">The in-chunk (integer) position of the block that was found.</param> /// <param name="faceTouched">The face that was first touched by the Ray.</param> /// <param name="successful">Whether ot not anything was found</param> public void BlockLookedAt(ref int chunkX, ref int chunkZ, Ray lookRay, int maxDistance, bool requireVisible, bool requireImpassable, out Block foundBlock, out Point3 blockPosition, out Face faceTouched, out bool successful) { Map.BlockLookedAt(ref chunkX, ref chunkZ, lookRay, maxDistance, requireVisible, requireImpassable, out foundBlock, out blockPosition, out faceTouched, out successful); }
/// <summary> /// Helper method for "BlockLookedAt." Determines whether or not the Block we found fits the requirements /// for a successful "find." Specifically, if "RequireImpassible" is true, then it checks for intersection /// with the passability bounding box, and if "RequireVisible" is true, then it checks for intersection with /// the visibility bounding box. /// </summary> /// <param name="lookRay"></param> /// <param name="requireVisible"></param> /// <param name="requireImpassable"></param> /// <param name="foundBlock"></param> /// <param name="blockBounds"></param> /// <returns></returns> private bool BlockLookedAt_TestCompatibility(Ray lookRay, bool requireVisible, bool requireImpassable, Block foundBlock, BoundingBox blockBounds) { bool compatible = true; if (requireVisible) { if (BlockHandler.IsVisible(foundBlock)) { BoundingBox visualBox = HelperMethods.TranslateBox(BlockHandler.VisualBoundingBox(foundBlock), blockBounds.Min); compatible = compatible && visualBox.Intersects(lookRay).HasValue; } else { compatible = false; } } if (requireImpassable) { if (BlockHandler.IsPassable(foundBlock)) { compatible = false; } else { BoundingBox blockingBox = HelperMethods.TranslateBox(BlockHandler.PhysicalBlockingBox(foundBlock), blockBounds.Min); compatible = compatible && blockingBox.Intersects(lookRay).HasValue; } } return compatible; }
/// <summary> /// Whether or not to draw this block. /// </summary> /// <param name="block"></param> /// <returns></returns> protected abstract bool isVisible(Block block);
protected override Vector3 frictionVelocity(Block block) { return Vector3.Zero; }
public void ChangeBlock(int chunkX, int chunkZ, int blockX, int blockY, int blockZ, Block newBlock) { HelperMethods.FixCoordinates(ref chunkX, ref chunkZ, ref blockX, ref blockY, ref blockZ); lock (CacheLock) { if (!IsReady(chunkX, chunkZ)) throw new ArgumentException("Can't modify a chunk we haven't generated!"); CacheData dat = this[chunkX, chunkZ]; dat.Chunk[blockX, blockY, blockZ] = newBlock; dat.Chunk.RecalculateVisualGeometry(); SaveChunk(chunkX, chunkZ); } }
protected abstract int textureIndex(Block block);
/// <summary> /// Whether or not this block, when drawn, /// fills the entire allotted space and is /// completely opaque (and in particular, is /// visible!). Used for culling drawing /// data, so be sure to be correct :P /// /// This particular form is whether it blocks the /// view to the right; that is, whether the right /// face is completely opaque and fills the space. /// /// Default behavior is to return IsVisible; /// this need only be overridden if this block is /// partially transparent. /// </summary> /// <param name="block"></param> /// <returns></returns> protected virtual bool isFullAndOpaqueToTheRight(Block block) { return instance.isVisible(block); }
public static GeometryPrimitive DrawingPrimitive(Block block, bool includeFrontFace, bool includeBackFace, bool includeTopFace, bool includeBottomFace, bool includeLeftFace, bool includeRightFace) { return instance.drawingPrimitive(block, includeFrontFace, includeBackFace, includeTopFace, includeBottomFace, includeLeftFace, includeRightFace); }
/// <summary> /// The model for drawing this block. Not required to /// be implemented when IsVisible is false. /// /// The optional parameters refer to possibly excluding occluded /// faces. Failure to take these into account will hurt performance /// significantly. /// </summary> /// <param name="block"></param> /// <returns></returns> protected abstract GeometryPrimitive drawingPrimitive(Block block, bool includeFrontFace, bool includeBackFace, bool includeTopFace, bool includeBottomFace, bool includeLeftFace, bool includeRightFace);
public static BoundingBox VisualBoundingBox(Block block) { return instance.visualBoundingBox(block); }
/// <summary> /// The boundingbox which this square actually blocks /// passage through. This method is only valid when IsVisible /// is true. /// </summary> /// <param name="block"></param> /// <returns></returns> protected BoundingBox visualBoundingBox(Block block) { return new BoundingBox(Vector3.Zero, Vector3.One); }
public static bool IsVisible(Block block) { return instance.isVisible(block); }
/// <summary> /// This takes as input a maximum distance, then finds the first Block impacted /// by the Camera's Forward Ray on this map, if any. Return types are through /// a large number of "out" parameters. Note it will never return the /// cell which contains the start of the Ray. /// /// This is just a convenient default parameter set for the other BlockLookedAt /// method. The effect and output are the same as filling in the arguments with /// information from Camera. /// /// Note: if successful is FALSE, the returned data will be garbage (since it's /// not easily nullable). So be aware of that. /// </summary> /// <param name="maxDistance">The maximum distance along the ray that collisions /// will be considered. Uses the MAX-norm.</param> /// <param name="requireVisible">Whether or not to skip invisible blocks.</param> /// <param name="requireImpassable">Whether or not to skip passable blocks.</param> /// <param name="foundBlock">The block that was actually found.</param> /// <param name="chunkX">The chunk(X) position we end on.</param> /// <param name="chunkZ">The chunk(Z) position we end on.</param> /// <param name="blockPosition">The in-chunk (integer) position of the block that was found.</param> /// <param name="faceTouched">The face that was first touched by the Ray.</param> /// <param name="successful">Whether ot not anything was found</param> public void BlockLookedAt(int maxDistance, bool requireVisible, bool requireImpassable, out Block foundBlock, out int chunkX, out int chunkZ, out Point3 blockPosition, out Face faceTouched, out bool successful) { chunkX = Camera.ChunkX; chunkZ = Camera.ChunkZ; Map.BlockLookedAt(ref chunkX, ref chunkZ, Camera.ForwardRay, maxDistance, requireVisible, requireImpassable, out foundBlock, out blockPosition, out faceTouched, out successful); }
public override void MakeChunkData(int chunkX, int chunkZ, Block[, ,] arrayToFill, List<EntitySchema> entityDataToFill) { addEntityData(entityDataToFill); addBlockData(chunkX, chunkZ, arrayToFill); }
public static bool IsFullAndOpaqueToTheRight(Block block) { return instance.isFullAndOpaqueToTheRight(block); }
public static Vector3 FrictionVelocity(Block block) { return instance.frictionVelocity(block); }
public static float Friction(Block block) { return instance.friction(block); }
protected override int textureIndex(Block block) { switch (block.blockID) { case 1: return 0; case 2: return 1; default: throw new NotImplementedException(); } }
public void ChangeBlock(int chunkX, int chunkZ, int blockX, int blockY, int blockZ, Block newBlock) { Cache.ChangeBlock(chunkX, chunkZ, blockX, blockY, blockZ, newBlock); }
protected abstract Vector3 frictionVelocity(Block block);
/// <summary> /// This is where you get to do your procedural generation! /// By whatever means you deem appropriate, construct a chunk /// of cubes at the designated "chunk coordinates." /// /// FUN FACT: you need to include a "buffer block" on each lateral /// (that is, x or z) side. It is important that this is the same /// block as would actually be generated by the appropriate other call! /// </summary> /// <param name="chunkX"></param> /// <param name="chunkZ"></param> /// <param name="chunkBlocksToFill">The array to fill with block data</param> /// <returns></returns> public abstract void MakeChunkData(int chunkX, int chunkZ, Block[, ,] chunkBlocksToFill, List<EntitySchema> entityListToFill);
/// <summary> /// This takes as input a position and a Ray and a maximum distance, then finds /// the first Block impacted by the Ray on this map, if any. Return types are /// through a large number of "out" parameters. Note it will never return the /// cell which contains the start of the Ray. /// /// Note: if successful is FALSE, the returned data will be garbage (since it's /// not easily nullable). So be aware of that. /// </summary> /// <param name="chunkX">The chunk(X) position to start the Ray from. This will be /// set to the chunk(X) position we end on.</param> /// <param name="chunkZ">The chunk(Z) position to start the Ray from. This will be /// set to the chunk(Z) position we end on.</param> /// <param name="lookRay">The Ray to look along.</param> /// <param name="maxDistance">The maximum distance along the ray that collisions /// will be considered. Uses the MAX distance.</param> /// <param name="requireVisible">Whether or not to skip invisible blocks.</param> /// <param name="requireImpassable">Whether or not to skip passable blocks.</param> /// <param name="foundBlock">The block that was actually found.</param> /// <param name="blockPosition">The in-chunk (integer) position of the block that was found.</param> /// <param name="faceTouched">The face that was first touched by the Ray.</param> /// <param name="successful">Whether ot not anything was found</param> public void BlockLookedAt(ref int chunkX, ref int chunkZ, Ray lookRay, int maxDistance, bool requireVisible, bool requireImpassable, out Block foundBlock, out Point3 blockPosition, out Face faceTouched, out bool successful) { if (lookRay.Position.Y < 0 || lookRay.Position.Y >= GameConstants.CHUNK_Y_HEIGHT) throw new ArgumentException("Can't cast rays from offscreen!"); successful = false; blockPosition = Point3.RoundDown(lookRay.Position); BoundingBox blockBounds = new BoundingBox( new Vector3(blockPosition.X, blockPosition.Y, blockPosition.Z), new Vector3(blockPosition.X + 1, blockPosition.Y + 1, blockPosition.Z + 1)); if (!blockBounds.Intersects(lookRay).HasValue) throw new NotImplementedException(); int xChange = Numerical.sign(lookRay.Direction.X); int yChange = Numerical.sign(lookRay.Direction.Y); int zChange = Numerical.sign(lookRay.Direction.Z); faceTouched = Face.FRONT; foundBlock = new Block(); while (blockPosition.Y >= 0 && blockPosition.Y < GameConstants.CHUNK_Y_HEIGHT) { bool canMoveX = (xChange != 0); bool canMoveY = (yChange != 0 && (blockPosition.Y + yChange >= 0 && blockPosition.Y + yChange < GameConstants.CHUNK_Y_HEIGHT)); bool canMoveZ = (zChange != 0); bool hitX = false; bool hitY = false; bool hitZ = false; BoundingBox oldBox = blockBounds; int hits = 0; #region Move and count hits... if (canMoveX) { BoundingBox box = new BoundingBox(oldBox.Min + new Vector3(xChange, 0, 0), oldBox.Max + new Vector3(xChange, 0, 0)); float? result = box.Intersects(lookRay); hitX = result.HasValue; if (hitX) { hits++; blockPosition.X += xChange; faceTouched = (xChange < 0 ? Face.RIGHT : Face.LEFT); blockBounds = box; if (Math.Abs(blockPosition.X - lookRay.Position.X) > maxDistance) return; } } if (canMoveY) { BoundingBox box = new BoundingBox(oldBox.Min + new Vector3(0, yChange, 0), oldBox.Max + new Vector3(0, yChange, 0)); float? result = box.Intersects(lookRay); hitY = result.HasValue; if (hitY) { hits++; blockPosition.Y += yChange; faceTouched = (yChange < 0 ? Face.TOP : Face.BOTTOM); blockBounds = box; if (Math.Abs(blockPosition.Y - lookRay.Position.Y) > maxDistance) return; } } if (canMoveZ) { BoundingBox box = new BoundingBox(oldBox.Min + new Vector3(0, 0, zChange), oldBox.Max + new Vector3(0, 0, zChange)); float? result = box.Intersects(lookRay); hitZ = result.HasValue; if (hitZ) { hits++; blockPosition.Z += zChange; faceTouched = (zChange < 0 ? Face.BACK : Face.FRONT); blockBounds = box; if (Math.Abs(blockPosition.Z - lookRay.Position.Z) > maxDistance) return; } } #endregion //We either hit a corner (yielding multiple matches) or ran off //the edge of the world. In either case, I'm OK with "no result." if (hits != 1) return; foundBlock = GetHighPriorityBlock(chunkX, chunkZ, blockPosition.X, blockPosition.Y, blockPosition.Z); if (BlockLookedAt_TestCompatibility(lookRay, requireVisible, requireImpassable, foundBlock, blockBounds)) { successful = true; HelperMethods.FixCoordinates(ref chunkX, ref chunkZ, ref blockPosition); return; } } }
public static int TextureIndex(Block block) { return instance.textureIndex(block); }