1cfd74d65d832137e20e193c960802afba73b5d38sm/* 23c1e67e433728684b5f228c5d4f3e5b1457bb271sm * Copyright (C) 2010 The Android Open Source Project 3cfd74d65d832137e20e193c960802afba73b5d38sm * 4cfd74d65d832137e20e193c960802afba73b5d38sm * Licensed under the Apache License, Version 2.0 (the "License"); 5cfd74d65d832137e20e193c960802afba73b5d38sm * you may not use this file except in compliance with the License. 6cfd74d65d832137e20e193c960802afba73b5d38sm * You may obtain a copy of the License at 7cfd74d65d832137e20e193c960802afba73b5d38sm * 8cfd74d65d832137e20e193c960802afba73b5d38sm * http://www.apache.org/licenses/LICENSE-2.0 9cfd74d65d832137e20e193c960802afba73b5d38sm * 10cfd74d65d832137e20e193c960802afba73b5d38sm * Unless required by applicable law or agreed to in writing, software 11cfd74d65d832137e20e193c960802afba73b5d38sm * distributed under the License is distributed on an "AS IS" BASIS, 12cfd74d65d832137e20e193c960802afba73b5d38sm * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13cfd74d65d832137e20e193c960802afba73b5d38sm * See the License for the specific language governing permissions and 14cfd74d65d832137e20e193c960802afba73b5d38sm * limitations under the License. 15cfd74d65d832137e20e193c960802afba73b5d38sm */ 16cfd74d65d832137e20e193c960802afba73b5d38sm 17cfd74d65d832137e20e193c960802afba73b5d38smpackage com.replica.replicaisland; 18cfd74d65d832137e20e193c960802afba73b5d38sm 19cfd74d65d832137e20e193c960802afba73b5d38smimport android.content.res.AssetManager; 20cfd74d65d832137e20e193c960802afba73b5d38sm 21cfd74d65d832137e20e193c960802afba73b5d38smimport java.io.IOException; 22cfd74d65d832137e20e193c960802afba73b5d38smimport java.io.InputStream; 23cfd74d65d832137e20e193c960802afba73b5d38sm 24cfd74d65d832137e20e193c960802afba73b5d38sm/** 25cfd74d65d832137e20e193c960802afba73b5d38sm * Collision detection system. Provides a ray-based interface for finding surfaces in the collision 26cfd74d65d832137e20e193c960802afba73b5d38sm * world. This version is based on a collision world of line segments, organized into an array of 27cfd74d65d832137e20e193c960802afba73b5d38sm * tiles. The underlying detection algorithm isn't relevant to calling code, however, so this class 28cfd74d65d832137e20e193c960802afba73b5d38sm * may be extended to provide a completely different collision detection scheme. 29cfd74d65d832137e20e193c960802afba73b5d38sm * 30cfd74d65d832137e20e193c960802afba73b5d38sm * This class also provides a system for runtime-generated collision segments. These temporary 31cfd74d65d832137e20e193c960802afba73b5d38sm * segments are cleared each frame, and consequently must be constantly re-submitted if they are 32cfd74d65d832137e20e193c960802afba73b5d38sm * intended to persist. Temporary segments are useful for dynamic solid objects, such as moving 33cfd74d65d832137e20e193c960802afba73b5d38sm * platforms. 34cfd74d65d832137e20e193c960802afba73b5d38sm * 35cfd74d65d832137e20e193c960802afba73b5d38sm * CollisionSystem.TileVisitor is an interface for traversing individual collision tiles. Ray casts 36cfd74d65d832137e20e193c960802afba73b5d38sm * can be used to run user code over the collision world by passing different TileVisitor 37cfd74d65d832137e20e193c960802afba73b5d38sm * implementations to executeRay. Provided is TileTestVisitor, a visitor that compares the segments 38cfd74d65d832137e20e193c960802afba73b5d38sm * of each tile visited with the ray and searches for points of intersection. 39cfd74d65d832137e20e193c960802afba73b5d38sm * 40cfd74d65d832137e20e193c960802afba73b5d38sm */ 41cfd74d65d832137e20e193c960802afba73b5d38smpublic class CollisionSystem extends BaseObject { 42cfd74d65d832137e20e193c960802afba73b5d38sm private TiledWorld mWorld; 43cfd74d65d832137e20e193c960802afba73b5d38sm private CollisionTile[] mCollisionTiles; 44cfd74d65d832137e20e193c960802afba73b5d38sm private LineSegmentPool mSegmentPool; 45cfd74d65d832137e20e193c960802afba73b5d38sm private int mTileWidth; 46cfd74d65d832137e20e193c960802afba73b5d38sm private int mTileHeight; 47cfd74d65d832137e20e193c960802afba73b5d38sm private TileTestVisitor mTileSegmentTester; 48cfd74d65d832137e20e193c960802afba73b5d38sm private FixedSizeArray<LineSegment> mTemporarySegments; 49cfd74d65d832137e20e193c960802afba73b5d38sm private FixedSizeArray<LineSegment> mPendingTemporarySegments; 50cfd74d65d832137e20e193c960802afba73b5d38sm private byte[] mWorkspaceBytes; // Included here to avoid runtime allocation during file io. 51cfd74d65d832137e20e193c960802afba73b5d38sm 52cfd74d65d832137e20e193c960802afba73b5d38sm private static final int MAX_TEMPORARY_SEGMENTS = 256; 53cfd74d65d832137e20e193c960802afba73b5d38sm 54cfd74d65d832137e20e193c960802afba73b5d38sm public CollisionSystem() { 55cfd74d65d832137e20e193c960802afba73b5d38sm super(); 56cfd74d65d832137e20e193c960802afba73b5d38sm mTileSegmentTester = new TileTestVisitor(); 57cfd74d65d832137e20e193c960802afba73b5d38sm mSegmentPool = new LineSegmentPool(MAX_TEMPORARY_SEGMENTS); 58cfd74d65d832137e20e193c960802afba73b5d38sm 59cfd74d65d832137e20e193c960802afba73b5d38sm mTemporarySegments = new FixedSizeArray<LineSegment>(MAX_TEMPORARY_SEGMENTS); 60cfd74d65d832137e20e193c960802afba73b5d38sm mPendingTemporarySegments = new FixedSizeArray<LineSegment>(MAX_TEMPORARY_SEGMENTS); 61cfd74d65d832137e20e193c960802afba73b5d38sm 62cfd74d65d832137e20e193c960802afba73b5d38sm mWorkspaceBytes = new byte[4]; 63cfd74d65d832137e20e193c960802afba73b5d38sm } 64cfd74d65d832137e20e193c960802afba73b5d38sm 65cfd74d65d832137e20e193c960802afba73b5d38sm @Override 66cfd74d65d832137e20e193c960802afba73b5d38sm public void reset() { 67cfd74d65d832137e20e193c960802afba73b5d38sm mWorld = null; 68cfd74d65d832137e20e193c960802afba73b5d38sm mCollisionTiles = null; 69cfd74d65d832137e20e193c960802afba73b5d38sm 70cfd74d65d832137e20e193c960802afba73b5d38sm final int count = mTemporarySegments.getCount(); 71cfd74d65d832137e20e193c960802afba73b5d38sm for (int x = 0; x < count; x++) { 72cfd74d65d832137e20e193c960802afba73b5d38sm mSegmentPool.release(mTemporarySegments.get(x)); 73cfd74d65d832137e20e193c960802afba73b5d38sm mTemporarySegments.set(x, null); 74cfd74d65d832137e20e193c960802afba73b5d38sm } 75cfd74d65d832137e20e193c960802afba73b5d38sm mTemporarySegments.clear(); 76cfd74d65d832137e20e193c960802afba73b5d38sm 77cfd74d65d832137e20e193c960802afba73b5d38sm final int pendingCount = mPendingTemporarySegments.getCount(); 78cfd74d65d832137e20e193c960802afba73b5d38sm for (int x = 0; x < pendingCount; x++) { 79cfd74d65d832137e20e193c960802afba73b5d38sm mSegmentPool.release(mPendingTemporarySegments.get(x)); 80cfd74d65d832137e20e193c960802afba73b5d38sm mPendingTemporarySegments.set(x, null); 81cfd74d65d832137e20e193c960802afba73b5d38sm } 82cfd74d65d832137e20e193c960802afba73b5d38sm mPendingTemporarySegments.clear(); 83cfd74d65d832137e20e193c960802afba73b5d38sm } 84cfd74d65d832137e20e193c960802afba73b5d38sm 85cfd74d65d832137e20e193c960802afba73b5d38sm /* Sets the current collision world to the supplied tile world. */ 86cfd74d65d832137e20e193c960802afba73b5d38sm public void initialize(TiledWorld world, int tileWidth, int tileHeight) { 87cfd74d65d832137e20e193c960802afba73b5d38sm mWorld = world; 88cfd74d65d832137e20e193c960802afba73b5d38sm 89cfd74d65d832137e20e193c960802afba73b5d38sm mTileWidth = tileWidth; 90cfd74d65d832137e20e193c960802afba73b5d38sm mTileHeight = tileHeight; 91cfd74d65d832137e20e193c960802afba73b5d38sm } 92cfd74d65d832137e20e193c960802afba73b5d38sm 93cfd74d65d832137e20e193c960802afba73b5d38sm /** 94cfd74d65d832137e20e193c960802afba73b5d38sm * Casts a ray into the collision world. The ray is bound by the start and end points supplied. 95cfd74d65d832137e20e193c960802afba73b5d38sm * The first intersecting segment that is found is returned; in the case where more than one 96cfd74d65d832137e20e193c960802afba73b5d38sm * segment is found, the segment closest to the start point is returned. 97cfd74d65d832137e20e193c960802afba73b5d38sm * 98cfd74d65d832137e20e193c960802afba73b5d38sm * @param startPoint The starting point for the ray in world units. 99cfd74d65d832137e20e193c960802afba73b5d38sm * @param endPoint The end point for the ray in world units. 100cfd74d65d832137e20e193c960802afba73b5d38sm * @param movementDirection If set, only segments with normals that oppose this direction will 101cfd74d65d832137e20e193c960802afba73b5d38sm * be counted as valid intersections. If null, all intersecting segments will be 102cfd74d65d832137e20e193c960802afba73b5d38sm * considered valid. 103cfd74d65d832137e20e193c960802afba73b5d38sm * @param hitPoint The point of intersection between a ray and a surface, if one is found. 104cfd74d65d832137e20e193c960802afba73b5d38sm * @param hitNormal The normal of the intersecting surface if an intersection is found. 105cfd74d65d832137e20e193c960802afba73b5d38sm * @param excludeObject If set, dynamic surfaces from this object will be ignored. 106cfd74d65d832137e20e193c960802afba73b5d38sm * @return true if a valid intersecting surface was found, false otherwise. 107cfd74d65d832137e20e193c960802afba73b5d38sm */ 108cfd74d65d832137e20e193c960802afba73b5d38sm // TODO: switch to return data as a HitPoint. 109cfd74d65d832137e20e193c960802afba73b5d38sm public boolean castRay(Vector2 startPoint, Vector2 endPoint, Vector2 movementDirection, 110cfd74d65d832137e20e193c960802afba73b5d38sm Vector2 hitPoint, Vector2 hitNormal, GameObject excludeObject) { 111cfd74d65d832137e20e193c960802afba73b5d38sm 112cfd74d65d832137e20e193c960802afba73b5d38sm boolean hit = false; 113cfd74d65d832137e20e193c960802afba73b5d38sm 114cfd74d65d832137e20e193c960802afba73b5d38sm mTileSegmentTester.setup(movementDirection, mTileWidth, mTileHeight); 115cfd74d65d832137e20e193c960802afba73b5d38sm 116cfd74d65d832137e20e193c960802afba73b5d38sm if (mCollisionTiles != null && 117cfd74d65d832137e20e193c960802afba73b5d38sm executeRay(startPoint, endPoint, hitPoint, hitNormal, mTileSegmentTester) != -1) { 118cfd74d65d832137e20e193c960802afba73b5d38sm hit = true; 119cfd74d65d832137e20e193c960802afba73b5d38sm } 120cfd74d65d832137e20e193c960802afba73b5d38sm 121cfd74d65d832137e20e193c960802afba73b5d38sm if (mTemporarySegments.getCount() > 0) { 122cfd74d65d832137e20e193c960802afba73b5d38sm VectorPool vectorPool = sSystemRegistry.vectorPool; 123cfd74d65d832137e20e193c960802afba73b5d38sm Vector2 tempHitPoint = vectorPool.allocate(); 124cfd74d65d832137e20e193c960802afba73b5d38sm Vector2 tempHitNormal = vectorPool.allocate(); 125cfd74d65d832137e20e193c960802afba73b5d38sm 126cfd74d65d832137e20e193c960802afba73b5d38sm if (testSegmentAgainstList(mTemporarySegments, startPoint, endPoint, tempHitPoint, 127cfd74d65d832137e20e193c960802afba73b5d38sm tempHitNormal, movementDirection, excludeObject)) { 128cfd74d65d832137e20e193c960802afba73b5d38sm if (hit) { 129cfd74d65d832137e20e193c960802afba73b5d38sm // Check to see whether this collision is closer to the one we already found or 130cfd74d65d832137e20e193c960802afba73b5d38sm // not. 131cfd74d65d832137e20e193c960802afba73b5d38sm final float firstCollisionDistance = startPoint.distance2(hitPoint); 132cfd74d65d832137e20e193c960802afba73b5d38sm if (firstCollisionDistance > startPoint.distance2(tempHitPoint)) { 133cfd74d65d832137e20e193c960802afba73b5d38sm // The temporary surface is closer. 134cfd74d65d832137e20e193c960802afba73b5d38sm hitPoint.set(tempHitPoint); 135cfd74d65d832137e20e193c960802afba73b5d38sm hitNormal.set(tempHitNormal); 136cfd74d65d832137e20e193c960802afba73b5d38sm } 137cfd74d65d832137e20e193c960802afba73b5d38sm } else { 138cfd74d65d832137e20e193c960802afba73b5d38sm hit = true; 139cfd74d65d832137e20e193c960802afba73b5d38sm hitPoint.set(tempHitPoint); 140cfd74d65d832137e20e193c960802afba73b5d38sm hitNormal.set(tempHitNormal); 141cfd74d65d832137e20e193c960802afba73b5d38sm } 142cfd74d65d832137e20e193c960802afba73b5d38sm } 143cfd74d65d832137e20e193c960802afba73b5d38sm 144cfd74d65d832137e20e193c960802afba73b5d38sm vectorPool.release(tempHitPoint); 145cfd74d65d832137e20e193c960802afba73b5d38sm vectorPool.release(tempHitNormal); 146cfd74d65d832137e20e193c960802afba73b5d38sm } 147cfd74d65d832137e20e193c960802afba73b5d38sm 148cfd74d65d832137e20e193c960802afba73b5d38sm return hit; 149cfd74d65d832137e20e193c960802afba73b5d38sm } 150cfd74d65d832137e20e193c960802afba73b5d38sm 151cfd74d65d832137e20e193c960802afba73b5d38sm public boolean testBox(float left, float right, float top, float bottom, 152cfd74d65d832137e20e193c960802afba73b5d38sm Vector2 movementDirection, FixedSizeArray<HitPoint> hitPoints, 153cfd74d65d832137e20e193c960802afba73b5d38sm GameObject excludeObject, boolean testDynamicSurfacesOnly) { 154cfd74d65d832137e20e193c960802afba73b5d38sm 155cfd74d65d832137e20e193c960802afba73b5d38sm boolean foundHit = false; 156cfd74d65d832137e20e193c960802afba73b5d38sm 157cfd74d65d832137e20e193c960802afba73b5d38sm // Test against the background. 158cfd74d65d832137e20e193c960802afba73b5d38sm if (!testDynamicSurfacesOnly) { 159cfd74d65d832137e20e193c960802afba73b5d38sm float startX = left; 160cfd74d65d832137e20e193c960802afba73b5d38sm float endX = right; 161cfd74d65d832137e20e193c960802afba73b5d38sm float startY = bottom; 162cfd74d65d832137e20e193c960802afba73b5d38sm float endY = top; 163cfd74d65d832137e20e193c960802afba73b5d38sm int xIncrement = 1; 164cfd74d65d832137e20e193c960802afba73b5d38sm int yIncrement = 1; 165cfd74d65d832137e20e193c960802afba73b5d38sm 166cfd74d65d832137e20e193c960802afba73b5d38sm if (movementDirection != null) { 167cfd74d65d832137e20e193c960802afba73b5d38sm if (movementDirection.x < 0.0f) { 168cfd74d65d832137e20e193c960802afba73b5d38sm startX = right; 169cfd74d65d832137e20e193c960802afba73b5d38sm endX = left; 170cfd74d65d832137e20e193c960802afba73b5d38sm xIncrement = -1; 171cfd74d65d832137e20e193c960802afba73b5d38sm } 172cfd74d65d832137e20e193c960802afba73b5d38sm if (movementDirection.y < 0.0f) { 173cfd74d65d832137e20e193c960802afba73b5d38sm startY = top; 174cfd74d65d832137e20e193c960802afba73b5d38sm endY = bottom; 175cfd74d65d832137e20e193c960802afba73b5d38sm yIncrement = -1; 176cfd74d65d832137e20e193c960802afba73b5d38sm } 177cfd74d65d832137e20e193c960802afba73b5d38sm } 178cfd74d65d832137e20e193c960802afba73b5d38sm final int startTileX = Utils.clamp((int)(startX / mTileWidth), 0, mWorld.getWidth() - 1); 179cfd74d65d832137e20e193c960802afba73b5d38sm final int endTileX = Utils.clamp((int)(endX / mTileWidth), 0, mWorld.getWidth() - 1); 180cfd74d65d832137e20e193c960802afba73b5d38sm final int startTileY = Utils.clamp((int)(startY / mTileHeight), 0, mWorld.getHeight() - 1); 181cfd74d65d832137e20e193c960802afba73b5d38sm final int endTileY = Utils.clamp((int)(endY / mTileHeight), 0, mWorld.getHeight() - 1); 182cfd74d65d832137e20e193c960802afba73b5d38sm 183cfd74d65d832137e20e193c960802afba73b5d38sm VectorPool vectorPool = sSystemRegistry.vectorPool; 184cfd74d65d832137e20e193c960802afba73b5d38sm Vector2 worldTileOffset = vectorPool.allocate(); 185cfd74d65d832137e20e193c960802afba73b5d38sm 186cfd74d65d832137e20e193c960802afba73b5d38sm final int[][] tileArray = mWorld.getTiles(); 187cfd74d65d832137e20e193c960802afba73b5d38sm final int worldHeight = mWorld.getHeight() - 1; 188cfd74d65d832137e20e193c960802afba73b5d38sm 189cfd74d65d832137e20e193c960802afba73b5d38sm 190cfd74d65d832137e20e193c960802afba73b5d38sm for (int y = startTileY; y != endTileY + yIncrement; y += yIncrement) { 191cfd74d65d832137e20e193c960802afba73b5d38sm for (int x = startTileX; x != endTileX + xIncrement; x += xIncrement) { 192cfd74d65d832137e20e193c960802afba73b5d38sm final int tileIndex = tileArray[x][worldHeight - y]; 193cfd74d65d832137e20e193c960802afba73b5d38sm if (tileIndex >= 0 && tileIndex < mCollisionTiles.length 194cfd74d65d832137e20e193c960802afba73b5d38sm && mCollisionTiles[tileIndex] != null) { 195cfd74d65d832137e20e193c960802afba73b5d38sm 196cfd74d65d832137e20e193c960802afba73b5d38sm final float xOffset = x * mTileWidth; 197cfd74d65d832137e20e193c960802afba73b5d38sm final float yOffset = y * mTileHeight; 198cfd74d65d832137e20e193c960802afba73b5d38sm 199cfd74d65d832137e20e193c960802afba73b5d38sm final float tileSpaceLeft = left - xOffset; 200cfd74d65d832137e20e193c960802afba73b5d38sm final float tileSpaceRight = right - xOffset; 201cfd74d65d832137e20e193c960802afba73b5d38sm final float tileSpaceTop = top - yOffset; 202cfd74d65d832137e20e193c960802afba73b5d38sm final float tileSpaceBottom = bottom - yOffset; 203cfd74d65d832137e20e193c960802afba73b5d38sm 204cfd74d65d832137e20e193c960802afba73b5d38sm worldTileOffset.set(xOffset, yOffset); 205cfd74d65d832137e20e193c960802afba73b5d38sm 206cfd74d65d832137e20e193c960802afba73b5d38sm boolean hit = testBoxAgainstList(mCollisionTiles[tileIndex].segments, 207cfd74d65d832137e20e193c960802afba73b5d38sm tileSpaceLeft, tileSpaceRight, tileSpaceTop, tileSpaceBottom, 208cfd74d65d832137e20e193c960802afba73b5d38sm movementDirection, excludeObject, worldTileOffset, hitPoints); 209cfd74d65d832137e20e193c960802afba73b5d38sm 210cfd74d65d832137e20e193c960802afba73b5d38sm if (hit) { 211cfd74d65d832137e20e193c960802afba73b5d38sm foundHit = true; 212cfd74d65d832137e20e193c960802afba73b5d38sm } 213cfd74d65d832137e20e193c960802afba73b5d38sm 214cfd74d65d832137e20e193c960802afba73b5d38sm } 215cfd74d65d832137e20e193c960802afba73b5d38sm } 216cfd74d65d832137e20e193c960802afba73b5d38sm } 217cfd74d65d832137e20e193c960802afba73b5d38sm 218cfd74d65d832137e20e193c960802afba73b5d38sm vectorPool.release(worldTileOffset); 219cfd74d65d832137e20e193c960802afba73b5d38sm } 220cfd74d65d832137e20e193c960802afba73b5d38sm // temporary segments 221cfd74d65d832137e20e193c960802afba73b5d38sm boolean tempHit = testBoxAgainstList(mTemporarySegments, 222cfd74d65d832137e20e193c960802afba73b5d38sm left, right, top, bottom, 223cfd74d65d832137e20e193c960802afba73b5d38sm movementDirection, excludeObject, Vector2.ZERO, hitPoints); 224cfd74d65d832137e20e193c960802afba73b5d38sm 225cfd74d65d832137e20e193c960802afba73b5d38sm if (tempHit) { 226cfd74d65d832137e20e193c960802afba73b5d38sm foundHit = true; 227cfd74d65d832137e20e193c960802afba73b5d38sm } 228cfd74d65d832137e20e193c960802afba73b5d38sm 229cfd74d65d832137e20e193c960802afba73b5d38sm 230cfd74d65d832137e20e193c960802afba73b5d38sm 231cfd74d65d832137e20e193c960802afba73b5d38sm return foundHit; 232cfd74d65d832137e20e193c960802afba73b5d38sm } 233cfd74d65d832137e20e193c960802afba73b5d38sm 234cfd74d65d832137e20e193c960802afba73b5d38sm /* Inserts a temporary surface into the collision world. It will persist for one frame. */ 235cfd74d65d832137e20e193c960802afba73b5d38sm public void addTemporarySurface(Vector2 startPoint, Vector2 endPoint, Vector2 normal, 236cfd74d65d832137e20e193c960802afba73b5d38sm GameObject ownerObject) { 237cfd74d65d832137e20e193c960802afba73b5d38sm LineSegment newSegment = mSegmentPool.allocate(); 238cfd74d65d832137e20e193c960802afba73b5d38sm 239cfd74d65d832137e20e193c960802afba73b5d38sm newSegment.set(startPoint, endPoint, normal); 240cfd74d65d832137e20e193c960802afba73b5d38sm newSegment.setOwner(ownerObject); 241cfd74d65d832137e20e193c960802afba73b5d38sm 242cfd74d65d832137e20e193c960802afba73b5d38sm mPendingTemporarySegments.add(newSegment); 243cfd74d65d832137e20e193c960802afba73b5d38sm } 244cfd74d65d832137e20e193c960802afba73b5d38sm 245cfd74d65d832137e20e193c960802afba73b5d38sm @Override 246cfd74d65d832137e20e193c960802afba73b5d38sm public void update(float timeDelta, BaseObject parent) { 247cfd74d65d832137e20e193c960802afba73b5d38sm // Clear temporary surfaces 248cfd74d65d832137e20e193c960802afba73b5d38sm final int count = mTemporarySegments.getCount(); 249cfd74d65d832137e20e193c960802afba73b5d38sm if (mCollisionTiles != null && count > 0) { 250cfd74d65d832137e20e193c960802afba73b5d38sm for (int x = 0; x < count; x++) { 251cfd74d65d832137e20e193c960802afba73b5d38sm mSegmentPool.release(mTemporarySegments.get(x)); 252cfd74d65d832137e20e193c960802afba73b5d38sm mTemporarySegments.set(x, null); 253cfd74d65d832137e20e193c960802afba73b5d38sm } 254cfd74d65d832137e20e193c960802afba73b5d38sm mTemporarySegments.clear(); 255cfd74d65d832137e20e193c960802afba73b5d38sm } 256cfd74d65d832137e20e193c960802afba73b5d38sm 257cfd74d65d832137e20e193c960802afba73b5d38sm // Temporary surfaces must persist for one frame in order to be reliable independent of 258cfd74d65d832137e20e193c960802afba73b5d38sm // frame execution order. So each frame we queue up inserted segments and then swap them 259cfd74d65d832137e20e193c960802afba73b5d38sm // into activity when this system is updated. 260cfd74d65d832137e20e193c960802afba73b5d38sm FixedSizeArray<LineSegment> swap = mTemporarySegments; 261cfd74d65d832137e20e193c960802afba73b5d38sm mTemporarySegments = mPendingTemporarySegments; 262cfd74d65d832137e20e193c960802afba73b5d38sm mPendingTemporarySegments = swap; 263cfd74d65d832137e20e193c960802afba73b5d38sm } 264cfd74d65d832137e20e193c960802afba73b5d38sm 265cfd74d65d832137e20e193c960802afba73b5d38sm /** 266cfd74d65d832137e20e193c960802afba73b5d38sm * Shoots a ray through the collision world. This function is similar to executeRay() below, 267cfd74d65d832137e20e193c960802afba73b5d38sm * except that it is optimized for straight lines (either completely horizontal or completely 268cfd74d65d832137e20e193c960802afba73b5d38sm * vertical). 269cfd74d65d832137e20e193c960802afba73b5d38sm * 270cfd74d65d832137e20e193c960802afba73b5d38sm * @param startPoint The starting point for the ray, in world space. 271cfd74d65d832137e20e193c960802afba73b5d38sm * @param endPoint The ending point for the ray in world space. 272cfd74d65d832137e20e193c960802afba73b5d38sm * @param hitPoint Set to the intersection coordinates if an intersection is found. 273cfd74d65d832137e20e193c960802afba73b5d38sm * @param hitNormal Set to the normal of the intersecting surface if an intersection is found. 274cfd74d65d832137e20e193c960802afba73b5d38sm * @param visitor Class defining what work to perform at each tile step. 275cfd74d65d832137e20e193c960802afba73b5d38sm * @return The index of the tile that intersected the ray, or -1 if no intersection was found. 276cfd74d65d832137e20e193c960802afba73b5d38sm */ 277cfd74d65d832137e20e193c960802afba73b5d38sm protected int executeStraigtRay(final Vector2 startPoint, final Vector2 endPoint, 278cfd74d65d832137e20e193c960802afba73b5d38sm final int startTileX, final int startTileY, final int endTileX, final int endTileY, 279cfd74d65d832137e20e193c960802afba73b5d38sm final int deltaX, final int deltaY, 280cfd74d65d832137e20e193c960802afba73b5d38sm Vector2 hitPoint, Vector2 hitNormal, TileVisitor visitor) { 281cfd74d65d832137e20e193c960802afba73b5d38sm 282cfd74d65d832137e20e193c960802afba73b5d38sm int currentX = startTileX; 283cfd74d65d832137e20e193c960802afba73b5d38sm int currentY = startTileY; 284cfd74d65d832137e20e193c960802afba73b5d38sm 285cfd74d65d832137e20e193c960802afba73b5d38sm int xIncrement = 0; 286cfd74d65d832137e20e193c960802afba73b5d38sm int yIncrement = 0; 287cfd74d65d832137e20e193c960802afba73b5d38sm int distance = 0; 288cfd74d65d832137e20e193c960802afba73b5d38sm 289cfd74d65d832137e20e193c960802afba73b5d38sm if (deltaX != 0) { 290cfd74d65d832137e20e193c960802afba73b5d38sm distance = Math.abs(deltaX) + 1; 291cfd74d65d832137e20e193c960802afba73b5d38sm xIncrement = Utils.sign(deltaX); 292cfd74d65d832137e20e193c960802afba73b5d38sm } else if (deltaY != 0) { 293cfd74d65d832137e20e193c960802afba73b5d38sm distance = Math.abs(deltaY) + 1; 294cfd74d65d832137e20e193c960802afba73b5d38sm yIncrement = Utils.sign(deltaY); 295cfd74d65d832137e20e193c960802afba73b5d38sm } 296cfd74d65d832137e20e193c960802afba73b5d38sm 297cfd74d65d832137e20e193c960802afba73b5d38sm int hitTile = -1; 298cfd74d65d832137e20e193c960802afba73b5d38sm final int worldHeight = mWorld.getHeight() - 1; 299cfd74d65d832137e20e193c960802afba73b5d38sm final int[][] tileArray = mWorld.getTiles(); 300cfd74d65d832137e20e193c960802afba73b5d38sm for (int x = 0; x < distance; x++) { 301cfd74d65d832137e20e193c960802afba73b5d38sm final int tileIndex = tileArray[currentX][worldHeight - currentY]; 302cfd74d65d832137e20e193c960802afba73b5d38sm if (tileIndex >= 0 && tileIndex < mCollisionTiles.length 303cfd74d65d832137e20e193c960802afba73b5d38sm && mCollisionTiles[tileIndex] != null) { 304cfd74d65d832137e20e193c960802afba73b5d38sm if (visitor.visit(mCollisionTiles[tileIndex], startPoint, endPoint, 305cfd74d65d832137e20e193c960802afba73b5d38sm hitPoint, hitNormal, currentX, currentY)) { 306cfd74d65d832137e20e193c960802afba73b5d38sm hitTile = tileIndex; 307cfd74d65d832137e20e193c960802afba73b5d38sm break; 308cfd74d65d832137e20e193c960802afba73b5d38sm } 309cfd74d65d832137e20e193c960802afba73b5d38sm } 310cfd74d65d832137e20e193c960802afba73b5d38sm currentX += xIncrement; 311cfd74d65d832137e20e193c960802afba73b5d38sm currentY += yIncrement; 312cfd74d65d832137e20e193c960802afba73b5d38sm } 313cfd74d65d832137e20e193c960802afba73b5d38sm 314cfd74d65d832137e20e193c960802afba73b5d38sm return hitTile; 315cfd74d65d832137e20e193c960802afba73b5d38sm } 316cfd74d65d832137e20e193c960802afba73b5d38sm 317cfd74d65d832137e20e193c960802afba73b5d38sm /** 318cfd74d65d832137e20e193c960802afba73b5d38sm * Shoots a ray through the collision world. Since the collision world is a 2D array of tiles, 319cfd74d65d832137e20e193c960802afba73b5d38sm * this algorithm traces a line in tile space and tests against each non-empty tile it visits. 320cfd74d65d832137e20e193c960802afba73b5d38sm * The Bresenham line algorithm is used for the actual traversal, but the action taken at each 321cfd74d65d832137e20e193c960802afba73b5d38sm * tile is defined by the visitor class passed to this function. 322cfd74d65d832137e20e193c960802afba73b5d38sm * 323cfd74d65d832137e20e193c960802afba73b5d38sm * @param startPoint The starting point for the ray, in world space. 324cfd74d65d832137e20e193c960802afba73b5d38sm * @param endPoint The ending point for the ray in world space. 325cfd74d65d832137e20e193c960802afba73b5d38sm * @param hitPoint Set to the intersection coordinates if an intersection is found. 326cfd74d65d832137e20e193c960802afba73b5d38sm * @param hitNormal Set to the normal of the intersecting surface if an intersection is found. 327cfd74d65d832137e20e193c960802afba73b5d38sm * @param visitor Class defining what work to perform at each tile step. 328cfd74d65d832137e20e193c960802afba73b5d38sm * @return The index of the tile that intersected the ray, or -1 if no intersection was found. 329cfd74d65d832137e20e193c960802afba73b5d38sm */ 330cfd74d65d832137e20e193c960802afba73b5d38sm protected int executeRay(Vector2 startPoint, Vector2 endPoint, 331cfd74d65d832137e20e193c960802afba73b5d38sm Vector2 hitPoint, Vector2 hitNormal, TileVisitor visitor) { 332cfd74d65d832137e20e193c960802afba73b5d38sm 333cfd74d65d832137e20e193c960802afba73b5d38sm final int worldHeight = mWorld.getHeight(); 334cfd74d65d832137e20e193c960802afba73b5d38sm final int worldWidth = mWorld.getWidth(); 335cfd74d65d832137e20e193c960802afba73b5d38sm 336cfd74d65d832137e20e193c960802afba73b5d38sm final int startTileX = worldToTileColumn(startPoint.x, worldWidth); 337cfd74d65d832137e20e193c960802afba73b5d38sm final int startTileY = worldToTileRow(startPoint.y, worldHeight); 338cfd74d65d832137e20e193c960802afba73b5d38sm 339cfd74d65d832137e20e193c960802afba73b5d38sm final int endTileX = worldToTileColumn(endPoint.x, worldWidth); 340cfd74d65d832137e20e193c960802afba73b5d38sm final int endTileY = worldToTileRow(endPoint.y, worldHeight); 341cfd74d65d832137e20e193c960802afba73b5d38sm 342cfd74d65d832137e20e193c960802afba73b5d38sm int currentX = startTileX; 343cfd74d65d832137e20e193c960802afba73b5d38sm int currentY = startTileY; 344cfd74d65d832137e20e193c960802afba73b5d38sm 345cfd74d65d832137e20e193c960802afba73b5d38sm final int deltaX = endTileX - startTileX; 346cfd74d65d832137e20e193c960802afba73b5d38sm final int deltaY = endTileY - startTileY; 347cfd74d65d832137e20e193c960802afba73b5d38sm 348cfd74d65d832137e20e193c960802afba73b5d38sm int hitTile = -1; 349cfd74d65d832137e20e193c960802afba73b5d38sm 350cfd74d65d832137e20e193c960802afba73b5d38sm if (deltaX == 0 || deltaY == 0) { 351cfd74d65d832137e20e193c960802afba73b5d38sm hitTile = executeStraigtRay(startPoint, endPoint, startTileX, startTileY, 352cfd74d65d832137e20e193c960802afba73b5d38sm endTileX, endTileY, deltaX, deltaY, hitPoint, hitNormal, visitor); 353cfd74d65d832137e20e193c960802afba73b5d38sm } else { 354cfd74d65d832137e20e193c960802afba73b5d38sm 355cfd74d65d832137e20e193c960802afba73b5d38sm final int xIncrement = deltaX != 0 ? Utils.sign(deltaX) : 0; 356cfd74d65d832137e20e193c960802afba73b5d38sm final int yIncrement = deltaY != 0 ? Utils.sign(deltaY) : 0; 357cfd74d65d832137e20e193c960802afba73b5d38sm 358cfd74d65d832137e20e193c960802afba73b5d38sm // Note: I'm deviating from the Bresenham algorithm here by adding one to force the end 359cfd74d65d832137e20e193c960802afba73b5d38sm // tile to be visited. 3609d4cc2572d37983607df38b0f4216ed76ac51814sm final int lateralDelta = (endTileX > 0 && endTileX < worldWidth - 1) ? Math.abs(deltaX) + 1 : Math.abs(deltaX); 3619d4cc2572d37983607df38b0f4216ed76ac51814sm final int verticalDelta = (endTileY > 0 && endTileY < worldHeight - 1) ? Math.abs(deltaY) + 1 : Math.abs(deltaY); 362cfd74d65d832137e20e193c960802afba73b5d38sm 363cfd74d65d832137e20e193c960802afba73b5d38sm final int deltaX2 = lateralDelta * 2; 364cfd74d65d832137e20e193c960802afba73b5d38sm final int deltaY2 = verticalDelta * 2; 365cfd74d65d832137e20e193c960802afba73b5d38sm 366cfd74d65d832137e20e193c960802afba73b5d38sm final int worldHeightMinusOne = worldHeight - 1; 367cfd74d65d832137e20e193c960802afba73b5d38sm final int[][]tileArray = mWorld.getTiles(); 368cfd74d65d832137e20e193c960802afba73b5d38sm 369cfd74d65d832137e20e193c960802afba73b5d38sm // Bresenham line algorithm in tile space. 370cfd74d65d832137e20e193c960802afba73b5d38sm if (lateralDelta >= verticalDelta) { 371cfd74d65d832137e20e193c960802afba73b5d38sm int error = deltaY2 - lateralDelta; 372cfd74d65d832137e20e193c960802afba73b5d38sm for (int i = 0; i < lateralDelta; i++) { 373cfd74d65d832137e20e193c960802afba73b5d38sm final int tileIndex = tileArray[currentX][worldHeightMinusOne - currentY]; 374cfd74d65d832137e20e193c960802afba73b5d38sm if (tileIndex >= 0 && tileIndex < mCollisionTiles.length 375cfd74d65d832137e20e193c960802afba73b5d38sm && mCollisionTiles[tileIndex] != null) { 376cfd74d65d832137e20e193c960802afba73b5d38sm if (visitor.visit(mCollisionTiles[tileIndex], startPoint, endPoint, 377cfd74d65d832137e20e193c960802afba73b5d38sm hitPoint, hitNormal, currentX, currentY)) { 378cfd74d65d832137e20e193c960802afba73b5d38sm hitTile = tileIndex; 379cfd74d65d832137e20e193c960802afba73b5d38sm break; 380cfd74d65d832137e20e193c960802afba73b5d38sm } 381cfd74d65d832137e20e193c960802afba73b5d38sm } 382cfd74d65d832137e20e193c960802afba73b5d38sm 383cfd74d65d832137e20e193c960802afba73b5d38sm if (error > 0) { 384cfd74d65d832137e20e193c960802afba73b5d38sm currentY += yIncrement; 385cfd74d65d832137e20e193c960802afba73b5d38sm error -= deltaX2; 386cfd74d65d832137e20e193c960802afba73b5d38sm } 387cfd74d65d832137e20e193c960802afba73b5d38sm 388cfd74d65d832137e20e193c960802afba73b5d38sm error += deltaY2; 389cfd74d65d832137e20e193c960802afba73b5d38sm currentX += xIncrement; 390cfd74d65d832137e20e193c960802afba73b5d38sm } 391cfd74d65d832137e20e193c960802afba73b5d38sm } 392cfd74d65d832137e20e193c960802afba73b5d38sm else if (verticalDelta >= lateralDelta) { 393cfd74d65d832137e20e193c960802afba73b5d38sm int error = deltaX2 - verticalDelta; 394cfd74d65d832137e20e193c960802afba73b5d38sm 395cfd74d65d832137e20e193c960802afba73b5d38sm for (int i = 0; i < verticalDelta; i++) { 396cfd74d65d832137e20e193c960802afba73b5d38sm final int tileIndex = tileArray[currentX][worldHeightMinusOne - currentY]; 397cfd74d65d832137e20e193c960802afba73b5d38sm if (tileIndex >= 0 && tileIndex < mCollisionTiles.length 398cfd74d65d832137e20e193c960802afba73b5d38sm && mCollisionTiles[tileIndex] != null) { 399cfd74d65d832137e20e193c960802afba73b5d38sm if (visitor.visit(mCollisionTiles[tileIndex], startPoint, endPoint, 400cfd74d65d832137e20e193c960802afba73b5d38sm hitPoint, hitNormal, currentX, currentY)) { 401cfd74d65d832137e20e193c960802afba73b5d38sm hitTile = tileIndex; 402cfd74d65d832137e20e193c960802afba73b5d38sm break; 403cfd74d65d832137e20e193c960802afba73b5d38sm } 404cfd74d65d832137e20e193c960802afba73b5d38sm } 405cfd74d65d832137e20e193c960802afba73b5d38sm 406cfd74d65d832137e20e193c960802afba73b5d38sm if (error > 0) { 407cfd74d65d832137e20e193c960802afba73b5d38sm currentX += xIncrement; 408cfd74d65d832137e20e193c960802afba73b5d38sm error -= deltaY2; 409cfd74d65d832137e20e193c960802afba73b5d38sm } 410cfd74d65d832137e20e193c960802afba73b5d38sm 411cfd74d65d832137e20e193c960802afba73b5d38sm error += deltaX2; 412cfd74d65d832137e20e193c960802afba73b5d38sm currentY += yIncrement; 413cfd74d65d832137e20e193c960802afba73b5d38sm } 414cfd74d65d832137e20e193c960802afba73b5d38sm } 415cfd74d65d832137e20e193c960802afba73b5d38sm } 416cfd74d65d832137e20e193c960802afba73b5d38sm return hitTile; 417cfd74d65d832137e20e193c960802afba73b5d38sm } 418cfd74d65d832137e20e193c960802afba73b5d38sm 419cfd74d65d832137e20e193c960802afba73b5d38sm protected final int worldToTileColumn(final float x, final int width) { 420cfd74d65d832137e20e193c960802afba73b5d38sm return Utils.clamp((int)Math.floor(x / mTileWidth), 0, width - 1); 421cfd74d65d832137e20e193c960802afba73b5d38sm } 422cfd74d65d832137e20e193c960802afba73b5d38sm 423cfd74d65d832137e20e193c960802afba73b5d38sm protected final int worldToTileRow(float y, final int height) { 424cfd74d65d832137e20e193c960802afba73b5d38sm return Utils.clamp((int)Math.floor(y / mTileHeight), 0, height - 1); 425cfd74d65d832137e20e193c960802afba73b5d38sm } 426cfd74d65d832137e20e193c960802afba73b5d38sm 427cfd74d65d832137e20e193c960802afba73b5d38sm /* 428cfd74d65d832137e20e193c960802afba73b5d38sm * Given a list of segments and a ray, this function performs an intersection search and 429cfd74d65d832137e20e193c960802afba73b5d38sm * returns the closest intersecting segment, if any exists. 430cfd74d65d832137e20e193c960802afba73b5d38sm */ 431cfd74d65d832137e20e193c960802afba73b5d38sm protected static boolean testSegmentAgainstList(FixedSizeArray<LineSegment> segments, 432cfd74d65d832137e20e193c960802afba73b5d38sm Vector2 startPoint, Vector2 endPoint, Vector2 hitPoint, Vector2 hitNormal, 433cfd74d65d832137e20e193c960802afba73b5d38sm Vector2 movementDirection, GameObject excludeObject) { 434cfd74d65d832137e20e193c960802afba73b5d38sm boolean foundHit = false; 435cfd74d65d832137e20e193c960802afba73b5d38sm float closestDistance = -1; 436cfd74d65d832137e20e193c960802afba73b5d38sm float hitX = 0; 437cfd74d65d832137e20e193c960802afba73b5d38sm float hitY = 0; 438cfd74d65d832137e20e193c960802afba73b5d38sm float normalX = 0; 439cfd74d65d832137e20e193c960802afba73b5d38sm float normalY = 0; 440cfd74d65d832137e20e193c960802afba73b5d38sm final int count = segments.getCount(); 441cfd74d65d832137e20e193c960802afba73b5d38sm final Object[] segmentArray = segments.getArray(); 442cfd74d65d832137e20e193c960802afba73b5d38sm for (int x = 0; x < count; x++) { 443cfd74d65d832137e20e193c960802afba73b5d38sm LineSegment segment = (LineSegment)segmentArray[x]; 444cfd74d65d832137e20e193c960802afba73b5d38sm // If a movement direction has been passed, filter out invalid surfaces by ignoring 445cfd74d65d832137e20e193c960802afba73b5d38sm // those that do not oppose movement. If no direction has been passed, accept all 446cfd74d65d832137e20e193c960802afba73b5d38sm // surfaces. 447cfd74d65d832137e20e193c960802afba73b5d38sm final float dot = movementDirection.length2() > 0.0f ? 448cfd74d65d832137e20e193c960802afba73b5d38sm movementDirection.dot(segment.mNormal) : -1.0f; 449cfd74d65d832137e20e193c960802afba73b5d38sm 450cfd74d65d832137e20e193c960802afba73b5d38sm if (dot < 0.0f && 451cfd74d65d832137e20e193c960802afba73b5d38sm (excludeObject == null || segment.owner != excludeObject) && 452cfd74d65d832137e20e193c960802afba73b5d38sm segment.calculateIntersection(startPoint, endPoint, hitPoint)) { 453cfd74d65d832137e20e193c960802afba73b5d38sm final float distance = hitPoint.distance2(startPoint); 454cfd74d65d832137e20e193c960802afba73b5d38sm 455cfd74d65d832137e20e193c960802afba73b5d38sm if (!foundHit || closestDistance > distance) { 456cfd74d65d832137e20e193c960802afba73b5d38sm closestDistance = distance; 457cfd74d65d832137e20e193c960802afba73b5d38sm foundHit = true; 458cfd74d65d832137e20e193c960802afba73b5d38sm normalX = segment.mNormal.x; 459cfd74d65d832137e20e193c960802afba73b5d38sm normalY = segment.mNormal.y; 460cfd74d65d832137e20e193c960802afba73b5d38sm // Store the components on their own so we don't have to allocate a vector 461cfd74d65d832137e20e193c960802afba73b5d38sm // in this loop. 462cfd74d65d832137e20e193c960802afba73b5d38sm hitX = hitPoint.x; 463cfd74d65d832137e20e193c960802afba73b5d38sm hitY = hitPoint.y; 464cfd74d65d832137e20e193c960802afba73b5d38sm } 465cfd74d65d832137e20e193c960802afba73b5d38sm } 466cfd74d65d832137e20e193c960802afba73b5d38sm } 467cfd74d65d832137e20e193c960802afba73b5d38sm 468cfd74d65d832137e20e193c960802afba73b5d38sm if (foundHit) { 469cfd74d65d832137e20e193c960802afba73b5d38sm hitPoint.set(hitX, hitY); 470cfd74d65d832137e20e193c960802afba73b5d38sm hitNormal.set(normalX, normalY); 471cfd74d65d832137e20e193c960802afba73b5d38sm } 472cfd74d65d832137e20e193c960802afba73b5d38sm return foundHit; 473cfd74d65d832137e20e193c960802afba73b5d38sm } 474cfd74d65d832137e20e193c960802afba73b5d38sm 475cfd74d65d832137e20e193c960802afba73b5d38sm protected static boolean testBoxAgainstList(FixedSizeArray<LineSegment> segments, 476cfd74d65d832137e20e193c960802afba73b5d38sm float left, float right, float top, float bottom, 477cfd74d65d832137e20e193c960802afba73b5d38sm Vector2 movementDirection, GameObject excludeObject, Vector2 outputOffset, 478cfd74d65d832137e20e193c960802afba73b5d38sm FixedSizeArray<HitPoint> outputHitPoints) { 479cfd74d65d832137e20e193c960802afba73b5d38sm int hitCount = 0; 480cfd74d65d832137e20e193c960802afba73b5d38sm final int maxSegments = outputHitPoints.getCapacity() - outputHitPoints.getCount(); 481cfd74d65d832137e20e193c960802afba73b5d38sm final int count = segments.getCount(); 482cfd74d65d832137e20e193c960802afba73b5d38sm final Object[] segmentArray = segments.getArray(); 483cfd74d65d832137e20e193c960802afba73b5d38sm 484cfd74d65d832137e20e193c960802afba73b5d38sm VectorPool vectorPool = sSystemRegistry.vectorPool; 485cfd74d65d832137e20e193c960802afba73b5d38sm HitPointPool hitPool = sSystemRegistry.hitPointPool; 486cfd74d65d832137e20e193c960802afba73b5d38sm 487cfd74d65d832137e20e193c960802afba73b5d38sm Vector2 tempHitPoint = vectorPool.allocate(); 488cfd74d65d832137e20e193c960802afba73b5d38sm 489cfd74d65d832137e20e193c960802afba73b5d38sm for (int x = 0; x < count && hitCount < maxSegments; x++) { 490cfd74d65d832137e20e193c960802afba73b5d38sm LineSegment segment = (LineSegment)segmentArray[x]; 491cfd74d65d832137e20e193c960802afba73b5d38sm // If a movement direction has been passed, filter out invalid surfaces by ignoring 492cfd74d65d832137e20e193c960802afba73b5d38sm // those that do not oppose movement. If no direction has been passed, accept all 493cfd74d65d832137e20e193c960802afba73b5d38sm // surfaces. 494cfd74d65d832137e20e193c960802afba73b5d38sm final float dot = movementDirection.length2() > 0.0f ? 495cfd74d65d832137e20e193c960802afba73b5d38sm movementDirection.dot(segment.mNormal) : -1.0f; 496cfd74d65d832137e20e193c960802afba73b5d38sm 497cfd74d65d832137e20e193c960802afba73b5d38sm if (dot < 0.0f && 498cfd74d65d832137e20e193c960802afba73b5d38sm (excludeObject == null || segment.owner != excludeObject) && 499cfd74d65d832137e20e193c960802afba73b5d38sm segment.calculateIntersectionBox(left, right, top, bottom, tempHitPoint)) { 500cfd74d65d832137e20e193c960802afba73b5d38sm 501cfd74d65d832137e20e193c960802afba73b5d38sm Vector2 hitPoint = vectorPool.allocate(tempHitPoint); 502cfd74d65d832137e20e193c960802afba73b5d38sm Vector2 hitNormal = vectorPool.allocate(segment.mNormal); 503cfd74d65d832137e20e193c960802afba73b5d38sm 504cfd74d65d832137e20e193c960802afba73b5d38sm hitPoint.add(outputOffset); 505cfd74d65d832137e20e193c960802afba73b5d38sm HitPoint hit = hitPool.allocate(); 506cfd74d65d832137e20e193c960802afba73b5d38sm 507cfd74d65d832137e20e193c960802afba73b5d38sm hit.hitPoint = hitPoint; 508cfd74d65d832137e20e193c960802afba73b5d38sm hit.hitNormal = hitNormal; 509cfd74d65d832137e20e193c960802afba73b5d38sm 510cfd74d65d832137e20e193c960802afba73b5d38sm outputHitPoints.add(hit); 511cfd74d65d832137e20e193c960802afba73b5d38sm 512cfd74d65d832137e20e193c960802afba73b5d38sm hitCount++; 513cfd74d65d832137e20e193c960802afba73b5d38sm } 514cfd74d65d832137e20e193c960802afba73b5d38sm } 515cfd74d65d832137e20e193c960802afba73b5d38sm 516cfd74d65d832137e20e193c960802afba73b5d38sm vectorPool.release(tempHitPoint); 517cfd74d65d832137e20e193c960802afba73b5d38sm 518cfd74d65d832137e20e193c960802afba73b5d38sm return hitCount > 0; 519cfd74d65d832137e20e193c960802afba73b5d38sm } 520cfd74d65d832137e20e193c960802afba73b5d38sm 521cfd74d65d832137e20e193c960802afba73b5d38sm /* 522cfd74d65d832137e20e193c960802afba73b5d38sm * Loads line segments from a binary file and builds the tiled collision database 523cfd74d65d832137e20e193c960802afba73b5d38sm * accordingly. 524cfd74d65d832137e20e193c960802afba73b5d38sm */ 525cfd74d65d832137e20e193c960802afba73b5d38sm public boolean loadCollisionTiles(InputStream stream) { 526cfd74d65d832137e20e193c960802afba73b5d38sm boolean success = false; 527cfd74d65d832137e20e193c960802afba73b5d38sm AssetManager.AssetInputStream byteStream = (AssetManager.AssetInputStream) stream; 528cfd74d65d832137e20e193c960802afba73b5d38sm int signature; 529cfd74d65d832137e20e193c960802afba73b5d38sm 530cfd74d65d832137e20e193c960802afba73b5d38sm // TODO: this is a hack. I really should only allocate an array that is the size of the 531cfd74d65d832137e20e193c960802afba73b5d38sm // tileset, but at this point I don't actually know that size, so I allocate a buffer that's 532cfd74d65d832137e20e193c960802afba73b5d38sm // probably large enough. 533cfd74d65d832137e20e193c960802afba73b5d38sm mCollisionTiles = new CollisionTile[256]; 534cfd74d65d832137e20e193c960802afba73b5d38sm try { 535cfd74d65d832137e20e193c960802afba73b5d38sm signature = (byte)byteStream.read(); 536cfd74d65d832137e20e193c960802afba73b5d38sm if (signature == 52) { 537cfd74d65d832137e20e193c960802afba73b5d38sm // This file has the following deserialization format: 538cfd74d65d832137e20e193c960802afba73b5d38sm // read the number of tiles 539cfd74d65d832137e20e193c960802afba73b5d38sm // for each tile 540cfd74d65d832137e20e193c960802afba73b5d38sm // read the tile id 541cfd74d65d832137e20e193c960802afba73b5d38sm // read the number of segments 542cfd74d65d832137e20e193c960802afba73b5d38sm // for each segment 543cfd74d65d832137e20e193c960802afba73b5d38sm // read startx, starty, endx, endy, normalx, normaly 544cfd74d65d832137e20e193c960802afba73b5d38sm final int tileCount = byteStream.read(); 545cfd74d65d832137e20e193c960802afba73b5d38sm final int size = (1 + 1 + 4 + 4 + 4 + 4 + 4 + 4) * tileCount; 546cfd74d65d832137e20e193c960802afba73b5d38sm if (byteStream.available() >= size) { 547cfd74d65d832137e20e193c960802afba73b5d38sm for (int x = 0; x < tileCount; x++) { 548cfd74d65d832137e20e193c960802afba73b5d38sm final int tileIndex = byteStream.read(); 549cfd74d65d832137e20e193c960802afba73b5d38sm final int segmentCount = byteStream.read(); 550cfd74d65d832137e20e193c960802afba73b5d38sm 551cfd74d65d832137e20e193c960802afba73b5d38sm if (mCollisionTiles[tileIndex] == null && segmentCount > 0) { 552cfd74d65d832137e20e193c960802afba73b5d38sm mCollisionTiles[tileIndex] = new CollisionTile(segmentCount); 553cfd74d65d832137e20e193c960802afba73b5d38sm } 554cfd74d65d832137e20e193c960802afba73b5d38sm 555cfd74d65d832137e20e193c960802afba73b5d38sm for (int y = 0; y < segmentCount; y++) { 556cfd74d65d832137e20e193c960802afba73b5d38sm byteStream.read(mWorkspaceBytes, 0, 4); 557cfd74d65d832137e20e193c960802afba73b5d38sm final float startX = Utils.byteArrayToFloat(mWorkspaceBytes); 558cfd74d65d832137e20e193c960802afba73b5d38sm byteStream.read(mWorkspaceBytes, 0, 4); 559cfd74d65d832137e20e193c960802afba73b5d38sm final float startY = Utils.byteArrayToFloat(mWorkspaceBytes); 560cfd74d65d832137e20e193c960802afba73b5d38sm byteStream.read(mWorkspaceBytes, 0, 4); 561cfd74d65d832137e20e193c960802afba73b5d38sm final float endX = Utils.byteArrayToFloat(mWorkspaceBytes); 562cfd74d65d832137e20e193c960802afba73b5d38sm byteStream.read(mWorkspaceBytes, 0, 4); 563cfd74d65d832137e20e193c960802afba73b5d38sm final float endY = Utils.byteArrayToFloat(mWorkspaceBytes); 564cfd74d65d832137e20e193c960802afba73b5d38sm byteStream.read(mWorkspaceBytes, 0, 4); 565cfd74d65d832137e20e193c960802afba73b5d38sm final float normalX = Utils.byteArrayToFloat(mWorkspaceBytes); 566cfd74d65d832137e20e193c960802afba73b5d38sm byteStream.read(mWorkspaceBytes, 0, 4); 567cfd74d65d832137e20e193c960802afba73b5d38sm final float normalY = Utils.byteArrayToFloat(mWorkspaceBytes); 568cfd74d65d832137e20e193c960802afba73b5d38sm 569cfd74d65d832137e20e193c960802afba73b5d38sm // TODO: it might be wise to pool line segments. I don't think that 570cfd74d65d832137e20e193c960802afba73b5d38sm // this data will be loaded very often though, so this is ok for now. 571cfd74d65d832137e20e193c960802afba73b5d38sm LineSegment newSegment = new LineSegment(); 572cfd74d65d832137e20e193c960802afba73b5d38sm newSegment.mStartPoint.set(startX, startY); 573cfd74d65d832137e20e193c960802afba73b5d38sm newSegment.mEndPoint.set(endX, endY); 574cfd74d65d832137e20e193c960802afba73b5d38sm newSegment.mNormal.set(normalX, normalY); 575cfd74d65d832137e20e193c960802afba73b5d38sm 576cfd74d65d832137e20e193c960802afba73b5d38sm mCollisionTiles[tileIndex].addSegment(newSegment); 577cfd74d65d832137e20e193c960802afba73b5d38sm } 578cfd74d65d832137e20e193c960802afba73b5d38sm } 579cfd74d65d832137e20e193c960802afba73b5d38sm } 580cfd74d65d832137e20e193c960802afba73b5d38sm } 581cfd74d65d832137e20e193c960802afba73b5d38sm } catch (IOException e) { 582cfd74d65d832137e20e193c960802afba73b5d38sm //TODO: figure out the best way to deal with this. Assert? 583cfd74d65d832137e20e193c960802afba73b5d38sm } 584cfd74d65d832137e20e193c960802afba73b5d38sm 585cfd74d65d832137e20e193c960802afba73b5d38sm return success; 586cfd74d65d832137e20e193c960802afba73b5d38sm } 587cfd74d65d832137e20e193c960802afba73b5d38sm 588cfd74d65d832137e20e193c960802afba73b5d38sm 589cfd74d65d832137e20e193c960802afba73b5d38sm /** 590cfd74d65d832137e20e193c960802afba73b5d38sm * An interface for visiting tiles during a ray cast. Implementations of TileVisitor 591cfd74d65d832137e20e193c960802afba73b5d38sm * can be passed to executeRay(); the visit() function will be invoked for each tile touched by 592cfd74d65d832137e20e193c960802afba73b5d38sm * the ray until the traversal is completed or visit() returns false. 593cfd74d65d832137e20e193c960802afba73b5d38sm */ 594cfd74d65d832137e20e193c960802afba73b5d38sm public abstract class TileVisitor extends AllocationGuard { 595cfd74d65d832137e20e193c960802afba73b5d38sm public TileVisitor() { 596cfd74d65d832137e20e193c960802afba73b5d38sm super(); 597cfd74d65d832137e20e193c960802afba73b5d38sm } 598cfd74d65d832137e20e193c960802afba73b5d38sm 599cfd74d65d832137e20e193c960802afba73b5d38sm // If true is returned, tile scanning continues. Otherwise it stops. 600cfd74d65d832137e20e193c960802afba73b5d38sm public abstract boolean visit(CollisionTile tile, Vector2 startPoint, Vector2 endPoint, 601cfd74d65d832137e20e193c960802afba73b5d38sm Vector2 hitPoint, Vector2 hitNormal, int tileX, int tileY); 602cfd74d65d832137e20e193c960802afba73b5d38sm } 603cfd74d65d832137e20e193c960802afba73b5d38sm 604cfd74d65d832137e20e193c960802afba73b5d38sm /** 605cfd74d65d832137e20e193c960802afba73b5d38sm * TileTestVisitor tests the ray against a list of segments assigned to each tile. If any 606cfd74d65d832137e20e193c960802afba73b5d38sm * segment in any tile of the ray's path is found to be intersecting with the ray, traversal 607cfd74d65d832137e20e193c960802afba73b5d38sm * stops and intersection information is recorded. 608cfd74d65d832137e20e193c960802afba73b5d38sm */ 609cfd74d65d832137e20e193c960802afba73b5d38sm protected class TileTestVisitor extends TileVisitor { 610cfd74d65d832137e20e193c960802afba73b5d38sm // These vectors are all temporary storage variables allocated as class members to avoid 611cfd74d65d832137e20e193c960802afba73b5d38sm // runtime allocation. 612cfd74d65d832137e20e193c960802afba73b5d38sm private Vector2 mDelta; 613cfd74d65d832137e20e193c960802afba73b5d38sm private Vector2 mTileSpaceStart; 614cfd74d65d832137e20e193c960802afba73b5d38sm private Vector2 mTileSpaceEnd; 615cfd74d65d832137e20e193c960802afba73b5d38sm private Vector2 mTileSpaceOffset; 616cfd74d65d832137e20e193c960802afba73b5d38sm private int mTileHeight; 617cfd74d65d832137e20e193c960802afba73b5d38sm private int mTileWidth; 618cfd74d65d832137e20e193c960802afba73b5d38sm 619cfd74d65d832137e20e193c960802afba73b5d38sm public TileTestVisitor() { 620cfd74d65d832137e20e193c960802afba73b5d38sm super(); 621cfd74d65d832137e20e193c960802afba73b5d38sm mDelta = new Vector2(); 622cfd74d65d832137e20e193c960802afba73b5d38sm mTileSpaceStart = new Vector2(); 623cfd74d65d832137e20e193c960802afba73b5d38sm mTileSpaceEnd = new Vector2(); 624cfd74d65d832137e20e193c960802afba73b5d38sm mTileSpaceOffset = new Vector2(); 625cfd74d65d832137e20e193c960802afba73b5d38sm } 626cfd74d65d832137e20e193c960802afba73b5d38sm 627cfd74d65d832137e20e193c960802afba73b5d38sm /** 628cfd74d65d832137e20e193c960802afba73b5d38sm * Sets the visitor up for a ray test. Initializes the size of the tiles and the direction 629cfd74d65d832137e20e193c960802afba73b5d38sm * of movement by which intersections should be filtered. 630cfd74d65d832137e20e193c960802afba73b5d38sm */ 631cfd74d65d832137e20e193c960802afba73b5d38sm public void setup(Vector2 movementDirection, int tileWidth, int tileHeight) { 632cfd74d65d832137e20e193c960802afba73b5d38sm if (movementDirection != null) { 633cfd74d65d832137e20e193c960802afba73b5d38sm mDelta.set(movementDirection); 634cfd74d65d832137e20e193c960802afba73b5d38sm mDelta.normalize(); 635cfd74d65d832137e20e193c960802afba73b5d38sm } else { 636cfd74d65d832137e20e193c960802afba73b5d38sm mDelta.zero(); 637cfd74d65d832137e20e193c960802afba73b5d38sm } 638cfd74d65d832137e20e193c960802afba73b5d38sm mTileWidth = tileWidth; 639cfd74d65d832137e20e193c960802afba73b5d38sm mTileHeight = tileHeight; 640cfd74d65d832137e20e193c960802afba73b5d38sm } 641cfd74d65d832137e20e193c960802afba73b5d38sm 642cfd74d65d832137e20e193c960802afba73b5d38sm /** 643cfd74d65d832137e20e193c960802afba73b5d38sm * Converts the ray into tile space and then compares it to the segments 644cfd74d65d832137e20e193c960802afba73b5d38sm * stored in the current tile. 645cfd74d65d832137e20e193c960802afba73b5d38sm */ 646cfd74d65d832137e20e193c960802afba73b5d38sm @Override 647cfd74d65d832137e20e193c960802afba73b5d38sm public boolean visit(CollisionTile tile, Vector2 startPoint, Vector2 endPoint, 648cfd74d65d832137e20e193c960802afba73b5d38sm Vector2 hitPoint, Vector2 hitNormal, int tileX, int tileY) { 649cfd74d65d832137e20e193c960802afba73b5d38sm mTileSpaceOffset.set(tileX * mTileWidth, tileY * mTileHeight); 650cfd74d65d832137e20e193c960802afba73b5d38sm mTileSpaceStart.set(startPoint); 651cfd74d65d832137e20e193c960802afba73b5d38sm mTileSpaceStart.subtract(mTileSpaceOffset); 652cfd74d65d832137e20e193c960802afba73b5d38sm mTileSpaceEnd.set(endPoint); 653cfd74d65d832137e20e193c960802afba73b5d38sm mTileSpaceEnd.subtract(mTileSpaceOffset); 654cfd74d65d832137e20e193c960802afba73b5d38sm // find all the hits in the tile and pick the closest to the start point. 655cfd74d65d832137e20e193c960802afba73b5d38sm boolean foundHit = testSegmentAgainstList(tile.segments, mTileSpaceStart, mTileSpaceEnd, 656cfd74d65d832137e20e193c960802afba73b5d38sm hitPoint, hitNormal, mDelta, null); 657cfd74d65d832137e20e193c960802afba73b5d38sm 658cfd74d65d832137e20e193c960802afba73b5d38sm if (foundHit) { 659cfd74d65d832137e20e193c960802afba73b5d38sm // The hitPoint is in tile space, so convert it back to world space. 660cfd74d65d832137e20e193c960802afba73b5d38sm hitPoint.add(mTileSpaceOffset); 661cfd74d65d832137e20e193c960802afba73b5d38sm } 662cfd74d65d832137e20e193c960802afba73b5d38sm 663cfd74d65d832137e20e193c960802afba73b5d38sm return foundHit; 664cfd74d65d832137e20e193c960802afba73b5d38sm } 665cfd74d65d832137e20e193c960802afba73b5d38sm } 666cfd74d65d832137e20e193c960802afba73b5d38sm 667cfd74d65d832137e20e193c960802afba73b5d38sm /** 668cfd74d65d832137e20e193c960802afba73b5d38sm * A class describing a single surface in the collision world. Surfaces are stored as a line 669cfd74d65d832137e20e193c960802afba73b5d38sm * segment and a normal. The normal must be normalized (its length must be 1.0) and should 670cfd74d65d832137e20e193c960802afba73b5d38sm * describe the direction that the segment "pushes against" in a collision. 671cfd74d65d832137e20e193c960802afba73b5d38sm */ 672cfd74d65d832137e20e193c960802afba73b5d38sm protected class LineSegment extends AllocationGuard { 673cfd74d65d832137e20e193c960802afba73b5d38sm private Vector2 mStartPoint; 674cfd74d65d832137e20e193c960802afba73b5d38sm private Vector2 mEndPoint; 675cfd74d65d832137e20e193c960802afba73b5d38sm public Vector2 mNormal; 676cfd74d65d832137e20e193c960802afba73b5d38sm public GameObject owner; 677cfd74d65d832137e20e193c960802afba73b5d38sm 678cfd74d65d832137e20e193c960802afba73b5d38sm public LineSegment() { 679cfd74d65d832137e20e193c960802afba73b5d38sm super(); 680cfd74d65d832137e20e193c960802afba73b5d38sm mStartPoint = new Vector2(); 681cfd74d65d832137e20e193c960802afba73b5d38sm mEndPoint = new Vector2(); 682cfd74d65d832137e20e193c960802afba73b5d38sm mNormal = new Vector2(); 683cfd74d65d832137e20e193c960802afba73b5d38sm } 684cfd74d65d832137e20e193c960802afba73b5d38sm 685cfd74d65d832137e20e193c960802afba73b5d38sm /* Sets up the line segment. Values are copied to local storage. */ 686cfd74d65d832137e20e193c960802afba73b5d38sm public void set(Vector2 start, Vector2 end, Vector2 norm) { 687cfd74d65d832137e20e193c960802afba73b5d38sm mStartPoint.set(start); 688cfd74d65d832137e20e193c960802afba73b5d38sm mEndPoint.set(end); 689cfd74d65d832137e20e193c960802afba73b5d38sm mNormal.set(norm); 690cfd74d65d832137e20e193c960802afba73b5d38sm } 691cfd74d65d832137e20e193c960802afba73b5d38sm 692cfd74d65d832137e20e193c960802afba73b5d38sm public void setOwner(GameObject ownerObject) { 693cfd74d65d832137e20e193c960802afba73b5d38sm owner = ownerObject; 694cfd74d65d832137e20e193c960802afba73b5d38sm } 695cfd74d65d832137e20e193c960802afba73b5d38sm /** 696cfd74d65d832137e20e193c960802afba73b5d38sm * Checks to see if these lines intersect by projecting one onto the other and then 697cfd74d65d832137e20e193c960802afba73b5d38sm * assuring that the collision point is within the range of each segment. 698cfd74d65d832137e20e193c960802afba73b5d38sm */ 699cfd74d65d832137e20e193c960802afba73b5d38sm public boolean calculateIntersection(Vector2 otherStart, Vector2 otherEnd, 700cfd74d65d832137e20e193c960802afba73b5d38sm Vector2 hitPoint) { 701cfd74d65d832137e20e193c960802afba73b5d38sm boolean intersecting = false; 702cfd74d65d832137e20e193c960802afba73b5d38sm 703cfd74d65d832137e20e193c960802afba73b5d38sm // Reference: http://local.wasp.uwa.edu.au/~pbourke/geometry/lineline2d/ 704cfd74d65d832137e20e193c960802afba73b5d38sm final float x1 = mStartPoint.x; 705cfd74d65d832137e20e193c960802afba73b5d38sm final float x2 = mEndPoint.x; 706cfd74d65d832137e20e193c960802afba73b5d38sm final float x3 = otherStart.x; 707cfd74d65d832137e20e193c960802afba73b5d38sm final float x4 = otherEnd.x; 708cfd74d65d832137e20e193c960802afba73b5d38sm final float y1 = mStartPoint.y; 709cfd74d65d832137e20e193c960802afba73b5d38sm final float y2 = mEndPoint.y; 710cfd74d65d832137e20e193c960802afba73b5d38sm final float y3 = otherStart.y; 711cfd74d65d832137e20e193c960802afba73b5d38sm final float y4 = otherEnd.y; 712cfd74d65d832137e20e193c960802afba73b5d38sm 713cfd74d65d832137e20e193c960802afba73b5d38sm final float denom = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1); 714cfd74d65d832137e20e193c960802afba73b5d38sm if (denom != 0) { 715cfd74d65d832137e20e193c960802afba73b5d38sm final float uA = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)) / denom; 716cfd74d65d832137e20e193c960802afba73b5d38sm final float uB = ((x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3)) / denom; 717cfd74d65d832137e20e193c960802afba73b5d38sm 718cfd74d65d832137e20e193c960802afba73b5d38sm if (uA >= 0.0f && uA <= 1.0f && uB >= 0.0f && uB <= 1.0f) { 719cfd74d65d832137e20e193c960802afba73b5d38sm final float hitX = x1 + (uA * (x2 - x1)); 720cfd74d65d832137e20e193c960802afba73b5d38sm final float hitY = y1 + (uA * (y2 - y1)); 721cfd74d65d832137e20e193c960802afba73b5d38sm hitPoint.set(hitX, hitY); 722cfd74d65d832137e20e193c960802afba73b5d38sm intersecting = true; 723cfd74d65d832137e20e193c960802afba73b5d38sm } 724cfd74d65d832137e20e193c960802afba73b5d38sm } 725cfd74d65d832137e20e193c960802afba73b5d38sm return intersecting; 726cfd74d65d832137e20e193c960802afba73b5d38sm } 727cfd74d65d832137e20e193c960802afba73b5d38sm 728cfd74d65d832137e20e193c960802afba73b5d38sm // Based on http://www.garagegames.com/community/resources/view/309 729cfd74d65d832137e20e193c960802afba73b5d38sm public boolean calculateIntersectionBox(float left, float right, float top, float bottom, 730cfd74d65d832137e20e193c960802afba73b5d38sm Vector2 hitPoint) { 731cfd74d65d832137e20e193c960802afba73b5d38sm 732cfd74d65d832137e20e193c960802afba73b5d38sm final float x1 = mStartPoint.x; 733cfd74d65d832137e20e193c960802afba73b5d38sm final float x2 = mEndPoint.x; 734cfd74d65d832137e20e193c960802afba73b5d38sm final float y1 = mStartPoint.y; 735cfd74d65d832137e20e193c960802afba73b5d38sm final float y2 = mEndPoint.y; 736cfd74d65d832137e20e193c960802afba73b5d38sm 737cfd74d65d832137e20e193c960802afba73b5d38sm float startIntersect; 738cfd74d65d832137e20e193c960802afba73b5d38sm float endIntersect; 739cfd74d65d832137e20e193c960802afba73b5d38sm float intersectTimeStart = 0.0f; 740cfd74d65d832137e20e193c960802afba73b5d38sm float intersectTimeEnd = 1.0f; 741cfd74d65d832137e20e193c960802afba73b5d38sm 742cfd74d65d832137e20e193c960802afba73b5d38sm if (x1 < x2) { 743cfd74d65d832137e20e193c960802afba73b5d38sm if (x1 > right || x2 < left) { 744cfd74d65d832137e20e193c960802afba73b5d38sm return false; 745cfd74d65d832137e20e193c960802afba73b5d38sm } 746cfd74d65d832137e20e193c960802afba73b5d38sm final float deltaX = x2 - x1; 747cfd74d65d832137e20e193c960802afba73b5d38sm startIntersect = (x1 < left) ? (left - x1) / deltaX : 0.0f; 748cfd74d65d832137e20e193c960802afba73b5d38sm endIntersect = (x2 > right) ? (right - x1) / deltaX : 1.0f; 749cfd74d65d832137e20e193c960802afba73b5d38sm } else { 750cfd74d65d832137e20e193c960802afba73b5d38sm if (x2 > right || x1 < left) { 751cfd74d65d832137e20e193c960802afba73b5d38sm return false; 752cfd74d65d832137e20e193c960802afba73b5d38sm } 753cfd74d65d832137e20e193c960802afba73b5d38sm final float deltaX = x2 - x1; 754cfd74d65d832137e20e193c960802afba73b5d38sm startIntersect = (x1 > right) ? (right - x1) / deltaX : 0.0f; 755cfd74d65d832137e20e193c960802afba73b5d38sm endIntersect = (x2 < left) ? (left - x1) / deltaX : 1.0f; 756cfd74d65d832137e20e193c960802afba73b5d38sm } 757cfd74d65d832137e20e193c960802afba73b5d38sm 758cfd74d65d832137e20e193c960802afba73b5d38sm if (startIntersect > intersectTimeStart) { 759cfd74d65d832137e20e193c960802afba73b5d38sm intersectTimeStart = startIntersect; 760cfd74d65d832137e20e193c960802afba73b5d38sm } 761cfd74d65d832137e20e193c960802afba73b5d38sm if (endIntersect < intersectTimeEnd) { 762cfd74d65d832137e20e193c960802afba73b5d38sm intersectTimeEnd = endIntersect; 763cfd74d65d832137e20e193c960802afba73b5d38sm } 764cfd74d65d832137e20e193c960802afba73b5d38sm if (intersectTimeEnd < intersectTimeStart) { 765cfd74d65d832137e20e193c960802afba73b5d38sm return false; 766cfd74d65d832137e20e193c960802afba73b5d38sm } 767cfd74d65d832137e20e193c960802afba73b5d38sm 768cfd74d65d832137e20e193c960802afba73b5d38sm // y 769cfd74d65d832137e20e193c960802afba73b5d38sm if (y1 < y2) { 770cfd74d65d832137e20e193c960802afba73b5d38sm if (y1 > top || y2 < bottom) { 771cfd74d65d832137e20e193c960802afba73b5d38sm return false; 772cfd74d65d832137e20e193c960802afba73b5d38sm } 773cfd74d65d832137e20e193c960802afba73b5d38sm final float deltaY = y2 - y1; 774cfd74d65d832137e20e193c960802afba73b5d38sm startIntersect = (y1 < bottom) ? (bottom - y1) / deltaY : 0.0f; 775cfd74d65d832137e20e193c960802afba73b5d38sm endIntersect = (y2 > top) ? (top - y1) / deltaY : 1.0f; 776cfd74d65d832137e20e193c960802afba73b5d38sm } else { 777cfd74d65d832137e20e193c960802afba73b5d38sm if (y2 > top || y1 < bottom) { 778cfd74d65d832137e20e193c960802afba73b5d38sm return false; 779cfd74d65d832137e20e193c960802afba73b5d38sm } 780cfd74d65d832137e20e193c960802afba73b5d38sm final float deltaY = y2 - y1; 781cfd74d65d832137e20e193c960802afba73b5d38sm startIntersect = (y1 > top) ? (top - y1) / deltaY : 0.0f; 782cfd74d65d832137e20e193c960802afba73b5d38sm endIntersect = (y2 < bottom) ? (bottom - y1) / deltaY : 1.0f; 783cfd74d65d832137e20e193c960802afba73b5d38sm } 784cfd74d65d832137e20e193c960802afba73b5d38sm 785cfd74d65d832137e20e193c960802afba73b5d38sm if (startIntersect > intersectTimeStart) { 786cfd74d65d832137e20e193c960802afba73b5d38sm intersectTimeStart = startIntersect; 787cfd74d65d832137e20e193c960802afba73b5d38sm } 788cfd74d65d832137e20e193c960802afba73b5d38sm if (endIntersect < intersectTimeEnd) { 789cfd74d65d832137e20e193c960802afba73b5d38sm intersectTimeEnd = endIntersect; 790cfd74d65d832137e20e193c960802afba73b5d38sm } 791cfd74d65d832137e20e193c960802afba73b5d38sm if (intersectTimeEnd < intersectTimeStart) { 792cfd74d65d832137e20e193c960802afba73b5d38sm return false; 793cfd74d65d832137e20e193c960802afba73b5d38sm } 794cfd74d65d832137e20e193c960802afba73b5d38sm 795cfd74d65d832137e20e193c960802afba73b5d38sm hitPoint.set(mEndPoint); 796cfd74d65d832137e20e193c960802afba73b5d38sm hitPoint.subtract(mStartPoint); 797cfd74d65d832137e20e193c960802afba73b5d38sm hitPoint.multiply(intersectTimeStart); 798cfd74d65d832137e20e193c960802afba73b5d38sm hitPoint.add(mStartPoint); 799cfd74d65d832137e20e193c960802afba73b5d38sm 800cfd74d65d832137e20e193c960802afba73b5d38sm return true; 801cfd74d65d832137e20e193c960802afba73b5d38sm } 802cfd74d65d832137e20e193c960802afba73b5d38sm 803cfd74d65d832137e20e193c960802afba73b5d38sm } 804cfd74d65d832137e20e193c960802afba73b5d38sm 805cfd74d65d832137e20e193c960802afba73b5d38sm /** 806cfd74d65d832137e20e193c960802afba73b5d38sm * A pool of line segments. 807cfd74d65d832137e20e193c960802afba73b5d38sm */ 808cfd74d65d832137e20e193c960802afba73b5d38sm protected class LineSegmentPool extends TObjectPool<LineSegment> { 809cfd74d65d832137e20e193c960802afba73b5d38sm public LineSegmentPool() { 810cfd74d65d832137e20e193c960802afba73b5d38sm super(); 811cfd74d65d832137e20e193c960802afba73b5d38sm } 812cfd74d65d832137e20e193c960802afba73b5d38sm 813cfd74d65d832137e20e193c960802afba73b5d38sm public LineSegmentPool(int count) { 814cfd74d65d832137e20e193c960802afba73b5d38sm super(count); 815cfd74d65d832137e20e193c960802afba73b5d38sm } 816cfd74d65d832137e20e193c960802afba73b5d38sm 817cfd74d65d832137e20e193c960802afba73b5d38sm @Override 818cfd74d65d832137e20e193c960802afba73b5d38sm public void reset() { 819cfd74d65d832137e20e193c960802afba73b5d38sm 820cfd74d65d832137e20e193c960802afba73b5d38sm } 821cfd74d65d832137e20e193c960802afba73b5d38sm 822cfd74d65d832137e20e193c960802afba73b5d38sm @Override 823cfd74d65d832137e20e193c960802afba73b5d38sm protected void fill() { 824cfd74d65d832137e20e193c960802afba73b5d38sm for (int x = 0; x < getSize(); x++) { 825cfd74d65d832137e20e193c960802afba73b5d38sm getAvailable().add(new LineSegment()); 826cfd74d65d832137e20e193c960802afba73b5d38sm } 827cfd74d65d832137e20e193c960802afba73b5d38sm } 828cfd74d65d832137e20e193c960802afba73b5d38sm 829cfd74d65d832137e20e193c960802afba73b5d38sm @Override 830cfd74d65d832137e20e193c960802afba73b5d38sm public void release(Object entry) { 831cfd74d65d832137e20e193c960802afba73b5d38sm ((LineSegment)entry).owner = null; 832cfd74d65d832137e20e193c960802afba73b5d38sm super.release(entry); 833cfd74d65d832137e20e193c960802afba73b5d38sm } 834cfd74d65d832137e20e193c960802afba73b5d38sm 835cfd74d65d832137e20e193c960802afba73b5d38sm 836cfd74d65d832137e20e193c960802afba73b5d38sm } 837cfd74d65d832137e20e193c960802afba73b5d38sm 838cfd74d65d832137e20e193c960802afba73b5d38sm /** 839cfd74d65d832137e20e193c960802afba73b5d38sm * A single collision tile. Manages a list of line segments. 840cfd74d65d832137e20e193c960802afba73b5d38sm */ 841cfd74d65d832137e20e193c960802afba73b5d38sm protected class CollisionTile extends AllocationGuard { 842cfd74d65d832137e20e193c960802afba73b5d38sm public FixedSizeArray<LineSegment> segments; 843cfd74d65d832137e20e193c960802afba73b5d38sm 844cfd74d65d832137e20e193c960802afba73b5d38sm public CollisionTile(int maxSegments) { 845cfd74d65d832137e20e193c960802afba73b5d38sm super(); 846cfd74d65d832137e20e193c960802afba73b5d38sm segments = new FixedSizeArray<LineSegment>(maxSegments); 847cfd74d65d832137e20e193c960802afba73b5d38sm } 848cfd74d65d832137e20e193c960802afba73b5d38sm 849cfd74d65d832137e20e193c960802afba73b5d38sm public boolean addSegment(LineSegment segment) { 850cfd74d65d832137e20e193c960802afba73b5d38sm boolean success = false; 851cfd74d65d832137e20e193c960802afba73b5d38sm if (segments.getCount() < segments.getCapacity()) { 852cfd74d65d832137e20e193c960802afba73b5d38sm success = true; 853cfd74d65d832137e20e193c960802afba73b5d38sm } 854cfd74d65d832137e20e193c960802afba73b5d38sm segments.add(segment); 855cfd74d65d832137e20e193c960802afba73b5d38sm return success; 856cfd74d65d832137e20e193c960802afba73b5d38sm } 857cfd74d65d832137e20e193c960802afba73b5d38sm } 858cfd74d65d832137e20e193c960802afba73b5d38sm} 859