diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000000000000000000000000000000000000..b68024fe2b70431a415b9215e1e292bacf070050 --- /dev/null +++ b/.clang-format @@ -0,0 +1,246 @@ +--- +Language: Cpp +# BasedOnStyle: LLVM +AccessModifierOffset: -2 +AlignAfterOpenBracket: Align +AlignArrayOfStructures: None +AlignConsecutiveAssignments: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionPointers: false + PadOperators: true +AlignConsecutiveBitFields: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionPointers: false + PadOperators: false +AlignConsecutiveDeclarations: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionPointers: false + PadOperators: false +AlignConsecutiveMacros: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionPointers: false + PadOperators: false +AlignConsecutiveShortCaseStatements: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCaseColons: false +AlignEscapedNewlines: Right +AlignOperands: Align +AlignTrailingComments: + Kind: Always + OverEmptyLines: 0 +AllowAllArgumentsOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowBreakBeforeNoexceptSpecifier: Never +AllowShortBlocksOnASingleLine: Never +AllowShortCaseLabelsOnASingleLine: false +AllowShortCompoundRequirementOnASingleLine: true +AllowShortEnumsOnASingleLine: true +AllowShortFunctionsOnASingleLine: All +AllowShortIfStatementsOnASingleLine: Never +AllowShortLambdasOnASingleLine: All +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: MultiLine +AttributeMacros: + - __capability +BinPackArguments: true +BinPackParameters: true +BitFieldColonSpacing: Both +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: Never + AfterEnum: false + AfterExternBlock: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + BeforeCatch: false + BeforeElse: false + BeforeLambdaBody: false + BeforeWhile: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakAdjacentStringLiterals: true +BreakAfterAttributes: Leave +BreakAfterJavaFieldAnnotations: false +BreakArrays: true +BreakBeforeBinaryOperators: None +BreakBeforeConceptDeclarations: Always +BreakBeforeBraces: Attach +BreakBeforeInlineASMColon: OnlyMultiline +BreakBeforeTernaryOperators: true +BreakConstructorInitializers: BeforeColon +BreakInheritanceList: BeforeColon +BreakStringLiterals: true +ColumnLimit: 80 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DerivePointerAlignment: false +DisableFormat: false +EmptyLineAfterAccessModifier: Never +EmptyLineBeforeAccessModifier: LogicalBlock +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IfMacros: + - KJ_IF_MAYBE +IncludeBlocks: Preserve +IncludeCategories: + - Regex: '^"(llvm|llvm-c|clang|clang-c)/' + Priority: 2 + SortPriority: 0 + CaseSensitive: false + - Regex: '^(<|"(gtest|gmock|isl|json)/)' + Priority: 3 + SortPriority: 0 + CaseSensitive: false + - Regex: '.*' + Priority: 1 + SortPriority: 0 + CaseSensitive: false +IncludeIsMainRegex: '(Test)?$' +IncludeIsMainSourceRegex: '' +IndentAccessModifiers: false +IndentCaseBlocks: false +IndentCaseLabels: false +IndentExternBlock: AfterExternBlock +IndentGotoLabels: true +IndentPPDirectives: None +IndentRequiresClause: true +IndentWidth: 2 +IndentWrappedFunctionNames: false +InsertBraces: false +InsertNewlineAtEOF: false +InsertTrailingCommas: None +IntegerLiteralSeparator: + Binary: 0 + BinaryMinDigits: 0 + Decimal: 0 + DecimalMinDigits: 0 + Hex: 0 + HexMinDigits: 0 +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: true +KeepEmptyLinesAtEOF: false +LambdaBodyIndentation: Signature +LineEnding: DeriveLF +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBinPackProtocolList: Auto +ObjCBlockIndentWidth: 2 +ObjCBreakBeforeNestedBlockParam: true +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PackConstructorInitializers: BinPack +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakOpenParenthesis: 0 +PenaltyBreakScopeResolution: 500 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyIndentedWhitespace: 0 +PenaltyReturnTypeOnItsOwnLine: 60 +PointerAlignment: Right +PPIndentWidth: -1 +QualifierAlignment: Leave +ReferenceAlignment: Pointer +ReflowComments: true +RemoveBracesLLVM: false +RemoveParentheses: Leave +RemoveSemicolon: false +RequiresClausePosition: OwnLine +RequiresExpressionIndentation: OuterScope +SeparateDefinitionBlocks: Leave +ShortNamespaceLines: 1 +SkipMacroDefinitionBody: false +SortIncludes: CaseSensitive +SortJavaStaticImport: Before +SortUsingDeclarations: LexicographicNumeric +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: true +SpaceAroundPointerQualifiers: Default +SpaceBeforeAssignmentOperators: true +SpaceBeforeCaseColon: false +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeJsonColon: false +SpaceBeforeParens: ControlStatements +SpaceBeforeParensOptions: + AfterControlStatements: true + AfterForeachMacros: true + AfterFunctionDefinitionName: false + AfterFunctionDeclarationName: false + AfterIfMacros: true + AfterOverloadedOperator: false + AfterPlacementOperator: true + AfterRequiresInClause: false + AfterRequiresInExpression: false + BeforeNonEmptyParentheses: false +SpaceBeforeRangeBasedForLoopColon: true +SpaceBeforeSquareBrackets: false +SpaceInEmptyBlock: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: Never +SpacesInContainerLiterals: true +SpacesInLineCommentPrefix: + Minimum: 1 + Maximum: -1 +SpacesInParens: Never +SpacesInParensOptions: + InCStyleCasts: false + InConditionalStatements: false + InEmptyParentheses: false + Other: false +SpacesInSquareBrackets: false +Standard: Latest +StatementAttributeLikeMacros: + - Q_EMIT +StatementMacros: + - Q_UNUSED + - QT_REQUIRE_VERSION +TabWidth: 8 +UseTab: Never +VerilogBreakBetweenInstancePorts: true +WhitespaceSensitiveMacros: + - BOOST_PP_STRINGIZE + - CF_SWIFT_NAME + - NS_SWIFT_NAME + - PP_STRINGIZE + - STRINGIZE +... + diff --git a/models/cube.obj b/models/cube.obj new file mode 100644 index 0000000000000000000000000000000000000000..909ab55982d94da97be8abe38d072ffea61a230f --- /dev/null +++ b/models/cube.obj @@ -0,0 +1,38 @@ +# Blender 4.2.3 LTS +# www.blender.org +o Cube +v -0.500000 -0.500000 0.500000 +v -0.500000 0.500000 0.500000 +v -0.500000 -0.500000 -0.500000 +v -0.500000 0.500000 -0.500000 +v 0.500000 -0.500000 0.500000 +v 0.500000 0.500000 0.500000 +v 0.500000 -0.500000 -0.500000 +v 0.500000 0.500000 -0.500000 +vn -1.0000 -0.0000 -0.0000 +vn -0.0000 -0.0000 -1.0000 +vn 1.0000 -0.0000 -0.0000 +vn -0.0000 -0.0000 1.0000 +vn -0.0000 -1.0000 -0.0000 +vn -0.0000 1.0000 -0.0000 +vt 0.375000 0.000000 +vt 0.625000 0.000000 +vt 0.625000 0.250000 +vt 0.375000 0.250000 +vt 0.625000 0.500000 +vt 0.375000 0.500000 +vt 0.625000 0.750000 +vt 0.375000 0.750000 +vt 0.625000 1.000000 +vt 0.375000 1.000000 +vt 0.125000 0.500000 +vt 0.125000 0.750000 +vt 0.875000 0.500000 +vt 0.875000 0.750000 +s 0 +f 1/1/1 2/2/1 4/3/1 3/4/1 +f 3/4/2 4/3/2 8/5/2 7/6/2 +f 7/6/3 8/5/3 6/7/3 5/8/3 +f 5/8/4 6/7/4 2/9/4 1/10/4 +f 3/11/5 7/6/5 5/8/5 1/12/5 +f 8/5/6 4/13/6 2/14/6 6/7/6 diff --git a/models/wide_cube.obj b/models/wide_cube.obj new file mode 100644 index 0000000000000000000000000000000000000000..1c547958254aebe312fe44264f145e0c0625883d --- /dev/null +++ b/models/wide_cube.obj @@ -0,0 +1,38 @@ +# Blender 4.2.3 LTS +# www.blender.org +o Cube +v 3.089371 -0.034801 0.761726 +v 3.089371 1.644930 0.761726 +v 3.089371 -0.034801 -0.761726 +v 3.089371 1.644930 -0.761726 +v 3.699787 -0.034801 0.761726 +v 3.699787 1.644930 0.761726 +v 3.699787 -0.034801 -0.761726 +v 3.699787 1.644930 -0.761726 +vn -1.0000 -0.0000 -0.0000 +vn -0.0000 -0.0000 -1.0000 +vn 1.0000 -0.0000 -0.0000 +vn -0.0000 -0.0000 1.0000 +vn -0.0000 -1.0000 -0.0000 +vn -0.0000 1.0000 -0.0000 +vt 0.375000 0.000000 +vt 0.625000 0.000000 +vt 0.625000 0.250000 +vt 0.375000 0.250000 +vt 0.625000 0.500000 +vt 0.375000 0.500000 +vt 0.625000 0.750000 +vt 0.375000 0.750000 +vt 0.625000 1.000000 +vt 0.375000 1.000000 +vt 0.125000 0.500000 +vt 0.125000 0.750000 +vt 0.875000 0.500000 +vt 0.875000 0.750000 +s 0 +f 1/1/1 2/2/1 4/3/1 3/4/1 +f 3/4/2 4/3/2 8/5/2 7/6/2 +f 7/6/3 8/5/3 6/7/3 5/8/3 +f 5/8/4 6/7/4 2/9/4 1/10/4 +f 3/11/5 7/6/5 5/8/5 1/12/5 +f 8/5/6 4/13/6 2/14/6 6/7/6 diff --git a/shaders/basic.vert b/shaders/basic.vert index a3adbe90a7a42e871e52704cbed2f275d47f6f1e..3774ffde1272d28cb47aa541d97805d61eafe113 100644 --- a/shaders/basic.vert +++ b/shaders/basic.vert @@ -9,9 +9,9 @@ uniform mat4 modelToWorldToView; out float shade; + void main(void) { shade = (mat3(modelToWorldToView)*in_Normal).z; // Fake shading gl_Position=projectionMatrix*modelToWorldToView*vec4(in_Position, 1.0); } - diff --git a/shaders/waterfall.frag b/shaders/waterfall.frag index 7d92382799e0b4e4370ba9fc4c86ebcb5e8c3c01..3244e1710286b70dbf76c27ecbc9f4b66dd5f6d7 100644 --- a/shaders/waterfall.frag +++ b/shaders/waterfall.frag @@ -1,11 +1,91 @@ #version 150 -in float shade; +const float step_size = 0.1; +const int NUMBER_OF_STEPS = 32; +const float MINIMUM_HIT_DISTANCE = 0.0001; +const float MAXIMUM_TRACE_DISTANCE = 1000.0; +uniform int num_balls; +uniform float screenWidth; +uniform float screenHeight; + +in vec3 world_pos; +uniform vec3 camera_pos; out vec4 out_Color; +struct Ball { + vec4 pos; + float radius; +}; + +layout(std140) uniform BallBuffer { + Ball balls[30]; // TODO: Need to manually update size +}; + +float distance_from_sphere(vec3 pos, Ball ball) { + return length(pos - ball.pos.xyz) - ball.radius; +} + +// Function taken from: https://iquilezles.org/articles/distfunctions/ +float opSmoothUnion(float d1, float d2, float k) { + float h = clamp(0.5 + 0.5*(d2-d1)/k, 0.0, 1.0); + return mix(d2, d1, h) - k*h*(1.0-h); +} + +float SDF(vec3 position) { + float min_dist = distance_from_sphere(position, balls[0]); + + // TODO: iterate over uniform num_balls + for (int i = 1; i < 30; ++i) { + float dist = distance_from_sphere(position, balls[i]); + + min_dist = opSmoothUnion(min_dist, dist, 0.2); + } + + return min_dist; +} + +vec3 calculate_normal(vec3 position) { + const vec3 small_step = vec3(0.001, 0, 0); + return normalize( + vec3( + SDF(position + small_step.xyy) - SDF(position - small_step.xyy), + SDF(position + small_step.yxy) - SDF(position - small_step.yxy), + SDF(position + small_step.yyx) - SDF(position - small_step.yyx) + ) + ); +} + +vec3 ray_march(vec3 ro, vec3 rd) { + + float total_distance_traveled = 0.0; + + for (int i = 0; i < NUMBER_OF_STEPS; ++i) { + + vec3 current_position = ro + total_distance_traveled * rd; + + float min_dist = SDF(current_position); + + // hit + if (min_dist < MINIMUM_HIT_DISTANCE) { + const vec3 color = vec3(0.1, 0.2, 0.7); + vec3 normal = normalize(calculate_normal(current_position)); + return color; + } + + if (total_distance_traveled > MAXIMUM_TRACE_DISTANCE) { + discard; + } + + total_distance_traveled += min_dist; + } +} + void main(void) { - out_Color=vec4(shade,shade,shade,1.0); + vec3 dir = normalize(world_pos - camera_pos); + vec3 shaded_color = ray_march(world_pos, dir); + out_Color = vec4(shaded_color, 1.0); + //out_Color = vec4(gl_FragCoord.x, gl_FragCoord.y, gl_FragCoord.z, 1); + //out_Color = vec4(shade, shade, shade, 0.0); } - diff --git a/shaders/waterfall.vert b/shaders/waterfall.vert new file mode 100644 index 0000000000000000000000000000000000000000..655b91d54b661f3108c5595e0a7267a7ceb835f8 --- /dev/null +++ b/shaders/waterfall.vert @@ -0,0 +1,21 @@ +#version 150 + +in vec3 in_Position; +in vec3 in_Normal; +in vec2 in_TexCoord; + +uniform mat4 projectionMatrix; +uniform mat4 modelToWorldToView; +uniform mat4 invModelToWorld; +uniform int num_balls; +uniform float screenWidth; +uniform float screenHeight; +uniform vec3 camera_pos; + +out vec3 world_pos; + +void main(void) +{ + gl_Position = projectionMatrix * modelToWorldToView * vec4(in_Position, 1.0); + world_pos = in_Position; +} diff --git a/src/main.cpp b/src/main.cpp index 51b4f34594bf5743786b035a6508504f6688232a..813928e1878e31e472b8ae987531bc5ecd650684 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,8 +1,14 @@ // Revised 2019 with a bit better variable names. // Experimental C++ version 2022. Almost no code changes. -#include <iostream> +#include <cstdlib> + +#include "object.h" +#include "waterfall.h" #include <GL/gl.h> +#include <GL/glext.h> +#include <iostream> +#include <ostream> #define MAIN #include "GL_utilities.h" #include "LittleOBJLoader.h" @@ -12,239 +18,226 @@ // uses framework OpenGL // uses framework Cocoa -struct Object { - Model* model; - GLuint program; - - Object() = default; - - void use() const - { - glUseProgram(program); - } - - void draw() const - { - DrawModel(model, program, "in_Position", "in_Normal", "in_TexCoord"); - } -}; - struct Scene { - Object ground; - Object surface; - Object waterfall; - Object skybox; - - GLuint skybox_tex; - - mat4 proj_matrix = perspective(70.0, 1.0, 0.2, 20.0); - vec3 pos; - float yaw; - float pitch; - mat4 view_matrix = lookAtv(vec3(-5, 4, -5), vec3(0, 0, 0), vec3(0, 1, 0)); - - void init() - { - Model* ground_model = LoadModel("models/ground.obj"); - Model* surface_model = LoadModel("models/surface.obj"); - Model* waterfall_model = LoadModel("models/waterfall.obj"); - Model* skybox_model = LoadModel("models/skybox.obj"); - - // This is not a ground logic program - GLuint ground_program = loadShaders("shaders/basic.vert", "shaders/ground.frag"); - GLuint surface_program = loadShaders("shaders/surface.vert", "shaders/surface.frag"); - GLuint waterfall_program = loadShaders("shaders/basic.vert", "shaders/waterfall.frag"); - GLuint skybox_program = loadShaders("shaders/skybox.vert", "shaders/skybox.frag"); - printError("compiled shaders"); - - ground = Object { ground_model, ground_program }; - surface = Object { surface_model, surface_program }; - waterfall = Object { waterfall_model, waterfall_program }; - skybox = Object { skybox_model, skybox_program }; - - LoadTGATextureSimple("textures/sky4k.tga", &skybox_tex); - glBindTexture(GL_TEXTURE_2D, skybox_tex); - printError("bind texture"); - //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT); - //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - printError("tex parameteri"); + Object ground; + Object surface; + Waterfall waterfall; + Object skybox; + + GLuint skybox_tex; + + mat4 proj_matrix = perspective(70.0, 1.0, 0.2, 20.0); + vec3 pos; + float yaw; + float pitch; + mat4 view_matrix = lookAtv(vec3(-5, 4, -5), vec3(0, 0, 0), vec3(0, 1, 0)); + + void init() { + Model *ground_model = LoadModel("models/ground.obj"); + Model *surface_model = LoadModel("models/surface.obj"); + Model *waterfall_model = LoadModel("models/wide_cube.obj"); + Model *skybox_model = LoadModel("models/skybox.obj"); + + // This is not a ground logic program + GLuint ground_program = + loadShaders("shaders/basic.vert", "shaders/ground.frag"); + GLuint surface_program = + loadShaders("shaders/surface.vert", "shaders/surface.frag"); + GLuint waterfall_program = + loadShaders("shaders/waterfall.vert", "shaders/waterfall.frag"); + GLuint skybox_program = + loadShaders("shaders/skybox.vert", "shaders/skybox.frag"); + printError("compiled shaders"); + ground = Object{ground_model, ground_program}; + surface = Object{surface_model, surface_program}; + waterfall = Waterfall{waterfall_model, waterfall_program}; + skybox = Object{skybox_model, skybox_program}; + + LoadTGATextureSimple("textures/sky4k.tga", &skybox_tex); + glBindTexture(GL_TEXTURE_2D, skybox_tex); + printError("bind texture"); + // glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, + // GL_MIRRORED_REPEAT); glTexParameteri(GL_TEXTURE_2D, + // GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + printError("tex parameteri"); + } + + void do_keyboard_input() { + vec3 fwd = vec3(sin(yaw), 0, -cos(yaw)); + vec3 right = vec3(cos(yaw), 0, sin(yaw)); + vec3 up = vec3(0, 1, 0); + float speed = 0.3; + + if (glutKeyIsDown('w')) { + pos += speed * fwd; } - - void do_keyboard_input() - { - vec3 fwd = vec3(sin(yaw), 0, -cos(yaw)); - vec3 right = vec3(cos(yaw), 0, sin(yaw)); - vec3 up = vec3(0, 1, 0); - float speed = 0.3; - - if (glutKeyIsDown('w')) { - pos += speed * fwd; - } - if (glutKeyIsDown('s')) { - pos -= speed * fwd; - } - if (glutKeyIsDown('d')) { - pos += speed * right; - } - if (glutKeyIsDown('a')) { - pos -= speed * right; - } - if (glutKeyIsDown('r')) { - pos += speed * up; - } - if (glutKeyIsDown('f')) { - pos -= speed * up; - } - } - - void update_view_matrix() - { - vec3 up { 0.0, 1.0, 0.0 }; - mat4 rot = Rx(pitch) * Ry(yaw); - mat4 translation = T(-pos.x, -pos.y, -pos.z); - view_matrix = rot * translation; - } - - void draw_skybox() - { - skybox.use(); - GLuint program = skybox.program; - glDisable(GL_DEPTH_TEST); - glDisable(GL_CULL_FACE); - - glUniformMatrix4fv(glGetUniformLocation(program, "projectionMatrix"), 1, GL_TRUE, proj_matrix.m); - glUniformMatrix4fv(glGetUniformLocation(program, "modelToWorldToView"), 1, GL_TRUE, view_matrix.m); - glUniform1i(glGetUniformLocation(program, "sky"), 0); - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, skybox_tex); - - - skybox.draw(); - glEnable(GL_DEPTH_TEST); - glEnable(GL_CULL_FACE); + if (glutKeyIsDown('s')) { + pos -= speed * fwd; } - - void draw_surface() - { - surface.use(); - GLuint program = surface.program; - glUniformMatrix4fv(glGetUniformLocation(program, "projectionMatrix"), 1, GL_TRUE, proj_matrix.m); - glUniformMatrix4fv(glGetUniformLocation(program, "modelToWorldToView"), 1, GL_TRUE, view_matrix.m); - - surface.draw(); + if (glutKeyIsDown('d')) { + pos += speed * right; } - - void draw_ground() - { - ground.use(); - GLuint program = ground.program; - glUniformMatrix4fv(glGetUniformLocation(program, "projectionMatrix"), 1, GL_TRUE, proj_matrix.m); - glUniformMatrix4fv(glGetUniformLocation(program, "modelToWorldToView"), 1, GL_TRUE, view_matrix.m); - - ground.draw(); + if (glutKeyIsDown('a')) { + pos -= speed * right; } - - void draw_waterfall() - { - waterfall.use(); - GLuint program = waterfall.program; - glUniformMatrix4fv(glGetUniformLocation(program, "projectionMatrix"), 1, GL_TRUE, proj_matrix.m); - glUniformMatrix4fv(glGetUniformLocation(program, "modelToWorldToView"), 1, GL_TRUE, view_matrix.m); - - waterfall.draw(); + if (glutKeyIsDown('r')) { + pos += speed * up; } - - void draw() - { - do_keyboard_input(); - update_view_matrix(); - - draw_skybox(); - draw_surface(); - draw_ground(); - draw_waterfall(); + if (glutKeyIsDown('f')) { + pos -= speed * up; } + } + + void update_view_matrix() { + vec3 up{0.0, 1.0, 0.0}; + mat4 rot = Rx(pitch) * Ry(yaw); + mat4 translation = T(-pos.x, -pos.y, -pos.z); + view_matrix = rot * translation; + } + + void draw_skybox() { + skybox.use(); + GLuint program = skybox.program; + glDisable(GL_DEPTH_TEST); + glDisable(GL_CULL_FACE); + + glUniformMatrix4fv(glGetUniformLocation(program, "projectionMatrix"), 1, + GL_TRUE, proj_matrix.m); + glUniformMatrix4fv(glGetUniformLocation(program, "modelToWorldToView"), 1, + GL_TRUE, view_matrix.m); + glUniform1i(glGetUniformLocation(program, "sky"), 0); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, skybox_tex); + + skybox.draw(); + glEnable(GL_DEPTH_TEST); + glEnable(GL_CULL_FACE); + } + + void draw_surface() { + surface.use(); + GLuint program = surface.program; + glUniformMatrix4fv(glGetUniformLocation(program, "projectionMatrix"), 1, + GL_TRUE, proj_matrix.m); + glUniformMatrix4fv(glGetUniformLocation(program, "modelToWorldToView"), 1, + GL_TRUE, view_matrix.m); + + surface.draw(); + } + + void draw_ground() { + ground.use(); + GLuint program = ground.program; + glUniformMatrix4fv(glGetUniformLocation(program, "projectionMatrix"), 1, + GL_TRUE, proj_matrix.m); + glUniformMatrix4fv(glGetUniformLocation(program, "modelToWorldToView"), 1, + GL_TRUE, view_matrix.m); + + ground.draw(); + } + + void draw_waterfall() { + waterfall.use(); + GLuint program = waterfall.program; + glUniformMatrix4fv(glGetUniformLocation(program, "projectionMatrix"), 1, + GL_TRUE, proj_matrix.m); + glUniformMatrix4fv(glGetUniformLocation(program, "worldToView"), 1, GL_TRUE, + view_matrix.m); + glUniformMatrix4fv(glGetUniformLocation(program, "modelToWorldToView"), 1, + GL_TRUE, view_matrix.m); + glUniform3f(glGetUniformLocation(program, "camera_pos"), pos.x, pos.y, + pos.z); + + waterfall.draw(); + waterfall.move_waterfall_balls(); + } + + void draw() { + do_keyboard_input(); + update_view_matrix(); + + draw_skybox(); + draw_surface(); + draw_ground(); + draw_waterfall(); + } }; -Scene scene {}; +Scene scene{}; int mouse_x = -1; int mouse_y = -1; -void init(void) -{ - dumpInfo(); - - // GL inits - glClearColor(0.2, 0.2, 0.5, 0); - glEnable(GL_DEPTH_TEST); - glEnable(GL_CULL_FACE); - glCullFace(GL_BACK); - printError("GL inits"); // This is merely a vague indication of where something might be wrong - // - scene.init(); - - // Load and compile shader - printError("init shader"); +void init(void) { + dumpInfo(); + + // GL inits + glClearColor(0.2, 0.2, 0.5, 0); + glEnable(GL_DEPTH_TEST); + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); + printError("GL inits"); // This is merely a vague indication of where + // something might be wrong + // + scene.init(); + + // Load and compile shader + printError("init shader"); } -void display(void) -{ - printError("pre display"); +void display(void) { + printError("pre display"); - // clear the screen - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + // clear the screen + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - // activate the program, and set its variables - scene.draw(); + // activate the program, and set its variables + scene.draw(); - printError("display"); + printError("display"); - glutSwapBuffers(); + glutSwapBuffers(); } -void on_mouse_move(int x, int y) -{ - const float sensitivity = 0.005f; - if (mouse_y == -1 && mouse_x == -1) { - mouse_x = x; - mouse_y = y; - - return; - } - - int dx = x - mouse_x; - int dy = y - mouse_y; +void on_mouse_move(int x, int y) { + const float sensitivity = 0.005f; + if (mouse_y == -1 && mouse_x == -1) { mouse_x = x; mouse_y = y; - scene.pitch -= dy * sensitivity; - scene.yaw += dx * sensitivity; + return; + } - scene.pitch = std::max(-1.5f, std::min(1.5f, scene.pitch)); + int dx = x - mouse_x; + int dy = y - mouse_y; + mouse_x = x; + mouse_y = y; + + scene.pitch -= dy * sensitivity; + scene.yaw += dx * sensitivity; + + scene.pitch = std::max(-1.5f, std::min(1.5f, scene.pitch)); } -void on_mouse_button(int button, int state, int x, int y) -{ - if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) { - mouse_x = x; - mouse_y = y; - } +void on_mouse_button(int button, int state, int x, int y) { + if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) { + mouse_x = x; + mouse_y = y; + } } -int main(int argc, char* argv[]) -{ - glutInit(&argc, argv); - glutInitDisplayMode(GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE); - glutInitContextVersion(3, 2); - glutInitWindowSize(800, 800); - glutCreateWindow("TSBK03 Project"); - glutDisplayFunc(display); - glutRepeatingTimer(20); - init(); - glutMotionFunc(on_mouse_move); - glutMouseFunc(on_mouse_button); - - glutMainLoop(); - exit(0); +int main(int argc, char *argv[]) { + glutInit(&argc, argv); + glutInitDisplayMode(GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE); + glutInitContextVersion(3, 2); + glutInitWindowSize(800, 800); + glutCreateWindow("TSBK03 Project"); + glutDisplayFunc(display); + glutRepeatingTimer(20); + init(); + glutMotionFunc(on_mouse_move); + glutMouseFunc(on_mouse_button); + + glutMainLoop(); + exit(0); } diff --git a/src/object.h b/src/object.h new file mode 100644 index 0000000000000000000000000000000000000000..48c805c674bcec12801cef3be1fbfdfdece26a25 --- /dev/null +++ b/src/object.h @@ -0,0 +1,18 @@ +#pragma once + +#include "LittleOBJLoader.h" +#include <GL/gl.h> + +struct Object { + Model *model; + GLuint program; + + Object() = default; + + void use() const { glUseProgram(program); } + + void draw() const { + DrawModel(model, program, "in_Position", "in_Normal", "in_TexCoord"); + // DrawModel(model, program, "in_Position", "in_Normal", NULL); + } +}; diff --git a/src/waterfall.cpp b/src/waterfall.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b1cfa99a8793c11e7556e0671e172db40ff8b9e3 --- /dev/null +++ b/src/waterfall.cpp @@ -0,0 +1,107 @@ +#include "waterfall.h" +#include "GL_utilities.h" +#include "object.h" +#include <cmath> +#include <iostream> +#include <ostream> + +Waterfall::Waterfall(Model *model, GLuint program) : Object{model, program} { + + float min_x = std::numeric_limits<float>::max(); + float max_x = std::numeric_limits<float>::min(); + float min_y = std::numeric_limits<float>::max(); + float max_y = std::numeric_limits<float>::min(); + float min_z = std::numeric_limits<float>::max(); + float max_z = std::numeric_limits<float>::min(); + + for (int i = 0; i < model->numVertices; ++i) { + min_x = std::min(min_x, model->vertexArray[i].x); + max_x = std::max(max_x, model->vertexArray[i].x); + min_y = std::min(min_y, model->vertexArray[i].y); + max_y = std::max(max_y, model->vertexArray[i].y); + min_z = std::min(min_z, model->vertexArray[i].z); + max_z = std::max(max_z, model->vertexArray[i].z); + } + + x = {min_x, max_x}; + y = {min_y, max_y}; + z = {min_z, max_z}; + + gen_balls(); + use(); + + // Create a uniform buffer object, used to send waves. + glGenBuffers(1, &ubo); + glBindBuffer(GL_UNIFORM_BUFFER, ubo); + + // Allocate memory in the buffer. + glBufferData(GL_UNIFORM_BUFFER, sizeof(balls), nullptr, GL_DYNAMIC_DRAW); + + block_index = glGetUniformBlockIndex(program, UNIFORM_BLOCK); + + if (block_index == GL_INVALID_INDEX) { + std::cout << "Waterfall: Block name could not be found." << std::endl; + } + + // Bind the buffer to a binding point. + glBindBufferBase(GL_UNIFORM_BUFFER, BINDING_POINT, ubo); + + // Link the UBO to the shader’s uniform block + glUniformBlockBinding(program, block_index, BINDING_POINT); +} + +void Waterfall::draw() const { + + // Bind the buffer before updating it. + glBindBuffer(GL_UNIFORM_BUFFER, ubo); + // Assign values to the `balls` array and update the UBO data + glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(balls), balls); + + // Upload the number of balls. + glUniform1i(glGetUniformLocation(program, "num_balls"), NUM_BALLS); + + DrawModel(model, program, "in_Position", "in_Normal", "in_TexCoord"); + printError("Waterfall draw"); +} + +void Waterfall::move_waterfall_balls() { + // Moves the balls one distance in their velocity. If they fall + // below 0 (end of the waterfall) their height is reset of waterfall + // height. + + for (int i{0}; i < NUM_BALLS; ++i) { + balls[i].pos += ball_velocities[i]; + + // TODO: check if balls are outside in x and z plane aswell + // maybe just generate a new ball if it falls outside + if (balls[i].pos.y < 0) { + balls[i].pos.y = y.second; + } + } +} + +float lerp(float a, float b, float t) { return a + t * (b - a); } + +void Waterfall::gen_balls() { + for (int i{0}; i < NUM_BALLS; ++i) { + + // pseudo random value between 0.01 and 0.1 + float radius = ((rand() / (float)RAND_MAX) * 0.9 + 0.1) / 10.0; + + // pseudo random value between min-radius and max-radius + float pos_x = lerp(x.first, x.second, (rand() / (float)RAND_MAX)); + float pos_y = lerp(y.first, y.second, (rand() / (float)RAND_MAX)); + float pos_z = lerp(z.first, z.second, (rand() / (float)RAND_MAX)); + vec4 pos = vec4(pos_x, pos_y, pos_z, 0); + + // pseudo random value between -0.001 and -0.01 + float velocity_y = -((rand() / (float)RAND_MAX) * 0.9 + 0.1) / 50.0; + vec4 velocity = vec4(0, velocity_y, 0, 0); + + balls[i] = Waterfall::Ball(pos, radius); + ball_velocities[i] = velocity; + } + // balls[0].pos = vec4(3.2, 1.0, 0, 0); + // balls[0].radius = 0.55; + // ball_velocities[0] = vec4(0); +} diff --git a/src/waterfall.h b/src/waterfall.h new file mode 100644 index 0000000000000000000000000000000000000000..76f849ac1fadf9b9c2d39f7d4b38ec3d89ecab30 --- /dev/null +++ b/src/waterfall.h @@ -0,0 +1,41 @@ +#pragma once + +#include "VectorUtils4.h" +#include "object.h" + +struct Waterfall : Object { + + Waterfall() = default; + Waterfall(Model *model, GLuint program); + void move_waterfall_balls(); + void gen_balls(); + void draw() const; + + static GLuint const BINDING_POINT{0}; + char const *UNIFORM_BLOCK = "BallBuffer"; + static int const NUM_BALLS{30}; + + // Dimensions of the waterfall + // on the form <min, max> + std::pair<float, float> x; + std::pair<float, float> y; + std::pair<float, float> z; + + // Properties of each ball + struct Ball { + vec4 pos; + float radius; + + // Glsls padding to make struct a multiple of vec4 (cringe) + vec3 padding; + + Ball(vec3 pos, float radius) : pos{pos}, radius{radius} {} + Ball() : pos{0, 0, 0, 0}, radius{0} {} + }; + + Ball balls[NUM_BALLS]; + vec4 ball_velocities[NUM_BALLS]; + + GLuint ubo; + GLuint block_index; +};