//helper function for ray terrain intersection static bool cellIntersect(float h1, float h2, float h3, float h4, float X, float Y, const WFMath::Vector<3> &nDir, float dirLen, const WFMath::Point<3> &sPt, WFMath::Point<3> &intersection, WFMath::Vector<3> &normal, float &par) { //ray plane intersection roughly using the following: //parametric ray eqn: p=po + par V //plane eqn: p dot N + d = 0 // //intersection: // -par = (po dot N + d ) / (V dot N) // // // effectively we calculate the ray parametric variable for the // intersection of the plane corresponding to each triangle // then clip them by endpints of the ray, and by the sides of the square // and by the diagonal // // if they both still intersect, then we choose the earlier intersection //intersection points for top and bottom triangles WFMath::Point<3> topInt, botInt; //point to use in plane equation for both triangles WFMath::Vector<3> p0 = WFMath::Vector<3>(X, Y, h1); // square is broken into two triangles // top triangle |/ bool topIntersected = false; WFMath::Vector<3> topNormal(h2-h3, h1-h2, 1.0); topNormal.normalize(); float t = Dot(nDir, topNormal); float topP=0.0; if ((t > 1e-7) || (t < -1e-7)) { topP = - (Dot((sPt-WFMath::Point<3>(0.,0.,0.)), topNormal) - Dot(topNormal, p0)) / t; topInt = sPt + nDir*topP; //check the intersection is inside the triangle, and within the ray extents if ((topP <= dirLen) && (topP > 0.0) && (topInt[0] >= X ) && (topInt[1] <= Y + 1 ) && ((topInt[0] - topInt[1]) <= (X - Y)) ) { topIntersected=true; } } // bottom triangle /| bool botIntersected = false; WFMath::Vector<3> botNormal(h1-h4, h4-h3, 1.0); botNormal.normalize(); float b = Dot(nDir, botNormal); float botP=0.0; if ((b > 1e-7) || (b < -1e-7)) { botP = - (Dot((sPt-WFMath::Point<3>(0.,0.,0.)), botNormal) - Dot(botNormal, p0)) / b; botInt = sPt + nDir*botP; //check the intersection is inside the triangle, and within the ray extents if ((botP <= dirLen) && (botP > 0.0) && (botInt[0] <= X + 1 ) && (botInt[1] >= Y ) && ((botInt[0] - botInt[1]) >= (X - Y)) ) { botIntersected = true; } } if (topIntersected && botIntersected) { //intersection with both if (botP <= topP) { intersection = botInt; normal = botNormal; par=botP/dirLen; if (botP == topP) { normal += topNormal; normal.normalize(); } return true; } else { intersection = topInt; normal = topNormal; par=topP/dirLen; return true; } } else if (topIntersected) { //intersection with top intersection = topInt; normal = topNormal; par=topP/dirLen; return true; } else if (botIntersected) { //intersection with bot intersection = botInt; normal = botNormal; par=botP/dirLen; return true; } return false; }
int main() { std::srand(static_cast<unsigned int>(std::time(NULL))); const float pi = 3.14159f; const int WIDTH = 900; const int HEIGHT = 600; sf::Time ball2ReleaseTime = sf::seconds(3); bool isMoving = false; bool isPlaying = false; sf::RenderWindow PONG(sf::VideoMode(WIDTH, HEIGHT, 32), "Pong", sf::Style::Titlebar | sf::Style::Close); PONG.setVerticalSyncEnabled(true); sf::Vector2f racketSize(20, 100); float racketSpeed = 1; sf::RectangleShape p1racket; p1racket.setSize(racketSize); p1racket.setFillColor(sf::Color::Red); sf::RectangleShape p2racket; p2racket.setSize(racketSize); p2racket.setFillColor(sf::Color::Blue); float ballSpeed = 0.5; float ballRad = 10; float ballDia = ballRad * 2; float ball1Angle = 0; do { ball1Angle = (std::rand() % 360) * pi / 180; } while (std::abs(std::cos(ball1Angle)) < 0.7f); float ball2Angle = 0; do { ball2Angle = (std::rand() % 360) * pi / 180; } while (std::abs(std::cos(ball1Angle)) < 0.7f); sf::Vector2f topNormal(0, ballSpeed); sf::Vector2f bottomNormal(0, -ballSpeed); sf::CircleShape ball1; ball1.setRadius(ballRad); ball1.setFillColor(sf::Color::Cyan); float ball1vx = std::cos(ball1Angle) * ballSpeed; float ball1vy = std::sin(ball1Angle) * ballSpeed; sf::CircleShape ball2; ball2.setRadius(ballRad); ball2.setFillColor(sf::Color::White); float ball2vx = std::cos(ball2Angle) * ballSpeed; float ball2vy = std::sin(ball2Angle) * ballSpeed; p1racket.setPosition (0, HEIGHT / 2); p2racket.setPosition(WIDTH - racketSize.x, HEIGHT / 2); ball1.setPosition(WIDTH / 2, HEIGHT / 2); ball2.setPosition(WIDTH / 2, HEIGHT / 2); sf::Clock clock; sf::Clock clock2; while (PONG.isOpen()) { sf::Event event; while (PONG.pollEvent(event)) { if (event.type == sf::Event::Closed) PONG.close(); } int gameTime = clock.restart().asMilliseconds(); isMoving = false; if (sf::Keyboard::isKeyPressed(sf::Keyboard::W)) { p1racket.move(0, -racketSpeed * gameTime); if (p1racket.getPosition().y < 0) p1racket.setPosition(p1racket.getPosition().x, 0); isMoving = true; } if (sf::Keyboard::isKeyPressed(sf::Keyboard::S)) { p1racket.move(0, racketSpeed * gameTime); if (p1racket.getPosition().y > HEIGHT - racketSize.y) p1racket.setPosition(p1racket.getPosition().x, HEIGHT - racketSize.y); isMoving = true; } if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up)) { p2racket.move(0, -racketSpeed * gameTime); if (p2racket.getPosition().y < 0) p2racket.setPosition(p2racket.getPosition().x, 0); isMoving = true; } if (sf::Keyboard::isKeyPressed(sf::Keyboard::Down)) { p2racket.move(0, racketSpeed * gameTime); if (p2racket.getPosition().y > HEIGHT - racketSize.y) p2racket.setPosition(p2racket.getPosition().x, HEIGHT - racketSize.y); isMoving = true; } sf::Vector2f ball1Movement(ball1vx * gameTime, ball1vy * gameTime); ball1.move(ball1Movement); if ((ball1.getPosition().y <= 0) || (ball1.getPosition().y >= HEIGHT - ballDia)) { ball1vy = -ball1vy; ball1Angle = -ball1Angle; } if ((ball2.getPosition().y <= 0) || (ball2.getPosition().y >= HEIGHT - ballDia)) { ball2vy = -ball2vy; ball2Angle = -ball2Angle; } if ((ball1.getPosition().x >= p1racket.getPosition().x + racketSize.x / 2) && (ball1.getPosition().x <= p1racket.getPosition().x + racketSize.x) && (ball1.getPosition().y >= p1racket.getPosition().y - .1) && (ball1.getPosition().y <= p1racket.getPosition().y + racketSize.y + .1)) { if (isMoving) { ball1Angle = ball1Angle - pi + ((rand() % 6) - 3) / 100; ball1vx = std::cos(ball1Angle) * ballSpeed * 1.5f; ball1vy = std::sin(ball1Angle) * ballSpeed * 1.5f; } else ball1vx = -ball1vx; } if ((ball2.getPosition().x >= p1racket.getPosition().x + racketSize.x / 2) && (ball2.getPosition().x <= p1racket.getPosition().x + racketSize.x) && (ball2.getPosition().y >= p1racket.getPosition().y - .1) && (ball2.getPosition().y <= p1racket.getPosition().y + racketSize.y + .1)) { if (isMoving) { if (ball2.getPosition().y > p1racket.getPosition().y) ball2Angle = ball2Angle - pi + ((rand() % 6) - 3) / 100; ball2vx = std::cos(ball2Angle) * ballSpeed * 1.5f; ball2vy = std::sin(ball2Angle) * ballSpeed * 1.5f; } else ball2vx = -ball2vx; } if ((ball1.getPosition().x + ballDia >= p2racket.getPosition().x) && (ball1.getPosition().x + ballDia <= p2racket.getPosition().x + racketSize.x / 2) && (ball1.getPosition().y >= p2racket.getPosition().y - .1) && (ball1.getPosition().y <= p2racket.getPosition().y + racketSize.y + .1)) { if (isMoving) { ball1Angle = ball1Angle - pi + ((rand() % 6) - 3) / 100; ball1vx = std::cos(ball1Angle) * ballSpeed * 1.5f; ball1vy = std::sin(ball1Angle) * ballSpeed * 1.5f; } else ball1vx = -ball1vx; } if ((ball2.getPosition().x + ballDia >= p2racket.getPosition().x) && (ball2.getPosition().x + ballDia <= p2racket.getPosition().x + racketSize.x /2) && (ball2.getPosition().y >= p2racket.getPosition().y - .1) && (ball2.getPosition().y <= p2racket.getPosition().y + racketSize.y + .1)) { if (isMoving) { ball2Angle = ball2Angle - pi + ((rand() % 6) - 3) / 100; ball2vx = std::cos(ball2Angle) * ballSpeed * 1.5f; ball2vy = std::sin(ball2Angle) * ballSpeed * 1.5f; } else ball2vx = -ball2vx; } PONG.clear(); PONG.draw(p1racket); PONG.draw(p2racket); PONG.draw(ball1); if (clock2.getElapsedTime() > ball2ReleaseTime) { PONG.draw(ball2); sf::Vector2f ball2Movement(ball2vx * gameTime, ball2vy * gameTime); ball2.move(ball2Movement); if (((ball1.getPosition().x >= ball2.getPosition().x) && (ball1.getPosition().x <= ball2.getPosition().x + ballDia) && (ball1.getPosition().y >= ball2.getPosition().y) && (ball1.getPosition().y <= ball2.getPosition().y + ballDia)) || ((ball2.getPosition().x >= ball1.getPosition().x) && (ball2.getPosition().x <= ball1.getPosition().x + ballDia) && (ball2.getPosition().y >= ball1.getPosition().y) && (ball2.getPosition().y <= ball1.getPosition().y + ballDia)) ) { ball1vx = -ball1vx; ball2vx = -ball2vx; } } PONG.display(); } }
//helper function for ray terrain intersection static bool cellIntersect(float h1, float h2, float h3, float h4, float X, float Y, const std::tuple<float, float, float> &nDir, float dirLen, const WFMath::Point<3> &sPt, WFMath::Point<3> &intersection, std::tuple<float, float, float> &normal, float &par) { //ray plane intersection roughly using the following: //parametric ray eqn: p=po + par V //plane eqn: p dot N + d = 0 // //intersection: // -par = (po dot N + d ) / (V dot N) // // // effectively we calculate the ray parametric variable for the // intersection of the plane corresponding to each triangle // then clip them by endpints of the ray, and by the sides of the square // and by the diagonal // // if they both still intersect, then we choose the earlier intersection //intersection points for top and bottom triangles WFMath::Point<3> topInt, botInt; //point to use in plane equation for both triangles std::tuple<float, float, float> p0 = std::tuple<float, float, float>(X, Y, h1); // square is broken into two triangles // top triangle |/ bool topIntersected = false; std::tuple<float, float, float> topNormal(h2 - h3, h1 - h2, 1.0); normalise_i(topNormal); float t = dot(nDir, topNormal); decltype(t) topP = 0.0; if ((t > 1e-7) || (t < -1e-7)) { topP = -(dot(std::tuple<float, float, float>(sPt[0], sPt[1], sPt[2]), topNormal) - dot(topNormal, p0)) / t; topInt = translate(sPt, scale(nDir, topP)); //check the intersection is inside the triangle, and within the ray extents if ((topP <= dirLen) && (topP > 0.0) && (topInt[0] >= X ) && (topInt[1] <= Y + 1 ) && ((topInt[0] - topInt[1]) <= (X - Y)) ) { topIntersected = true; } } // bottom triangle /| bool botIntersected = false; std::tuple<float, float, float> botNormal(h1 - h4, h4 - h3, 1.0); normalise_i(botNormal); auto b = dot(nDir, botNormal); decltype(b) botP = 0.0; if ((b > 1e-7) || (b < -1e-7)) { botP = -(dot(std::tuple<float, float, float>(sPt[0], sPt[1], sPt[2]), botNormal) - dot(botNormal, p0)) / b; botInt = translate(sPt, scale(nDir, botP)); //check the intersection is inside the triangle, and within the ray extents if ((botP <= dirLen) && (botP > 0.0) && (botInt[0] <= X + 1 ) && (botInt[1] >= Y ) && ((botInt[0] - botInt[1]) >= (X - Y)) ) { botIntersected = true; } } if (topIntersected && botIntersected) //intersection with both { if (botP <= topP) { intersection = botInt; normal = botNormal; par = botP / dirLen; if (botP == topP) { add_i(normal, topNormal); normalise_i(normal); } return true; } else { intersection = topInt; normal = topNormal; par = topP / dirLen; return true; } } else if (topIntersected) //intersection with top { intersection = topInt; normal = topNormal; par = topP / dirLen; return true; } else if (botIntersected) //intersection with bot { intersection = botInt; normal = botNormal; par = botP / dirLen; return true; } return false; }