I'm trying to create my own CFD in C++. I have watched some videos on youtube about the Lattice Boltzmann method, but I cant get my simulations to look like the simulations performed in the videos with lattice Boltzmann implemented in Python.
I use SDL2 to create an image on my screen. I am not trying to create anything fast. Just something that will make pretty simulations on the CPU.
Here is my class for each cell:
//cell class
class cell {
public:
double Fi[nL] = {0,0,0,0,0,0,0,0,0};
double density = 0;
double momentumX = 0;
double momentumY = 0;
double velocityX = 0;
double velocityY = 0;
double Fieq[nL] = {0,0,0,0,0,0,0,0,0};
//obstacle
bool obstacle = false;
void densityOperator() {
for (int i = 0; i < nL; i++) {
density += Fi[i];
}
}
void momentumOperator() {
for (int i = 0; i < nL; i++) {
momentumX += Fi[i] * cX[i];
momentumY += Fi[i] * cY[i];
}
}
void velocityOperator() {
for (int i = 0; i < nL; i++) {
if (density == 0) {
density += 0.001;
}
velocityX += momentumX / density; // prolly very slow
velocityY += momentumY / density;
//velocityX += cX[i];
//velocityY += cY[i];
}
}
void FieqOperator() {
for (int i = 0; i < nL; i++) {
Fieq[i] = weights[i] * density *
(
1 +
(cX[i] * velocityX + cY[i] * velocityY) / Cs +
pow((cX[i] * velocityX + cY[i] * velocityY), 2) / (2 * pow(Cs, 4)) -
(velocityX * velocityX + velocityY * velocityY) / (2 * pow(Cs, 2))
);
}
}
void FiOperator() {
for (int i = 0; i < nL; i++) {
Fi[i] = Fi[i] - (timestep / tau) * (Fi[i] - Fieq[i]);
}
}
void addRightVelocity() {
Fi[0] = 1.f;
Fi[1] = 1.f;
Fi[2] = 1.f;
Fi[3] = 6.f;
Fi[4] = 1.f;
Fi[5] = 1.f;
Fi[6] = 1.f;
Fi[7] = 1.f;
Fi[8] = 1.f;
}
};
Please note that im am using a vector for my cells instead of a 2d array. I am using a index function to go from x,y to 1d cordinate.
int index(int x, int y) {
return x * nY + y;
}
Variables:
//box
const int nX = 400;
const int nY = 100;
//viscosity
float tau = 0.5; // 0.53
//time delta time per iteration
float timestep = 1;
//distance between cells
float dist = 1000;
//Speed of sound
float Cs = 1 / sqrt(3) * (dist / timestep);
//viscociti
float v = pow(Cs, 2) * (tau - timestep / 2); // tau will need to be much smaller
//time steps
int nT = 3000;
//lattice speeds and weights
const int nL = 9;
//Ci vector direction, discrete velocity
int cX[9] = { 0, 0, 1, 1, 1, 0, -1, -1, -1 };
int cY[9] = { 0, 1, 1, 0, -1, -1, -1, 0 , 1 };
//weights, based on navier stokes
float weights[9] = { 4 / 9, 1 / 9, 1 / 36, 1 / 9, 1 / 36, 1 / 9, 1 / 36, 1 / 4, 1 / 36 };
//opposite populations
int cO[9] = { 0, 5, 6, 7, 8, 1, 2, 3, 4 };
My main function:
int main() {
//init vector cells
for (int x = 0; x < nX; x++) {
for (int y = 0; y < nY; y++) {
cell cellUnit;
cells.push_back(cellUnit);
TempCells.push_back(cellUnit);
}
}
//SDL
//SDL
//-------------------------------------------------------------
SDL_Window* window = nullptr;
SDL_Renderer* renderer = nullptr;
SDL_Init(SDL_INIT_VIDEO);
SDL_CreateWindowAndRenderer(nX* 3, nY * 3, 0, &window, &renderer);
SDL_RenderSetScale(renderer, 3, 3);
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
SDL_RenderClear(renderer);
//-------------------------------------------------------------//
//Circle Object Gen
for (int x = 0; x < nX; x++) {
for (int y = 0; y < nY; y++) {
//cicle position
int circleX = 5;
int circleY = 50;
//circle radius
float radius = 10;
//distance bewtween cell and circle pos
float distance = sqrt(pow(circleX - x, 2) + pow(circleY - y, 2));
if (distance < radius) {
cells[index(x,y)].obstacle = true;
}
else {
cells[index(x, y)].obstacle = false;
}
}
}
//add velocity
for (int x = 0; x < nX; x++) {
for (int y = 0; y < nY; y++) {
cells[index(x, y)].addRightVelocity();
//random velocity
for (int i = 0; i < nL; i++) {
cells[index(x,y)].Fi[i] += (rand() % 200) / 100;
}
}
}
for (int t = 0; t < nT; t++) {
//SDL
//--------------------------------------------------------------
//clear renderer
if (t % 20 == 0) {
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
SDL_RenderClear(renderer);
}
//--------------------------------------------------------------
//streaming:
//because we will loop over the same populations we do not want to switch the same population twice
for (int x = 0; x < nX; x++) {
for (int y = 0; y < nY; y++) {
if (x == 0) {
cells[index(x, y)].Fi[3] += 0.4;
}
//for populations
for (int i = 0; i < nL; i++) {
//boundary
//checs if cell is object or air
if (cells[index(x, y)].obstacle == false) {
//air
//targetet cell
int cellX = x + cX[i];
int cellY = y + cY[i];
//out of bounds check + rearange to other side
if (cellX < 0) {
//left to right
cellX = nX;
}
if (cellX >= nX) {
//right to left
cellX = 0;
continue;
}
if (cellY < 0) {
//top to buttom
cellY = nY;
}
if (cellY >= nY) {
//bottom to top
cellY = 0;
}
//if neighborinig cell is object --> collision with object
if (cells[index(cellX, cellY)].obstacle == true) {
//Boundary handling https://youtu.be/jfk4feD7rFQ?t=2821
TempCells[index(x,y)].Fi[cO[i]] = cells[index(x, y)].Fi[i];
}
//if not then stream to neighbor air cell with oposite population
TempCells[index(cellX, cellY)].Fi[cO[i]] = cells[index(x, y)].Fi[i];
}
else {
//wall
//SDL GRAPICHS
if (t % 20 == 0) {
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
SDL_RenderDrawPoint(renderer, x, y);
}
}
}
}
}
for (int x = 0; x < nX; x++) {
for (int y = 0; y < nY; y++) {
for (int i = 0; i < nL; i++) {
cells[index(x, y)].Fi[i] = TempCells[index(x, y)].Fi[cO[i]];
}
}
}
//collision:
for (int x = 0; x < nX; x++) {
for (int y = 0; y < nY; y++) {
//density:
cells[index(x, y)].densityOperator();
//momentum:
cells[index(x, y)].momentumOperator();
//velocity:
cells[index(x, y)].velocityOperator();
//Fieq + new new Fi:
for (int i = 0; i < nL; i++) {
cells[index(x, y)].FieqOperator();
}
//SDL Graphics
if (t % 20 == 0) {
if (cells[index(x, y)].obstacle == false) {
SDL_SetRenderDrawColor(renderer, cells[index(x, y)].density, cells[index(x, y)].density , 255 , 255);
SDL_RenderDrawPoint(renderer, x, y);
}
}
}
}
for (int x = 0; x < nX; x++) {
for (int y = 0; y < nY; y++) {
cells[index(x, y)].FiOperator();
}
}
//SDL Graphics
if (t % 20 == 0 ) {
SDL_RenderPresent(renderer);
}
}
return 0;
}
I do realize my code might be a bit messy and not easy to understand at first. And it is definitely not optimal.
If anyone has any experience in programming their own LBM in c++ i would like to hear your input.
It seams like my simulations is working but i do not get those bueatiful animations like in, https://youtu.be/ZUXmO4hu-20?t=3394
Thanks for any help.
Edit:
I have edited my script to reset, density, velocity X Y and Momentum X Y
Simulation visualised by density, pink is higher, loops if density exceeds color range of 255
Simulation visualised by density
Simulation visualised by density
I started Learning C++ yesterday And In that time i was rewriting my java "Falling Sand" Sandbox Game code in C++ using SFML (bit simplified since i don't know C++). but Performance in C++ was much worse in than java, what could be the reason for it, I Know this is very unfocused question, but my code is simple, i probably have a newbie mistakes which should be easy to correct.
#include <SFML/Graphics.hpp>
#include <iostream>
sf::Clock sclock;
const int WIDTH = 1440, HEIGHT = 960;
const char Blank = 0, Sand = 1, Water = 2;
const char* title = "Sandbox Simulation";
char map[WIDTH*HEIGHT];
sf::Vector2i mousePos;
int dist(int x1, int x2, int y1, int y2) {
return sqrt(pow(x1 - x2, 2) + pow(y1 - y2, 2));
}
int localBrushSize = 48;
short halfBrush = (short)floor(localBrushSize / 2);
char chosen = Sand;
void place() {
int randY = 0;
int randX = 0;
randX = randY = 1;
for (int y = mousePos.y - halfBrush; y <= mousePos.y + halfBrush; y += randY) {
for (int x = mousePos.x - halfBrush; x <= mousePos.x + halfBrush; x += randX) {
int I = x + y * WIDTH;
int distance = dist(mousePos.x, x, mousePos.y, y);
if (distance < halfBrush && I > 0) {
map[I] = chosen;
}
}
}
}
float Delta_Time() {
return sclock.restart().asSeconds();
}
int main() {
map[11111] = 2;
sf::RenderWindow window(sf::VideoMode(WIDTH, HEIGHT), title);
sf::Event evnt;
sf::RectangleShape pixel(sf::Vector2f(1.0f, 1.0f));
window.clear();
while (window.isOpen()) {
while (window.pollEvent(evnt)) {
switch (evnt.type) {
case sf::Event::Closed:
window.close();
break;
}
}
if (sf::Mouse::isButtonPressed(sf::Mouse::Left)) {
mousePos = sf::Mouse::getPosition(window);
place();
}
for (int y = 0; y < HEIGHT; y++) {
for (int x = 0; x < WIDTH; x++) {
int I = x + y * WIDTH;
switch (map[I]) {
case Sand:
pixel.setPosition(x, y);
pixel.setFillColor(sf::Color::Yellow);
window.draw(pixel);
break;
case Water:
pixel.setPosition(x, y);
pixel.setFillColor(sf::Color::Cyan);
window.draw(pixel);
break;
}
}
}
window.display();
}
return 0;
}
You might be able to make a cached / "framebuffer" like this
TOTALLY untested concept code. WIDTH/HEIGHT might be mixed up, endianess is not OK, etc.
sf::Image image;
sf:Image.create(WIDTH, HEIGHT, sf::Color(0, 0, 0));
sf::Sprite sprite;
std::array<sf::Uint8, WIDTH * HEIGHT * 4> pixels; // you can reuse this. The 4 is the size of RGBA
...
for(int y = 0; y < HEIGHT; y++) {
for(int x = 0; x < WIDTH; x++) {
int offset = (x + y * WIDTH) * 4;
pixels[offset] = sf::Color::Yellow.toInteger(); // in case BIG/Litte endian confusion you might have to do the below.
//pixels[offset + 0 ] = 255; // R?
//pixels[offset + 1 ] = 255; // G?
//pixels[offset + 2 ] = 255; // B?
//pixels[offset + 3 ] = 255; // A?
}
}
image.LoadFromPixels(WIDTH, HEIGHT, pixels);
sprite.SetImage(image);
window.Draw(sprite);
window.Display();
Hello I'm currently trying to rotate blocks that are within a square. But when I start to rotate them it starts to create weird spaces between blocks that I don't want. Could you help me to fix the problem of spaces beetween blocks? Here are some code and screenshots how does it look.
https://imgur.com/a/BLuO7FF
I have already checked if all angles and radiuses are calculated correctly and I don't see any problem there.
World.h
#pragma once
#include <SFML/Graphics.hpp>
class World
{
public:
World(sf::Vector2f Wpos);
float AngleToRadian(int angle);
void RotateWorld();
void draw(sf::RenderWindow &window);
sf::Texture tx;
sf::Sprite** Block;
sf::Vector2f Pos;
sf::Vector2i Size;
float** radius;
float** angle;
};
World.cpp
#include "World.h"
#include <SFML/Graphics.hpp>
#include <iostream>
#include <cmath>
#define PI 3.14159
World::World(sf::Vector2f Wpos)
{
Pos = Wpos;
Size = sf::Vector2i(10, 10);
Block = new sf::Sprite*[Size.y];
radius = new float*[Size.y];
angle = new float*[Size.y];
for (int i = 0; i < Size.y; i++)
{
Block[i] = new sf::Sprite[Size.x];
radius[i] = new float[Size.x];
angle[i] = new float[Size.x];
}
tx.loadFromFile("Img/Block.png");
sf::Vector2i off(Size.x * tx.getSize().x / 2, Size.y * tx.getSize().y / 2); //tx size is 32px x 32px
for (int y = 0; y < Size.y; y++)
{
for (int x = 0; x < Size.x; x++)
{
Block[y][x].setTexture(tx);
Block[y][x].setOrigin(tx.getSize().x / 2, tx.getSize().y / 2);
Block[y][x].setPosition(x*tx.getSize().x + Wpos.x - off.x + Block[y][x].getOrigin().x, y*tx.getSize().y + Wpos.y - off.y + Block[y][x].getOrigin().y);
radius[y][x] = sqrt(pow(Pos.x - Block[y][x].getPosition().x, 2) + pow(Pos.y - Block[y][x].getPosition().y, 2));
angle[y][x] = (atan2(Block[y][x].getPosition().y - Pos.y, Block[y][x].getPosition().x - Pos.x) * 180.0) / PI;
if ((atan2(Block[y][x].getPosition().y - Pos.y, Block[y][x].getPosition().x - Pos.x) * 180.0) / PI < 0)
{
angle[y][x] += 360;
}
//angle[y][x] = round(angle[y][x]);
/*radius[y][x] = round(radius[y][x]);*/
}
}
}
void World::RotateWorld()
{
float dx = 0, dy = 0;
if (sf::Keyboard::isKeyPressed(sf::Keyboard::E))
{
for (int y = 0; y < Size.y; y++)
{
for (int x = 0; x < Size.x; x++)
{
Block[y][x].rotate(1);
if (angle[y][x] >= 360)
{
angle[y][x] = 0;
}
angle[y][x]++;
dx = cos(AngleToRadian(angle[y][x])) * radius[y][x];
dy = sin(AngleToRadian(angle[y][x])) * radius[y][x];
Block[y][x].setPosition(Pos.x + dx, Pos.y + dy);
}
}
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Q))
{
for (int y = 0; y < Size.y; y++)
{
for (int x = 0; x < Size.x; x++)
{
Block[y][x].rotate(-1);
if (angle[y][x] >= 360)
{
angle[y][x] = 0;
}
angle[y][x]--;
dx = cos(AngleToRadian(angle[y][x])) * radius[y][x];
dy = sin(AngleToRadian(angle[y][x])) * radius[y][x];
Block[y][x].setPosition(Pos.x + dx, Pos.y + dy);
}
}
}
}
I expected it to rotate withouth any spaces between. I would be really thankfull if someone would help me.
I would try with setting the origin of the sf::Sprite using it's getGlobalBounds() method instead of the sf::Texture size getter.
The difference seems minor and something like that might be the case.
Block[y][x].setTexture(tx);
Block[y][x].setOrigin(Block[y][x].getGlobalBouds().width / 2, Block[y][x].getGlobalBouds().height / 2);
Block[y][x].setPosition(x*Block[y][x].getGlobalBouds().width + Wpos.x - off.x + Block[y][x].getOrigin().x, y*Block[y][x].getGlobalBouds().height + Wpos.y - off.y + Block[y][x].getOrigin().y);
Hello I'm new to C++ SFML. I'm supposed to draw some rectangles and render their AABB while rotating and I want to detect if the dimensions set for them intersect another rotating AABB rectangle. Here is what I use to detect them.
Is it enough to check it that way if theyre rotating? would i need to use stuff like the separating axis theorem? or is there a way to not need to use that if its just an AABB than an OBB
#define RECT 5
sf::RectangleShape Rect[RECT];
Rect[0].setSize(sf::Vector2f(50.0f, 50.0f));
Rect[1].setSize(sf::Vector2f(50.0f, 100.0f));
Rect[2].setSize(sf::Vector2f(60.0f, 80.0f));
Rect[3].setSize(sf::Vector2f(100.0f, 60.0f));
Rect[4].setSize(sf::Vector2f(30.0f, 250.0f));
sf::Vector2f MinPoint[RECT];
sf::Vector2f MaxPoint[RECT];
for (int x = 0; x < RECT; x++)
{
//Starting Position
Rect[x].setOrigin(Rect[x].getSize().x / 2, Rect[x].getSize().y / 2);
xpos += 150;
Rect[x].setPosition(xpos, ypos);
colcount++;
if (colcount == 3)
{
xpos = 0;
ypos += 200;
colcount = 0;
}
Rect[x].setFillColor(sf::Color::Red);
}
while (window.isOpen())
{
window.clear(sf::Color::Black);
//Drawing Shapes
for (int x = 0; x < RECT; x++)
{
window.draw(Rect[x]);
}
Rect[0].rotate(90*3.14/180);
Rect[1].rotate(12 * 3.14 / 180);
Rect[2].rotate(10 * 3.14 / 180);
Rect[3].rotate(180 * 3.14 / 180);
Rect[4].rotate(360 * 3.14 / 180);
for (int i = 0; i < RECT; i++)
{
MinPoint[i].x = Rect[i].getPosition().x - (Rect[i].getSize().x / 2);
MaxPoint[i].x = Rect[i].getPosition().x + (Rect[i].getSize().x / 2);
MinPoint[i].y = Rect[i].getPosition().y - (Rect[i].getSize().y / 2);
MaxPoint[i].y = Rect[i].getPosition().y + (Rect[i].getSize().y / 2);
}
//Collision Detection
for (int i = 0; i < RECT; i++)
{
for (int j = i + 1; j < RECT; j++)
{
if (i != j)
{
if (MaxPoint[i].x >= MinPoint[j].x && MaxPoint[j].x >= MinPoint[i].x && MaxPoint[i].y >= MinPoint[j].y && MaxPoint[j].y >= MinPoint[i].y)
{
Rect[i].setFillColor(sf::Color::Green);
Rect[j].setFillColor(sf::Color::Green);
}
}
}
}
Apparently all I needed to do was make another set of transparent rectangles with outlines that were set at the same position as my rotating rectangle boxes then set their sizes to getGlobalBounds of my rotating rectangles. the collision check would then instead be put under these transparent bounding boxes instead of the rotating rectangle itself.
#define RECT 5
sf::RectangleShape Rect[RECT];
sf::RectangleShape AABB[RECT];
Rect[0].setSize(sf::Vector2f(50.0f, 50.0f));
Rect[1].setSize(sf::Vector2f(50.0f, 100.0f));
Rect[2].setSize(sf::Vector2f(60.0f, 80.0f));
Rect[3].setSize(sf::Vector2f(100.0f, 60.0f));
Rect[4].setSize(sf::Vector2f(30.0f, 250.0f));
sf::Vector2f MinPoint[RECT];
sf::Vector2f MaxPoint[RECT];
for (int x = 0; x < RECT; x++)
{
//Starting Position
Rect[x].setOrigin(Rect[x].getSize().x / 2, Rect[x].getSize().y / 2);
AABB[x].setOrigin(AABB[x].getSize().x / 2, AABB[x].getSize().y / 2);
xpos += 150;
Rect[x].setPosition(xpos, ypos);
AABB[x].setSize(sf::Vector2f(Rect[x].getGlobalBounds().width, Rect[x].getGlobalBounds().height));
AABB[x].setPosition(Rect[x].getPosition().x, Rect[x].getPosition().y);
colcount++;
if (colcount == 3)
{
xpos = 0;
ypos += 200;
colcount = 0;
}
Rect[x].setFillColor(sf::Color::Red);
AABB[x].setFillColor(sf::Color::Transparent);
AABB[x].setOutlineThickness(1);
AABB[x].setOutlineColor(sf::Color::White);
}
while (window.isOpen())
{
window.clear(sf::Color::Black);
//Drawing Shapes
for (int x = 0; x < RECT; x++)
{
window.draw(Rect[x]);
window.draw(AABB[x]);
}
//Rotation
Rect[0].rotate(1);
Rect[1].rotate(45);
Rect[2].rotate(11.25);
Rect[3].rotate(5.625);
Rect[4].rotate(22.5);
for (int i = 0; i < RECT; i++)
{
MinPoint[i].x = AABB[i].getPosition().x - (AABB[i].getSize().x / 2);
MaxPoint[i].x = AABB[i].getPosition().x + (AABB[i].getSize().x / 2);
MinPoint[i].y = AABB[i].getPosition().y - (AABB[i].getSize().y / 2);
MaxPoint[i].y = AABB[i].getPosition().y + (AABB[i].getSize().y / 2);
AABB[i].setOrigin(AABB[i].getSize().x / 2, AABB[i].getSize().y / 2);
AABB[i].setSize(sf::Vector2f(Rect[i].getGlobalBounds().width, Rect[i].getGlobalBounds().height));
AABB[i].setPosition(Rect[i].getPosition().x, Rect[i].getPosition().y);
}
//Collision Detection
for (int i = 0; i < RECT; i++)
{
for (int j = i + 1; j < RECT; j++)
{
if (i != j)
{
if (MaxPoint[i].x >= MinPoint[j].x && MaxPoint[j].x >= MinPoint[i].x && MaxPoint[i].y >= MinPoint[j].y && MaxPoint[j].y >= MinPoint[i].y)
{
Rect[i].setFillColor(sf::Color::Green);
Rect[j].setFillColor(sf::Color::Green);
AABB[i].setOutlineColor(sf::Color::Blue);
AABB[j].setOutlineColor(sf::Color::Blue);
}
}
}
}
I'm trying to rotate an image using openFrameworks, but I have a problem. My rotated image is red instead of its original color.
void testApp::setup(){
image.loadImage("abe2.jpg");
rotatedImage.allocate(image.width, image.height, OF_IMAGE_COLOR);
imageCenterX = image.getWidth() / 2;
imageCenterY = image.getHeight() / 2;
w = image.getWidth();
h = image.getHeight();
int degrees = 180;
float radians = (degrees*(PI / 180));
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++) {
int index = image.getPixelsRef().getPixelIndex(x, y);
int newX = (cos(radians) * (x - imageCenterX) - sin(radians) * (y - imageCenterY) + imageCenterX);
int newY = (sin(radians) * (x - imageCenterX) + cos(radians) * (y - imageCenterY) + imageCenterY);
int newIndex = rotatedImage.getPixelsRef().getPixelIndex(newX, newY);
rotatedImage.getPixelsRef()[newIndex] = image.getPixelsRef()[index];
}
}
rotatedImage.update();
}
void testApp::update(){
}
void testApp::draw(){
image.draw(0,0);
rotatedImage.draw(0,400);
}
Can someone tell me what I am doing wrong?
If your image has three color components (Red, Green, Blue), you need to transform all three of those. The following should do the trick:
rotatedImage.getPixelsRef()[newIndex] = image.getPixelsRef()[index];
rotatedImage.getPixelsRef()[newIndex+1] = image.getPixelsRef()[index+1];
rotatedImage.getPixelsRef()[newIndex+2] = image.getPixelsRef()[index+2];