#include "Welt2D.h" #include "Bild.h" using namespace Framework; Object2D::Object2D() : ReferenceCounter() { rSpeed = 0; rotation = 0; size = 1; collision = 1; } Object2D::~Object2D() {} // Übergibt einen Void Funktionspointer auf eine Aktion die einmalig vom // Hauptthread ausgeführt werden soll. (Passiert nach dem Tick) void Object2D::postAction(std::function action) { actions.push(action); } void Object2D::explosion(Vertex worldPos, float intensity) { intensity /= (position - worldPos).getLengthSq(); speed += (position - worldPos) * intensity; } void Object2D::impuls(Vertex start, Vertex speed, float strength) {} void Object2D::setSpeed(Vertex speed) { this->speed = speed; } void Object2D::setSpeed(float x, float y) { speed.x = x, speed.y = y; } void Object2D::setPosition(Vertex pos) { position = pos; } void Object2D::setPosition(float x, float y) { position = Vertex(x, y); } void Object2D::setDrehungSpeed(float ds) { rSpeed = ds; } void Object2D::setDrehung(float drehung) { rotation = drehung; while (rotation > PI * 2) rotation -= (float)PI * 2; while (rotation < 0) rotation += (float)PI * 2; } void Object2D::addDrehung(float drehung) { rotation += drehung; while (rotation > PI * 2) rotation -= (float)PI * 2; while (rotation < 0) rotation += (float)PI * 2; } void Object2D::setSize(float size) { this->size = size; } void Object2D::addSize(float size) { this->size += size; } void Object2D::setCollision(bool handle) { collision = handle; } bool Object2D::handleCollision(Object2D* obj) { Vertex hp; if (istModelInnen(obj, &hp)) // hp wird auf den aufprallpunkt gesetzt { // Geschwindigkeit von diesem objekt mit rotation Vertex v1 = getSpeed() + getWorldDir(getObjectPos(hp).rotation(rSpeed) - getObjectPos(hp)); // Geschwindigkeit des anderen Objektes mit rotation Vertex v2 = obj->getSpeed() + getWorldDir(obj->getObjectPos(hp).rotation(obj->getDrehungSpeed()) - obj->getObjectPos(hp)); if ((hp - obj->getPosition()).getLengthSq() > (hp + v1 * 0.03f - obj->getPosition()).getLengthSq() || (hp - getPosition()).getLengthSq() > (hp + v2 * 0.03f - getPosition()).getLengthSq()) { // nur wenn sie sich aufeinander zu bewegen float m1 = getMasse() * v1.getLength(); // fläche von Objekt 1 float m2 = obj->getMasse() * v2.getLength(); // fläche von Objekt 2 if (m1 == 0 || m2 == 0) return 0; // falls ein objekt keine masse hat ignoriere die // kollision float nm1 = m1 / (m1 + m2); // koeffizient für objekt 2 float nm2 = m2 / (m1 + m2); // koeffizient für Objekt 1 // rSpeed *= nm1; // Drehgeschwindigkeit anpassen (objekt 1) // speed *= nm1; // Bewegungsgeschwindigkeit anpassen (objekt 1) // obj->setDrehungSpeed( obj->getDrehungSpeed() * nm2 ); // // Drehgeschwindigkeit anpassen (objekt 2) obj->setSpeed( // obj->getSpeed() * nm2 ); // Bewegungsgeschwindigkeit anpassen // (objekt 2) float speedSumLength = getSpeed().getLength() + obj->getSpeed().getLength(); rSpeed = 0; speed = Vertex(); obj->setDrehungSpeed(0); obj->setSpeed(Vertex()); if (v2.x || v2.y) impuls(hp - v2, v2); if (getSpeed().getLength() > 0) setSpeed(getSpeed().normalize() * speedSumLength * nm2); if (v1.x || v1.y) obj->impuls(hp - v1, v1); if (obj->getSpeed().getLength() > 0) obj->setSpeed( obj->getSpeed().normalize() * speedSumLength * nm1); return 1; } } return 0; } bool Object2D::tick(const WeltInfo& info, double zeit) { while (!actions.empty()) { actions.front()(); actions.pop(); } rotation += rSpeed * (float)zeit; while (rotation > PI * 2) rotation -= (float)PI * 2; while (rotation < 0) rotation += (float)PI * 2; position += speed * (float)zeit; while (zeit > 1) { rSpeed -= rSpeed - (rSpeed / (1 + info.airResistance * getLuftWiederstand())); speed -= speed - (speed / (1 + info.airResistance * getLuftWiederstand())); zeit -= 1; } rSpeed -= (rSpeed - (rSpeed / (1 + info.airResistance * getLuftWiederstand()))) * (float)zeit; speed -= (speed - (speed / (1 + info.airResistance * getLuftWiederstand()))) * (float)zeit; if (info.circular && info.hasSize && info.size.x && info.size.y) { while (position.x > (float)info.size.x) position.x -= (float)info.size.x; while (position.x < 0) position.x += (float)info.size.x; while (position.y > (float)info.size.y) position.y -= (float)info.size.y; while (position.y < 0) position.y += (float)info.size.y; } return rSpeed != 0 || speed != Vertex(0, 0); } bool Object2D::istPunktInnen(Vertex p, bool ignoreTransparent) const { return 0; } bool Object2D::istLinieInnen(Vertex a, Vertex b, bool ignoreTransparent) const { return 0; } bool Object2D::istModelInnen( const Object2D* zObj, Vertex* sp, bool end, bool ignoreTransparent) const { return 0; } Mat3 Object2D::getObjectMatrix() const { return Mat3::translation(position) * Mat3::rotation(rotation) * Mat3::scaling(size); } Mat3 Object2D::getInverseObjectMatrix() const { return Mat3::scaling(1 / size) * Mat3::rotation(-rotation) * Mat3::translation(-position); } Vertex Object2D::getObjectPos(Vertex worldPos) const { return (worldPos - position).rotation(-rotation) * (1 / size); } Vertex Object2D::getObjectDir(Vertex worldDir) const { return Vertex(worldDir.x, worldDir.y).rotation(-rotation) * (1 / size); } Vertex Object2D::getWorldPos(Vertex objectPos) const { return (Vertex(objectPos) * size).rotation(rotation) + position; } Vertex Object2D::getWorldDir(Vertex objectDir) const { return (Vertex(objectDir) * size).rotation(rotation); } Vertex Object2D::getSpeed() const { return speed; } Vertex Object2D::getPosition() const { return position; } float Object2D::getDrehungSpeed() const { return rSpeed; } float Object2D::getDrehung() const { return rotation; } float Object2D::getSize() const { return size; } bool Object2D::calcHitPoint(Vertex pos, Vertex dir, Vertex& hitpoint) const { return 0; } float Object2D::getLuftWiederstand() const { return 0; } float Object2D::getMasse() const { return 0; } bool Object2D::canCollide() { return collision; } Welt2D::Welt2D() : ReferenceCounter() { objects = new RCArray(); memset(&info, 0, sizeof(WeltInfo)); } Welt2D::~Welt2D() { objects->release(); } void Welt2D::setAirResistance(float resistance) { info.airResistance = resistance; } void Welt2D::setSize(int width, int height) { info.size.x = width; info.size.y = height; } void Welt2D::setSize(bool hasSize) { info.hasSize = hasSize; } void Welt2D::setCircular(bool circular) { info.circular = circular; } Object2D* Welt2D::zObjectAt(int x, int y, bool ignoreTransparentFlag) { for (auto o : *objects) { if (o->istPunktInnen(Punkt(x, y), ignoreTransparentFlag)) return o; } return 0; } Object2D* Welt2D::getObjectAt(int x, int y, bool ignoreTransparentFlag) { Object2D* tmp = zObjectAt(x, y, ignoreTransparentFlag); return tmp ? dynamic_cast(tmp->getThis()) : 0; } void Welt2D::addObject(Object2D* obj) { objects->add(obj); } void Welt2D::removeObject(Object2D* zObj) { int anz = objects->getEintragAnzahl(); for (int i = 0; i < anz; i++) { if (objects->z(i) == zObj) { objects->remove(i); i--; } } } void Welt2D::removeAll() { objects->leeren(); } void Welt2D::explosion(Vertex worldPos, float intensity, float maxRad) { maxRad = maxRad * maxRad; for (auto obj : *objects) { if (info.circular && info.hasSize && info.size.x && info.size.y) { Vertex offsets[] = {Vertex(0, 0), Vertex((float)info.size.x, 0), Vertex(0, (float)info.size.y), Vertex((float)info.size.x, (float)info.size.y), Vertex((float)-info.size.x, 0), Vertex(0, (float)-info.size.y), Vertex((float)-info.size.x, (float)-info.size.y), Vertex((float)-info.size.x, (float)info.size.y), Vertex((float)info.size.x, (float)-info.size.y)}; Vertex offset; float minDist = INFINITY; for (Vertex p : offsets) { float dist = (obj->getPosition() - (worldPos - p)).getLengthSq(); if (dist < minDist) { minDist = dist; offset = p; } } if ((obj->getPosition() - (worldPos - offset)).getLengthSq() < maxRad) obj->explosion(worldPos - offset, intensity); } else if ((obj->getPosition() - worldPos).getLengthSq() < maxRad) obj->explosion(worldPos, intensity); } } void Welt2D::impuls(Vertex worldPos, Vertex worldDir) { Vertex hitPoint; float dist = INFINITY; Object2D* o = 0; for (auto obj : *objects) { if (obj->calcHitPoint(worldPos, worldDir, hitPoint)) { if ((hitPoint - worldPos).getLengthSq() < dist) { dist = (hitPoint - worldPos).getLengthSq(); o = obj; } } } if (o) o->impuls(worldPos, worldDir); } bool Welt2D::tick(double zeit) { bool ret = 0; for (auto obj = objects->begin(); obj; obj++) { if (obj.hasNext() && obj->canCollide()) { for (auto obj2 = obj.next(); obj2; obj2++) { if (obj2->canCollide()) obj->handleCollision(obj2); } } ret |= obj->tick(info, zeit); } return ret; } void Welt2D::render(Mat3& kamMat, Punkt size, Bild& zRObj, int xOffset, int yOffset, const char* kamName) { for (auto obj : *objects) { Rect2 bnd = obj->getBoundingBox(); Vertex topRight = Vertex(bnd.bottomRight.x, bnd.topLeft.y); Vertex bottomLeft = Vertex(bnd.topLeft.x, bnd.bottomRight.y); Mat3 km = kamMat * Mat3::translation(Vertex((float)xOffset, (float)yOffset)); bnd.bottomRight = km * bnd.bottomRight; bnd.topLeft = km * bnd.topLeft; topRight = km * topRight; bottomLeft = km * bottomLeft; if ((bnd.bottomRight.x >= 0 && bnd.bottomRight.x < (float)size.x) || (bnd.bottomRight.y >= 0 && bnd.bottomRight.y < (float)size.y) || (bnd.topLeft.x >= 0 && bnd.topLeft.x < (float)size.x) || (bnd.topLeft.y >= 0 && bnd.topLeft.y < (float)size.y) || (topRight.x >= 0 && topRight.x < (float)size.x) || (topRight.y >= 0 && topRight.y < (float)size.y) || (bottomLeft.x >= 0 && bottomLeft.x < (float)size.x) || (bottomLeft.y >= 0 && bottomLeft.y < (float)size.y)) obj->render(km, zRObj, kamName); } } void Welt2D::render( Mat3& kamMat, Punkt size, Bild& zRObj, const char* kamName) { if (!info.hasSize || !info.circular) { for (auto obj : *objects) { Rect2 bnd = obj->getBoundingBox(); Vertex topRight = Vertex(bnd.topLeft.y, bnd.bottomRight.x); Vertex bottomLeft = Vertex(bnd.topLeft.x, bnd.bottomRight.y); bnd.bottomRight = kamMat * bnd.bottomRight; bnd.topLeft = kamMat * bnd.topLeft; topRight = kamMat * topRight; bottomLeft = kamMat * bottomLeft; if ((bnd.bottomRight.x >= 0 && bnd.bottomRight.x < (float)size.x) || (bnd.bottomRight.y >= 0 && bnd.bottomRight.y < (float)size.y) || (bnd.topLeft.x >= 0 && bnd.topLeft.x < (float)size.x) || (bnd.topLeft.y >= 0 && bnd.topLeft.y < (float)size.y) || (topRight.x >= 0 && topRight.x < (float)size.x) || (topRight.y >= 0 && topRight.y < (float)size.y) || (bottomLeft.x >= 0 && bottomLeft.x < (float)size.x) || (bottomLeft.y >= 0 && bottomLeft.y < (float)size.y)) obj->render(kamMat, zRObj, kamName); } } else if (info.circular) { render(kamMat, size, zRObj, 0, 0, kamName); render(kamMat, size, zRObj, info.size.x, 0, kamName); render(kamMat, size, zRObj, 0, info.size.y, kamName); render(kamMat, size, zRObj, info.size.x, info.size.y, kamName); render(kamMat, size, zRObj, -info.size.x, 0, kamName); render(kamMat, size, zRObj, 0, -info.size.y, kamName); render(kamMat, size, zRObj, -info.size.x, -info.size.y, kamName); render(kamMat, size, zRObj, -info.size.x, +info.size.y, kamName); render(kamMat, size, zRObj, +info.size.x, -info.size.y, kamName); } } const WeltInfo& Welt2D::getWorldInfo() const { return info; } ArrayIterator Welt2D::getMembers() { return objects->begin(); }