#ifndef MAIN #define MAIN #ifdef _WIN32 #include #include #include #endif #ifndef _WIN32 #include #include #endif #include #include "SDL.h" #include "SDL_opengl.h" #include "SDL_image.h" #include "ode.cpp" #include "thread_pool.cpp" #define _USE_MATH_DEFINES #include #include #pragma comment(lib,"SDL2.lib") #pragma comment(lib,"SDL2main.lib") static SDL_Window* window; static SDL_Renderer* renderer; #pragma comment(lib,"opengl32.lib") #pragma comment(lib,"glu32.lib") #include #pragma comment(lib, "shell32.lib") static int const screen_size[2] = { 1000, 800 }; using glf = GLfloat; using point3d = std::array; using method = RungeKutta; // для выбора численного метода достаточно изменить значение method double cameraX = 0.0; double cameraY = 0.0; const double cameraZ = 20.0; const double R = 10.0; double t; const int number_of_points = 300000; double dt = 20.0 / 100.0; // переменные для создания потоков const int number_of_cores = std::thread::hardware_concurrency(); std::vector attractors; std::array points; ThreadPool thread_pool(number_of_cores); int b_val_index = 0; // красивые формы аттрактора получаются при данных коэффициентах const int len = 5; std::array b_values = { 0.11, 0.15, 0.215, 0.265, 0.32899 }; double red = 1.0f; double blue = 1.0f; double green = 1.0f; double step_red = 0.05; double step_blue = 0.025; double step_green = 0.016; float x = 16.0; float y = 16.0; // для обеспечения работы нескольких потоков, цикл с вызовом численного метода был вынесен в отделтную ф-ию void process(method& attr, int start, int end, double dt) { for (int i = start; i < end; ++i) { attr.SetInit({ points[i].at(0), points[i].at(1), points[i].at(2) }); attr.NextStep(dt); points[i] = { static_cast(attr.getX(0)), static_cast(attr.getX(1)), static_cast(attr.getX(2)) }; } } // для каждого потока необходимо создать отдельный объект класса у которого будет вызываться численный метод, в противном случае одна и та же точка может просчитываться в разных потоках void inittializeAttractors() { for (size_t i = 0; i < number_of_cores; ++i) attractors.push_back(method()); } Uint32 callback(Uint32 interval, void* name) { //Задаем с период вращения камеры std::chrono::time_point tp = std::chrono::high_resolution_clock::now(); std::chrono::duration dur = tp.time_since_epoch(); t = dur.count(); cameraX = R * cos((2 * M_PI) / (60.0 * 1000.0) * (t / 1000.0)); cameraY = R * sin((2 * M_PI) / (60.0 * 1000.0) * (t / 1000.0)); // ф-ия enqueue класса ThreadPool отвечает за пробуждение потока при появлении нового запроса на просчет координат точки for (size_t i = 0; i < number_of_cores; ++i) { thread_pool.enqueue([i] { process(std::ref(attractors[i]), i * number_of_points / number_of_cores, (i + 1) * number_of_points / number_of_cores, dt); }); } return interval; } Uint32 colorChange(Uint32 interval, void* name) { red += step_red; blue += step_blue; green += step_green; step_red = (red >= 1 || red <= 0) ? -step_red : step_red; step_blue = (blue >= 1 || blue <= 0) ? -step_blue : step_blue; step_green = (green >= 1 || green <= 0) ? -step_green : step_green; return interval; } static bool get_input(void) { SDL_Event event; while (SDL_PollEvent(&event)) { switch (event.type) { case SDL_QUIT: return false; //The little X in the window got pressed case SDL_KEYDOWN: if (event.key.keysym.sym == SDLK_ESCAPE) { return false; } if (event.key.keysym.sym == SDLK_w) { b_val_index = (b_val_index + 1) % len; for (size_t i = 0; i < number_of_cores; ++i) attractors[i].b = b_values[b_val_index]; } if (event.key.keysym.sym == SDLK_s) { b_val_index = (b_val_index - 1) % len >= 0 ? (b_val_index - 1) % len : len + (b_val_index - 1) % len; for (size_t i = 0; i < number_of_cores; ++i) attractors[i].b = b_values[b_val_index]; } std::cout << b_values[b_val_index] << '\n'; break; } } return true; } static void draw(void) { //Clear the screen's color and depth (default color is black, but can change with glClearColor(...)) glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //Drawing to an area starting at the bottom left, screen_size[0] wide, and screen_size[1] high. glViewport(0, 0, screen_size[0], screen_size[1]); //OpenGL is a state machine. Tell it that future commands changing the matrix are to change OpenGL's projection matrix glMatrixMode(GL_PROJECTION); //Reset the projection matrix glLoadIdentity(); //Multiply a perspective projection matrix into OpenGL's projection matrix gluPerspective(45.0, (double)(screen_size[0]) / (double)(screen_size[1]), 0.1, 100.0); //Tell OpenGL that future commands changing the matrix are to change the modelview matrix glMatrixMode(GL_MODELVIEW); //Reset the modelview matrix glLoadIdentity(); //Multiply OpenGL's modelview matrix with a transform matrix that simulates a camera at (2,3,4) looking towards the location (0,0,0) with up defined to be (0,1,0) gluLookAt(cameraX, cameraY, cameraZ, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0); //gluLookAt(-20.0f, -15.0f, 20.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glColor3f(red, blue, green); // Set the color to white //int ind = 6; //glColor3f(colors[ind].at(0), colors[ind].at(1), colors[ind].at(2)); // Set the color to white glBegin(GL_POINTS); for (size_t i = 0; i < number_of_points; i++) { glVertex3f(points[i].at(0), points[i].at(1), points[i].at(2)); } glEnd(); //OpenGL works best double-buffered. SDL automatically sets that up for us. This will draw what we have // just drawn to the screen so that we can see it. SDL_GL_SwapWindow(window); } int main(int argc, char* argv[]) { //TTF_Init(); inittializeAttractors(); //считываем заготовленные точки из файла std::ifstream in("prep.txt"); if (in.is_open()) { glf x, y, z; int check = 0; while (in >> x >> y >> z && check < number_of_points) { points[check] = { x, y, z }; check++; } } in.close(); //Initialize everything, but don't catch fatal signals; give them to the OS. SDL_Init(SDL_INIT_EVERYTHING | SDL_INIT_NOPARACHUTE); //Creates the window window = SDL_CreateWindow("Thomas' cyclically symmetric attractor", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, screen_size[0], screen_size[1], SDL_WINDOW_OPENGL | SDL_WINDOW_ALLOW_HIGHDPI); //Create an OpenGL context. In SDL 1, this was done automatically. SDL_GLContext context = SDL_GL_CreateContext(window); //We now have an OpenGL context, and can call OpenGL functions. //Objects need to test each other to see which one is in front. If you don't do this, you'll "see through" things! glEnable(GL_DEPTH_TEST); SDL_TimerID timerID = SDL_AddTimer(20, callback, const_cast("SDL")); SDL_TimerID timerID_color = SDL_AddTimer(1000, colorChange, const_cast("SDL")); // Clean up //Main application loop while (true) { if (!get_input()) break; draw(); } //Мы можем отрисовать наш текст, как и любую другую текстуру, так как //он был отрендерен в текстуру SDL_RemoveTimer(timerID); SDL_RemoveTimer(timerID_color); //Clean up SDL_GL_DeleteContext(context); SDL_DestroyWindow(window); //TTF_Quit(); SDL_Quit(); //Return success; program exits return 0; } #endif // !MAIN