UNPKG

@awayfl/awayfl-player

Version:

Flash Player emulator for executing SWF files (published for FP versions 6 and up) in javascript

470 lines (413 loc) 15 kB
/* * Copyright (c) 2006-2007 Erin Catto http://www.gphysics.com * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ import { Features } from "../../Collision/Features"; import { b2ConcaveArcShape } from "../../Collision/Shapes/b2ConcaveArcShape"; import { b2PolygonShape } from "../../Collision/Shapes/b2PolygonShape"; import { b2Shape } from "../../Collision/Shapes/b2Shape"; import { b2Collision } from "../../Collision/b2Collision"; import { b2Manifold } from "../../Collision/b2Manifold"; import { b2ManifoldPoint } from "../../Collision/b2ManifoldPoint"; import { b2Mat22, b2Math, b2Vec2 } from "../../Common/Math"; import { b2Body } from "../b2Body"; import { b2ContactListener } from "../b2ContactListener"; import { b2Contact } from "./b2Contact"; import { b2PolygonContact } from "./b2PolygonContact"; export class b2PolyAndConcaveArcContact extends b2PolygonContact { public static Create(shape1:b2Shape, shape2:b2Shape, allocator:any):b2Contact{ //void* mem = allocator->Allocate(sizeof(b2PolyContact)); return new b2PolyAndConcaveArcContact(shape1, shape2); } public static Destroy(contact:b2Contact, allocator:any): void{ //((b2PolyContact*)contact)->~b2PolyContact(); //allocator->Free(contact, sizeof(b2PolyContact)); } constructor(shape1:b2Shape, shape2:b2Shape){ super(shape1, shape2); } //~b2PolyContact() {} //Flash only: Allow variable sized manifold field private enlargeManifolds(l:number):void{ while(this.m_arcManifolds.length<l){ var tMani:b2Manifold = new b2Manifold(); tMani.pointCount=0; tMani.points[0].normalImpulse=0; tMani.points[0].tangentImpulse=0; this.m_arcManifolds.push(tMani); } } public override Evaluate(listener:b2ContactListener): void{ var b1:b2Body = this.m_shape1.m_body; var b2:b2Body = this.m_shape2.m_body; var b2_nullFeature:number /** uint */=b2Collision.b2_nullFeature; //var cornerMatchTol=b2Settings.b2_linearSlop; var i:number /** int */,j:number /** int */; var tMani:b2Manifold; //Store previous impulses //Flash only: Using dictionary for contact matching //TODO: Avoid creating or destroying objects here, as in super() var impulses:b2ManifoldPoint[]=[]; var cp:b2ManifoldPoint,cp0:b2ManifoldPoint; var singleKey:number; if(!this.m_arcColl) { for(i=0; i<this.m_manifoldCount; i++) { for(j=0; j<this.m_manifolds[i].pointCount; j++) { cp0 = this.m_manifolds[i].points[j]; impulses[cp0.id.key] = cp = new b2ManifoldPoint(); cp.normalImpulse = cp0.normalImpulse; cp.tangentImpulse = cp0.tangentImpulse; } } if(this.m_manifoldCount==1 && this.m_manifolds[0].pointCount==1) { singleKey = this.m_manifolds[0].points[0].id.key; }else{ singleKey = -1; } }else{ for(i=0; i<this.m_manifoldCount; i++) { for(j=0; j<this.m_arcManifolds[i].pointCount; j++) { cp0 = this.m_arcManifolds[i].points[j]; impulses[cp0.id.key] = cp = new b2ManifoldPoint(); cp.normalImpulse = cp0.normalImpulse; cp.tangentImpulse = cp0.tangentImpulse; } } if(this.m_manifoldCount==1 && this.m_arcManifolds[0].pointCount==1) { singleKey = this.m_arcManifolds[0].points[0].id.key; }else{ singleKey = -1; } } //Do ordinary poly collision super.Evaluate(listener); var polyManiCount:number = this.m_manifoldCount; //The poly bounds the arc anyway if(this.m_manifoldCount==0) return; cp = this.m_manifolds[0].points[0]; var features:Features = cp.id.features; var edge:number = features.flip?features.referenceEdge:features.incidentEdge; var polySep:number = -Number.MAX_VALUE; if(this.m_manifoldCount==1 && edge!=0 && cp.separation<0) polySep = cp.separation; //Do arc vs poly collision { //We have hit the arc edge, so we now test against the arc var poly:b2PolygonShape = this.m_shape1 as b2PolygonShape; var arc:b2ConcaveArcShape = this.m_shape2 as b2ConcaveArcShape; var arcSep:number = Number.MAX_VALUE; // Compute circle position in the frame of the polygon. //TODO: Inline the calculations //From local in arc to local in poly v= poly.R^T*(arc.R * v+arc.pos-poly.pos) var localCenter:b2Vec2 = b2Math.b2MulXT(b1.m_xf, b2Math.b2MulX(b2.m_xf,arc.m_arcCenter)) var local0:b2Vec2 = b2Math.b2MulX(b2.m_xf,arc.m_vertices[0]) var world0:b2Vec2 = local0.Copy(); local0 = b2Math.b2MulXT(b1.m_xf,local0); var local1:b2Vec2 = b2Math.b2MulX(b2.m_xf,arc.m_vertices[1]) var world1:b2Vec2 = local1.Copy(); local1 = b2Math.b2MulXT(b1.m_xf,local1); var localNorm:b2Vec2 = b2Math.b2MulTMV(b1.m_xf.R,b2Math.b2MulMV(b2.m_xf.R,arc.m_normals[0].Copy())); var worlds:b2Vec2[] = [world0,world1]; var locals:b2Vec2[] = [local0,local1]; var strictMode:boolean = false; this.m_manifoldCount = 0; //Used for when we are investigating near ends of the arc, but find a vx collision that is not actually on the end. Stores the vx var autoApprove:number[] = [-1,-1]; //If we have found a collision near the ends, we can use that information later to restrict collisions not on the end. Stores the edge var arcLimitations:number[] = [-1,-1]; //Find a collision between the ends of the arc and the poly //We do this by treating each end as if it were a wedge extending to infinity //Where one side of the wedge is colinear with the flat side of the end //And the other side is the tangent to the arc. //We then do a simplified version of PolyContact if(edge==0) for(i=0;i<2;i++) { var maxS:number = -Number.MAX_VALUE; var maxEdge:number = -1; var local:b2Vec2 = locals[i]; var s:number; var arcNormal1:b2Vec2,arcNormal2:b2Vec2; if(i==0) { arcNormal1 = b2Math.b2MulTMV(b1.m_xf.R,b2Math.b2MulMV(b2.m_xf.R,arc.m_normals[arc.m_vertexCount-1])); arcNormal2 = b2Math.SubtractVV(localCenter,local); arcNormal2.Normalize(); }else{ arcNormal2 = b2Math.b2MulTMV(b1.m_xf.R,b2Math.b2MulMV(b2.m_xf.R,arc.m_normals[1])); arcNormal1 = b2Math.SubtractVV(localCenter,local); arcNormal1.Normalize(); } //Check for wedge stabbing the poly //FindMaxSeparation for(j=0;j<poly.m_vertexCount;j++) { //Check that the edge is angled correctly var polyVx:b2Vec2 = poly.m_vertices[j] var polyNormal:b2Vec2 = poly.m_normals[j]; if(polyNormal.x*arcNormal1.y-polyNormal.y*arcNormal1.x<0) continue; if(polyNormal.x*arcNormal2.y-polyNormal.y*arcNormal2.x>0) continue; var v2x:number = local.x-polyVx.x; var v2y:number = local.y-polyVx.y; //Get separation s = v2x*polyNormal.x+v2y*polyNormal.y; if(s>0) { maxEdge = -1; break; } if(s>maxS) { maxS = s; maxEdge = j; } } if(maxEdge!=-1) { polyNormal = poly.m_normals[maxEdge]; polyVx = poly.m_vertices[maxEdge]; var polyVx2:b2Vec2 = poly.m_vertices[maxEdge+1<poly.m_vertexCount?maxEdge+1:0]; //FindIncidentEdge var dot1:number = polyNormal.x*arcNormal1.x+polyNormal.y*arcNormal1.y; var dot2:number = polyNormal.x*arcNormal2.x+polyNormal.y*arcNormal2.y; var tangent:b2Vec2; if(dot1<dot2) { tangent = b2Math.b2CrossVF(arcNormal1,1); }else{ tangent = b2Math.b2CrossVF(arcNormal2,-1); } //Clip var t1:number = (local.x-polyVx.x)*polyNormal.x+(local.y-polyVx.y)*polyNormal.y; var t2:number = tangent.x*polyNormal.x+tangent.y*polyNormal.y; var t3:number = -t1/t2; v2x = polyVx2.x-polyVx.x; v2y = polyVx2.y-polyVx.y; var t4:number = (local.x+t3*tangent.x-polyVx.x)*v2x+(local.y+t3*tangent.y-polyVx.y)*v2y; if(t4<0 || t4>v2x*v2x+v2y*v2y){ maxEdge=-1; } } //Technically, we should check for the poly stabbing the wedge case as well //But this has problems about treating the wedge as infinite //Instead we do a cheaper test. It's not so much wrong as just different from PolyContact if(maxEdge!=-1) { arcLimitations[i] = maxEdge; if(i==0) { s = (polyVx2.x-local.x)*arcNormal2.x+(polyVx2.y-local.y)*arcNormal2.y; if(maxS<s&&s<0) { autoApprove[i] = maxEdge+1<poly.m_vertexCount?maxEdge+1:0; maxEdge = -1; } }else{ s = (polyVx.x-local.x)*arcNormal1.x+(polyVx.y-local.y)*arcNormal1.y; if(maxS<s&&s<0) { autoApprove[i] = maxEdge; maxEdge = -1; } } } if(maxEdge!=-1){ //Generate a collision this.m_manifoldCount++; this.enlargeManifolds(this.m_manifoldCount); tMani = this.m_arcManifolds[this.m_manifoldCount-1]; tMani.pointCount = 1; var tPoint:b2ManifoldPoint = tMani.points[0]; tPoint.localPoint1.SetV(locals[i]); tPoint.localPoint2.SetV(arc.m_vertices[i]) tPoint.id.features.incidentEdge = b2_nullFeature; tPoint.id.features.incidentVertex = i; tPoint.id.features.referenceEdge = maxEdge; tPoint.id.features.flip = 0; tPoint.separation = maxS; tPoint.normalImpulse = 0; tPoint.tangentImpulse = 0; tMani.normal = b2Math.b2MulMV(b1.m_xf.R, poly.m_normals[maxEdge]); arcSep = Math.min(arcSep,tPoint.separation); } } //Find collision. between poly and the main part of arc //Note due to concavity, we can have a seperate manifold for each point //On the other hand, we will have no two point manifolds, which makes life easier var bool1:boolean; var tVec:b2Vec2; var topVx:b2Vec2 var d2:number,d:number,s1:number,s2:number; var l:number; var direction:number; var currVx:number,startVx:number; if(arcLimitations[1]==-1) { if(arcLimitations[0]==-1) { direction = 1; startVx = 0; }else{ direction = poly.m_vertexCount-1;//I.e. -1 mod poly.m_vertexCount startVx = arcLimitations[0]+1==poly.m_vertexCount?0:arcLimitations[0]+1; } }else{ direction = 1; startVx = arcLimitations[1]; } //We restrict our matches to one contiguous block var foundBlock: boolean =false; //direction = 1; //startVx = 0; currVx=startVx; do { //Innocent until proven guilty bool1=true; //Test vs arcLimitations tVec = poly.m_vertices[currVx]; if(arcLimitations[0]!=-1) { l = arcLimitations[0]; topVx = poly.m_vertices[l+1==poly.m_vertexCount?0:l+1]; bool1 = bool1 && ( (tVec.x-topVx.x)*poly.m_normals[l].y-(tVec.y-topVx.y)*poly.m_normals[l].x>0 ) } if(arcLimitations[1]!=-1) { l = arcLimitations[1]; topVx = poly.m_vertices[l]; bool1 = bool1 && ( (tVec.x-topVx.x)*poly.m_normals[l].y-(tVec.y-topVx.y)*poly.m_normals[l].x<0 ) } if(foundBlock&&!bool1) { //Make this loop the last, just incase it's autoapprove startVx = (currVx+direction) % poly.m_vertexCount; } if(bool1) foundBlock=true; //Test vs __^__ tVec = new b2Vec2(poly.m_vertices[currVx].x-localCenter.x,poly.m_vertices[currVx].y-localCenter.y); d2=tVec.x*tVec.x+tVec.y*tVec.y; d = Math.sqrt(d2); s1 = arc.m_radius-d; s2 = (poly.m_vertices[currVx].x-local0.x)*localNorm.x+(poly.m_vertices[currVx].y-local0.y)*localNorm.y; s = Math.max(s1,s2); arcSep = Math.min(arcSep, s); bool1 = bool1 && (s<0) if(bool1||currVx==autoApprove[0]||currVx==autoApprove[1]) { this.m_manifoldCount++; this.enlargeManifolds(this.m_manifoldCount); tMani=this.m_arcManifolds[this.m_manifoldCount-1]; tMani.pointCount=1; tPoint = tMani.points[0]; tPoint.id.features.incidentEdge = b2_nullFeature; tPoint.id.features.incidentVertex = currVx; tPoint.id.features.referenceEdge = 0; tPoint.id.features.flip = 0; tPoint.normalImpulse = 0; tPoint.tangentImpulse = 0; tVec.x /= d; tVec.y /= d; var tMat:b2Mat22 = b1.m_xf.R; tMani.normal.x = tMat.col1.x * tVec.x + tMat.col2.x * tVec.y; tMani.normal.y = tMat.col1.y * tVec.x + tMat.col2.y * tVec.y; tVec=poly.m_vertices[currVx]; //tVec=new b2Vec2(localCenter.x,localCenter.y);//Use this instead? tPoint.localPoint1.SetV(tVec); tPoint.localPoint2.SetV(b2Math.b2MulXT(b2.m_xf,b2Math.b2MulX(b1.m_xf,tVec))) tPoint.separation = arc.m_radius-d; } currVx = (currVx+direction) % poly.m_vertexCount; }while(currVx!=startVx) } var arcManiCount:number /** int */=this.m_manifoldCount; if(polySep>arcSep) { this.m_arcColl = false; this.m_manifoldCount = polyManiCount; //Match contact points for(i=0;i<this.m_manifoldCount;i++) { for(j=0;j<this.m_manifolds[i].pointCount;j++) { cp=this.m_manifolds[i].points[j]; cp0=impulses[cp.id.key] if(cp0) { cp.normalImpulse=cp0.normalImpulse; cp.tangentImpulse=cp0.tangentImpulse; } } } if(singleKey!=-1&&this.m_manifoldCount==1&&this.m_manifolds[0].pointCount==1){ cp=this.m_manifolds[0].points[0]; cp0=impulses[singleKey] if(cp0) { cp.normalImpulse=cp0.normalImpulse; cp.tangentImpulse=cp0.tangentImpulse; } } }else{ this.m_arcColl = true; this.m_manifoldCount = arcManiCount; //Match contact points for(i=0;i<this.m_manifoldCount;i++) { for(j=0;j<this.m_arcManifolds[i].pointCount;j++) { cp=this.m_arcManifolds[i].points[j]; cp0=impulses[cp.id.key] if(cp0) { cp.normalImpulse=cp0.normalImpulse; cp.tangentImpulse=cp0.tangentImpulse; } } } if(singleKey!=-1&&this.m_manifoldCount==1&&this.m_arcManifolds[0].pointCount==1){ cp=this.m_arcManifolds[0].points[0]; cp0=impulses[singleKey] if(cp0) { cp.normalImpulse=cp0.normalImpulse; cp.tangentImpulse=cp0.tangentImpulse; } } } } public GetManifolds():b2Manifold[] { if(this.m_arcColl) return this.m_arcManifolds; else return this.m_manifolds; } //Manifolds to return if collision with arc private m_arcManifolds:b2Manifold[]=[]; //Is collision with arc part, or poly part? private m_arcColl:boolean; }