diff --git a/Makefile b/Makefile index 9d73d4b..4193dae 100644 --- a/Makefile +++ b/Makefile @@ -12,8 +12,8 @@ ifeq ($(OS),Windows_NT) RM := del /S /Q DIR_DUP = if not exist "$(@D)" mkdir "$(@D)" CC := g++ -O3 - IFLAGS := -I./includes -I./includes/RT -I./includes/imgui - LDFLAGS := -L./lib -lglfw3 -lopengl32 -lgdi32 -lcglm + IFLAGS := -I./includes -I./includes/RT -I./includes/imgui -I"D:/ffmpeg/include" + LDFLAGS := -L./lib -L"D:/ffmpeg/lib" -lglfw3 -lopengl32 -lgdi32 -lcglm -lavformat -lavcodec -lavutil -lswscale -lswresample else BLACK = \033[30;49;3m RED = \033[31;49;3m @@ -29,8 +29,8 @@ else DIR_DUP = mkdir -p $(@D) CC := clang++ CFLAGS := -Wall -Wextra -Werror -g -O3 - IFLAGS := -I./includes -I./includes/RT -I./includes/imgui -I/usr/include - LDFLAGS := -L/usr/lib/x86_64-linux-gnu -lglfw -lGL -lGLU -lX11 -lpthread -ldl -lstdc++ + IFLAGS := -I./includes -I./includes/RT -I./includes/imgui + LDFLAGS += -lglfw -lGL -lGLU -lX11 -lpthread -ldl -lavformat -lavcodec -lavutil -lswscale -lswresample FILE = $(shell ls -lR srcs/ | grep -F .c | wc -l) CMP = 1 endif @@ -55,25 +55,26 @@ ALL_SRCS := $(IMGUI_SRCS) \ class/SceneParser.cpp \ class/ObjParser.cpp \ class/BVH.cpp \ + class/Renderer.cpp \ SRCS := $(ALL_SRCS:%=$(SRCS_DIR)/%) OBJS := $(addprefix $(OBJS_DIR)/, $(SRCS:%.cpp=%.o)) HEADERS := includes/RT.hpp MAKEFLAGS += --no-print-directory -ifeq ($(OS),Windows_NT) -all: windows -else -all: linux -endif +all: $(NAME) -windows: $(OBJS) $(HEADERS) +ifeq ($(OS),Windows_NT) +$(NAME): $(OBJS) $(HEADERS) @$(CC) $(OBJS) $(IFLAGS) $(LDFLAGS) -o $(NAME) @echo $(WHITE) $(NAME): PROJECT COMPILED !$(RESET) - -linux: $(OBJS) $(HEADERS) +else +$(NAME): $(OBJS) $(HEADERS) @$(CC) $(OBJS) $(IFLAGS) $(CFLAGS) $(LDFLAGS) -o $(NAME) @printf "$(LINE_CLR)$(WHITE) $(NAME): PROJECT COMPILED !$(RESET)\n\n" +endif + + $(OBJS_DIR)/%.o: %.cpp @$(DIR_DUP) @@ -108,10 +109,6 @@ else @$(RM) $(OBJS_DIR) endif -ifeq ($(OS),Windows_NT) -re: fclean windows -else -re: fclean linux -endif +re: fclean $(NAME) .PHONY: all clean fclean re windows linux diff --git a/imgui.ini b/imgui.ini index 92379a4..3138f80 100644 --- a/imgui.ini +++ b/imgui.ini @@ -22,3 +22,7 @@ Size=260,143 Pos=1644,648 Size=274,131 +[Window][Renderer] +Pos=374,0 +Size=284,382 + diff --git a/includes/RT.hpp b/includes/RT.hpp index bfde952..b9a2dfa 100644 --- a/includes/RT.hpp +++ b/includes/RT.hpp @@ -6,7 +6,7 @@ /* By: ycontre +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2024/09/27 14:52:10 by TheRed #+# #+# */ -/* Updated: 2025/01/16 15:02:34 by tomoron ### ########.fr */ +/* Updated: 2025/01/22 16:37:32 by tomoron ### ########.fr */ /* */ /* ************************************************************************** */ @@ -57,6 +57,7 @@ struct Vertex { # include "objects/Cylinder.hpp" # include "Camera.hpp" +# include "Renderer.hpp" # include "Window.hpp" # include "Shader.hpp" # include "Scene.hpp" diff --git a/includes/RT/Renderer.hpp b/includes/RT/Renderer.hpp new file mode 100644 index 0000000..e180dc3 --- /dev/null +++ b/includes/RT/Renderer.hpp @@ -0,0 +1,91 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* Renderer.hpp :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: tomoron +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/01/22 16:29:26 by tomoron #+# #+# */ +/* Updated: 2025/01/30 22:15:55 by tomoron ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#ifndef RENDERER_HPP +# define RENDERER_HPP + +# include "RT.hpp" +extern "C" { + #include + #include + #include + #include + #include +} + +class Scene; +class Window; +class Shader; + +typedef struct s_pathPoint +{ + glm::vec3 pos; + glm::vec2 dir; + double time; +} t_pathPoint; + +class Renderer +{ + public: + Renderer(Scene *scene, Window *win); + void renderImgui(void); + void update(Shader &shader); + int rendering(void) const; + + private: + void addPoint(float time); + void makeMovement(float timeFromStart, float curSplitTimeReset); + void initRender(); + void addImageToRender(Shader &shader); + void endRender(void); + void imguiPathCreation(void); + void imguiRenderInfo(void); + std::string floatToTime(float timef); + glm::vec2 bezierSphereInterpolate(glm::vec4 control, glm::vec2 from, glm::vec2 to, float time); + void updateAvailableCodecs(void); + void fillGoodCodecList(std::vector &lst); + glm::vec3 hermiteInterpolate(glm::vec3 points[4], double alpha); + + int _min; + int _sec; + int _samples; + int _testSamples; + bool _autoTime; + int _fps; + char _filenameBuffer[512]; + std::vector _path; + std::string _outputFilename; + Scene *_scene; + Window *_win; + std::vector _codecList; + std::vector _codecListStr; + int _codecIndex; + + int _curPathIndex; + int _destPathIndex; + double _curSplitStart; + int _curSamples; + int _testMode; + long int _frameCount; + float _renderStartTime; + + AVFormatContext *_format; + AVCodecContext *_codec_context; + AVFrame *_rgb_frame; + AVFrame *_yuv_frame; + SwsContext *_sws_context; + AVStream *_stream; + AVDictionary *_codecOptions; + +}; + +#endif diff --git a/includes/RT/Shader.hpp b/includes/RT/Shader.hpp index 1ea0f9c..59a9afe 100644 --- a/includes/RT/Shader.hpp +++ b/includes/RT/Shader.hpp @@ -6,7 +6,7 @@ /* By: ycontre +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2024/10/13 18:10:10 by TheRed #+# #+# */ -/* Updated: 2025/01/30 17:52:20 by ycontre ### ########.fr */ +/* Updated: 2025/01/30 22:27:43 by tomoron ### ########.fr */ /* */ /* ************************************************************************** */ @@ -37,6 +37,8 @@ class Shader GLuint getProgram(void) const; GLuint getProgramCompute(void) const; + + std::vector getOutputImage(void); private: @@ -57,4 +59,4 @@ class Shader void checkCompileErrors(unsigned int shader); }; -#endif \ No newline at end of file +#endif diff --git a/includes/RT/Window.hpp b/includes/RT/Window.hpp index 803f58f..72cbb6a 100644 --- a/includes/RT/Window.hpp +++ b/includes/RT/Window.hpp @@ -6,7 +6,7 @@ /* By: ycontre +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2024/10/13 16:15:41 by TheRed #+# #+# */ -/* Updated: 2025/01/11 16:14:11 by tomoron ### ########.fr */ +/* Updated: 2025/01/25 03:09:23 by tomoron ### ########.fr */ /* */ /* ************************************************************************** */ @@ -26,7 +26,7 @@ class Window void display(); void pollEvents(); bool shouldClose(); - + void process_input(); static void keyCallback(GLFWwindow *window, int key, int scancode, int action, int mods); @@ -44,9 +44,13 @@ class Window bool &getAccumulate(void); + void setFrameCount(int nb); + + void rendererUpdate(Shader &shader); private: GLFWwindow *_window; Scene *_scene; + Renderer *_renderer; float _fps; float _delta; diff --git a/scenes/test.rt b/scenes/test.rt index 30edc1c..6e4ebcb 100644 --- a/scenes/test.rt +++ b/scenes/test.rt @@ -4,4 +4,4 @@ MAT 50 50 50 0. 0. 0. // 0 gray pl 0 0 0 0 1 0 0 // floor OBJ obj/jinx.obj 0 1.5 0 -# OBJ obj/sponza.obj 0 5 0 0.01 \ No newline at end of file +# OBJ obj/sponza.obj 0 5 0 0.01 diff --git a/shell.nix b/shell.nix index 117df0c..016fa2c 100644 --- a/shell.nix +++ b/shell.nix @@ -1,5 +1,6 @@ { pkgs ? import {} }: pkgs.mkShell { - nativeBuildInputs = with pkgs; [ libGL xorg.libX11 libGLU glfw]; + nativeBuildInputs = with pkgs; [ libGL xorg.libX11 libGLU glfw ffmpeg]; + shellHook="make -j"; } diff --git a/srcs/RT.cpp b/srcs/RT.cpp index 4153917..679c94f 100644 --- a/srcs/RT.cpp +++ b/srcs/RT.cpp @@ -6,7 +6,7 @@ /* By: ycontre +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2024/09/27 14:51:49 by TheRed #+# #+# */ -/* Updated: 2025/01/30 18:02:33 by ycontre ### ########.fr */ +/* Updated: 2025/01/30 22:29:00 by tomoron ### ########.fr */ /* */ /* ************************************************************************** */ @@ -125,8 +125,10 @@ int main(int argc, char **argv) std::vector gpu_lights_array(gpu_lights.begin(), gpu_lights.end()); glBindBuffer(GL_SHADER_STORAGE_BUFFER, lightSSBO); glBufferSubData(GL_SHADER_STORAGE_BUFFER, 0, gpu_lights_array.size() * sizeof(int), gpu_lights_array.data()); + window.rendererUpdate(shader); GPUCamera camera_data = scene.getCamera()->getGPUData(); + glBindBuffer(GL_UNIFORM_BUFFER, cameraUBO); glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(GPUCamera), &camera_data); @@ -159,7 +161,7 @@ int main(int argc, char **argv) window.imGuiRender(); window.display(); - window.pollEvents(); + window.pollEvents(); } ImGui_ImplOpenGL3_Shutdown(); diff --git a/srcs/class/Camera.cpp b/srcs/class/Camera.cpp index 065955e..aaab1b0 100644 --- a/srcs/class/Camera.cpp +++ b/srcs/class/Camera.cpp @@ -6,7 +6,7 @@ /* By: ycontre +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2024/10/15 14:00:38 by TheRed #+# #+# */ -/* Updated: 2025/01/15 19:32:41 by ycontre ### ########.fr */ +/* Updated: 2025/01/28 02:15:41 by tomoron ### ########.fr */ /* */ /* ************************************************************************** */ @@ -59,6 +59,10 @@ void Camera::processMouse(float xoffset, float yoffset, bool constraint_pitch = _yaw += xoffset * _sensitivity; _pitch += yoffset * _sensitivity; +// while(_yaw < 0) +// _yaw += 360; +// while(_yaw > 360) +// _yaw -= 360; if (constraint_pitch) { if (_pitch > 89.0f) _pitch = 89.0f; @@ -150,6 +154,7 @@ void Camera::setDirection(float pitch, float yaw) { _pitch = pitch; _yaw = yaw; + updateCameraVectors(); } void Camera::setDOV(float aperture, float focus) @@ -166,4 +171,4 @@ void Camera::setBounce(int bounce) void Camera::setFov(float fov) { _fov = fov; -} \ No newline at end of file +} diff --git a/srcs/class/Renderer.cpp b/srcs/class/Renderer.cpp new file mode 100644 index 0000000..543ddfc --- /dev/null +++ b/srcs/class/Renderer.cpp @@ -0,0 +1,630 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* Renderer.cpp :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: tomoron +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/01/22 16:34:53 by tomoron #+# #+# */ +/* Updated: 2025/01/30 22:22:07 by tomoron ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "RT.hpp" + +Renderer::Renderer(Scene *scene, Window *win) +{ + _scene = scene; + _win = win; + _min = 0; + _sec = 0; + _fps = 30; + _autoTime = 0; + _samples = 1; + _testSamples = 1; + _curSamples = 0; + _destPathIndex = 0; + _frameCount = 0; + _outputFilename = "output.avi"; + memcpy(_filenameBuffer, _outputFilename.c_str(), _outputFilename.length()); + _filenameBuffer[_outputFilename.length()] = 0; + + _rgb_frame = 0; + _yuv_frame = 0; + _format = 0; + _codec_context = 0; + updateAvailableCodecs(); +} + +void Renderer::fillGoodCodecList(std::vector &lst) +{ + lst.push_back(AV_CODEC_ID_FFV1); + lst.push_back(AV_CODEC_ID_H264); + lst.push_back(AV_CODEC_ID_HUFFYUV); + lst.push_back(AV_CODEC_ID_UTVIDEO); + lst.push_back(AV_CODEC_ID_PRORES); + lst.push_back(AV_CODEC_ID_V210); +} + +void Renderer::updateAvailableCodecs(void) +{ + const AVCodec *codec; + AVCodecContext *ctx; + const AVOutputFormat *muxer; + const char *format; + std::vector goodCodecList; + + fillGoodCodecList(goodCodecList); + _codecList.clear(); + _codecListStr.clear(); + _codecIndex = 0; + av_log_set_level(AV_LOG_QUIET); + format = _outputFilename.c_str(); + if(_outputFilename.find(".") != std::string::npos) + format += _outputFilename.find(".") + 1; + + muxer = av_guess_format(format, 0, 0); + for(std::vector::iterator it = goodCodecList.begin(); it != goodCodecList.end(); it++) + { + codec = avcodec_find_encoder(*it); + if(!codec) + continue; + ctx = avcodec_alloc_context3(codec); + if(ctx) + { + if (avformat_query_codec(muxer, codec->id, FF_COMPLIANCE_STRICT) > 0) + { + ctx->width = WIDTH; + ctx->height = HEIGHT; + ctx->time_base = {1, _fps}; + ctx->framerate = {_fps, 1}; + ctx->pix_fmt = AV_PIX_FMT_YUV420P; + ctx->gop_size = 10; + ctx->max_b_frames = 1; + if (avcodec_open2(ctx, codec, NULL) == 0) + _codecList.push_back(codec); + } + avcodec_free_context(&ctx); + } + } + for(auto it = _codecList.begin(); it != _codecList.end(); it++) + { + _codecListStr.push_back((*it)->name); + } +} + +void Renderer::initRender(void) +{ + + _codecOptions = 0; + _destPathIndex = _path.size() - 1; + _curPathIndex = 0; + _frameCount = 0; + _curSamples = 0; + _curSplitStart = 0; + _testMode = 0; + _renderStartTime = glfwGetTime(); + _scene->getCamera()->setPosition(_path[0].pos); + _scene->getCamera()->setDirection(_path[0].dir.x, _path[0].dir.y); + _win->setFrameCount(-1); + avformat_alloc_output_context2(&_format, nullptr, nullptr, _outputFilename.c_str()); + + _codec_context = avcodec_alloc_context3(_codecList[_codecIndex]); + _codec_context->width = WIDTH; + _codec_context->height = HEIGHT; + _codec_context->time_base = {1, _fps}; + _codec_context->framerate = {_fps, 1}; + _codec_context->pix_fmt = AV_PIX_FMT_YUV420P; + _codec_context->gop_size = 10; + _codec_context->max_b_frames = 1; + if(_codecList[_codecIndex]->id == AV_CODEC_ID_H264 || _codecList[_codecIndex]->id == AV_CODEC_ID_HEVC) + av_dict_set(&_codecOptions, "crf", "0", 0); + + if (_format->oformat->flags & AVFMT_GLOBALHEADER) + _codec_context->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; + + if (avcodec_open2(_codec_context, _codecList[_codecIndex], &_codecOptions) < 0) + throw std::runtime_error("Failed to open codec"); + + _stream = avformat_new_stream(_format, _codecList[_codecIndex]); + if (!_stream) + throw std::runtime_error("Failed to create stream"); + _stream->time_base = _codec_context->time_base; + avcodec_parameters_from_context(_stream->codecpar, _codec_context); + + if (!(_format->flags & AVFMT_NOFILE)) + { + if (avio_open(&_format->pb, _outputFilename.c_str(), AVIO_FLAG_WRITE) < 0) + throw std::runtime_error("couldn't open " + _outputFilename); + } + (void)avformat_write_header(_format, nullptr); + + _rgb_frame = av_frame_alloc(); + _rgb_frame->format = AV_PIX_FMT_RGB24; + _rgb_frame->width = WIDTH; + _rgb_frame->height = HEIGHT; + av_image_alloc(_rgb_frame->data, _rgb_frame->linesize, WIDTH, HEIGHT, AV_PIX_FMT_RGB24, 32); + + _yuv_frame = av_frame_alloc(); + _yuv_frame->format = _codec_context->pix_fmt; + _yuv_frame->width = WIDTH; + _yuv_frame->height = HEIGHT; + av_image_alloc(_yuv_frame->data, _yuv_frame->linesize, WIDTH, HEIGHT, _codec_context->pix_fmt, 32); + + _sws_context = sws_getContext( + WIDTH, HEIGHT, AV_PIX_FMT_RGB24, + WIDTH, HEIGHT, AV_PIX_FMT_YUV420P, + SWS_BILINEAR, nullptr, nullptr, nullptr); +} + +void Renderer::addImageToRender(Shader &shader) +{ + std::vector image; + AVPacket *pkt; + long int videoFrameOffset; + long int outputImageOffset; + + image = shader.getOutputImage(); + + for (int x = 0; x < WIDTH; x++) + { + for(int y = 0; y < HEIGHT; y++) + { + videoFrameOffset = (y * _rgb_frame->linesize[0]) + (x * 3); + outputImageOffset = (((HEIGHT - 1) - y) * (WIDTH * 4)) + (x * 4); + glm::vec3 colors(image[outputImageOffset], image[outputImageOffset + 1], image[outputImageOffset + 2]); + // if(colors.x > 1 || colors.y > 1 || colors.z > 1) + // colors = glm::normalize(colors); + colors.x = fmin(colors.x, 1); + colors.y = fmin(colors.y, 1); + colors.z = fmin(colors.z, 1); + _rgb_frame->data[0][videoFrameOffset] = colors.x * 255; + _rgb_frame->data[0][videoFrameOffset + 1] = colors.y * 255; + _rgb_frame->data[0][videoFrameOffset + 2] = colors.z * 255; + } + } + sws_scale(_sws_context, _rgb_frame->data, _rgb_frame->linesize, 0, HEIGHT, _yuv_frame->data, _yuv_frame->linesize); + _yuv_frame->pts = _frameCount; + + if (avcodec_send_frame(_codec_context, _yuv_frame) == 0) { + pkt = av_packet_alloc(); + while (avcodec_receive_packet(_codec_context, pkt) == 0) { + pkt->stream_index = _stream->index; + pkt->pts = av_rescale_q(pkt->pts, _codec_context->time_base, _stream->time_base); + pkt->dts = av_rescale_q(pkt->dts, _codec_context->time_base, _stream->time_base); + av_interleaved_write_frame(_format, pkt); + av_packet_unref(pkt); + } + av_packet_free(&pkt); + } +} + +void Renderer::endRender(void) +{ + AVPacket *pkt; + + avcodec_send_frame(_codec_context, 0); + pkt = av_packet_alloc(); + while (avcodec_receive_packet(_codec_context, pkt) == 0) { + pkt->stream_index = _stream->index; + pkt->pts = av_rescale_q(pkt->pts, _codec_context->time_base, _stream->time_base); + pkt->dts = av_rescale_q(pkt->dts, _codec_context->time_base, _stream->time_base); + av_interleaved_write_frame(_format, pkt); + av_packet_unref(pkt); + } + av_packet_free(&pkt); + + av_write_trailer(_format); + av_frame_free(&_rgb_frame); + av_frame_free(&_yuv_frame); + avcodec_free_context(&_codec_context); + avio_close(_format->pb); + avformat_free_context(_format); + av_dict_free(&_codecOptions); + + _format = 0; + _rgb_frame = 0; + _yuv_frame = 0; + _codec_context = 0; +} + +void Renderer::addPoint(float time) +{ + t_pathPoint newPoint; + Camera *cam; + std::vector::iterator pos; + + cam = _scene->getCamera(); + newPoint.pos = cam->getPosition(); + newPoint.dir = cam->getDirection(); + newPoint.time = time; + pos = _path.begin(); + while(pos != _path.end() && pos->time <= newPoint.time) + pos++; + _path.insert(pos, newPoint); +} + +void Renderer::update(Shader &shader) +{ + double curTime; + + (void)shader; + if(!_destPathIndex) + return; + + _curSamples++; + if((_testMode && _curSamples < _testSamples) || (!_testMode && _curSamples < _samples)) + return; + + if(_testMode) + curTime = glfwGetTime(); + else + curTime = (1 / (double)_fps) * (double)_frameCount; + + if(!_testMode) + { + addImageToRender(shader); + _frameCount++; + } + makeMovement(curTime - _curSplitStart, curTime); + _curSamples = 0; +} + +glm::vec3 Renderer::hermiteInterpolate(glm::vec3 points[4], double alpha) +{ + double tension; + double bias; + glm::vec3 tang[2]; + double alphaSqr[2]; + glm::vec3 coef[4]; + + tension = 0; + bias = 0; + + alphaSqr[0] = alpha * alpha; + alphaSqr[1] = alphaSqr[0] * alpha; + + tang[0] = (points[1] - points[0]) * glm::vec3(1 + bias) * glm::vec3(1 - tension) / glm::vec3(2); + tang[0] += (points[2] - points[1]) * glm::vec3(1 - bias) * glm::vec3(1 - tension) / glm::vec3(2); + tang[1] = (points[2] - points[1]) * glm::vec3(1 + bias) * glm::vec3(1 - tension) / glm::vec3(2); + tang[1] += (points[3] - points[2]) * glm::vec3(1 - bias) * glm::vec3(1 - tension) / glm::vec3(2); + + coef[0] = glm::vec3(2 * alphaSqr[1] - 3 * alphaSqr[0] + 1); + coef[1] = glm::vec3(alphaSqr[1] - 2 * alphaSqr[0] + alpha); + coef[2] = glm::vec3(alphaSqr[1] - alphaSqr[0]); + coef[3] = glm::vec3(-2 * alphaSqr[1] + 3 * alphaSqr[0]); + + return(coef[0] * points[1] + coef[1] * tang[0] + coef[2] * tang[1] + coef[3] * points[2]); +} + +//glm::quat eulerToQuaternion(float pitch, float yaw) +//{ +// glm::quat qPitch = glm::angleAxis(glm::radians(pitch), glm::vec3(1, 0, 0)); +// glm::quat qYaw = glm::angleAxis(glm::radians(yaw), glm::vec3(0, 1, 0)); +// +// glm::quat result = qYaw* qPitch; +// return(result); +//} + +//glm::vec2 Renderer::sphereInterpolate(glm::vec2 from, glm::vec2 to, float time) // gud but bad +//{ +// glm::vec3 eulerRes; +// glm::quat qFrom; +// glm::quat qTo; +// glm::quat res; +// float angle; +// float dot; +// +// qFrom = glm::normalize(eulerToQuaternion(from.y, from.x)); +// qTo = glm::normalize(eulerToQuaternion(to.y, to.x)); +// +// dot = glm::dot(qFrom, qTo); +// if(dot < 0) +// to = -to; +// angle = 2 * glm::acos(dot); +// res = (glm::sin((1 - time) * angle / glm::sin(angle)) * qFrom) + ((glm::sin(time * angle) / glm::sin(angle)) * qTo); +// eulerRes = glm::degrees(glm::eulerAngles(res)); +// return(glm::vec2(eulerRes.y, eulerRes.x)); +//} + + +glm::vec2 Renderer::bezierSphereInterpolate(glm::vec4 control, glm::vec2 from, glm::vec2 to, float time) +{ + glm::vec2 delta; + glm::vec2 p1, p2; + float t; + + p1 = glm::vec2(control.x, control.y); + p2 = glm::vec2(control.z, control.w); + t = time; + for(int i = 0; i < 5; i++) { + float currentX = 3.0f * ((1 - t) * (1 - t)) * t * p1.x + 3.0f * (1 - t) * (t * t) * p2.x + (t * t * t); + + if(abs(currentX - time) < 0.00001f) { + break; + } + + glm::vec2 derivative = glm::vec2( + 3.0f * (1 - t) * (1 - t) * p1.x + + 6.0f * (1.0f - t) * t * (p2.x - p1.x) + + 3.0f * t * t * (1.0f - p2.x), + + 3.0f * (1 - t) * (1 - t) * p1.y + + 6.0f * (1.0f - t) * t * (p2.y - p1.y) + + 3.0f * t * t * (1.0f - p2.y) + ); + + if(abs(derivative.x) > 0.00001f){ + t = t - (currentX - time) / derivative.x; + t = glm::clamp(t, 0.0f, 1.0f); + } + } + t = 3.0f * ((1 - t) * (1 - t)) * t * p1.y + 3.0f * (1 - t) * (t * t) * p2.y + (t * t * t); + + delta = to - from; + return(from + glm::vec2(delta.x * t, delta.y * t)); +} + + +void Renderer::makeMovement(float timeFromStart, float curSplitTimeReset) +{ + t_pathPoint from; + t_pathPoint to; + t_pathPoint prev; + t_pathPoint next; + float pathTime; + Camera *cam; + glm::vec3 pos; + glm::vec2 dir; + float normalTime; + bool smallDistPrev; + bool smallDistNext; + glm::vec4 bezierControl; + + from = _path[_curPathIndex]; + to = _path[_curPathIndex + 1]; + if(_curPathIndex) + prev = _path[_curPathIndex - 1]; + else + prev = from; + if((size_t)_curPathIndex + 2 < _path.size()) + next = _path[_curPathIndex + 2]; + else + next = to; + + cam = _scene->getCamera(); + pathTime = (to.time - from.time) * 60; + normalTime = 1 - ((pathTime - timeFromStart) / pathTime); + + pos = hermiteInterpolate((glm::vec3 [4]){prev.pos, from.pos, to.pos, next.pos}, normalTime); + + smallDistPrev = glm::distance((to.dir - from.dir) / glm::vec2(pathTime), (from.dir - prev.dir) / glm::vec2((from.time - prev.time) * 60)) < 40; + smallDistNext = glm::distance((to.dir - from.dir) / glm::vec2(pathTime), (next.dir - to.dir) / glm::vec2((next.time - to.time) * 60)) < 40; + bezierControl.x = 0.2f; + bezierControl.y = !_curPathIndex || smallDistPrev ? .1f : .0f; + bezierControl.z = 0.8f; + bezierControl.w = (size_t)_curPathIndex + 2 >= _path.size() || smallDistNext ? .9f : 1.0f; + dir = bezierSphereInterpolate(bezierControl, from.dir, to.dir, normalTime); + if(std::isnan(dir.x) || std::isnan(dir.y)) + dir = from.dir; + if(timeFromStart >= pathTime) + { + pos = to.pos; + dir = to.dir; + _curSplitStart = curSplitTimeReset; + _curPathIndex++; + } + cam->setPosition(pos); + cam->setDirection(dir.x, dir.y); + _win->setFrameCount(0); + if(_curPathIndex == _destPathIndex) + { + _destPathIndex = 0; + if(!_testMode) + endRender(); + } +} + +int Renderer::rendering(void) const +{ + return(_destPathIndex != 0 && !_testMode); +} + +void Renderer::imguiPathCreation(void) +{ + float prevSpeed; + float time; + + if(_path.size() > 1) + prevSpeed = glm::distance(_path[_path.size() - 2].pos, _path[_path.size() - 1].pos) / (_path[_path.size() - 1].time - _path[_path.size() - 2].time); + else + prevSpeed = 0; + + ImGui::SliderInt("test spi", &_testSamples, 1, 10); + ImGui::SliderInt("render spi", &_samples, 1, 1000); + ImGui::SliderInt("render fps", &_fps, 30, 120); + + ImGui::Combo("codec", &_codecIndex, _codecListStr.data(), _codecListStr.size()); + if(ImGui::InputText("file name", _filenameBuffer, 512)) + { + _outputFilename = _filenameBuffer; + updateAvailableCodecs(); + } + if(_path.size() > 1 && ImGui::Button("try full path")) + { + _scene->getCamera()->setPosition(_path[0].pos); + _scene->getCamera()->setDirection(_path[0].dir.x, _path[0].dir.y); + _win->setFrameCount(-1); + _curSplitStart = glfwGetTime(); + _curPathIndex = 0; + _destPathIndex = _path.size() - 1; + _testMode = 1; + } + if(_path.size() > 1 && _codecList.size() && ImGui::Button("start render")) + initRender(); + + ImGui::Separator(); + + if(ImGui::SliderInt("minutes", &_min, 0, 2)) + _autoTime = 0; + if(ImGui::SliderInt("seconds", &_sec, 0, 60)) + _autoTime = 0; + if(_autoTime) + { + if(_path.size() > 1) + time = _path[_path.size() - 1].time + (glm::distance(_path[_path.size() - 1].pos, _scene->getCamera()->getPosition()) / prevSpeed); + else + time = (float)_path.size() / 60; + _min = time; + _sec = (time - (int)time) * 60; + } + else + time = (float)_min + ((float)_sec / 60); + ImGui::Checkbox("guess time automatically", &_autoTime); + if(ImGui::Button("add step")) + addPoint(time); + + ImGui::Separator(); + + for(unsigned long i = 0; i < _path.size(); i++) + { + ImGui::Text("pos : %f, %f, %f",_path[i].pos.x, _path[i].pos.y, _path[i].pos.z); + ImGui::Text("dir : %f, %f",_path[i].dir.x, _path[i].dir.y); + ImGui::Text("time : %dm%ds", (int)_path[i].time, (int)(((_path[i].time - (int)_path[i].time)) * 60)); + + if(ImGui::Button(("delete##" + std::to_string(i)).c_str())) + { + _path.erase(_path.begin() + i); + } + + ImGui::SameLine(); + if(ImGui::Button(("go to pos##" + std::to_string(i)).c_str())) + { + _scene->getCamera()->setPosition(_path[i].pos); + _scene->getCamera()->setDirection(_path[i].dir.x, _path[i].dir.y); + _win->setFrameCount(-1); + } + + ImGui::SameLine(); + if(ImGui::Button(("edit pos##" + std::to_string(i)).c_str())) + { + _path[i].pos = _scene->getCamera()->getPosition(); + _path[i].dir = _scene->getCamera()->getDirection(); + } + + if(i) + ImGui::SameLine(); + if(i && ImGui::Button(("test split##" + std::to_string(i)).c_str())) + { + _scene->getCamera()->setPosition(_path[i].pos); + _scene->getCamera()->setDirection(_path[i].dir.x, _path[i].dir.y); + _win->setFrameCount(-1); + _curSplitStart = glfwGetTime(); + _curPathIndex = i - 1; + _destPathIndex = i; + _testMode = 1; + } + ImGui::Separator(); + } +} + +std::string Renderer::floatToTime(float timef) +{ + std::string res; + uint64_t time; + uint64_t values[7]; + int firstValue; + + time = timef; + values[0] = time / 3600 * 24 * 365; + time = time % (3600 * 24 * 365); + values[1] = time / 3600 * 24 * 30; + time = time % (3600 * 24 * 30); + values[2] = time / 3600 * 24 * 7; + time = time % (3600 * 24 * 7); + values[3] = time / 3600 * 24; + time = time % (3600 * 24); + values[4] = time / 3600; + time = time % 3600; + values[5] = time / 60; + time = time % 60; + values[6] = time; + + firstValue = 0; + while(firstValue < 6 && values[firstValue] == 0 ) + firstValue++; + + res = ""; + switch(firstValue) + { + case 0: + res += std::to_string(values[0]); + res += "Y"; + case 1: + res += std::to_string(values[1]); + res += "M"; + case 2: + res += std::to_string(values[2]); + res += "W"; + case 3: + res += std::to_string(values[3]); + res += "d"; + case 4: + res += std::to_string(values[4]); + res += "h"; + case 5: + res += std::to_string(values[5]); + res += "m"; + case 6: + res += std::to_string(values[6]); + res += "s"; + } + return(res); +} + +void Renderer::imguiRenderInfo(void) +{ + long int totalFrames; + float renderTime; + float progress; + float timeElapsed; + float timeEst; + + totalFrames = (_path[_destPathIndex].time - _path[0].time) * 60 * _fps; + renderTime = ((float)_frameCount / _fps) / 60; + + timeElapsed = glfwGetTime() - _renderStartTime; + timeEst = timeElapsed / ((_frameCount * _samples) + _curSamples); + timeEst *= (totalFrames * _samples) - ((_frameCount * _samples) + _curSamples); + if(timeEst > 1e15) + timeEst = 0; + ImGui::Text("render in progress"); + ImGui::Text("samples per frame : %d", _samples); + ImGui::Text("render fps : %d", _fps); + ImGui::Text("total render time : %s", floatToTime((_path[_destPathIndex].time - _path[0].time) * 60).c_str()); + ImGui::Separator(); + ImGui::Text("Frames : %ld / %ld", _frameCount, totalFrames); + ImGui::Text("Frames (with accumulation) : %ld / %ld", (_frameCount * _samples) + _curSamples, totalFrames * _samples); + ImGui::Text("Render time : %dm%f", (int)renderTime, (renderTime - (int)renderTime) * 60); + ImGui::Text("elapsed time : %s", floatToTime(timeElapsed).c_str()); + ImGui::Text("estimated time remaining : %s", floatToTime(timeEst).c_str()); + progress = ((float)_frameCount * _samples) + _curSamples; + progress /= (float)totalFrames * _samples; + ImGui::ProgressBar(progress, ImVec2(0, 0)); + if(ImGui::Button("stop")) + { + _destPathIndex = 0; + endRender(); + } +} + +void Renderer::renderImgui(void) +{ + ImGui::Begin("Renderer"); + + if(rendering()) + imguiRenderInfo(); + else + imguiPathCreation(); + ImGui::End(); +} diff --git a/srcs/class/SceneParser.cpp b/srcs/class/SceneParser.cpp index 78097c5..9d6ef15 100644 --- a/srcs/class/SceneParser.cpp +++ b/srcs/class/SceneParser.cpp @@ -6,7 +6,7 @@ /* By: ycontre +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2024/12/26 21:43:51 by TheRed #+# #+# */ -/* Updated: 2025/01/24 18:50:33 by ycontre ### ########.fr */ +/* Updated: 2025/01/30 22:29:15 by tomoron ### ########.fr */ /* */ /* ************************************************************************** */ @@ -93,7 +93,6 @@ void SceneParser::parseCamera(std::stringstream &line) _scene->getCamera()->setPosition(glm::vec3(x, y, z)); _scene->getCamera()->setDirection(yaw, pitch); - _scene->getCamera()->updateCameraVectors(); _scene->getCamera()->setDOV(aperture, focus); _scene->getCamera()->setFov(fov); diff --git a/srcs/class/Shader.cpp b/srcs/class/Shader.cpp index 9d53f68..10fac6b 100644 --- a/srcs/class/Shader.cpp +++ b/srcs/class/Shader.cpp @@ -6,7 +6,7 @@ /* By: ycontre +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2024/10/13 20:21:13 by ycontre #+# #+# */ -/* Updated: 2025/01/30 17:52:32 by ycontre ### ########.fr */ +/* Updated: 2025/01/30 22:29:39 by tomoron ### ########.fr */ /* */ /* ************************************************************************** */ @@ -228,4 +228,14 @@ GLuint Shader::getProgram(void) const GLuint Shader::getProgramCompute(void) const { return (_program_compute); -} \ No newline at end of file +} + +std::vector Shader::getOutputImage(void) +{ + std::vector res(WIDTH * HEIGHT * 4); + + glBindTexture(GL_TEXTURE_2D, _output_texture); + glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_FLOAT, res.data()); + glBindTexture(GL_TEXTURE_2D, 0); + return (res); +} diff --git a/srcs/class/Window.cpp b/srcs/class/Window.cpp index 5acc4cc..28b4f74 100644 --- a/srcs/class/Window.cpp +++ b/srcs/class/Window.cpp @@ -6,7 +6,7 @@ /* By: ycontre +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2024/10/13 16:16:24 by TheRed #+# #+# */ -/* Updated: 2025/01/18 19:24:04 by ycontre ### ########.fr */ +/* Updated: 2025/01/28 15:15:53 by tomoron ### ########.fr */ /* */ /* ************************************************************************** */ @@ -15,8 +15,10 @@ Window::Window(Scene *scene, int width, int height, const char *title, int sleep) { _scene = scene; + _fps = 0; _frameCount = 0; _pixelisation = 0; + _renderer = new Renderer(scene, this); if (!glfwInit()) { @@ -69,6 +71,8 @@ void Window::process_input() bool up = glfwGetKey(_window, GLFW_KEY_SPACE) == GLFW_PRESS; bool down = glfwGetKey(_window, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS; + if(_renderer->rendering()) + return ; if (forward || backward || left || right || up || down) _frameCount = 0; @@ -160,6 +164,11 @@ bool Window::shouldClose() return glfwWindowShouldClose(_window); } +void Window::rendererUpdate(Shader &shader) +{ + _renderer->update(shader); +} + void Window::imGuiNewFrame() { ImGui_ImplOpenGL3_NewFrame(); @@ -258,9 +267,12 @@ void Window::imGuiRender() ImGui::End(); + _renderer->renderImgui();; + ImGui::Render(); ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); + if (has_changed) _frameCount = (accumulate == 0) - 1; } @@ -280,6 +292,11 @@ int Window::getFrameCount(void) const return (_frameCount); } +void Window::setFrameCount(int nb) +{ + _frameCount = nb; +} + bool &Window::getAccumulate(void) { return (accumulate);