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