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