1 module PixelPerfectEngine.collision.objectCollision; 2 3 /* 4 * Copyright (C) 2015-2020, by Laszlo Szeremi under the Boost license. 5 * 6 * Pixel Perfect Engine, collision.objectCollision module. 7 */ 8 9 import PixelPerfectEngine.system.advBitArray; 10 11 import collections.treemap; 12 import PixelPerfectEngine.collision.common; 13 //import PixelPerfectEngine.collision.boxCollision; 14 15 /** 16 * Object-to-object collision detector. 17 */ 18 public class ObjectCollisionDetector { 19 ObjectMap objects; ///Contains all of the objects 20 int contextID; ///Stores the identifier of this detector 21 /** 22 * The delegate where the events will be passed. 23 * Must be set up before using the collision detector. 24 */ 25 void delegate(ObjectCollisionEvent event) objectToObjectCollision; 26 /** 27 * Tests all shapes against each other 28 */ 29 public void testAll() { 30 foreach (int iA, ref CollisionShape shA; objects) { 31 testSingle(iA, &shA); 32 } 33 } 34 /** 35 * Tests a single shape against the others 36 */ 37 public void testSingle(int objectID) { 38 testSingle(objectID, objects.ptrOf(objectID)); 39 } 40 ///Ditto 41 protected void testSingle(int iA, CollisionShape* shA) { 42 foreach (int iB, ref CollisionShape shB; objects) { 43 if (iA != iB) { 44 ObjectCollisionEvent event = testCollision(shA, &shB); 45 if (event! is null) { 46 event.idA = iA; 47 event.idB = iB; 48 objectToObjectCollision(event); 49 } 50 } 51 } 52 } 53 /** 54 * Tests two objects. Calls cl if collision have happened, with the appropriate values. 55 */ 56 protected ObjectCollisionEvent testCollision(CollisionShape* shA, CollisionShape* shB) pure { 57 if (shA.position.bottom < shB.position.top || shA.position.top > shB.position.bottom || 58 shA.position.right < shB.position.left || shA.position.left > shB.position.right){ 59 //test if edge collision have happened with side edges 60 if (shA.position.bottom >= shB.position.top && shA.position.top <= shB.position.bottom && 61 (shA.position.right - shB.position.left == -1 || shA.position.left - shB.position.right == 1)) { 62 //calculate edge collision area 63 Coordinate cc; 64 if(shA.position.top >= shB.position.top) 65 cc.top = shA.position.top; 66 else 67 cc.top = shB.position.top; 68 if(shA.position.bottom >= shB.position.bottom) 69 cc.bottom = shB.position.bottom; 70 else 71 cc.bottom = shA.position.bottom; 72 if (shA.position.right < shB.position.left) { 73 cc.left = shA.position.right; 74 cc.right = shB.position.left; 75 } else { 76 cc.left = shB.position.right; 77 cc.right = shA.position.left; 78 } 79 return new ObjectCollisionEvent(shA, shB, contextID, cc, ObjectCollisionEvent.Type.BoxEdge); 80 } else if (shA.position.right >= shB.position.left && shA.position.left <= shB.position.right && 81 (shA.position.bottom - shB.position.top == -1 || shA.position.top - shB.position.bottom == 1)) { 82 //calculate edge collision area 83 Coordinate cc; 84 if(shA.position.left >= shB.position.left) 85 cc.left = shA.position.left; 86 else 87 cc.left = shB.position.left; 88 if(shA.position.right >= shB.position.right) 89 cc.right = shB.position.right; 90 else 91 cc.right = shA.position.right; 92 if (shA.position.bottom < shB.position.top) { 93 cc.top = shA.position.bottom; 94 cc.bottom = shB.position.top; 95 } else { 96 cc.top = shB.position.bottom; 97 cc.bottom = shA.position.top; 98 } 99 return new ObjectCollisionEvent(shA, shB, contextID, cc, ObjectCollisionEvent.Type.BoxEdge); 100 } else return null; 101 } else { 102 //if there's a bitmap for both shapes, then proceed to per-pixel testing 103 Coordinate ca, cb, cc; // test area coordinates 104 ca.right = shA.position.width; 105 ca.bottom = shA.position.height; 106 cb.right = shB.position.width; 107 cb.bottom = shB.position.height; 108 if(shA.position.top >= shB.position.top){ 109 ca.top += shA.position.top - shB.position.top; 110 cc.top = shA.position.top; 111 } else { 112 cb.top += shB.position.top - shA.position.top; 113 cc.top = shB.position.top; 114 } 115 if(shA.position.bottom >= shB.position.bottom) { 116 ca.bottom -= shA.position.bottom - shB.position.bottom; 117 cc.bottom = shB.position.bottom; 118 } else { 119 cb.bottom -= shB.position.bottom - shA.position.bottom; 120 cc.bottom = shA.position.bottom; 121 } 122 if(shA.position.left >= shB.position.left) { 123 ca.left += shA.position.left - shB.position.left; 124 cc.left = shA.position.left; 125 } else { 126 cb.left += shB.position.left - shA.position.left; 127 cc.left = shB.position.left; 128 } 129 if(shA.position.right >= shB.position.right) { 130 ca.right -= shA.position.right - shB.position.right; 131 cc.right = shB.position.right; 132 } else { 133 cb.right -= shB.position.right - shA.position.right; 134 cc.right = shA.position.right; 135 } 136 debug { 137 assert ((ca.width == cb.width) && (cb.width == cc.width), "Width mismatch error!"); 138 assert ((ca.height == cb.height) && (cb.height == cc.height), "Height mismatch error!"); 139 } 140 ObjectCollisionEvent event = new ObjectCollisionEvent(shA, shB, contextID, cc, ObjectCollisionEvent.Type.BoxOverlap); 141 if(shA.shape !is null && shB.shape !is null) { 142 for (int y ; y < cc.width ; y++) { 143 for (int x ; x < cc.height ; x++) { 144 if (shA.shape.readPixel(ca.left + x, ca.top + y) && shB.shape.readPixel(cb.left + x, cb.top + y)) { 145 event.type = ObjectCollisionEvent.Type.ShapeOverlap; 146 return event; 147 } 148 } 149 } 150 } 151 return event; 152 } 153 } 154 /+/** 155 * Tests two boxes together. Returns true on collision. 156 */ 157 protected bool areColliding(ref CollisionShape a, ref CollisionShape b){ 158 if (a.position.bottom < b.position.top) return false; 159 if (a.position.top > b.position.bottom) return false; 160 161 if (a.position.right < b.position.left) return false; 162 if (a.position.left > b.position.right) return false; 163 164 Coordinate ca, cb, cc; // test area coordinates 165 int cTPA, cTPB; // testpoints 166 167 // process the test area and calculate the test points 168 if(a.position.top >= b.position.top){ 169 cc.top = a.position.top; 170 cTPA = a.position.width() * (a.position.top - b.position.top); 171 }else{ 172 cc.top = b.position.top; 173 cTPB = b.position.width() * (b.position.top - a.position.top); 174 } 175 if(a.position.bottom >= b.position.bottom){ 176 cc.bottom = b.position.bottom; 177 }else{ 178 cc.bottom = a.position.bottom; 179 } 180 if(a.position.left >= b.position.left){ 181 cc.left = a.position.left; 182 cTPA += a.position.left - b.position.left; 183 }else{ 184 cc.left = b.position.left; 185 cTPB += b.position.left - a.position.left; 186 } 187 if(a.position.right >= b.position.right){ 188 cc.right = b.position.right; 189 }else{ 190 cc.right = a.position.right; 191 } 192 //writeln("A: x: ", ca.left," y: ", ca.top, "B: x: ", cb.left," y: ", cb.top, "C: x: ", cc.left," y: ", cc.top); 193 for(int y ; y < cc.height ; y++) { 194 for(int x ; x < cc.width ; x++) { 195 196 } 197 cTPA += a.position.width(); 198 cTPB += b.position.width(); 199 } 200 201 return false; 202 }+/ 203 }