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
17cfd74d65d832137e20e193c960802afba73b5d38sm
18cfd74d65d832137e20e193c960802afba73b5d38smpackage com.replica.replicaisland;
19cfd74d65d832137e20e193c960802afba73b5d38sm
20cfd74d65d832137e20e193c960802afba73b5d38smimport java.util.Comparator;
21cfd74d65d832137e20e193c960802afba73b5d38sm
22cfd74d65d832137e20e193c960802afba73b5d38sm/**
23cfd74d65d832137e20e193c960802afba73b5d38sm * Handles collision against the background.  Snaps colliding objects out of collision and reports
24cfd74d65d832137e20e193c960802afba73b5d38sm * the hit to the parent game object.
25cfd74d65d832137e20e193c960802afba73b5d38sm */
26cfd74d65d832137e20e193c960802afba73b5d38smpublic class BackgroundCollisionComponent extends GameComponent {
27cfd74d65d832137e20e193c960802afba73b5d38sm    private Vector2 mPreviousPosition;
28cfd74d65d832137e20e193c960802afba73b5d38sm    private int mWidth;
29cfd74d65d832137e20e193c960802afba73b5d38sm    private int mHeight;
30cfd74d65d832137e20e193c960802afba73b5d38sm    private int mHorizontalOffset;
31cfd74d65d832137e20e193c960802afba73b5d38sm    private int mVerticalOffset;
32cfd74d65d832137e20e193c960802afba73b5d38sm
33cfd74d65d832137e20e193c960802afba73b5d38sm    // Workspace vectors.  Allocated up front for speed.
34cfd74d65d832137e20e193c960802afba73b5d38sm    private Vector2 mCurrentPosition;
35cfd74d65d832137e20e193c960802afba73b5d38sm    private Vector2 mPreviousCenter;
36cfd74d65d832137e20e193c960802afba73b5d38sm    private Vector2 mDelta;
37cfd74d65d832137e20e193c960802afba73b5d38sm    private Vector2 mFilterDirection;
38cfd74d65d832137e20e193c960802afba73b5d38sm    private Vector2 mHorizontalHitPoint;
39cfd74d65d832137e20e193c960802afba73b5d38sm    private Vector2 mHorizontalHitNormal;
40cfd74d65d832137e20e193c960802afba73b5d38sm    private Vector2 mVerticalHitPoint;
41cfd74d65d832137e20e193c960802afba73b5d38sm    private Vector2 mVerticalHitNormal;
42cfd74d65d832137e20e193c960802afba73b5d38sm    private Vector2 mRayStart;
43cfd74d65d832137e20e193c960802afba73b5d38sm    private Vector2 mRayEnd;
44cfd74d65d832137e20e193c960802afba73b5d38sm    private Vector2 mTestPointStart;
45cfd74d65d832137e20e193c960802afba73b5d38sm    private Vector2 mTestPointEnd;
46cfd74d65d832137e20e193c960802afba73b5d38sm    private Vector2 mMergedNormal;
47cfd74d65d832137e20e193c960802afba73b5d38sm
48cfd74d65d832137e20e193c960802afba73b5d38sm    /**
49cfd74d65d832137e20e193c960802afba73b5d38sm     * Sets up the collision bounding box.  This box may be a different size than the bounds of the
50cfd74d65d832137e20e193c960802afba73b5d38sm     * sprite that this object controls.
51cfd74d65d832137e20e193c960802afba73b5d38sm     * @param width  The width of the collision box.
52cfd74d65d832137e20e193c960802afba73b5d38sm     * @param height  The height of the collision box.
53cfd74d65d832137e20e193c960802afba73b5d38sm     * @param horzOffset  The offset of the collision box from the object's origin in the x axis.
54cfd74d65d832137e20e193c960802afba73b5d38sm     * @param vertOffset  The offset of the collision box from the object's origin in the y axis.
55cfd74d65d832137e20e193c960802afba73b5d38sm     */
56cfd74d65d832137e20e193c960802afba73b5d38sm    public BackgroundCollisionComponent(int width, int height, int horzOffset, int vertOffset) {
57cfd74d65d832137e20e193c960802afba73b5d38sm        super();
58cfd74d65d832137e20e193c960802afba73b5d38sm        setPhase(ComponentPhases.COLLISION_RESPONSE.ordinal());
59cfd74d65d832137e20e193c960802afba73b5d38sm        mPreviousPosition = new Vector2();
60cfd74d65d832137e20e193c960802afba73b5d38sm        mWidth = width;
61cfd74d65d832137e20e193c960802afba73b5d38sm        mHeight = height;
62cfd74d65d832137e20e193c960802afba73b5d38sm        mHorizontalOffset = horzOffset;
63cfd74d65d832137e20e193c960802afba73b5d38sm        mVerticalOffset = vertOffset;
64cfd74d65d832137e20e193c960802afba73b5d38sm
65cfd74d65d832137e20e193c960802afba73b5d38sm        mCurrentPosition = new Vector2();
66cfd74d65d832137e20e193c960802afba73b5d38sm        mPreviousCenter = new Vector2();
67cfd74d65d832137e20e193c960802afba73b5d38sm        mDelta = new Vector2();
68cfd74d65d832137e20e193c960802afba73b5d38sm        mFilterDirection = new Vector2();
69cfd74d65d832137e20e193c960802afba73b5d38sm        mHorizontalHitPoint = new Vector2();
70cfd74d65d832137e20e193c960802afba73b5d38sm        mHorizontalHitNormal = new Vector2();
71cfd74d65d832137e20e193c960802afba73b5d38sm        mVerticalHitPoint = new Vector2();
72cfd74d65d832137e20e193c960802afba73b5d38sm        mVerticalHitNormal = new Vector2();
73cfd74d65d832137e20e193c960802afba73b5d38sm        mRayStart = new Vector2();
74cfd74d65d832137e20e193c960802afba73b5d38sm        mRayEnd = new Vector2();
75cfd74d65d832137e20e193c960802afba73b5d38sm        mTestPointStart = new Vector2();
76cfd74d65d832137e20e193c960802afba73b5d38sm        mTestPointEnd = new Vector2();
77cfd74d65d832137e20e193c960802afba73b5d38sm        mMergedNormal = new Vector2();
78cfd74d65d832137e20e193c960802afba73b5d38sm    }
79cfd74d65d832137e20e193c960802afba73b5d38sm
80cfd74d65d832137e20e193c960802afba73b5d38sm    public BackgroundCollisionComponent() {
81cfd74d65d832137e20e193c960802afba73b5d38sm        super();
82cfd74d65d832137e20e193c960802afba73b5d38sm        setPhase(ComponentPhases.COLLISION_RESPONSE.ordinal());
83cfd74d65d832137e20e193c960802afba73b5d38sm        mPreviousPosition = new Vector2();
84cfd74d65d832137e20e193c960802afba73b5d38sm        mCurrentPosition = new Vector2();
85cfd74d65d832137e20e193c960802afba73b5d38sm        mPreviousCenter = new Vector2();
86cfd74d65d832137e20e193c960802afba73b5d38sm        mDelta = new Vector2();
87cfd74d65d832137e20e193c960802afba73b5d38sm        mFilterDirection = new Vector2();
88cfd74d65d832137e20e193c960802afba73b5d38sm        mHorizontalHitPoint = new Vector2();
89cfd74d65d832137e20e193c960802afba73b5d38sm        mHorizontalHitNormal = new Vector2();
90cfd74d65d832137e20e193c960802afba73b5d38sm        mVerticalHitPoint = new Vector2();
91cfd74d65d832137e20e193c960802afba73b5d38sm        mVerticalHitNormal = new Vector2();
92cfd74d65d832137e20e193c960802afba73b5d38sm        mRayStart = new Vector2();
93cfd74d65d832137e20e193c960802afba73b5d38sm        mRayEnd = new Vector2();
94cfd74d65d832137e20e193c960802afba73b5d38sm        mTestPointStart = new Vector2();
95cfd74d65d832137e20e193c960802afba73b5d38sm        mTestPointEnd = new Vector2();
96cfd74d65d832137e20e193c960802afba73b5d38sm        mMergedNormal = new Vector2();
97cfd74d65d832137e20e193c960802afba73b5d38sm    }
98cfd74d65d832137e20e193c960802afba73b5d38sm
99cfd74d65d832137e20e193c960802afba73b5d38sm    @Override
100cfd74d65d832137e20e193c960802afba73b5d38sm    public void reset() {
101cfd74d65d832137e20e193c960802afba73b5d38sm        mPreviousPosition.zero();
102cfd74d65d832137e20e193c960802afba73b5d38sm    }
103cfd74d65d832137e20e193c960802afba73b5d38sm
104cfd74d65d832137e20e193c960802afba73b5d38sm    public void setSize(int width, int height) {
105cfd74d65d832137e20e193c960802afba73b5d38sm        mWidth = width;
106cfd74d65d832137e20e193c960802afba73b5d38sm        mHeight = height;
107cfd74d65d832137e20e193c960802afba73b5d38sm        // TODO: Resize might cause new collisions.
108cfd74d65d832137e20e193c960802afba73b5d38sm    }
109cfd74d65d832137e20e193c960802afba73b5d38sm
110cfd74d65d832137e20e193c960802afba73b5d38sm    public void setOffset(int horzOffset, int vertOffset) {
111cfd74d65d832137e20e193c960802afba73b5d38sm        mHorizontalOffset = horzOffset;
112cfd74d65d832137e20e193c960802afba73b5d38sm        mVerticalOffset = vertOffset;
113cfd74d65d832137e20e193c960802afba73b5d38sm    }
114cfd74d65d832137e20e193c960802afba73b5d38sm
115cfd74d65d832137e20e193c960802afba73b5d38sm    /**
116cfd74d65d832137e20e193c960802afba73b5d38sm     * This function is the meat of the collision response logic.  Our collision detection and
117cfd74d65d832137e20e193c960802afba73b5d38sm     * response must be capable of dealing with arbitrary surfaces and must be frame rate
118cfd74d65d832137e20e193c960802afba73b5d38sm     * independent (we must sweep the space in-between frames to find collisions reliably).  The
119cfd74d65d832137e20e193c960802afba73b5d38sm     * following algorithm is used to keep the collision box out of the collision world.
120cfd74d65d832137e20e193c960802afba73b5d38sm     *      1.  Cast a ray from the center point of the box at its position last frame to the edge
121cfd74d65d832137e20e193c960802afba73b5d38sm     *          of the box at its current position.  If the ray intersects anything, snap the box
122cfd74d65d832137e20e193c960802afba73b5d38sm     *          back to the point of intersection.
123cfd74d65d832137e20e193c960802afba73b5d38sm     *      2.  Perform Step 1 twice: once looking for surfaces opposing horizontal movement and
124cfd74d65d832137e20e193c960802afba73b5d38sm     *          again for surfaces opposing vertical movement.  These two ray tests approximate the
125cfd74d65d832137e20e193c960802afba73b5d38sm     *          movement of the box between the previous frame and this one.
126cfd74d65d832137e20e193c960802afba73b5d38sm     *      3.  Since most collisions are collisions with the ground, more precision is required for
127cfd74d65d832137e20e193c960802afba73b5d38sm     *          vertical intersections.  Perform another ray test, this time from the top of the
128cfd74d65d832137e20e193c960802afba73b5d38sm     *          box's position (after snapping in Step 2) to the bottom.  Snap out of any vertical
129cfd74d65d832137e20e193c960802afba73b5d38sm     *          surfaces that the ray encounters.  This will ensure consistent snapping behavior on
130cfd74d65d832137e20e193c960802afba73b5d38sm     *          incline surfaces.
131cfd74d65d832137e20e193c960802afba73b5d38sm     *      4.  Add the normals of the surfaces that were hit up and normalize the result to produce
132cfd74d65d832137e20e193c960802afba73b5d38sm     *          a direction describing the average slope of the surfaces that the box is resting on.
133cfd74d65d832137e20e193c960802afba73b5d38sm     *          Physics will use this value as a normal to resolve collisions with the background.
134cfd74d65d832137e20e193c960802afba73b5d38sm     */
135cfd74d65d832137e20e193c960802afba73b5d38sm    @Override
136cfd74d65d832137e20e193c960802afba73b5d38sm    public void update(float timeDelta, BaseObject parent) {
137cfd74d65d832137e20e193c960802afba73b5d38sm        GameObject parentObject = (GameObject) parent;
138cfd74d65d832137e20e193c960802afba73b5d38sm        parentObject.setBackgroundCollisionNormal(Vector2.ZERO);
139cfd74d65d832137e20e193c960802afba73b5d38sm        if (mPreviousPosition.length2() != 0) {
140cfd74d65d832137e20e193c960802afba73b5d38sm            CollisionSystem collision = sSystemRegistry.collisionSystem;
141cfd74d65d832137e20e193c960802afba73b5d38sm            if (collision != null) {
142cfd74d65d832137e20e193c960802afba73b5d38sm                final int left = mHorizontalOffset;
143cfd74d65d832137e20e193c960802afba73b5d38sm                final int bottom = mVerticalOffset;
144cfd74d65d832137e20e193c960802afba73b5d38sm                final int right = left + mWidth;
145cfd74d65d832137e20e193c960802afba73b5d38sm                final int top = bottom + mHeight;
146cfd74d65d832137e20e193c960802afba73b5d38sm                final float centerOffsetX = ((mWidth) / 2.0f) + left;
147cfd74d65d832137e20e193c960802afba73b5d38sm                final float centerOffsetY = ((mHeight) / 2.0f) + bottom;
148cfd74d65d832137e20e193c960802afba73b5d38sm
149cfd74d65d832137e20e193c960802afba73b5d38sm                mCurrentPosition.set(parentObject.getPosition());
150cfd74d65d832137e20e193c960802afba73b5d38sm                mDelta.set(mCurrentPosition);
151cfd74d65d832137e20e193c960802afba73b5d38sm                mDelta.subtract(mPreviousPosition);
152cfd74d65d832137e20e193c960802afba73b5d38sm
153cfd74d65d832137e20e193c960802afba73b5d38sm                mPreviousCenter.set(centerOffsetX, centerOffsetY);
154cfd74d65d832137e20e193c960802afba73b5d38sm                mPreviousCenter.add(mPreviousPosition);
155cfd74d65d832137e20e193c960802afba73b5d38sm
156cfd74d65d832137e20e193c960802afba73b5d38sm                boolean horizontalHit = false;
157cfd74d65d832137e20e193c960802afba73b5d38sm                boolean verticalHit = false;
158cfd74d65d832137e20e193c960802afba73b5d38sm
159cfd74d65d832137e20e193c960802afba73b5d38sm                mVerticalHitPoint.zero();
160cfd74d65d832137e20e193c960802afba73b5d38sm                mVerticalHitNormal.zero();
161cfd74d65d832137e20e193c960802afba73b5d38sm                mHorizontalHitPoint.zero();
162cfd74d65d832137e20e193c960802afba73b5d38sm                mHorizontalHitNormal.zero();
163cfd74d65d832137e20e193c960802afba73b5d38sm
164cfd74d65d832137e20e193c960802afba73b5d38sm
165cfd74d65d832137e20e193c960802afba73b5d38sm                // The order in which we sweep the horizontal and vertical space can affect the
166cfd74d65d832137e20e193c960802afba73b5d38sm                // final result because we perform incremental snapping mid-sweep.  So it is
167cfd74d65d832137e20e193c960802afba73b5d38sm                // necessary to sweep in the primary direction of movement first.
168cfd74d65d832137e20e193c960802afba73b5d38sm                if (Math.abs(mDelta.x) > Math.abs(mDelta.y)) {
169cfd74d65d832137e20e193c960802afba73b5d38sm                    horizontalHit = sweepHorizontal(mPreviousCenter, mCurrentPosition, mDelta, left,
170cfd74d65d832137e20e193c960802afba73b5d38sm                            right, centerOffsetY, mHorizontalHitPoint, mHorizontalHitNormal,
171cfd74d65d832137e20e193c960802afba73b5d38sm                            parentObject);
172cfd74d65d832137e20e193c960802afba73b5d38sm                    verticalHit = sweepVertical(mPreviousCenter, mCurrentPosition, mDelta, bottom,
173cfd74d65d832137e20e193c960802afba73b5d38sm                            top, centerOffsetX, mVerticalHitPoint, mVerticalHitNormal,
174cfd74d65d832137e20e193c960802afba73b5d38sm                            parentObject);
175cfd74d65d832137e20e193c960802afba73b5d38sm
176cfd74d65d832137e20e193c960802afba73b5d38sm                } else {
177cfd74d65d832137e20e193c960802afba73b5d38sm                    verticalHit = sweepVertical(mPreviousCenter, mCurrentPosition, mDelta, bottom,
178cfd74d65d832137e20e193c960802afba73b5d38sm                            top, centerOffsetX, mVerticalHitPoint, mVerticalHitNormal,
179cfd74d65d832137e20e193c960802afba73b5d38sm                            parentObject);
180cfd74d65d832137e20e193c960802afba73b5d38sm                    horizontalHit = sweepHorizontal(mPreviousCenter, mCurrentPosition, mDelta, left,
181cfd74d65d832137e20e193c960802afba73b5d38sm                            right, centerOffsetY, mHorizontalHitPoint, mHorizontalHitNormal,
182cfd74d65d832137e20e193c960802afba73b5d38sm                            parentObject);
183cfd74d65d832137e20e193c960802afba73b5d38sm                }
184cfd74d65d832137e20e193c960802afba73b5d38sm
185cfd74d65d832137e20e193c960802afba73b5d38sm                // force the collision volume to stay within the bounds of the world.
186cfd74d65d832137e20e193c960802afba73b5d38sm                LevelSystem level = sSystemRegistry.levelSystem;
187cfd74d65d832137e20e193c960802afba73b5d38sm                if (level != null) {
188cfd74d65d832137e20e193c960802afba73b5d38sm                    if (mCurrentPosition.x + left < 0.0f) {
189cfd74d65d832137e20e193c960802afba73b5d38sm                        mCurrentPosition.x = (-left + 1);
190cfd74d65d832137e20e193c960802afba73b5d38sm                        horizontalHit = true;
191cfd74d65d832137e20e193c960802afba73b5d38sm                        mHorizontalHitNormal.x = (mHorizontalHitNormal.x + 1.0f);
192cfd74d65d832137e20e193c960802afba73b5d38sm                        mHorizontalHitNormal.normalize();
193cfd74d65d832137e20e193c960802afba73b5d38sm                    } else if (mCurrentPosition.x + right > level.getLevelWidth()) {
194cfd74d65d832137e20e193c960802afba73b5d38sm                        mCurrentPosition.x = (level.getLevelWidth() - right - 1);
195cfd74d65d832137e20e193c960802afba73b5d38sm                        mHorizontalHitNormal.x = (mHorizontalHitNormal.x - 1.0f);
196cfd74d65d832137e20e193c960802afba73b5d38sm                        mHorizontalHitNormal.normalize();
197cfd74d65d832137e20e193c960802afba73b5d38sm                        horizontalHit = true;
198cfd74d65d832137e20e193c960802afba73b5d38sm                    }
199cfd74d65d832137e20e193c960802afba73b5d38sm
200cfd74d65d832137e20e193c960802afba73b5d38sm                    /*if (mCurrentPosition.y + bottom < 0.0f) {
201cfd74d65d832137e20e193c960802afba73b5d38sm                        mCurrentPosition.y = (-bottom + 1);
202cfd74d65d832137e20e193c960802afba73b5d38sm                        verticalHit = true;
203cfd74d65d832137e20e193c960802afba73b5d38sm                        mVerticalHitNormal.y = (mVerticalHitNormal.y + 1.0f);
204cfd74d65d832137e20e193c960802afba73b5d38sm                        mVerticalHitNormal.normalize();
205cfd74d65d832137e20e193c960802afba73b5d38sm                    } else*/ if (mCurrentPosition.y + top > level.getLevelHeight()) {
206cfd74d65d832137e20e193c960802afba73b5d38sm                        mCurrentPosition.y = (level.getLevelHeight() - top - 1);
207cfd74d65d832137e20e193c960802afba73b5d38sm                        mVerticalHitNormal.y = (mVerticalHitNormal.y - 1.0f);
208cfd74d65d832137e20e193c960802afba73b5d38sm                        mVerticalHitNormal.normalize();
209cfd74d65d832137e20e193c960802afba73b5d38sm                        verticalHit = true;
210cfd74d65d832137e20e193c960802afba73b5d38sm                    }
211cfd74d65d832137e20e193c960802afba73b5d38sm
212cfd74d65d832137e20e193c960802afba73b5d38sm                }
213cfd74d65d832137e20e193c960802afba73b5d38sm
214cfd74d65d832137e20e193c960802afba73b5d38sm
215cfd74d65d832137e20e193c960802afba73b5d38sm                // One more set of tests to make sure that we are aligned with the surface.
216cfd74d65d832137e20e193c960802afba73b5d38sm                // This time we will just check the inside of the bounding box for intersections.
217cfd74d65d832137e20e193c960802afba73b5d38sm                // The sweep tests above will keep us out of collision in most cases, but this
218cfd74d65d832137e20e193c960802afba73b5d38sm                // test will ensure that we are aligned to incline surfaces correctly.
219cfd74d65d832137e20e193c960802afba73b5d38sm
220cfd74d65d832137e20e193c960802afba73b5d38sm                // Shoot a vertical line through the middle of the box.
221cfd74d65d832137e20e193c960802afba73b5d38sm                if (mDelta.x != 0.0f && mDelta.y != 0.0f) {
222cfd74d65d832137e20e193c960802afba73b5d38sm                    float yStart = top;
223cfd74d65d832137e20e193c960802afba73b5d38sm                    float yEnd = bottom;
224cfd74d65d832137e20e193c960802afba73b5d38sm
225cfd74d65d832137e20e193c960802afba73b5d38sm
226cfd74d65d832137e20e193c960802afba73b5d38sm                    mRayStart.set(centerOffsetX, yStart);
227cfd74d65d832137e20e193c960802afba73b5d38sm                    mRayStart.add(mCurrentPosition);
228cfd74d65d832137e20e193c960802afba73b5d38sm
229cfd74d65d832137e20e193c960802afba73b5d38sm                    mRayEnd.set(centerOffsetX, yEnd);
230cfd74d65d832137e20e193c960802afba73b5d38sm                    mRayEnd.add(mCurrentPosition);
231cfd74d65d832137e20e193c960802afba73b5d38sm
232cfd74d65d832137e20e193c960802afba73b5d38sm                    mFilterDirection.set(mDelta);
233cfd74d65d832137e20e193c960802afba73b5d38sm
234cfd74d65d832137e20e193c960802afba73b5d38sm                    if (collision.castRay(mRayStart, mRayEnd, mFilterDirection, mVerticalHitPoint,
235cfd74d65d832137e20e193c960802afba73b5d38sm                            mVerticalHitNormal, parentObject)) {
236cfd74d65d832137e20e193c960802afba73b5d38sm
237cfd74d65d832137e20e193c960802afba73b5d38sm                        // If we found a collision, use this surface as our vertical intersection
238cfd74d65d832137e20e193c960802afba73b5d38sm                        // for this frame, even if the sweep above also found something.
239cfd74d65d832137e20e193c960802afba73b5d38sm                        verticalHit = true;
240cfd74d65d832137e20e193c960802afba73b5d38sm                        // snap
241cfd74d65d832137e20e193c960802afba73b5d38sm                        if (mVerticalHitNormal.y > 0.0f) {
242cfd74d65d832137e20e193c960802afba73b5d38sm                            mCurrentPosition.y = (mVerticalHitPoint.y - bottom);
243cfd74d65d832137e20e193c960802afba73b5d38sm                        } else if (mVerticalHitNormal.y < 0.0f) {
244cfd74d65d832137e20e193c960802afba73b5d38sm                            mCurrentPosition.y = (mVerticalHitPoint.y - top);
245cfd74d65d832137e20e193c960802afba73b5d38sm                        }
246cfd74d65d832137e20e193c960802afba73b5d38sm                    }
247cfd74d65d832137e20e193c960802afba73b5d38sm
248cfd74d65d832137e20e193c960802afba73b5d38sm
249cfd74d65d832137e20e193c960802afba73b5d38sm                    // Now the horizontal version of the same test
250cfd74d65d832137e20e193c960802afba73b5d38sm                    float xStart = left;
251cfd74d65d832137e20e193c960802afba73b5d38sm                    float xEnd = right;
252cfd74d65d832137e20e193c960802afba73b5d38sm                    if (mDelta.x < 0.0f) {
253cfd74d65d832137e20e193c960802afba73b5d38sm                    	xStart = right;
254cfd74d65d832137e20e193c960802afba73b5d38sm                    	xEnd = left;
255cfd74d65d832137e20e193c960802afba73b5d38sm                    }
256cfd74d65d832137e20e193c960802afba73b5d38sm
257cfd74d65d832137e20e193c960802afba73b5d38sm
258cfd74d65d832137e20e193c960802afba73b5d38sm                    mRayStart.set(xStart, centerOffsetY);
259cfd74d65d832137e20e193c960802afba73b5d38sm                    mRayStart.add(mCurrentPosition);
260cfd74d65d832137e20e193c960802afba73b5d38sm
261cfd74d65d832137e20e193c960802afba73b5d38sm                    mRayEnd.set(xEnd, centerOffsetY);
262cfd74d65d832137e20e193c960802afba73b5d38sm                    mRayEnd.add(mCurrentPosition);
263cfd74d65d832137e20e193c960802afba73b5d38sm
264cfd74d65d832137e20e193c960802afba73b5d38sm                    mFilterDirection.set(mDelta);
265cfd74d65d832137e20e193c960802afba73b5d38sm
266cfd74d65d832137e20e193c960802afba73b5d38sm                    if (collision.castRay(mRayStart, mRayEnd, mFilterDirection, mHorizontalHitPoint,
267cfd74d65d832137e20e193c960802afba73b5d38sm                            mHorizontalHitNormal, parentObject)) {
268cfd74d65d832137e20e193c960802afba73b5d38sm
269cfd74d65d832137e20e193c960802afba73b5d38sm                        // If we found a collision, use this surface as our horizontal intersection
270cfd74d65d832137e20e193c960802afba73b5d38sm                        // for this frame, even if the sweep above also found something.
271cfd74d65d832137e20e193c960802afba73b5d38sm                        horizontalHit = true;
272cfd74d65d832137e20e193c960802afba73b5d38sm                        // snap
273cfd74d65d832137e20e193c960802afba73b5d38sm                        if (mHorizontalHitNormal.x > 0.0f) {
274cfd74d65d832137e20e193c960802afba73b5d38sm                            mCurrentPosition.x = (mHorizontalHitPoint.x - left);
275cfd74d65d832137e20e193c960802afba73b5d38sm                        } else if (mHorizontalHitNormal.x < 0.0f) {
276cfd74d65d832137e20e193c960802afba73b5d38sm                            mCurrentPosition.x = (mHorizontalHitPoint.x - right);
277cfd74d65d832137e20e193c960802afba73b5d38sm                        }
278cfd74d65d832137e20e193c960802afba73b5d38sm                    }
279cfd74d65d832137e20e193c960802afba73b5d38sm                }
280cfd74d65d832137e20e193c960802afba73b5d38sm
281cfd74d65d832137e20e193c960802afba73b5d38sm
282cfd74d65d832137e20e193c960802afba73b5d38sm                // Record the intersection for other systems to use.
283cfd74d65d832137e20e193c960802afba73b5d38sm                final TimeSystem timeSystem = sSystemRegistry.timeSystem;
284cfd74d65d832137e20e193c960802afba73b5d38sm
285cfd74d65d832137e20e193c960802afba73b5d38sm                if (timeSystem != null) {
286cfd74d65d832137e20e193c960802afba73b5d38sm                    float time = timeSystem.getGameTime();
287cfd74d65d832137e20e193c960802afba73b5d38sm                    if (horizontalHit) {
288cfd74d65d832137e20e193c960802afba73b5d38sm                        if (mHorizontalHitNormal.x > 0.0f) {
289cfd74d65d832137e20e193c960802afba73b5d38sm                            parentObject.setLastTouchedLeftWallTime(time);
290cfd74d65d832137e20e193c960802afba73b5d38sm                        } else {
291cfd74d65d832137e20e193c960802afba73b5d38sm                            parentObject.setLastTouchedRightWallTime(time);
292cfd74d65d832137e20e193c960802afba73b5d38sm                        }
293cfd74d65d832137e20e193c960802afba73b5d38sm                        //parentObject.setBackgroundCollisionNormal(mHorizontalHitNormal);
294cfd74d65d832137e20e193c960802afba73b5d38sm                    }
295cfd74d65d832137e20e193c960802afba73b5d38sm
296cfd74d65d832137e20e193c960802afba73b5d38sm                    if (verticalHit) {
297cfd74d65d832137e20e193c960802afba73b5d38sm                        if (mVerticalHitNormal.y > 0.0f) {
298cfd74d65d832137e20e193c960802afba73b5d38sm                            parentObject.setLastTouchedFloorTime(time);
299cfd74d65d832137e20e193c960802afba73b5d38sm                        } else {
300cfd74d65d832137e20e193c960802afba73b5d38sm                            parentObject.setLastTouchedCeilingTime(time);
301cfd74d65d832137e20e193c960802afba73b5d38sm                        }
302cfd74d65d832137e20e193c960802afba73b5d38sm                        //parentObject.setBackgroundCollisionNormal(mVerticalHitNormal);
303cfd74d65d832137e20e193c960802afba73b5d38sm                    }
304cfd74d65d832137e20e193c960802afba73b5d38sm
305cfd74d65d832137e20e193c960802afba73b5d38sm
306cfd74d65d832137e20e193c960802afba73b5d38sm                    // If we hit multiple surfaces, merge their normals together to produce an
307cfd74d65d832137e20e193c960802afba73b5d38sm                    // average direction of obstruction.
308cfd74d65d832137e20e193c960802afba73b5d38sm                    if (true) { //(verticalHit && horizontalHit) {
309cfd74d65d832137e20e193c960802afba73b5d38sm                        mMergedNormal.set(mVerticalHitNormal);
310cfd74d65d832137e20e193c960802afba73b5d38sm                        mMergedNormal.add(mHorizontalHitNormal);
311cfd74d65d832137e20e193c960802afba73b5d38sm                        mMergedNormal.normalize();
312cfd74d65d832137e20e193c960802afba73b5d38sm                        parentObject.setBackgroundCollisionNormal(mMergedNormal);
313cfd74d65d832137e20e193c960802afba73b5d38sm                    }
314cfd74d65d832137e20e193c960802afba73b5d38sm
315cfd74d65d832137e20e193c960802afba73b5d38sm                    parentObject.setPosition(mCurrentPosition);
316cfd74d65d832137e20e193c960802afba73b5d38sm                }
317cfd74d65d832137e20e193c960802afba73b5d38sm
318cfd74d65d832137e20e193c960802afba73b5d38sm            }
319cfd74d65d832137e20e193c960802afba73b5d38sm        }
320cfd74d65d832137e20e193c960802afba73b5d38sm        mPreviousPosition.set(parentObject.getPosition());
321cfd74d65d832137e20e193c960802afba73b5d38sm    }
322cfd74d65d832137e20e193c960802afba73b5d38sm
323cfd74d65d832137e20e193c960802afba73b5d38sm    /* Sweeps the space between two points looking for surfaces that oppose horizontal movement. */
324cfd74d65d832137e20e193c960802afba73b5d38sm    protected boolean sweepHorizontal(Vector2 previousPosition, Vector2 currentPosition, Vector2 delta,
325cfd74d65d832137e20e193c960802afba73b5d38sm            int left, int right, float centerY, Vector2 hitPoint, Vector2 hitNormal,
326cfd74d65d832137e20e193c960802afba73b5d38sm            GameObject parentObject) {
327cfd74d65d832137e20e193c960802afba73b5d38sm        boolean hit = false;
328cfd74d65d832137e20e193c960802afba73b5d38sm        if (!Utils.close(delta.x, 0.0f)) {
329cfd74d65d832137e20e193c960802afba73b5d38sm            CollisionSystem collision = sSystemRegistry.collisionSystem;
330cfd74d65d832137e20e193c960802afba73b5d38sm
331cfd74d65d832137e20e193c960802afba73b5d38sm            // Shoot a ray from the center of the previous frame's box to the edge (left or right,
332cfd74d65d832137e20e193c960802afba73b5d38sm            // depending on the direction of movement) of the current box.
333cfd74d65d832137e20e193c960802afba73b5d38sm            mTestPointStart.y = (centerY);
334cfd74d65d832137e20e193c960802afba73b5d38sm            mTestPointStart.x = (left);
335cfd74d65d832137e20e193c960802afba73b5d38sm            int offset = -left;
336cfd74d65d832137e20e193c960802afba73b5d38sm            if (delta.x > 0.0f) {
337cfd74d65d832137e20e193c960802afba73b5d38sm                mTestPointStart.x = (right);
338cfd74d65d832137e20e193c960802afba73b5d38sm                offset = -right;
339cfd74d65d832137e20e193c960802afba73b5d38sm            }
340cfd74d65d832137e20e193c960802afba73b5d38sm
341cfd74d65d832137e20e193c960802afba73b5d38sm            // Filter out surfaces that do not oppose motion in the horizontal direction, or
342cfd74d65d832137e20e193c960802afba73b5d38sm            // push in the same direction as movement.
343cfd74d65d832137e20e193c960802afba73b5d38sm            mFilterDirection.set(delta);
344cfd74d65d832137e20e193c960802afba73b5d38sm            mFilterDirection.y = (0);
345cfd74d65d832137e20e193c960802afba73b5d38sm
346cfd74d65d832137e20e193c960802afba73b5d38sm            mTestPointEnd.set(currentPosition);
347cfd74d65d832137e20e193c960802afba73b5d38sm            mTestPointEnd.add(mTestPointStart);
348cfd74d65d832137e20e193c960802afba73b5d38sm            if (collision.castRay(previousPosition, mTestPointEnd, mFilterDirection,
349cfd74d65d832137e20e193c960802afba73b5d38sm                    hitPoint, hitNormal, parentObject)) {
350cfd74d65d832137e20e193c960802afba73b5d38sm                // snap
351cfd74d65d832137e20e193c960802afba73b5d38sm                currentPosition.x = (hitPoint.x + offset);
352cfd74d65d832137e20e193c960802afba73b5d38sm                hit = true;
353cfd74d65d832137e20e193c960802afba73b5d38sm            }
354cfd74d65d832137e20e193c960802afba73b5d38sm        }
355cfd74d65d832137e20e193c960802afba73b5d38sm        return hit;
356cfd74d65d832137e20e193c960802afba73b5d38sm    }
357cfd74d65d832137e20e193c960802afba73b5d38sm
358cfd74d65d832137e20e193c960802afba73b5d38sm    /* Sweeps the space between two points looking for surfaces that oppose vertical movement. */
359cfd74d65d832137e20e193c960802afba73b5d38sm    protected boolean sweepVertical(Vector2 previousPosition, Vector2 currentPosition, Vector2 delta,
360cfd74d65d832137e20e193c960802afba73b5d38sm            int bottom, int top, float centerX, Vector2 hitPoint, Vector2 hitNormal,
361cfd74d65d832137e20e193c960802afba73b5d38sm            GameObject parentObject) {
362cfd74d65d832137e20e193c960802afba73b5d38sm        boolean hit = false;
363cfd74d65d832137e20e193c960802afba73b5d38sm        if (!Utils.close(delta.y, 0.0f)) {
364cfd74d65d832137e20e193c960802afba73b5d38sm            CollisionSystem collision = sSystemRegistry.collisionSystem;
365cfd74d65d832137e20e193c960802afba73b5d38sm            // Shoot a ray from the center of the previous frame's box to the edge (top or bottom,
366cfd74d65d832137e20e193c960802afba73b5d38sm            // depending on the direction of movement) of the current box.
367cfd74d65d832137e20e193c960802afba73b5d38sm            mTestPointStart.x = (centerX);
368cfd74d65d832137e20e193c960802afba73b5d38sm            mTestPointStart.y = (bottom);
369cfd74d65d832137e20e193c960802afba73b5d38sm            int offset = -bottom;
370cfd74d65d832137e20e193c960802afba73b5d38sm            if (delta.y > 0.0f) {
371cfd74d65d832137e20e193c960802afba73b5d38sm                mTestPointStart.y = (top);
372cfd74d65d832137e20e193c960802afba73b5d38sm                offset = -top;
373cfd74d65d832137e20e193c960802afba73b5d38sm            }
374cfd74d65d832137e20e193c960802afba73b5d38sm
375cfd74d65d832137e20e193c960802afba73b5d38sm            mFilterDirection.set(delta);
376cfd74d65d832137e20e193c960802afba73b5d38sm            mFilterDirection.x = (0);
377cfd74d65d832137e20e193c960802afba73b5d38sm
378cfd74d65d832137e20e193c960802afba73b5d38sm            mTestPointEnd.set(currentPosition);
379cfd74d65d832137e20e193c960802afba73b5d38sm            mTestPointEnd.add(mTestPointStart);
380cfd74d65d832137e20e193c960802afba73b5d38sm            if (collision.castRay(previousPosition, mTestPointEnd, mFilterDirection,
381cfd74d65d832137e20e193c960802afba73b5d38sm                    hitPoint, hitNormal, parentObject)) {
382cfd74d65d832137e20e193c960802afba73b5d38sm                hit = true;
383cfd74d65d832137e20e193c960802afba73b5d38sm                // snap
384cfd74d65d832137e20e193c960802afba73b5d38sm                currentPosition.y = (hitPoint.y + offset);
385cfd74d65d832137e20e193c960802afba73b5d38sm            }
386cfd74d65d832137e20e193c960802afba73b5d38sm
387cfd74d65d832137e20e193c960802afba73b5d38sm        }
388cfd74d65d832137e20e193c960802afba73b5d38sm        return hit;
389cfd74d65d832137e20e193c960802afba73b5d38sm    }
390cfd74d65d832137e20e193c960802afba73b5d38sm
391cfd74d65d832137e20e193c960802afba73b5d38sm    /** Comparator for hit points. */
392cfd74d65d832137e20e193c960802afba73b5d38sm    private static class HitPointDistanceComparator implements Comparator<HitPoint>  {
393cfd74d65d832137e20e193c960802afba73b5d38sm        private Vector2 mOrigin;
394cfd74d65d832137e20e193c960802afba73b5d38sm
395cfd74d65d832137e20e193c960802afba73b5d38sm        public HitPointDistanceComparator() {
396cfd74d65d832137e20e193c960802afba73b5d38sm            super();
397cfd74d65d832137e20e193c960802afba73b5d38sm            mOrigin = new Vector2();
398cfd74d65d832137e20e193c960802afba73b5d38sm        }
399cfd74d65d832137e20e193c960802afba73b5d38sm
400cfd74d65d832137e20e193c960802afba73b5d38sm        public final void setOrigin(Vector2 origin) {
401cfd74d65d832137e20e193c960802afba73b5d38sm            mOrigin.set(origin);
402cfd74d65d832137e20e193c960802afba73b5d38sm        }
403cfd74d65d832137e20e193c960802afba73b5d38sm
404cfd74d65d832137e20e193c960802afba73b5d38sm        public final void setOrigin(float x, float y) {
405cfd74d65d832137e20e193c960802afba73b5d38sm            mOrigin.set(x, y);
406cfd74d65d832137e20e193c960802afba73b5d38sm        }
407cfd74d65d832137e20e193c960802afba73b5d38sm
408cfd74d65d832137e20e193c960802afba73b5d38sm        public int compare(HitPoint object1, HitPoint object2) {
409cfd74d65d832137e20e193c960802afba73b5d38sm            int result = 0;
410cfd74d65d832137e20e193c960802afba73b5d38sm            if (object1 != null && object2 != null) {
411cfd74d65d832137e20e193c960802afba73b5d38sm                final float obj1Distance = object1.hitPoint.distance2(mOrigin);
412cfd74d65d832137e20e193c960802afba73b5d38sm                final float obj2Distance = object2.hitPoint.distance2(mOrigin);
413cfd74d65d832137e20e193c960802afba73b5d38sm                final float distanceDelta = obj1Distance - obj2Distance;
414cfd74d65d832137e20e193c960802afba73b5d38sm                result = distanceDelta < 0.0f ? -1 : 1;
415cfd74d65d832137e20e193c960802afba73b5d38sm            } else if (object1 == null && object2 != null) {
416cfd74d65d832137e20e193c960802afba73b5d38sm                result = 1;
417cfd74d65d832137e20e193c960802afba73b5d38sm            } else if (object2 == null && object1 != null) {
418cfd74d65d832137e20e193c960802afba73b5d38sm                result = -1;
419cfd74d65d832137e20e193c960802afba73b5d38sm            }
420cfd74d65d832137e20e193c960802afba73b5d38sm            return result;
421cfd74d65d832137e20e193c960802afba73b5d38sm        }
422cfd74d65d832137e20e193c960802afba73b5d38sm    }
423cfd74d65d832137e20e193c960802afba73b5d38sm}
424