├── .gitignore ├── .gitmodules ├── .vscode └── settings.json ├── CMakeLists.txt ├── README.md ├── deps ├── glad │ ├── include │ │ ├── KHR │ │ │ └── khrplatform.h │ │ └── glad │ │ │ └── glad.h │ └── src │ │ └── glad.c ├── lodepng │ ├── lodepng.c │ └── lodepng.h ├── miniz │ ├── ChangeLog.md │ ├── LICENSE │ ├── miniz.c │ ├── miniz.h │ └── readme.md └── noise │ ├── noise1234.c │ └── noise1234.h ├── res ├── shaders │ ├── blocks.fsh │ ├── blocks.vsh │ ├── gui.fsh │ ├── gui.vsh │ ├── lines.fsh │ └── lines.vsh └── textures │ ├── ascii.png │ ├── terrain.png │ └── widgets.png ├── screenshot.png └── src ├── block.h ├── block_data.c ├── block_data.h ├── bounding_box.c ├── bounding_box.h ├── chunk.c ├── chunk.h ├── entity.c ├── entity.h ├── game.c ├── game.h ├── gui.c ├── gui.h ├── input.c ├── input.h ├── main.c ├── mesh.c ├── mesh.h ├── mvmath.c ├── mvmath.h ├── noise.c ├── noise.h ├── server ├── main.c ├── server.c └── server.h ├── shader.h ├── sky.c ├── sky.h ├── sockets.h ├── util.c ├── util.h ├── world.c ├── world.h ├── worldgen.c └── worldgen.h /.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "deps/glfw"] 2 | path = deps/glfw 3 | url = https://github.com/glfw/glfw 4 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "bit": "c", 4 | "*.tcc": "c", 5 | "chrono": "c", 6 | "functional": "c", 7 | "string_view": "c", 8 | "random": "c", 9 | "istream": "c", 10 | "limits": "c", 11 | "numeric": "c", 12 | "semaphore": "c", 13 | "valarray": "c", 14 | "noise1234.h": "c" 15 | } 16 | } -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | 3 | project(b C) 4 | 5 | add_executable( 6 | b 7 | src/mvmath.c 8 | src/util.c 9 | src/bounding_box.c 10 | src/mesh.c 11 | src/block_data.c 12 | src/chunk.c 13 | src/entity.c 14 | src/world.c 15 | src/gui.c 16 | src/game.c 17 | src/input.c 18 | src/noise.c 19 | src/main.c 20 | src/sky.c 21 | src/worldgen.c 22 | deps/glad/src/glad.c 23 | deps/miniz/miniz.c 24 | deps/noise/noise1234.c 25 | deps/lodepng/lodepng.c) 26 | 27 | add_executable( 28 | bserver 29 | src/server/server.c 30 | src/server/main.c 31 | deps/miniz/miniz.c) 32 | 33 | set(GLFW_BUILD_EXAMPLES OFF CACHE BOOL "Build the GLFW example programs" FORCE) 34 | set(GLFW_BUILD_TESTS OFF CACHE BOOL "Build the GLFW test programs" FORCE) 35 | set(BUILD_EXAMPLES OFF CACHE BOOL "Build example apps" FORCE) 36 | 37 | add_subdirectory(deps/glfw) 38 | include_directories(deps/miniz) 39 | include_directories(deps/noise) 40 | include_directories(deps/glad/include) 41 | include_directories(deps/glfw/include) 42 | include_directories(deps/lodepng) 43 | 44 | if(WIN32) 45 | target_link_libraries(b wsock32 ws2_32 opengl32) 46 | target_link_libraries(bserver wsock32 ws2_32) 47 | elseif(APPLE) 48 | target_link_libraries(b 49 | "-framework Cocoa" 50 | "-framework IOKit" 51 | "-framework CoreVideo" 52 | "-framework OpenGL" 53 | ) 54 | elseif(UNIX) 55 | target_link_libraries(b GL) 56 | target_link_libraries(bserver m) 57 | endif() 58 | 59 | target_link_libraries(b glfw ${GLFW_LIBRARIES}) 60 | 61 | add_custom_command(TARGET b POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/res $/res) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # b 2 | 3 | ![image](screenshot.png) 4 | 5 | ## this project is originated from knicked's [CCraft](https://github.com/knicked/CCraft) 6 | 7 | # WARNING 8 | glfw is used as a submodule here, please run `git submodule update --init` after cloning to get glfw. 9 | 10 | to build on linux/mac 11 | ``` 12 | (if youre running on WSL with x11) sudo apt-get install libx11-dev 13 | mkdir build && cd build 14 | cmake .. 15 | make 16 | ``` 17 | win: 18 | ``` 19 | mkdir build && cd build 20 | cmake .. -G "MinGW Makefiles" 21 | make 22 | ``` 23 | run 24 | ``` 25 | ./b 26 | ``` 27 | -------------------------------------------------------------------------------- /deps/glad/include/KHR/khrplatform.h: -------------------------------------------------------------------------------- 1 | #ifndef __khrplatform_h_ 2 | #define __khrplatform_h_ 3 | 4 | /* 5 | ** Copyright (c) 2008-2018 The Khronos Group Inc. 6 | ** 7 | ** Permission is hereby granted, free of charge, to any person obtaining a 8 | ** copy of this software and/or associated documentation files (the 9 | ** "Materials"), to deal in the Materials without restriction, including 10 | ** without limitation the rights to use, copy, modify, merge, publish, 11 | ** distribute, sublicense, and/or sell copies of the Materials, and to 12 | ** permit persons to whom the Materials are furnished to do so, subject to 13 | ** the following conditions: 14 | ** 15 | ** The above copyright notice and this permission notice shall be included 16 | ** in all copies or substantial portions of the Materials. 17 | ** 18 | ** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | ** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | ** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | ** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23 | ** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 | ** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. 25 | */ 26 | 27 | /* Khronos platform-specific types and definitions. 28 | * 29 | * The master copy of khrplatform.h is maintained in the Khronos EGL 30 | * Registry repository at https://github.com/KhronosGroup/EGL-Registry 31 | * The last semantic modification to khrplatform.h was at commit ID: 32 | * 67a3e0864c2d75ea5287b9f3d2eb74a745936692 33 | * 34 | * Adopters may modify this file to suit their platform. Adopters are 35 | * encouraged to submit platform specific modifications to the Khronos 36 | * group so that they can be included in future versions of this file. 37 | * Please submit changes by filing pull requests or issues on 38 | * the EGL Registry repository linked above. 39 | * 40 | * 41 | * See the Implementer's Guidelines for information about where this file 42 | * should be located on your system and for more details of its use: 43 | * http://www.khronos.org/registry/implementers_guide.pdf 44 | * 45 | * This file should be included as 46 | * #include 47 | * by Khronos client API header files that use its types and defines. 48 | * 49 | * The types in khrplatform.h should only be used to define API-specific types. 50 | * 51 | * Types defined in khrplatform.h: 52 | * khronos_int8_t signed 8 bit 53 | * khronos_uint8_t unsigned 8 bit 54 | * khronos_int16_t signed 16 bit 55 | * khronos_uint16_t unsigned 16 bit 56 | * khronos_int32_t signed 32 bit 57 | * khronos_uint32_t unsigned 32 bit 58 | * khronos_int64_t signed 64 bit 59 | * khronos_uint64_t unsigned 64 bit 60 | * khronos_intptr_t signed same number of bits as a pointer 61 | * khronos_uintptr_t unsigned same number of bits as a pointer 62 | * khronos_ssize_t signed size 63 | * khronos_usize_t unsigned size 64 | * khronos_float_t signed 32 bit floating point 65 | * khronos_time_ns_t unsigned 64 bit time in nanoseconds 66 | * khronos_utime_nanoseconds_t unsigned time interval or absolute time in 67 | * nanoseconds 68 | * khronos_stime_nanoseconds_t signed time interval in nanoseconds 69 | * khronos_boolean_enum_t enumerated boolean type. This should 70 | * only be used as a base type when a client API's boolean type is 71 | * an enum. Client APIs which use an integer or other type for 72 | * booleans cannot use this as the base type for their boolean. 73 | * 74 | * Tokens defined in khrplatform.h: 75 | * 76 | * KHRONOS_FALSE, KHRONOS_TRUE Enumerated boolean false/true values. 77 | * 78 | * KHRONOS_SUPPORT_INT64 is 1 if 64 bit integers are supported; otherwise 0. 79 | * KHRONOS_SUPPORT_FLOAT is 1 if floats are supported; otherwise 0. 80 | * 81 | * Calling convention macros defined in this file: 82 | * KHRONOS_APICALL 83 | * KHRONOS_APIENTRY 84 | * KHRONOS_APIATTRIBUTES 85 | * 86 | * These may be used in function prototypes as: 87 | * 88 | * KHRONOS_APICALL void KHRONOS_APIENTRY funcname( 89 | * int arg1, 90 | * int arg2) KHRONOS_APIATTRIBUTES; 91 | */ 92 | 93 | #if defined(__SCITECH_SNAP__) && !defined(KHRONOS_STATIC) 94 | # define KHRONOS_STATIC 1 95 | #endif 96 | 97 | /*------------------------------------------------------------------------- 98 | * Definition of KHRONOS_APICALL 99 | *------------------------------------------------------------------------- 100 | * This precedes the return type of the function in the function prototype. 101 | */ 102 | #if defined(KHRONOS_STATIC) 103 | /* If the preprocessor constant KHRONOS_STATIC is defined, make the 104 | * header compatible with static linking. */ 105 | # define KHRONOS_APICALL 106 | #elif defined(_WIN32) 107 | # define KHRONOS_APICALL __declspec(dllimport) 108 | #elif defined (__SYMBIAN32__) 109 | # define KHRONOS_APICALL IMPORT_C 110 | #elif defined(__ANDROID__) 111 | # define KHRONOS_APICALL __attribute__((visibility("default"))) 112 | #else 113 | # define KHRONOS_APICALL 114 | #endif 115 | 116 | /*------------------------------------------------------------------------- 117 | * Definition of KHRONOS_APIENTRY 118 | *------------------------------------------------------------------------- 119 | * This follows the return type of the function and precedes the function 120 | * name in the function prototype. 121 | */ 122 | #if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__SCITECH_SNAP__) 123 | /* Win32 but not WinCE */ 124 | # define KHRONOS_APIENTRY __stdcall 125 | #else 126 | # define KHRONOS_APIENTRY 127 | #endif 128 | 129 | /*------------------------------------------------------------------------- 130 | * Definition of KHRONOS_APIATTRIBUTES 131 | *------------------------------------------------------------------------- 132 | * This follows the closing parenthesis of the function prototype arguments. 133 | */ 134 | #if defined (__ARMCC_2__) 135 | #define KHRONOS_APIATTRIBUTES __softfp 136 | #else 137 | #define KHRONOS_APIATTRIBUTES 138 | #endif 139 | 140 | /*------------------------------------------------------------------------- 141 | * basic type definitions 142 | *-----------------------------------------------------------------------*/ 143 | #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__) 144 | 145 | 146 | /* 147 | * Using 148 | */ 149 | #include 150 | typedef int32_t khronos_int32_t; 151 | typedef uint32_t khronos_uint32_t; 152 | typedef int64_t khronos_int64_t; 153 | typedef uint64_t khronos_uint64_t; 154 | #define KHRONOS_SUPPORT_INT64 1 155 | #define KHRONOS_SUPPORT_FLOAT 1 156 | /* 157 | * To support platform where unsigned long cannot be used interchangeably with 158 | * inptr_t (e.g. CHERI-extended ISAs), we can use the stdint.h intptr_t. 159 | * Ideally, we could just use (u)intptr_t everywhere, but this could result in 160 | * ABI breakage if khronos_uintptr_t is changed from unsigned long to 161 | * unsigned long long or similar (this results in different C++ name mangling). 162 | * To avoid changes for existing platforms, we restrict usage of intptr_t to 163 | * platforms where the size of a pointer is larger than the size of long. 164 | */ 165 | #if defined(__SIZEOF_LONG__) && defined(__SIZEOF_POINTER__) 166 | #if __SIZEOF_POINTER__ > __SIZEOF_LONG__ 167 | #define KHRONOS_USE_INTPTR_T 168 | #endif 169 | #endif 170 | 171 | #elif defined(__VMS ) || defined(__sgi) 172 | 173 | /* 174 | * Using 175 | */ 176 | #include 177 | typedef int32_t khronos_int32_t; 178 | typedef uint32_t khronos_uint32_t; 179 | typedef int64_t khronos_int64_t; 180 | typedef uint64_t khronos_uint64_t; 181 | #define KHRONOS_SUPPORT_INT64 1 182 | #define KHRONOS_SUPPORT_FLOAT 1 183 | 184 | #elif defined(_WIN32) && !defined(__SCITECH_SNAP__) 185 | 186 | /* 187 | * Win32 188 | */ 189 | typedef __int32 khronos_int32_t; 190 | typedef unsigned __int32 khronos_uint32_t; 191 | typedef __int64 khronos_int64_t; 192 | typedef unsigned __int64 khronos_uint64_t; 193 | #define KHRONOS_SUPPORT_INT64 1 194 | #define KHRONOS_SUPPORT_FLOAT 1 195 | 196 | #elif defined(__sun__) || defined(__digital__) 197 | 198 | /* 199 | * Sun or Digital 200 | */ 201 | typedef int khronos_int32_t; 202 | typedef unsigned int khronos_uint32_t; 203 | #if defined(__arch64__) || defined(_LP64) 204 | typedef long int khronos_int64_t; 205 | typedef unsigned long int khronos_uint64_t; 206 | #else 207 | typedef long long int khronos_int64_t; 208 | typedef unsigned long long int khronos_uint64_t; 209 | #endif /* __arch64__ */ 210 | #define KHRONOS_SUPPORT_INT64 1 211 | #define KHRONOS_SUPPORT_FLOAT 1 212 | 213 | #elif 0 214 | 215 | /* 216 | * Hypothetical platform with no float or int64 support 217 | */ 218 | typedef int khronos_int32_t; 219 | typedef unsigned int khronos_uint32_t; 220 | #define KHRONOS_SUPPORT_INT64 0 221 | #define KHRONOS_SUPPORT_FLOAT 0 222 | 223 | #else 224 | 225 | /* 226 | * Generic fallback 227 | */ 228 | #include 229 | typedef int32_t khronos_int32_t; 230 | typedef uint32_t khronos_uint32_t; 231 | typedef int64_t khronos_int64_t; 232 | typedef uint64_t khronos_uint64_t; 233 | #define KHRONOS_SUPPORT_INT64 1 234 | #define KHRONOS_SUPPORT_FLOAT 1 235 | 236 | #endif 237 | 238 | 239 | /* 240 | * Types that are (so far) the same on all platforms 241 | */ 242 | typedef signed char khronos_int8_t; 243 | typedef unsigned char khronos_uint8_t; 244 | typedef signed short int khronos_int16_t; 245 | typedef unsigned short int khronos_uint16_t; 246 | 247 | /* 248 | * Types that differ between LLP64 and LP64 architectures - in LLP64, 249 | * pointers are 64 bits, but 'long' is still 32 bits. Win64 appears 250 | * to be the only LLP64 architecture in current use. 251 | */ 252 | #ifdef KHRONOS_USE_INTPTR_T 253 | typedef intptr_t khronos_intptr_t; 254 | typedef uintptr_t khronos_uintptr_t; 255 | #elif defined(_WIN64) 256 | typedef signed long long int khronos_intptr_t; 257 | typedef unsigned long long int khronos_uintptr_t; 258 | #else 259 | typedef signed long int khronos_intptr_t; 260 | typedef unsigned long int khronos_uintptr_t; 261 | #endif 262 | 263 | #if defined(_WIN64) 264 | typedef signed long long int khronos_ssize_t; 265 | typedef unsigned long long int khronos_usize_t; 266 | #else 267 | typedef signed long int khronos_ssize_t; 268 | typedef unsigned long int khronos_usize_t; 269 | #endif 270 | 271 | #if KHRONOS_SUPPORT_FLOAT 272 | /* 273 | * Float type 274 | */ 275 | typedef float khronos_float_t; 276 | #endif 277 | 278 | #if KHRONOS_SUPPORT_INT64 279 | /* Time types 280 | * 281 | * These types can be used to represent a time interval in nanoseconds or 282 | * an absolute Unadjusted System Time. Unadjusted System Time is the number 283 | * of nanoseconds since some arbitrary system event (e.g. since the last 284 | * time the system booted). The Unadjusted System Time is an unsigned 285 | * 64 bit value that wraps back to 0 every 584 years. Time intervals 286 | * may be either signed or unsigned. 287 | */ 288 | typedef khronos_uint64_t khronos_utime_nanoseconds_t; 289 | typedef khronos_int64_t khronos_stime_nanoseconds_t; 290 | #endif 291 | 292 | /* 293 | * Dummy value used to pad enum types to 32 bits. 294 | */ 295 | #ifndef KHRONOS_MAX_ENUM 296 | #define KHRONOS_MAX_ENUM 0x7FFFFFFF 297 | #endif 298 | 299 | /* 300 | * Enumerated boolean type 301 | * 302 | * Values other than zero should be considered to be true. Therefore 303 | * comparisons should not be made against KHRONOS_TRUE. 304 | */ 305 | typedef enum { 306 | KHRONOS_FALSE = 0, 307 | KHRONOS_TRUE = 1, 308 | KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM 309 | } khronos_boolean_enum_t; 310 | 311 | #endif /* __khrplatform_h_ */ 312 | -------------------------------------------------------------------------------- /deps/miniz/ChangeLog.md: -------------------------------------------------------------------------------- 1 | ## Changelog 2 | 3 | ### 3.0.1 4 | 5 | - Fix compilation error with MINIZ_USE_UNALIGNED_LOADS_AND_STORES=1 6 | 7 | ### 3.0.0 8 | 9 | - Reduce memory usage for inflate. This changes `struct tinfl_decompressor_tag` and therefore requires a major version bump (breaks ABI compatibility) 10 | - Add padding to structures so it continues to work if features differ. This also changes some structures 11 | - Use _ftelli64, _fseeki64 and stat with MinGW32 and OpenWatcom 12 | - Fix varios warnings with OpenWatcom compiler 13 | - Avoid using unaligned memory access in UBSan builds 14 | - Set MINIZ_LITTLE_ENDIAN only if not set 15 | - Add MINIZ_NO_DEFLATE_APIS and MINIZ_NO_INFLATE_APIS 16 | - Fix use of uninitialized memory in tinfl_decompress_mem_to_callback() 17 | - Use wfopen on windows 18 | - Use _wstat64 instead _stat64 on windows 19 | - Use level_and_flags after MZ_DEFAULT_COMPRESSION has been handled 20 | - Improve endianess detection 21 | - Don't use unaligned stores and loads per default 22 | - Fix function declaration if MINIZ_NO_STDIO is used 23 | - Fix MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8 not being set 24 | - Remove total files check (its 32-bit uint) 25 | - tinfl_decompress: avoid NULL ptr arithmetic UB 26 | - miniz_zip: fix mz_zip_reader_extract_to_heap to read correct sizes 27 | - Eliminate 64-bit operations on 32-bit machines 28 | - Disable treating warnings as error with MSVC 29 | - Disable building shared lib via CMake by default 30 | - Fixed alignment problems on MacOS 31 | - Fixed get error string for MZ_ZIP_TOTAL_ERRORS 32 | - Write correct FLEVEL 2-bit value in zlib header 33 | - miniz.pc.in: fix include path not containing the "miniz" suffix 34 | - Fix compatibility with FreeBSD 35 | - pkg-config tweaks 36 | - Fix integer overflow in header corruption check 37 | - Fix some warnings 38 | - tdefl_compress_normal: Avoid NULL ptr arithmetic UB 39 | - replace use of stdint.h types with mz_ variants 40 | 41 | 42 | ### 2.2.0 43 | 44 | - Fix examples with amalgamation 45 | - Modified cmake script to support shared library mode and find_package 46 | - Fix for misleading doc comment on `mz_zip_reader_init_cfile` function 47 | - Add include location tolerance and stop forcing `_GNU_SOURCE` 48 | - Fix: mz_zip_reader_locate_file_v2 returns an mz_bool 49 | - Fix large file system checks 50 | - Add #elif to enable an external mz_crc32() to be linked in 51 | - Write with dynamic size (size of file/data to be added not known before adding) 52 | - Added uncompress2 for zlib compatibility 53 | - Add support for building as a Meson subproject 54 | - Added OSSFuzz support; Integrate with CIFuzz 55 | - Add pkg-config file 56 | - Fixed use-of-uninitialized value msan error when copying dist bytes with no output bytes written. 57 | - mz_zip_validate_file(): fix memory leak on errors 58 | - Fixed MSAN use-of-uninitialized in tinfl_decompress when invalid dist is decoded. In this instance dist was 31 which s_dist_base translates as 0 59 | - Add flag to set (compressed) size in local file header 60 | - avoid use of uninitialized value in tdefl_record_literal 61 | 62 | ### 2.1.0 63 | 64 | - More instances of memcpy instead of cast and use memcpy per default 65 | - Remove inline for c90 support 66 | - New function to read files via callback functions when adding them 67 | - Fix out of bounds read while reading Zip64 extended information 68 | - guard memcpy when n == 0 because buffer may be NULL 69 | - Implement inflateReset() function 70 | - Move comp/decomp alloc/free prototypes under guarding #ifndef MZ_NO_MALLOC 71 | - Fix large file support under Windows 72 | - Don't warn if _LARGEFILE64_SOURCE is not defined to 1 73 | - Fixes for MSVC warnings 74 | - Remove check that path of file added to archive contains ':' or '\' 75 | - Add !defined check on MINIZ_USE_ALIGNED_LOADS_AND_STORES 76 | 77 | ### 2.0.8 78 | 79 | - Remove unimplemented functions (mz_zip_locate_file and mz_zip_locate_file_v2) 80 | - Add license, changelog, readme and example files to release zip 81 | - Fix heap overflow to user buffer in tinfl_status tinfl_decompress 82 | - Fix corrupt archive if uncompressed file smaller than 4 byte and the file is added by mz_zip_writer_add_mem* 83 | 84 | ### 2.0.7 85 | 86 | - Removed need in C++ compiler in cmake build 87 | - Fixed a lot of uninitialized value errors found with Valgrind by memsetting m_dict to 0 in tdefl_init 88 | - Fix resource leak in mz_zip_reader_init_file_v2 89 | - Fix assert with mz_zip_writer_add_mem* w/MZ_DEFAULT_COMPRESSION 90 | - cmake build: install library and headers 91 | - Remove _LARGEFILE64_SOURCE requirement from apple defines for large files 92 | 93 | ### 2.0.6 94 | 95 | - Improve MZ_ZIP_FLAG_WRITE_ZIP64 documentation 96 | - Remove check for cur_archive_file_ofs > UINT_MAX because cur_archive_file_ofs is not used after this point 97 | - Add cmake debug configuration 98 | - Fix PNG height when creating png files 99 | - Add "iterative" file extraction method based on mz_zip_reader_extract_to_callback. 100 | - Option to use memcpy for unaligned data access 101 | - Define processor/arch macros as zero if not set to one 102 | 103 | ### 2.0.4/2.0.5 104 | 105 | - Fix compilation with the various omission compile definitions 106 | 107 | ### 2.0.3 108 | 109 | - Fix GCC/clang compile warnings 110 | - Added callback for periodic flushes (for ZIP file streaming) 111 | - Use UTF-8 for file names in ZIP files per default 112 | 113 | ### 2.0.2 114 | 115 | - Fix source backwards compatibility with 1.x 116 | - Fix a ZIP bit not being set correctly 117 | 118 | ### 2.0.1 119 | 120 | - Added some tests 121 | - Added CI 122 | - Make source code ANSI C compatible 123 | 124 | ### 2.0.0 beta 125 | 126 | - Matthew Sitton merged miniz 1.x to Rich Geldreich's vogl ZIP64 changes. Miniz is now licensed as MIT since the vogl code base is MIT licensed 127 | - Miniz is now split into several files 128 | - Miniz does now not seek backwards when creating ZIP files. That is the ZIP files can be streamed 129 | - Miniz automatically switches to the ZIP64 format when the created ZIP files goes over ZIP file limits 130 | - Similar to [SQLite](https://www.sqlite.org/amalgamation.html) the Miniz source code is amalgamated into one miniz.c/miniz.h pair in a build step (amalgamate.sh). Please use miniz.c/miniz.h in your projects 131 | - Miniz 2 is only source back-compatible with miniz 1.x. It breaks binary compatibility because structures changed 132 | 133 | ### v1.16 BETA Oct 19, 2013 134 | 135 | Still testing, this release is downloadable from [here](http://www.tenacioussoftware.com/miniz_v116_beta_r1.7z). Two key inflator-only robustness and streaming related changes. Also merged in tdefl_compressor_alloc(), tdefl_compressor_free() helpers to make script bindings easier for rustyzip. I would greatly appreciate any help with testing or any feedback. 136 | 137 | The inflator in raw (non-zlib) mode is now usable on gzip or similar streams that have a bunch of bytes following the raw deflate data (problem discovered by rustyzip author williamw520). This version should never read beyond the last byte of the raw deflate data independent of how many bytes you pass into the input buffer. 138 | 139 | The inflator now has a new failure status TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS (-4). Previously, if the inflator was starved of bytes and could not make progress (because the input buffer was empty and the caller did not set the TINFL_FLAG_HAS_MORE_INPUT flag - say on truncated or corrupted compressed data stream) it would append all 0's to the input and try to soldier on. This is scary behavior if the caller didn't know when to stop accepting output (because it didn't know how much uncompressed data was expected, or didn't enforce a sane maximum). v1.16 will instead return TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS immediately if it needs 1 or more bytes to make progress, the input buf is empty, and the caller has indicated that no more input is available. This is a "soft" failure, so you can call the inflator again with more input and it will try to continue, or you can give up and fail. This could be very useful in network streaming scenarios. 140 | 141 | - The inflator coroutine func. is subtle and complex so I'm being cautious about this release. I would greatly appreciate any help with testing or any feedback. 142 | I feel good about these changes, and they've been through several hours of automated testing, but they will probably not fix anything for the majority of prev. users so I'm 143 | going to mark this release as beta for a few weeks and continue testing it at work/home on various things. 144 | - The inflator in raw (non-zlib) mode is now usable on gzip or similar data streams that have a bunch of bytes following the raw deflate data (problem discovered by rustyzip author williamw520). 145 | This version should *never* read beyond the last byte of the raw deflate data independent of how many bytes you pass into the input buffer. This issue was caused by the various Huffman bitbuffer lookahead optimizations, and 146 | would not be an issue if the caller knew and enforced the precise size of the raw compressed data *or* if the compressed data was in zlib format (i.e. always followed by the byte aligned zlib adler32). 147 | So in other words, you can now call the inflator on deflate streams that are followed by arbitrary amounts of data and it's guaranteed that decompression will stop exactly on the last byte. 148 | - The inflator now has a new failure status: TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS (-4). Previously, if the inflator was starved of bytes and could not make progress (because the input buffer was empty and the 149 | caller did not set the TINFL_FLAG_HAS_MORE_INPUT flag - say on truncated or corrupted compressed data stream) it would append all 0's to the input and try to soldier on. 150 | This is scary, because in the worst case, I believe it was possible for the prev. inflator to start outputting large amounts of literal data. If the caller didn't know when to stop accepting output 151 | (because it didn't know how much uncompressed data was expected, or didn't enforce a sane maximum) it could continue forever. v1.16 cannot fall into this failure mode, instead it'll return 152 | TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS immediately if it needs 1 or more bytes to make progress, the input buf is empty, and the caller has indicated that no more input is available. This is a "soft" 153 | failure, so you can call the inflator again with more input and it will try to continue, or you can give up and fail. This could be very useful in network streaming scenarios. 154 | - Added documentation to all the tinfl return status codes, fixed miniz_tester so it accepts double minus params for Linux, tweaked example1.c, added a simple "follower bytes" test to miniz_tester.cpp. 155 | ### v1.15 r4 STABLE - Oct 13, 2013 156 | 157 | Merged over a few very minor bug fixes that I fixed in the zip64 branch. This is downloadable from [here](http://code.google.com/p/miniz/downloads/list) and also in SVN head (as of 10/19/13). 158 | 159 | 160 | ### v1.15 - Oct. 13, 2013 161 | 162 | Interim bugfix release while I work on the next major release with zip64 and streaming compression/decompression support. Fixed the MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY bug (thanks kahmyong.moon@hp.com), which could cause the locate files func to not find files when this flag was specified. Also fixed a bug in mz_zip_reader_extract_to_mem_no_alloc() with user provided read buffers (thanks kymoon). I also merged lots of compiler fixes from various github repo branches and Google Code issue reports. I finally added cmake support (only tested under for Linux so far), compiled and tested with clang v3.3 and gcc 4.6 (under Linux), added defl_write_image_to_png_file_in_memory_ex() (supports Y flipping for OpenGL use, real-time compression), added a new PNG example (example6.c - Mandelbrot), and I added 64-bit file I/O support (stat64(), etc.) for glibc. 163 | 164 | - Critical fix for the MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY bug (thanks kahmyong.moon@hp.com) which could cause locate files to not find files. This bug 165 | would only have occurred in earlier versions if you explicitly used this flag, OR if you used mz_zip_extract_archive_file_to_heap() or mz_zip_add_mem_to_archive_file_in_place() 166 | (which used this flag). If you can't switch to v1.15 but want to fix this bug, just remove the uses of this flag from both helper funcs (and of course don't use the flag). 167 | - Bugfix in mz_zip_reader_extract_to_mem_no_alloc() from kymoon when pUser_read_buf is not NULL and compressed size is > uncompressed size 168 | - Fixing mz_zip_reader_extract_*() funcs so they don't try to extract compressed data from directory entries, to account for weird zipfiles which contain zero-size compressed data on dir entries. 169 | Hopefully this fix won't cause any issues on weird zip archives, because it assumes the low 16-bits of zip external attributes are DOS attributes (which I believe they always are in practice). 170 | - Fixing mz_zip_reader_is_file_a_directory() so it doesn't check the internal attributes, just the filename and external attributes 171 | - mz_zip_reader_init_file() - missing MZ_FCLOSE() call if the seek failed 172 | - Added cmake support for Linux builds which builds all the examples, tested with clang v3.3 and gcc v4.6. 173 | - Clang fix for tdefl_write_image_to_png_file_in_memory() from toffaletti 174 | - Merged MZ_FORCEINLINE fix from hdeanclark 175 | - Fix include before config #ifdef, thanks emil.brink 176 | - Added tdefl_write_image_to_png_file_in_memory_ex(): supports Y flipping (super useful for OpenGL apps), and explicit control over the compression level (so you can 177 | set it to 1 for real-time compression). 178 | - Merged in some compiler fixes from paulharris's github repro. 179 | - Retested this build under Windows (VS 2010, including static analysis), tcc 0.9.26, gcc v4.6 and clang v3.3. 180 | - Added example6.c, which dumps an image of the mandelbrot set to a PNG file. 181 | - Modified example2 to help test the MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY flag more. 182 | - In r3: Bugfix to mz_zip_writer_add_file() found during merge: Fix possible src file fclose() leak if alignment bytes+local header file write faiiled 183 | - In r4: Minor bugfix to mz_zip_writer_add_from_zip_reader(): Was pushing the wrong central dir header offset, appears harmless in this release, but it became a problem in the zip64 branch 184 | 185 | ### v1.14 - May 20, 2012 186 | 187 | (SVN Only) Minor tweaks to get miniz.c compiling with the Tiny C Compiler, added #ifndef MINIZ_NO_TIME guards around utime.h includes. Adding mz_free() function, so the caller can free heap blocks returned by miniz using whatever heap functions it has been configured to use, MSVC specific fixes to use "safe" variants of several functions (localtime_s, fopen_s, freopen_s). 188 | 189 | MinGW32/64 GCC 4.6.1 compiler fixes: added MZ_FORCEINLINE, #include (thanks fermtect). 190 | 191 | Compiler specific fixes, some from fermtect. I upgraded to TDM GCC 4.6.1 and now static __forceinline is giving it fits, so I'm changing all usage of __forceinline to MZ_FORCEINLINE and forcing gcc to use __attribute__((__always_inline__)) (and MSVC to use __forceinline). Also various fixes from fermtect for MinGW32: added #include , 64-bit ftell/fseek fixes. 192 | 193 | ### v1.13 - May 19, 2012 194 | 195 | From jason@cornsyrup.org and kelwert@mtu.edu - Most importantly, fixed mz_crc32() so it doesn't compute the wrong CRC-32's when mz_ulong is 64-bits. Temporarily/locally slammed in "typedef unsigned long mz_ulong" and re-ran a randomized regression test on ~500k files. Other stuff: 196 | 197 | Eliminated a bunch of warnings when compiling with GCC 32-bit/64. Ran all examples, miniz.c, and tinfl.c through MSVC 2008's /analyze (static analysis) option and fixed all warnings (except for the silly "Use of the comma-operator in a tested expression.." analysis warning, which I purposely use to work around a MSVC compiler warning). 198 | 199 | Created 32-bit and 64-bit Codeblocks projects/workspace. Built and tested Linux executables. The codeblocks workspace is compatible with Linux+Win32/x64. Added miniz_tester solution/project, which is a useful little app derived from LZHAM's tester app that I use as part of the regression test. Ran miniz.c and tinfl.c through another series of regression testing on ~500,000 files and archives. Modified example5.c so it purposely disables a bunch of high-level functionality (MINIZ_NO_STDIO, etc.). (Thanks to corysama for the MINIZ_NO_STDIO bug report.) 200 | 201 | Fix ftell() usage in a few of the examples so they exit with an error on files which are too large (a limitation of the examples, not miniz itself). Fix fail logic handling in mz_zip_add_mem_to_archive_file_in_place() so it always calls mz_zip_writer_finalize_archive() and mz_zip_writer_end(), even if the file add fails. 202 | 203 | - From jason@cornsyrup.org and kelwert@mtu.edu - Fix mz_crc32() so it doesn't compute the wrong CRC-32's when mz_ulong is 64-bit. 204 | - Temporarily/locally slammed in "typedef unsigned long mz_ulong" and re-ran a randomized regression test on ~500k files. 205 | - Eliminated a bunch of warnings when compiling with GCC 32-bit/64. 206 | - Ran all examples, miniz.c, and tinfl.c through MSVC 2008's /analyze (static analysis) option and fixed all warnings (except for the silly 207 | "Use of the comma-operator in a tested expression.." analysis warning, which I purposely use to work around a MSVC compiler warning). 208 | - Created 32-bit and 64-bit Codeblocks projects/workspace. Built and tested Linux executables. The codeblocks workspace is compatible with Linux+Win32/x64. 209 | - Added miniz_tester solution/project, which is a useful little app derived from LZHAM's tester app that I use as part of the regression test. 210 | - Ran miniz.c and tinfl.c through another series of regression testing on ~500,000 files and archives. 211 | - Modified example5.c so it purposely disables a bunch of high-level functionality (MINIZ_NO_STDIO, etc.). (Thanks to corysama for the MINIZ_NO_STDIO bug report.) 212 | - Fix ftell() usage in examples so they exit with an error on files which are too large (a limitation of the examples, not miniz itself). 213 | 214 | ### v1.12 - 4/12/12 215 | 216 | More comments, added low-level example5.c, fixed a couple minor level_and_flags issues in the archive API's. 217 | level_and_flags can now be set to MZ_DEFAULT_COMPRESSION. Thanks to Bruce Dawson for the feedback/bug report. 218 | 219 | ### v1.11 - 5/28/11 220 | 221 | Added statement from unlicense.org 222 | 223 | ### v1.10 - 5/27/11 224 | 225 | - Substantial compressor optimizations: 226 | - Level 1 is now ~4x faster than before. The L1 compressor's throughput now varies between 70-110MB/sec. on a Core i7 (actual throughput varies depending on the type of data, and x64 vs. x86). 227 | - Improved baseline L2-L9 compression perf. Also, greatly improved compression perf. issues on some file types. 228 | - Refactored the compression code for better readability and maintainability. 229 | - Added level 10 compression level (L10 has slightly better ratio than level 9, but could have a potentially large drop in throughput on some files). 230 | 231 | ### v1.09 - 5/15/11 232 | 233 | Initial stable release. 234 | 235 | 236 | -------------------------------------------------------------------------------- /deps/miniz/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2013-2014 RAD Game Tools and Valve Software 2 | Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC 3 | 4 | All Rights Reserved. 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /deps/miniz/readme.md: -------------------------------------------------------------------------------- 1 | ## Miniz 2 | 3 | Miniz is a lossless, high performance data compression library in a single source file that implements the zlib (RFC 1950) and Deflate (RFC 1951) compressed data format specification standards. It supports the most commonly used functions exported by the zlib library, but is a completely independent implementation so zlib's licensing requirements do not apply. Miniz also contains simple to use functions for writing .PNG format image files and reading/writing/appending .ZIP format archives. Miniz's compression speed has been tuned to be comparable to zlib's, and it also has a specialized real-time compressor function designed to compare well against fastlz/minilzo. 4 | 5 | ## Usage 6 | 7 | Releases are available at the [releases page](https://github.com/richgel999/miniz/releases) as a pair of `miniz.c`/`miniz.h` files which can be simply added to a project. To create this file pair the different source and header files are [amalgamated](https://www.sqlite.org/amalgamation.html) during build. Alternatively use as cmake or meson module (or build system of your choice). 8 | 9 | ## Features 10 | 11 | * MIT licensed 12 | * A portable, single source and header file library written in plain C. Tested with GCC, clang and Visual Studio. 13 | * Easily tuned and trimmed down by defines 14 | * A drop-in replacement for zlib's most used API's (tested in several open source projects that use zlib, such as libpng and libzip). 15 | * Fills a single threaded performance vs. compression ratio gap between several popular real-time compressors and zlib. For example, at level 1, miniz.c compresses around 5-9% better than minilzo, but is approx. 35% slower. At levels 2-9, miniz.c is designed to compare favorably against zlib's ratio and speed. See the miniz performance comparison page for example timings. 16 | * Not a block based compressor: miniz.c fully supports stream based processing using a coroutine-style implementation. The zlib-style API functions can be called a single byte at a time if that's all you've got. 17 | * Easy to use. The low-level compressor (tdefl) and decompressor (tinfl) have simple state structs which can be saved/restored as needed with simple memcpy's. The low-level codec API's don't use the heap in any way. 18 | * Entire inflater (including optional zlib header parsing and Adler-32 checking) is implemented in a single function as a coroutine, which is separately available in a small (~550 line) source file: miniz_tinfl.c 19 | * A fairly complete (but totally optional) set of .ZIP archive manipulation and extraction API's. The archive functionality is intended to solve common problems encountered in embedded, mobile, or game development situations. (The archive API's are purposely just powerful enough to write an entire archiver given a bit of additional higher-level logic.) 20 | 21 | ## Building miniz - Using vcpkg 22 | 23 | You can download and install miniz using the [vcpkg](https://github.com/Microsoft/vcpkg) dependency manager: 24 | 25 | git clone https://github.com/Microsoft/vcpkg.git 26 | cd vcpkg 27 | ./bootstrap-vcpkg.sh 28 | ./vcpkg integrate install 29 | ./vcpkg install miniz 30 | 31 | The miniz port in vcpkg is kept up to date by Microsoft team members and community contributors. If the version is out of date, please [create an issue or pull request](https://github.com/Microsoft/vcpkg) on the vcpkg repository. 32 | 33 | ## Known Problems 34 | 35 | * No support for encrypted archives. Not sure how useful this stuff is in practice. 36 | * Minimal documentation. The assumption is that the user is already familiar with the basic zlib API. I need to write an API wiki - for now I've tried to place key comments before each enum/API, and I've included 6 examples that demonstrate how to use the module's major features. 37 | 38 | ## Special Thanks 39 | 40 | Thanks to Alex Evans for the PNG writer function. Also, thanks to Paul Holden and Thorsten Scheuermann for feedback and testing, Matt Pritchard for all his encouragement, and Sean Barrett's various public domain libraries for inspiration (and encouraging me to write miniz.c in C, which was much more enjoyable and less painful than I thought it would be considering I've been programming in C++ for so long). 41 | 42 | Thanks to Bruce Dawson for reporting a problem with the level_and_flags archive API parameter (which is fixed in v1.12) and general feedback, and Janez Zemva for indirectly encouraging me into writing more examples. 43 | 44 | ## Patents 45 | 46 | I was recently asked if miniz avoids patent issues. miniz purposely uses the same core algorithms as the ones used by zlib. The compressor uses vanilla hash chaining as described [here](https://datatracker.ietf.org/doc/html/rfc1951#section-4). Also see the [gzip FAQ](https://web.archive.org/web/20160308045258/http://www.gzip.org/#faq11). In my opinion, if miniz falls prey to a patent attack then zlib/gzip are likely to be at serious risk too. 47 | -------------------------------------------------------------------------------- /deps/noise/noise1234.c: -------------------------------------------------------------------------------- 1 | // noise1234 2 | // 3 | // Author: Stefan Gustavson, 2003-2005 4 | // Contact: stefan.gustavson@liu.se 5 | // 6 | // This code was GPL licensed until February 2011. 7 | // As the original author of this code, I hereby 8 | // release it into the public domain. 9 | // Please feel free to use it for whatever you want. 10 | // Credit is appreciated where appropriate, and I also 11 | // appreciate being told where this code finds any use, 12 | // but you may do as you like. 13 | 14 | /* 15 | * This implementation is "Improved Noise" as presented by 16 | * Ken Perlin at Siggraph 2002. The 3D function is a direct port 17 | * of his Java reference code which was once publicly available 18 | * on www.noisemachine.com (although I cleaned it up, made it 19 | * faster and made the code more readable), but the 1D, 2D and 20 | * 4D functions were implemented from scratch by me. 21 | * 22 | * This is a backport to C of my improved noise class in C++ 23 | * which was included in the Aqsis renderer project. 24 | * It is highly reusable without source code modifications. 25 | * 26 | */ 27 | 28 | #include "noise1234.h" 29 | 30 | // This is the new and improved, C(2) continuous interpolant 31 | #define FADE(t) ( t * t * t * ( t * ( t * 6 - 15 ) + 10 ) ) 32 | 33 | #define FASTFLOOR(x) ( ((int)(x)<(x)) ? ((int)x) : ((int)x-1 ) ) 34 | #define LERP(t, a, b) ((a) + (t)*((b)-(a))) 35 | 36 | 37 | //--------------------------------------------------------------------- 38 | // Static data 39 | 40 | /* 41 | * Permutation table. This is just a random jumble of all numbers 0-255, 42 | * repeated twice to avoid wrapping the index at 255 for each lookup. 43 | * This needs to be exactly the same for all instances on all platforms, 44 | * so it's easiest to just keep it as static explicit data. 45 | * This also removes the need for any initialisation of this class. 46 | * 47 | * Note that making this an int[] instead of a char[] might make the 48 | * code run faster on platforms with a high penalty for unaligned single 49 | * byte addressing. Intel x86 is generally single-byte-friendly, but 50 | * some other CPUs are faster with 4-aligned reads. 51 | * However, a char[] is smaller, which avoids cache trashing, and that 52 | * is probably the most important aspect on most architectures. 53 | * This array is accessed a *lot* by the noise functions. 54 | * A vector-valued noise over 3D accesses it 96 times, and a 55 | * float-valued 4D noise 64 times. We want this to fit in the cache! 56 | */ 57 | unsigned char perm[] = {151,160,137,91,90,15, 58 | 131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23, 59 | 190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33, 60 | 88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166, 61 | 77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244, 62 | 102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196, 63 | 135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123, 64 | 5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42, 65 | 223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9, 66 | 129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228, 67 | 251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107, 68 | 49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254, 69 | 138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180, 70 | 151,160,137,91,90,15, 71 | 131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23, 72 | 190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33, 73 | 88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166, 74 | 77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244, 75 | 102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196, 76 | 135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123, 77 | 5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42, 78 | 223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9, 79 | 129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228, 80 | 251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107, 81 | 49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254, 82 | 138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180 83 | }; 84 | 85 | //--------------------------------------------------------------------- 86 | 87 | /* 88 | * Helper functions to compute gradients-dot-residualvectors (1D to 4D) 89 | * Note that these generate gradients of more than unit length. To make 90 | * a close match with the value range of classic Perlin noise, the final 91 | * noise values need to be rescaled. To match the RenderMan noise in a 92 | * statistical sense, the approximate scaling values (empirically 93 | * determined from test renderings) are: 94 | * 1D noise needs rescaling with 0.188 95 | * 2D noise needs rescaling with 0.507 96 | * 3D noise needs rescaling with 0.936 97 | * 4D noise needs rescaling with 0.87 98 | * Note that these noise functions are the most practical and useful 99 | * signed version of Perlin noise. To return values according to the 100 | * RenderMan specification from the SL noise() and pnoise() functions, 101 | * the noise values need to be scaled and offset to [0,1], like this: 102 | * float SLnoise = (noise3(x,y,z) + 1.0) * 0.5; 103 | */ 104 | 105 | float grad1( int hash, float x ) { 106 | int h = hash & 15; 107 | float grad = 1.0 + (h & 7); // Gradient value 1.0, 2.0, ..., 8.0 108 | if (h&8) grad = -grad; // and a random sign for the gradient 109 | return ( grad * x ); // Multiply the gradient with the distance 110 | } 111 | 112 | float grad2( int hash, float x, float y ) { 113 | int h = hash & 7; // Convert low 3 bits of hash code 114 | float u = h<4 ? x : y; // into 8 simple gradient directions, 115 | float v = h<4 ? y : x; // and compute the dot product with (x,y). 116 | return ((h&1)? -u : u) + ((h&2)? -2.0*v : 2.0*v); 117 | } 118 | 119 | float grad3( int hash, float x, float y , float z ) { 120 | int h = hash & 15; // Convert low 4 bits of hash code into 12 simple 121 | float u = h<8 ? x : y; // gradient directions, and compute dot product. 122 | float v = h<4 ? y : h==12||h==14 ? x : z; // Fix repeats at h = 12 to 15 123 | return ((h&1)? -u : u) + ((h&2)? -v : v); 124 | } 125 | 126 | float grad4( int hash, float x, float y, float z, float t ) { 127 | int h = hash & 31; // Convert low 5 bits of hash code into 32 simple 128 | float u = h<24 ? x : y; // gradient directions, and compute dot product. 129 | float v = h<16 ? y : z; 130 | float w = h<8 ? z : t; 131 | return ((h&1)? -u : u) + ((h&2)? -v : v) + ((h&4)? -w : w); 132 | } 133 | 134 | //--------------------------------------------------------------------- 135 | /** 1D float Perlin noise, SL "noise()" 136 | */ 137 | float noise1( float x ) 138 | { 139 | int ix0, ix1; 140 | float fx0, fx1; 141 | float s, n0, n1; 142 | 143 | ix0 = FASTFLOOR( x ); // Integer part of x 144 | fx0 = x - ix0; // Fractional part of x 145 | fx1 = fx0 - 1.0f; 146 | ix1 = ( ix0+1 ) & 0xff; 147 | ix0 = ix0 & 0xff; // Wrap to 0..255 148 | 149 | s = FADE( fx0 ); 150 | 151 | n0 = grad1( perm[ ix0 ], fx0 ); 152 | n1 = grad1( perm[ ix1 ], fx1 ); 153 | return 0.188f * ( LERP( s, n0, n1 ) ); 154 | } 155 | 156 | //--------------------------------------------------------------------- 157 | /** 1D float Perlin periodic noise, SL "pnoise()" 158 | */ 159 | float pnoise1( float x, int px ) 160 | { 161 | int ix0, ix1; 162 | float fx0, fx1; 163 | float s, n0, n1; 164 | 165 | ix0 = FASTFLOOR( x ); // Integer part of x 166 | fx0 = x - ix0; // Fractional part of x 167 | fx1 = fx0 - 1.0f; 168 | ix1 = (( ix0 + 1 ) % px) & 0xff; // Wrap to 0..px-1 *and* wrap to 0..255 169 | ix0 = ( ix0 % px ) & 0xff; // (because px might be greater than 256) 170 | 171 | s = FADE( fx0 ); 172 | 173 | n0 = grad1( perm[ ix0 ], fx0 ); 174 | n1 = grad1( perm[ ix1 ], fx1 ); 175 | return 0.188f * ( LERP( s, n0, n1 ) ); 176 | } 177 | 178 | 179 | //--------------------------------------------------------------------- 180 | /** 2D float Perlin noise. 181 | */ 182 | float noise2( float x, float y ) 183 | { 184 | int ix0, iy0, ix1, iy1; 185 | float fx0, fy0, fx1, fy1; 186 | float s, t, nx0, nx1, n0, n1; 187 | 188 | ix0 = FASTFLOOR( x ); // Integer part of x 189 | iy0 = FASTFLOOR( y ); // Integer part of y 190 | fx0 = x - ix0; // Fractional part of x 191 | fy0 = y - iy0; // Fractional part of y 192 | fx1 = fx0 - 1.0f; 193 | fy1 = fy0 - 1.0f; 194 | ix1 = (ix0 + 1) & 0xff; // Wrap to 0..255 195 | iy1 = (iy0 + 1) & 0xff; 196 | ix0 = ix0 & 0xff; 197 | iy0 = iy0 & 0xff; 198 | 199 | t = FADE( fy0 ); 200 | s = FADE( fx0 ); 201 | 202 | nx0 = grad2(perm[ix0 + perm[iy0]], fx0, fy0); 203 | nx1 = grad2(perm[ix0 + perm[iy1]], fx0, fy1); 204 | n0 = LERP( t, nx0, nx1 ); 205 | 206 | nx0 = grad2(perm[ix1 + perm[iy0]], fx1, fy0); 207 | nx1 = grad2(perm[ix1 + perm[iy1]], fx1, fy1); 208 | n1 = LERP(t, nx0, nx1); 209 | 210 | return 0.507f * ( LERP( s, n0, n1 ) ); 211 | } 212 | 213 | //--------------------------------------------------------------------- 214 | /** 2D float Perlin periodic noise. 215 | */ 216 | float pnoise2( float x, float y, int px, int py ) 217 | { 218 | int ix0, iy0, ix1, iy1; 219 | float fx0, fy0, fx1, fy1; 220 | float s, t, nx0, nx1, n0, n1; 221 | 222 | ix0 = FASTFLOOR( x ); // Integer part of x 223 | iy0 = FASTFLOOR( y ); // Integer part of y 224 | fx0 = x - ix0; // Fractional part of x 225 | fy0 = y - iy0; // Fractional part of y 226 | fx1 = fx0 - 1.0f; 227 | fy1 = fy0 - 1.0f; 228 | ix1 = (( ix0 + 1 ) % px) & 0xff; // Wrap to 0..px-1 and wrap to 0..255 229 | iy1 = (( iy0 + 1 ) % py) & 0xff; // Wrap to 0..py-1 and wrap to 0..255 230 | ix0 = ( ix0 % px ) & 0xff; 231 | iy0 = ( iy0 % py ) & 0xff; 232 | 233 | t = FADE( fy0 ); 234 | s = FADE( fx0 ); 235 | 236 | nx0 = grad2(perm[ix0 + perm[iy0]], fx0, fy0); 237 | nx1 = grad2(perm[ix0 + perm[iy1]], fx0, fy1); 238 | n0 = LERP( t, nx0, nx1 ); 239 | 240 | nx0 = grad2(perm[ix1 + perm[iy0]], fx1, fy0); 241 | nx1 = grad2(perm[ix1 + perm[iy1]], fx1, fy1); 242 | n1 = LERP(t, nx0, nx1); 243 | 244 | return 0.507f * ( LERP( s, n0, n1 ) ); 245 | } 246 | 247 | 248 | //--------------------------------------------------------------------- 249 | /** 3D float Perlin noise. 250 | */ 251 | float noise3( float x, float y, float z ) 252 | { 253 | int ix0, iy0, ix1, iy1, iz0, iz1; 254 | float fx0, fy0, fz0, fx1, fy1, fz1; 255 | float s, t, r; 256 | float nxy0, nxy1, nx0, nx1, n0, n1; 257 | 258 | ix0 = FASTFLOOR( x ); // Integer part of x 259 | iy0 = FASTFLOOR( y ); // Integer part of y 260 | iz0 = FASTFLOOR( z ); // Integer part of z 261 | fx0 = x - ix0; // Fractional part of x 262 | fy0 = y - iy0; // Fractional part of y 263 | fz0 = z - iz0; // Fractional part of z 264 | fx1 = fx0 - 1.0f; 265 | fy1 = fy0 - 1.0f; 266 | fz1 = fz0 - 1.0f; 267 | ix1 = ( ix0 + 1 ) & 0xff; // Wrap to 0..255 268 | iy1 = ( iy0 + 1 ) & 0xff; 269 | iz1 = ( iz0 + 1 ) & 0xff; 270 | ix0 = ix0 & 0xff; 271 | iy0 = iy0 & 0xff; 272 | iz0 = iz0 & 0xff; 273 | 274 | r = FADE( fz0 ); 275 | t = FADE( fy0 ); 276 | s = FADE( fx0 ); 277 | 278 | nxy0 = grad3(perm[ix0 + perm[iy0 + perm[iz0]]], fx0, fy0, fz0); 279 | nxy1 = grad3(perm[ix0 + perm[iy0 + perm[iz1]]], fx0, fy0, fz1); 280 | nx0 = LERP( r, nxy0, nxy1 ); 281 | 282 | nxy0 = grad3(perm[ix0 + perm[iy1 + perm[iz0]]], fx0, fy1, fz0); 283 | nxy1 = grad3(perm[ix0 + perm[iy1 + perm[iz1]]], fx0, fy1, fz1); 284 | nx1 = LERP( r, nxy0, nxy1 ); 285 | 286 | n0 = LERP( t, nx0, nx1 ); 287 | 288 | nxy0 = grad3(perm[ix1 + perm[iy0 + perm[iz0]]], fx1, fy0, fz0); 289 | nxy1 = grad3(perm[ix1 + perm[iy0 + perm[iz1]]], fx1, fy0, fz1); 290 | nx0 = LERP( r, nxy0, nxy1 ); 291 | 292 | nxy0 = grad3(perm[ix1 + perm[iy1 + perm[iz0]]], fx1, fy1, fz0); 293 | nxy1 = grad3(perm[ix1 + perm[iy1 + perm[iz1]]], fx1, fy1, fz1); 294 | nx1 = LERP( r, nxy0, nxy1 ); 295 | 296 | n1 = LERP( t, nx0, nx1 ); 297 | 298 | return 0.936f * ( LERP( s, n0, n1 ) ); 299 | } 300 | 301 | //--------------------------------------------------------------------- 302 | /** 3D float Perlin periodic noise. 303 | */ 304 | float pnoise3( float x, float y, float z, int px, int py, int pz ) 305 | { 306 | int ix0, iy0, ix1, iy1, iz0, iz1; 307 | float fx0, fy0, fz0, fx1, fy1, fz1; 308 | float s, t, r; 309 | float nxy0, nxy1, nx0, nx1, n0, n1; 310 | 311 | ix0 = FASTFLOOR( x ); // Integer part of x 312 | iy0 = FASTFLOOR( y ); // Integer part of y 313 | iz0 = FASTFLOOR( z ); // Integer part of z 314 | fx0 = x - ix0; // Fractional part of x 315 | fy0 = y - iy0; // Fractional part of y 316 | fz0 = z - iz0; // Fractional part of z 317 | fx1 = fx0 - 1.0f; 318 | fy1 = fy0 - 1.0f; 319 | fz1 = fz0 - 1.0f; 320 | ix1 = (( ix0 + 1 ) % px ) & 0xff; // Wrap to 0..px-1 and wrap to 0..255 321 | iy1 = (( iy0 + 1 ) % py ) & 0xff; // Wrap to 0..py-1 and wrap to 0..255 322 | iz1 = (( iz0 + 1 ) % pz ) & 0xff; // Wrap to 0..pz-1 and wrap to 0..255 323 | ix0 = ( ix0 % px ) & 0xff; 324 | iy0 = ( iy0 % py ) & 0xff; 325 | iz0 = ( iz0 % pz ) & 0xff; 326 | 327 | r = FADE( fz0 ); 328 | t = FADE( fy0 ); 329 | s = FADE( fx0 ); 330 | 331 | nxy0 = grad3(perm[ix0 + perm[iy0 + perm[iz0]]], fx0, fy0, fz0); 332 | nxy1 = grad3(perm[ix0 + perm[iy0 + perm[iz1]]], fx0, fy0, fz1); 333 | nx0 = LERP( r, nxy0, nxy1 ); 334 | 335 | nxy0 = grad3(perm[ix0 + perm[iy1 + perm[iz0]]], fx0, fy1, fz0); 336 | nxy1 = grad3(perm[ix0 + perm[iy1 + perm[iz1]]], fx0, fy1, fz1); 337 | nx1 = LERP( r, nxy0, nxy1 ); 338 | 339 | n0 = LERP( t, nx0, nx1 ); 340 | 341 | nxy0 = grad3(perm[ix1 + perm[iy0 + perm[iz0]]], fx1, fy0, fz0); 342 | nxy1 = grad3(perm[ix1 + perm[iy0 + perm[iz1]]], fx1, fy0, fz1); 343 | nx0 = LERP( r, nxy0, nxy1 ); 344 | 345 | nxy0 = grad3(perm[ix1 + perm[iy1 + perm[iz0]]], fx1, fy1, fz0); 346 | nxy1 = grad3(perm[ix1 + perm[iy1 + perm[iz1]]], fx1, fy1, fz1); 347 | nx1 = LERP( r, nxy0, nxy1 ); 348 | 349 | n1 = LERP( t, nx0, nx1 ); 350 | 351 | return 0.936f * ( LERP( s, n0, n1 ) ); 352 | } 353 | 354 | 355 | //--------------------------------------------------------------------- 356 | /** 4D float Perlin noise. 357 | */ 358 | 359 | float noise4( float x, float y, float z, float w ) 360 | { 361 | int ix0, iy0, iz0, iw0, ix1, iy1, iz1, iw1; 362 | float fx0, fy0, fz0, fw0, fx1, fy1, fz1, fw1; 363 | float s, t, r, q; 364 | float nxyz0, nxyz1, nxy0, nxy1, nx0, nx1, n0, n1; 365 | 366 | ix0 = FASTFLOOR( x ); // Integer part of x 367 | iy0 = FASTFLOOR( y ); // Integer part of y 368 | iz0 = FASTFLOOR( z ); // Integer part of y 369 | iw0 = FASTFLOOR( w ); // Integer part of w 370 | fx0 = x - ix0; // Fractional part of x 371 | fy0 = y - iy0; // Fractional part of y 372 | fz0 = z - iz0; // Fractional part of z 373 | fw0 = w - iw0; // Fractional part of w 374 | fx1 = fx0 - 1.0f; 375 | fy1 = fy0 - 1.0f; 376 | fz1 = fz0 - 1.0f; 377 | fw1 = fw0 - 1.0f; 378 | ix1 = ( ix0 + 1 ) & 0xff; // Wrap to 0..255 379 | iy1 = ( iy0 + 1 ) & 0xff; 380 | iz1 = ( iz0 + 1 ) & 0xff; 381 | iw1 = ( iw0 + 1 ) & 0xff; 382 | ix0 = ix0 & 0xff; 383 | iy0 = iy0 & 0xff; 384 | iz0 = iz0 & 0xff; 385 | iw0 = iw0 & 0xff; 386 | 387 | q = FADE( fw0 ); 388 | r = FADE( fz0 ); 389 | t = FADE( fy0 ); 390 | s = FADE( fx0 ); 391 | 392 | nxyz0 = grad4(perm[ix0 + perm[iy0 + perm[iz0 + perm[iw0]]]], fx0, fy0, fz0, fw0); 393 | nxyz1 = grad4(perm[ix0 + perm[iy0 + perm[iz0 + perm[iw1]]]], fx0, fy0, fz0, fw1); 394 | nxy0 = LERP( q, nxyz0, nxyz1 ); 395 | 396 | nxyz0 = grad4(perm[ix0 + perm[iy0 + perm[iz1 + perm[iw0]]]], fx0, fy0, fz1, fw0); 397 | nxyz1 = grad4(perm[ix0 + perm[iy0 + perm[iz1 + perm[iw1]]]], fx0, fy0, fz1, fw1); 398 | nxy1 = LERP( q, nxyz0, nxyz1 ); 399 | 400 | nx0 = LERP ( r, nxy0, nxy1 ); 401 | 402 | nxyz0 = grad4(perm[ix0 + perm[iy1 + perm[iz0 + perm[iw0]]]], fx0, fy1, fz0, fw0); 403 | nxyz1 = grad4(perm[ix0 + perm[iy1 + perm[iz0 + perm[iw1]]]], fx0, fy1, fz0, fw1); 404 | nxy0 = LERP( q, nxyz0, nxyz1 ); 405 | 406 | nxyz0 = grad4(perm[ix0 + perm[iy1 + perm[iz1 + perm[iw0]]]], fx0, fy1, fz1, fw0); 407 | nxyz1 = grad4(perm[ix0 + perm[iy1 + perm[iz1 + perm[iw1]]]], fx0, fy1, fz1, fw1); 408 | nxy1 = LERP( q, nxyz0, nxyz1 ); 409 | 410 | nx1 = LERP ( r, nxy0, nxy1 ); 411 | 412 | n0 = LERP( t, nx0, nx1 ); 413 | 414 | nxyz0 = grad4(perm[ix1 + perm[iy0 + perm[iz0 + perm[iw0]]]], fx1, fy0, fz0, fw0); 415 | nxyz1 = grad4(perm[ix1 + perm[iy0 + perm[iz0 + perm[iw1]]]], fx1, fy0, fz0, fw1); 416 | nxy0 = LERP( q, nxyz0, nxyz1 ); 417 | 418 | nxyz0 = grad4(perm[ix1 + perm[iy0 + perm[iz1 + perm[iw0]]]], fx1, fy0, fz1, fw0); 419 | nxyz1 = grad4(perm[ix1 + perm[iy0 + perm[iz1 + perm[iw1]]]], fx1, fy0, fz1, fw1); 420 | nxy1 = LERP( q, nxyz0, nxyz1 ); 421 | 422 | nx0 = LERP ( r, nxy0, nxy1 ); 423 | 424 | nxyz0 = grad4(perm[ix1 + perm[iy1 + perm[iz0 + perm[iw0]]]], fx1, fy1, fz0, fw0); 425 | nxyz1 = grad4(perm[ix1 + perm[iy1 + perm[iz0 + perm[iw1]]]], fx1, fy1, fz0, fw1); 426 | nxy0 = LERP( q, nxyz0, nxyz1 ); 427 | 428 | nxyz0 = grad4(perm[ix1 + perm[iy1 + perm[iz1 + perm[iw0]]]], fx1, fy1, fz1, fw0); 429 | nxyz1 = grad4(perm[ix1 + perm[iy1 + perm[iz1 + perm[iw1]]]], fx1, fy1, fz1, fw1); 430 | nxy1 = LERP( q, nxyz0, nxyz1 ); 431 | 432 | nx1 = LERP ( r, nxy0, nxy1 ); 433 | 434 | n1 = LERP( t, nx0, nx1 ); 435 | 436 | return 0.87f * ( LERP( s, n0, n1 ) ); 437 | } 438 | 439 | //--------------------------------------------------------------------- 440 | /** 4D float Perlin periodic noise. 441 | */ 442 | 443 | float pnoise4( float x, float y, float z, float w, 444 | int px, int py, int pz, int pw ) 445 | { 446 | int ix0, iy0, iz0, iw0, ix1, iy1, iz1, iw1; 447 | float fx0, fy0, fz0, fw0, fx1, fy1, fz1, fw1; 448 | float s, t, r, q; 449 | float nxyz0, nxyz1, nxy0, nxy1, nx0, nx1, n0, n1; 450 | 451 | ix0 = FASTFLOOR( x ); // Integer part of x 452 | iy0 = FASTFLOOR( y ); // Integer part of y 453 | iz0 = FASTFLOOR( z ); // Integer part of y 454 | iw0 = FASTFLOOR( w ); // Integer part of w 455 | fx0 = x - ix0; // Fractional part of x 456 | fy0 = y - iy0; // Fractional part of y 457 | fz0 = z - iz0; // Fractional part of z 458 | fw0 = w - iw0; // Fractional part of w 459 | fx1 = fx0 - 1.0f; 460 | fy1 = fy0 - 1.0f; 461 | fz1 = fz0 - 1.0f; 462 | fw1 = fw0 - 1.0f; 463 | ix1 = (( ix0 + 1 ) % px ) & 0xff; // Wrap to 0..px-1 and wrap to 0..255 464 | iy1 = (( iy0 + 1 ) % py ) & 0xff; // Wrap to 0..py-1 and wrap to 0..255 465 | iz1 = (( iz0 + 1 ) % pz ) & 0xff; // Wrap to 0..pz-1 and wrap to 0..255 466 | iw1 = (( iw0 + 1 ) % pw ) & 0xff; // Wrap to 0..pw-1 and wrap to 0..255 467 | ix0 = ( ix0 % px ) & 0xff; 468 | iy0 = ( iy0 % py ) & 0xff; 469 | iz0 = ( iz0 % pz ) & 0xff; 470 | iw0 = ( iw0 % pw ) & 0xff; 471 | 472 | q = FADE( fw0 ); 473 | r = FADE( fz0 ); 474 | t = FADE( fy0 ); 475 | s = FADE( fx0 ); 476 | 477 | nxyz0 = grad4(perm[ix0 + perm[iy0 + perm[iz0 + perm[iw0]]]], fx0, fy0, fz0, fw0); 478 | nxyz1 = grad4(perm[ix0 + perm[iy0 + perm[iz0 + perm[iw1]]]], fx0, fy0, fz0, fw1); 479 | nxy0 = LERP( q, nxyz0, nxyz1 ); 480 | 481 | nxyz0 = grad4(perm[ix0 + perm[iy0 + perm[iz1 + perm[iw0]]]], fx0, fy0, fz1, fw0); 482 | nxyz1 = grad4(perm[ix0 + perm[iy0 + perm[iz1 + perm[iw1]]]], fx0, fy0, fz1, fw1); 483 | nxy1 = LERP( q, nxyz0, nxyz1 ); 484 | 485 | nx0 = LERP ( r, nxy0, nxy1 ); 486 | 487 | nxyz0 = grad4(perm[ix0 + perm[iy1 + perm[iz0 + perm[iw0]]]], fx0, fy1, fz0, fw0); 488 | nxyz1 = grad4(perm[ix0 + perm[iy1 + perm[iz0 + perm[iw1]]]], fx0, fy1, fz0, fw1); 489 | nxy0 = LERP( q, nxyz0, nxyz1 ); 490 | 491 | nxyz0 = grad4(perm[ix0 + perm[iy1 + perm[iz1 + perm[iw0]]]], fx0, fy1, fz1, fw0); 492 | nxyz1 = grad4(perm[ix0 + perm[iy1 + perm[iz1 + perm[iw1]]]], fx0, fy1, fz1, fw1); 493 | nxy1 = LERP( q, nxyz0, nxyz1 ); 494 | 495 | nx1 = LERP ( r, nxy0, nxy1 ); 496 | 497 | n0 = LERP( t, nx0, nx1 ); 498 | 499 | nxyz0 = grad4(perm[ix1 + perm[iy0 + perm[iz0 + perm[iw0]]]], fx1, fy0, fz0, fw0); 500 | nxyz1 = grad4(perm[ix1 + perm[iy0 + perm[iz0 + perm[iw1]]]], fx1, fy0, fz0, fw1); 501 | nxy0 = LERP( q, nxyz0, nxyz1 ); 502 | 503 | nxyz0 = grad4(perm[ix1 + perm[iy0 + perm[iz1 + perm[iw0]]]], fx1, fy0, fz1, fw0); 504 | nxyz1 = grad4(perm[ix1 + perm[iy0 + perm[iz1 + perm[iw1]]]], fx1, fy0, fz1, fw1); 505 | nxy1 = LERP( q, nxyz0, nxyz1 ); 506 | 507 | nx0 = LERP ( r, nxy0, nxy1 ); 508 | 509 | nxyz0 = grad4(perm[ix1 + perm[iy1 + perm[iz0 + perm[iw0]]]], fx1, fy1, fz0, fw0); 510 | nxyz1 = grad4(perm[ix1 + perm[iy1 + perm[iz0 + perm[iw1]]]], fx1, fy1, fz0, fw1); 511 | nxy0 = LERP( q, nxyz0, nxyz1 ); 512 | 513 | nxyz0 = grad4(perm[ix1 + perm[iy1 + perm[iz1 + perm[iw0]]]], fx1, fy1, fz1, fw0); 514 | nxyz1 = grad4(perm[ix1 + perm[iy1 + perm[iz1 + perm[iw1]]]], fx1, fy1, fz1, fw1); 515 | nxy1 = LERP( q, nxyz0, nxyz1 ); 516 | 517 | nx1 = LERP ( r, nxy0, nxy1 ); 518 | 519 | n1 = LERP( t, nx0, nx1 ); 520 | 521 | return 0.87f * ( LERP( s, n0, n1 ) ); 522 | } 523 | 524 | //--------------------------------------------------------------------- -------------------------------------------------------------------------------- /deps/noise/noise1234.h: -------------------------------------------------------------------------------- 1 | // noise1234 2 | // 3 | // Author: Stefan Gustavson, 2003-2005 4 | // Contact: stefan.gustavson@liu.se 5 | // 6 | // This code was GPL licensed until February 2011. 7 | // As the original author of this code, I hereby 8 | // release it into the public domain. 9 | // Please feel free to use it for whatever you want. 10 | // Credit is appreciated where appropriate, and I also 11 | // appreciate being told where this code finds any use, 12 | // but you may do as you like. 13 | 14 | /* 15 | * This implementation is "Improved Noise" as presented by 16 | * Ken Perlin at Siggraph 2002. The 3D function is a direct port 17 | * of his Java reference code which was once publicly available 18 | * on www.noisemachine.com (although I cleaned it up, made it 19 | * faster and made the code more readable), but the 1D, 2D and 20 | * 4D functions were implemented from scratch by me. 21 | * 22 | * This is a backport to C of my improved noise class in C++ 23 | * which was included in the Aqsis renderer project. 24 | * It is highly reusable without source code modifications. 25 | * 26 | */ 27 | 28 | /** 1D, 2D, 3D and 4D float Perlin noise 29 | */ 30 | extern float noise1( float x ); 31 | extern float noise2( float x, float y ); 32 | extern float noise3( float x, float y, float z ); 33 | extern float noise4( float x, float y, float z, float w ); 34 | 35 | /** 1D, 2D, 3D and 4D float Perlin periodic noise 36 | */ 37 | extern float pnoise1( float x, int px ); 38 | extern float pnoise2( float x, float y, int px, int py ); 39 | extern float pnoise3( float x, float y, float z, int px, int py, int pz ); 40 | extern float pnoise4( float x, float y, float z, float w, 41 | int px, int py, int pz, int pw ); -------------------------------------------------------------------------------- /res/shaders/blocks.fsh: -------------------------------------------------------------------------------- 1 | #version 150 core 2 | 3 | in float pass_brightness; 4 | in vec2 pass_tex_coord; 5 | 6 | uniform sampler2D blocks_texture; 7 | 8 | out vec4 out_color; 9 | 10 | void main() 11 | { 12 | vec4 tex_color = texture(blocks_texture, pass_tex_coord); 13 | if (tex_color.a == 0.0) 14 | discard; 15 | out_color = vec4(tex_color.xyz * pass_brightness, tex_color.a); 16 | } -------------------------------------------------------------------------------- /res/shaders/blocks.vsh: -------------------------------------------------------------------------------- 1 | #version 150 core 2 | 3 | in vec3 position; 4 | in vec3 normal; 5 | in vec2 tex_coord; 6 | 7 | uniform mat4 model; 8 | uniform mat4 view; 9 | uniform mat4 projection; 10 | 11 | out float pass_brightness; 12 | out vec2 pass_tex_coord; 13 | 14 | void main() 15 | { 16 | gl_Position = projection * view * model * vec4(position, 1.0); 17 | vec3 normal = normalize(mat3(transpose(inverse(model))) * normal); 18 | pass_brightness = 0.6; 19 | pass_brightness += clamp(dot(vec3(-0.5, 0.4, 0.0), normal), -0.15, 1.0) * 0.5; 20 | pass_brightness += clamp(dot(vec3(0.5, 0.4, 0.0), normal), -0.15, 1.0) * 0.5; 21 | pass_brightness = clamp(pass_brightness, 0.0, 1.0); 22 | pass_tex_coord = tex_coord; 23 | } -------------------------------------------------------------------------------- /res/shaders/gui.fsh: -------------------------------------------------------------------------------- 1 | #version 150 core 2 | 3 | in vec2 pass_tex_coord; 4 | in float pass_tex_id; 5 | in vec3 pass_color; 6 | 7 | uniform sampler2D gui_textures[2]; 8 | 9 | out vec4 out_color; 10 | 11 | void main() 12 | { 13 | vec4 texture_color; 14 | if (pass_tex_id == 0.0) 15 | texture_color = texture(gui_textures[0], pass_tex_coord); 16 | else 17 | texture_color = texture(gui_textures[1], pass_tex_coord); 18 | out_color = vec4(texture_color.rgb * pass_color, texture_color.a); 19 | if (out_color.a == 0.0) 20 | discard; 21 | } 22 | -------------------------------------------------------------------------------- /res/shaders/gui.vsh: -------------------------------------------------------------------------------- 1 | #version 150 core 2 | 3 | in vec2 position; 4 | in vec2 tex_coord; 5 | in float tex_id; 6 | in vec3 color; 7 | 8 | uniform mat4 projection; 9 | uniform mat4 model; 10 | 11 | out vec2 pass_tex_coord; 12 | out float pass_tex_id; 13 | out vec3 pass_color; 14 | 15 | void main() 16 | { 17 | gl_Position = projection * model * vec4(position, 0.0, 1.0); 18 | pass_tex_coord = tex_coord; 19 | pass_tex_id = tex_id; 20 | pass_color = color; 21 | } -------------------------------------------------------------------------------- /res/shaders/lines.fsh: -------------------------------------------------------------------------------- 1 | #version 150 core 2 | 3 | out vec4 out_color; 4 | 5 | void main() 6 | { 7 | out_color = vec4(0.0, 0.0, 0.0, 0.7); 8 | } -------------------------------------------------------------------------------- /res/shaders/lines.vsh: -------------------------------------------------------------------------------- 1 | #version 150 core 2 | 3 | in vec3 position; 4 | 5 | uniform mat4 view; 6 | uniform mat4 projection; 7 | 8 | void main() 9 | { 10 | gl_Position = projection * view * vec4(position, 1.0); 11 | gl_Position.z -= 0.0006; 12 | } -------------------------------------------------------------------------------- /res/textures/ascii.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/knvi/b/86346652dae36fd40e4af5b786039fbee60bfb25/res/textures/ascii.png -------------------------------------------------------------------------------- /res/textures/terrain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/knvi/b/86346652dae36fd40e4af5b786039fbee60bfb25/res/textures/terrain.png -------------------------------------------------------------------------------- /res/textures/widgets.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/knvi/b/86346652dae36fd40e4af5b786039fbee60bfb25/res/textures/widgets.png -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/knvi/b/86346652dae36fd40e4af5b786039fbee60bfb25/screenshot.png -------------------------------------------------------------------------------- /src/block.h: -------------------------------------------------------------------------------- 1 | #ifndef BLOCK_H 2 | #define BLOCK_H 3 | 4 | #define AIR 0 5 | #define STONE 1 6 | #define GRASS 2 7 | #define DIRT 3 8 | #define COBBLESTONE 4 9 | #define WOOD_PLANKS 5 10 | #define SAPLING 6 11 | #define BEDROCK 7 12 | #define FLOWING_WATER 8 13 | #define STILL_WATER 9 14 | #define FLOWING_LAVA 10 15 | #define STILL_LAVA 11 16 | #define SAND 12 17 | #define GRAVEL 13 18 | #define GOLD_ORE 14 19 | #define IRON_ORE 15 20 | #define COAL_ORE 16 21 | #define WOOD 17 22 | #define LEAVES 18 23 | #define SPONGE 19 24 | #define GLASS 20 25 | #define BUTTERCUP 21 26 | #define ROSE 22 27 | #define WOOL 33 28 | #define CLAY 34 29 | #define BRICKS 35 30 | 31 | typedef unsigned char block_id; 32 | 33 | #endif -------------------------------------------------------------------------------- /src/block_data.c: -------------------------------------------------------------------------------- 1 | #include "block_data.h" 2 | 3 | const block_data blocks[256] = { 4 | {0, 0, 0, 0, 0, 0}, 5 | {1, 1, 1, 1, 1, 1}, // stone 6 | {3, 3, 3, 3, 0, 2}, // grass 7 | {2, 2, 2, 2, 2, 2}, // dirt 8 | {16, 16, 16, 16, 16, 16}, // cobblestone 9 | {4, 4, 4, 4, 4, 4}, // wooden planks 10 | {15, 15, 15, 15, 15, 15}, // sapling 11 | {17, 17, 17, 17, 17, 17}, // bedrock 12 | {205, 205, 205, 205, 205, 205}, // water 13 | {205, 205, 205, 205, 205, 205}, // also water 14 | {237, 237, 237, 237, 237, 237}, // lava 15 | {237, 237, 237, 237, 237, 237}, // also lava 16 | {18, 18, 18, 18, 18, 18}, // sand 17 | {19, 19, 19, 19, 19, 19}, // gravel 18 | {32, 32, 32, 32, 32, 32}, // gold ore 19 | {33, 33, 33, 33, 33, 33}, // iron ore 20 | {34, 34, 34, 34, 34, 34}, // coal ore 21 | {20, 20, 20, 20, 21, 21}, // wood 22 | {52, 52, 52, 52, 52, 52}, // leaves 23 | {48, 48, 48, 48, 48, 48}, // sponge 24 | {49, 49, 49, 49, 49, 49}, // glass 25 | {13, 13, 13, 13, 13, 13}, // buttercup? 26 | {12, 12, 12, 12, 12, 12}, // rose 27 | {64, 64, 64, 64, 64, 64}, // wool 28 | {72, 72, 72, 72, 72, 72}, // clay 29 | {7, 7, 7, 7, 7, 7}, // bricks 30 | }; 31 | 32 | bounding_box block_box = {{1.0f, 1.0f, 1.0f}}; 33 | 34 | int block_is_opaque(block_id block) 35 | { 36 | switch (block) 37 | { 38 | case AIR: 39 | case FLOWING_WATER: 40 | case STILL_WATER: 41 | case LEAVES: 42 | case SAPLING: 43 | case BUTTERCUP: 44 | case ROSE: 45 | case GLASS: 46 | return 0; 47 | default: 48 | return 1; 49 | } 50 | } 51 | 52 | int block_is_obstacle(block_id block) 53 | { 54 | switch(block) 55 | { 56 | case AIR: 57 | case FLOWING_WATER: 58 | case STILL_WATER: 59 | case STILL_LAVA: 60 | case FLOWING_LAVA: 61 | case SAPLING: 62 | case BUTTERCUP: 63 | case ROSE: 64 | return 0; 65 | default: 66 | return 1; 67 | } 68 | } 69 | 70 | int block_connects(block_id block) 71 | { 72 | switch (block) 73 | { 74 | case FLOWING_WATER: 75 | case STILL_WATER: 76 | case GLASS: 77 | return 1; 78 | default: 79 | return 0; 80 | } 81 | } 82 | 83 | int block_hand_breakable(block_id block) 84 | { 85 | switch (block) 86 | { 87 | case AIR: 88 | case FLOWING_WATER: 89 | case STILL_WATER: 90 | case STILL_LAVA: 91 | case FLOWING_LAVA: 92 | case SAPLING: 93 | case BUTTERCUP: 94 | case ROSE: 95 | return 1; 96 | default: 97 | return 0; 98 | } 99 | } -------------------------------------------------------------------------------- /src/block_data.h: -------------------------------------------------------------------------------- 1 | #ifndef BLOCK_DATA_H 2 | #define BLOCK_DATA_H 3 | 4 | #include "block.h" 5 | #include "bounding_box.h" 6 | 7 | typedef struct 8 | { 9 | int face_tiles[6]; 10 | } block_data; 11 | 12 | extern const block_data blocks[256]; 13 | extern bounding_box block_box; 14 | 15 | int block_is_opaque(block_id block); 16 | int block_is_obstacle(block_id block); 17 | int block_connects(block_id block); 18 | int block_hand_breakable(block_id block); 19 | 20 | #endif -------------------------------------------------------------------------------- /src/bounding_box.c: -------------------------------------------------------------------------------- 1 | #include "bounding_box.h" 2 | 3 | int bounding_box_update(bounding_box *box, vec3 *pos) { 4 | box->min.x = pos->x - box->size.x * 0.5f; 5 | box->min.y = pos->y; 6 | box->min.z = pos->z - box->size.z * 0.5f; 7 | box->max.x = pos->x + box->size.x * 0.5f; 8 | box->max.y = pos->y + box->size.y; 9 | box->max.z = pos->z + box->size.z * 0.5f; 10 | } 11 | 12 | int is_colliding(bounding_box *b1, bounding_box *b2) 13 | { 14 | return b1->max.x > b2->min.x && b1->max.z > b2->min.z && b1->max.y > b2->min.y 15 | && b1->min.x < b2->max.x && b1->min.z < b2->max.z && b1->min.y < b2->max.y; 16 | } 17 | 18 | int is_touching(bounding_box *b1, bounding_box *b2) { 19 | return b1->max.x >= b2->min.x && b1->max.z >= b2->min.z && b1->max.y >= b2->min.y 20 | && b1->min.x <= b2->max.x && b1->min.z <= b2->max.z && b1->min.y <= b2->max.y; 21 | } -------------------------------------------------------------------------------- /src/bounding_box.h: -------------------------------------------------------------------------------- 1 | #ifndef BOUNDING_BOX_H 2 | #define BOUNDING_BOX_H 3 | 4 | #include "mvmath.h" 5 | 6 | typedef struct { 7 | vec3 size; 8 | vec3 min; 9 | vec3 max; 10 | } bounding_box; 11 | 12 | int bounding_box_update(bounding_box *box, vec3 *position); 13 | int is_colliding(bounding_box *b1, bounding_box *b2); 14 | int is_touching(bounding_box *b1, bounding_box *b2); 15 | 16 | #endif // BOUNDING_BOX_H -------------------------------------------------------------------------------- /src/chunk.c: -------------------------------------------------------------------------------- 1 | #include "chunk.h" 2 | #include "world.h" 3 | #include "block_data.h" 4 | #include "noise.h" 5 | #include "assert.h" 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | void chunk_build_buffer(struct Chunk *c, block_vertex *data_buffer) 12 | { 13 | c->vert_count = 0; 14 | 15 | block_id neighbours[6]; 16 | 17 | int x_off = CHUNK_SIZE * c->x; 18 | int z_off = CHUNK_SIZE * c->z; 19 | 20 | for (int x = 0; x < CHUNK_SIZE; x++) 21 | { 22 | for (int y = 0; y < WORLD_HEIGHT; y++) 23 | { 24 | for (int z = 0; z < CHUNK_SIZE; z++) 25 | { 26 | block_id b = c->blocks[x][y][z]; 27 | if (b != AIR && b != STILL_WATER && b != FLOWING_WATER) 28 | { 29 | neighbours[0] = world_get_block(c->world, x + x_off, y, z + z_off + 1); 30 | neighbours[1] = world_get_block(c->world, x + x_off, y, z + z_off - 1); 31 | neighbours[2] = world_get_block(c->world, x + x_off + 1, y, z + z_off); 32 | neighbours[3] = world_get_block(c->world, x + x_off - 1, y, z + z_off); 33 | neighbours[4] = world_get_block(c->world, x + x_off, y + 1, z + z_off); 34 | neighbours[5] = world_get_block(c->world, x + x_off, y - 1, z + z_off); 35 | 36 | c->vert_count += make_block(data_buffer + c->vert_count, (vec3){x, y, z}, b, neighbours); 37 | } 38 | } 39 | } 40 | } 41 | 42 | c->water_offset = c->vert_count; 43 | c->water_count = 0; 44 | 45 | for (int x = 0; x < CHUNK_SIZE; x++) 46 | { 47 | for (int y = 0; y < WORLD_HEIGHT; y++) 48 | { 49 | for (int z = 0; z < CHUNK_SIZE; z++) 50 | { 51 | block_id b = c->blocks[x][y][z]; 52 | if (b == STILL_WATER || b == FLOWING_WATER) 53 | { 54 | neighbours[0] = world_get_block(c->world, x + x_off, y, z + z_off + 1); 55 | neighbours[1] = world_get_block(c->world, x + x_off, y, z + z_off - 1); 56 | neighbours[2] = world_get_block(c->world, x + x_off + 1, y, z + z_off); 57 | neighbours[3] = world_get_block(c->world, x + x_off - 1, y, z + z_off); 58 | neighbours[4] = world_get_block(c->world, x + x_off, y + 1, z + z_off); 59 | if(y > 0) { 60 | neighbours[5] = world_get_block(c->world, x + x_off, y - 1, z + z_off); 61 | } else { 62 | neighbours[5] = AIR; 63 | } 64 | 65 | c->water_count += make_block(data_buffer + c->vert_count + c->water_count, (vec3){x, y, z}, b, neighbours); 66 | } 67 | } 68 | } 69 | } 70 | 71 | glBufferData(GL_ARRAY_BUFFER, (c->vert_count + c->water_count) * sizeof(block_vertex), data_buffer, GL_STATIC_DRAW); 72 | 73 | c->dirty = 0; 74 | } 75 | 76 | void chunk_init(struct Chunk *c, struct World *w, int x, int z, shader *blocks_shader) 77 | { 78 | glGenBuffers(1, &c->vbo); 79 | glBindBuffer(GL_ARRAY_BUFFER, c->vbo); 80 | 81 | glGenVertexArrays(1, &c->vao); 82 | glBindVertexArray(c->vao); 83 | glEnableVertexAttribArray(blocks_shader->position_location); 84 | glVertexAttribPointer(blocks_shader->position_location, 3, GL_FLOAT, GL_FALSE, sizeof(block_vertex), NULL); 85 | glEnableVertexAttribArray(blocks_shader->normal_location); 86 | glVertexAttribPointer(blocks_shader->normal_location, 3, GL_FLOAT, GL_FALSE, sizeof(block_vertex), (GLvoid *)sizeof(vec3)); 87 | glEnableVertexAttribArray(blocks_shader->tex_coord_location); 88 | glVertexAttribPointer(blocks_shader->tex_coord_location, 2, GL_FLOAT, GL_FALSE, sizeof(block_vertex), (GLvoid *)(sizeof(vec3) * 2)); 89 | 90 | c->x = x; 91 | c->z = z; 92 | c->world = w; 93 | 94 | c->vert_count = 0; 95 | c->dirty = 0; 96 | } 97 | 98 | void chunk_destroy(struct Chunk *c) 99 | { 100 | glDeleteBuffers(1, &c->vbo); 101 | glDeleteVertexArrays(1, &c->vao); 102 | } 103 | 104 | // returns true if pos is on chunk boundaries (borders another chunk) 105 | int chunk_on_bounds(int x, int y, int z) { 106 | return x == 0 || z == 0 || x == (CHUNK_SIZE - 1) || z == (CHUNK_SIZE - 1); 107 | } 108 | 109 | int chunk_in_bounds(int x, int y, int z) { 110 | return x >= 0 && y >= 0 && z >= 0 && 111 | x < CHUNK_SIZE && y < WORLD_HEIGHT && z < CHUNK_SIZE; 112 | } 113 | 114 | block_id chunk_get_block(struct Chunk *c, int x, int y, int z) 115 | { 116 | assert(chunk_in_bounds(x,y,z)); 117 | 118 | return c->blocks[x][y][z]; 119 | } 120 | 121 | void chunk_set_block(struct Chunk *c, int x, int y, int z, block_id b) 122 | { 123 | assert(chunk_in_bounds(x,y,z)); 124 | 125 | c->blocks[x][y][z] = b; 126 | c->dirty = 1; 127 | 128 | if(x == 0) { 129 | struct Chunk *c1 = world_get_chunk(c->world, c->x - 1, c->z); 130 | if(c1) c1->dirty = 1; 131 | } 132 | 133 | if(x == CHUNK_SIZE - 1) { 134 | struct Chunk *c1 = world_get_chunk(c->world, c->x + 1, c->z); 135 | if(c1) c1->dirty = 1; 136 | } 137 | 138 | if(z == 0) { 139 | struct Chunk *c1 = world_get_chunk(c->world, c->x, c->z - 1); 140 | if(c1) c1->dirty = 1; 141 | } 142 | 143 | if(z == CHUNK_SIZE - 1) { 144 | struct Chunk *c1 = world_get_chunk(c->world, c->x, c->z + 1); 145 | if(c1) c1->dirty = 1; 146 | } 147 | } -------------------------------------------------------------------------------- /src/chunk.h: -------------------------------------------------------------------------------- 1 | #ifndef CHUNK_H 2 | #define CHUNK_H 3 | 4 | #include "block.h" 5 | #include "mesh.h" 6 | #include "shader.h" 7 | #include "world.h" 8 | 9 | #include 10 | 11 | #define CHUNK_SIZE 16 12 | #define WORLD_HEIGHT 128 13 | 14 | struct Chunk 15 | { 16 | block_id blocks[CHUNK_SIZE][WORLD_HEIGHT][CHUNK_SIZE]; 17 | int x; 18 | int z; 19 | int dirty; 20 | GLuint vao; 21 | GLuint vbo; 22 | GLuint vert_count; 23 | GLuint water_offset; 24 | GLuint water_count; 25 | 26 | struct World *world; 27 | 28 | }; 29 | 30 | void chunk_build_buffer(struct Chunk *c, block_vertex *data_buffer); 31 | void chunk_init(struct Chunk *c, struct World *w, int x, int z, shader *blocks_shader); 32 | void chunk_destroy(struct Chunk *c); 33 | void chunk_set_block(struct Chunk *c, int x, int y, int z, block_id b); 34 | block_id chunk_get_block(struct Chunk *c, int x, int y, int z); 35 | int chunk_in_bounds(int x, int y, int z); 36 | 37 | 38 | #endif -------------------------------------------------------------------------------- /src/entity.c: -------------------------------------------------------------------------------- 1 | #include "entity.h" 2 | 3 | #include "world.h" 4 | #include "block_data.h" 5 | 6 | #include 7 | 8 | void entity_move(entity *e, void *w, vec3 *delta_pos) 9 | { 10 | for (int axis = 0; axis < 3; axis++) 11 | { 12 | for (int y = roundf(e->box.min.y) - 1; y <= roundf(e->box.max.y) + 1; y++) 13 | { 14 | for (int x = roundf(e->box.min.x) - 1; x <= roundf(e->box.max.x) + 1; x++) 15 | { 16 | for (int z = roundf(e->box.min.z) - 1; z <= roundf(e->box.max.z) + 1; z++) 17 | { 18 | if (!(block_is_obstacle(world_get_block((struct World*) w, x, y, z)))) 19 | continue; 20 | 21 | vec3 block_position = {x, y - 0.5f, z}; 22 | bounding_box_update(&block_box, &block_position); 23 | 24 | if (axis == 0) 25 | { 26 | if (e->box.min.z < block_box.max.z && e->box.max.z > block_box.min.z && e->box.min.x < block_box.max.x && e->box.max.x > block_box.min.x) 27 | { 28 | if (delta_pos->y > 0.0f && e->box.max.y <= block_box.min.y) 29 | { 30 | float difference = block_box.min.y - e->box.max.y; 31 | if (difference < delta_pos->y) 32 | delta_pos->y = difference; 33 | } 34 | if (delta_pos->y < 0.0f && e->box.min.y >= block_box.max.y) 35 | { 36 | float difference = block_box.max.y - e->box.min.y; 37 | if (difference > delta_pos->y) 38 | delta_pos->y = difference; 39 | } 40 | } 41 | } 42 | else if (axis == 1) 43 | { 44 | if (e->box.min.z < block_box.max.z && e->box.max.z > block_box.min.z && e->box.min.y < block_box.max.y && e->box.max.y > block_box.min.y) 45 | { 46 | if (delta_pos->x > 0.0f && e->box.max.x <= block_box.min.x) 47 | { 48 | float difference = block_box.min.x - e->box.max.x; 49 | if (difference < delta_pos->x) 50 | delta_pos->x = difference; 51 | } 52 | if (delta_pos->x < 0.0f && e->box.min.x >= block_box.max.x) 53 | { 54 | float difference = block_box.max.x - e->box.min.x; 55 | if (difference > delta_pos->x) 56 | delta_pos->x = difference; 57 | } 58 | } 59 | } 60 | else 61 | { 62 | if (e->box.min.x < block_box.max.x && e->box.max.x > block_box.min.x && e->box.min.y < block_box.max.y && e->box.max.y > block_box.min.y) 63 | { 64 | if (delta_pos->z > 0.0f && e->box.max.z <= block_box.min.z) 65 | { 66 | float difference = block_box.min.z - e->box.max.z; 67 | if (difference < delta_pos->z) 68 | delta_pos->z = difference; 69 | } 70 | if (delta_pos->z < 0.0f && e->box.min.z >= block_box.max.z) 71 | { 72 | float difference = block_box.max.z - e->box.min.z; 73 | if (difference > delta_pos->z) 74 | delta_pos->z = difference; 75 | } 76 | } 77 | } 78 | } 79 | } 80 | } 81 | if (axis == 0) 82 | { 83 | e->box.min.y += delta_pos->y; 84 | e->box.max.y += delta_pos->y; 85 | } 86 | else if (axis == 1) 87 | { 88 | e->box.min.x += delta_pos->x; 89 | e->box.max.x += delta_pos->x; 90 | } 91 | } 92 | 93 | add_v3(&e->position, &e->position, delta_pos); 94 | } -------------------------------------------------------------------------------- /src/entity.h: -------------------------------------------------------------------------------- 1 | #ifndef ENTITY_H 2 | #define ENTITY_H 3 | 4 | #include "bounding_box.h" 5 | 6 | typedef struct { 7 | vec3 position; 8 | vec3 velocity; 9 | vec3 move_direction; 10 | int jumping; 11 | int on_ground; 12 | bounding_box box; 13 | int tool; 14 | } entity; 15 | 16 | void entity_move(entity *e, void *w, vec3 *delta_pos); 17 | 18 | #endif -------------------------------------------------------------------------------- /src/game.c: -------------------------------------------------------------------------------- 1 | #include "game.h" 2 | #include "GLFW/glfw3.h" 3 | #include "worldgen.h" 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | int recv_all(SOCKET s, char *buf, size_t buf_size) 10 | { 11 | int data_size = recv(s, buf, buf_size, 0); 12 | int data_position = 0; 13 | 14 | if (data_size == 0) 15 | return 0; 16 | 17 | while (data_position < data_size) 18 | { 19 | switch (buf[data_position]) 20 | { 21 | case SET_BLOCK_ID: 22 | { 23 | data_position += sizeof(set_block_packet); 24 | } 25 | break; 26 | case SPAWN_PLAYER_ID: 27 | { 28 | data_position += sizeof(spawn_player_packet); 29 | } 30 | break; 31 | case DESPAWN_PLAYER_ID: 32 | { 33 | data_position += sizeof(despawn_player_packet); 34 | } 35 | break; 36 | case POSITION_UPDATE_ID: 37 | { 38 | data_position += sizeof(position_update_packet); 39 | } 40 | break; 41 | case CHUNK_DATA_ID: 42 | { 43 | data_position += sizeof(chunk_data_packet); 44 | } 45 | break; 46 | } 47 | 48 | if (data_size < data_position) 49 | { 50 | data_size += recv(s, buf + data_size, data_position - data_size, 0); 51 | } 52 | } 53 | 54 | return data_size; 55 | } 56 | 57 | void game_init(game *g, GLFWwindow *window) 58 | { 59 | g->window = window; 60 | glfwGetWindowSize(window, &g->window_width, &g->window_height); 61 | 62 | glEnable(GL_CULL_FACE); 63 | glEnable(GL_DEPTH_TEST); 64 | glDepthFunc(GL_LEQUAL); 65 | glEnable(GL_BLEND); 66 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 67 | 68 | world_init(&g->w); 69 | gui_init(&g->gui, &g->w); 70 | 71 | g->debug_text = gui_create_text(&g->gui); 72 | 73 | if (g->online) 74 | { 75 | printf("Connecting to the server...\n"); 76 | g->server_socket = socket(PF_INET, SOCK_STREAM, 0); 77 | g->server_addr.sin_family = AF_INET; 78 | if (SOCKET_VALID(connect(g->server_socket, (struct sockaddr *) &g->server_addr, sizeof(g->server_addr)))) 79 | { 80 | printf("Successfully connected to the server.\n"); 81 | g->buffer = malloc(DATA_BUFFER_SIZE); 82 | player_identification_packet packet; 83 | packet.id = PLAYER_IDENTIFICATION_ID; 84 | strcpy(packet.nickname, g->player_nickname); 85 | send(g->server_socket, &packet, sizeof(packet), 0); 86 | } 87 | else 88 | { 89 | printf ("Couldn't connect to the server.\n"); 90 | g->online = 0; 91 | close(g->server_socket); 92 | } 93 | g->tv.tv_sec = 0; 94 | g->tv.tv_usec = 0; 95 | } 96 | 97 | if (!g->online) worldgen_init(&g->w); 98 | } 99 | 100 | void game_destroy(game *g) 101 | { 102 | world_destroy(&g->w); 103 | gui_destroy(&g->gui); 104 | 105 | if (g->online) 106 | { 107 | #ifdef _WIN32 108 | closesocket(g->server_socket); 109 | #else 110 | close(g->server_socket); 111 | #endif 112 | free(g->buffer); 113 | } 114 | } 115 | 116 | void game_handle_input(game *g, input *i) 117 | { 118 | g->window_width = i->window_width; 119 | g->window_height = i->window_height; 120 | 121 | if (i->keys_down[GLFW_KEY_ESCAPE]) 122 | { 123 | if (i->mouse_locked) 124 | { 125 | input_unlock_mouse(i, g->window); 126 | } 127 | } 128 | 129 | if (i->mouse_buttons_down[GLFW_MOUSE_BUTTON_LEFT]) 130 | { 131 | if (!i->mouse_locked) 132 | { 133 | input_lock_mouse(i, g->window); 134 | } 135 | } 136 | 137 | world_handle_input(&g->w, i); 138 | gui_handle_input(&g->gui, i); 139 | } 140 | 141 | void game_tick(game *g) 142 | { 143 | world_tick(&g->w); 144 | 145 | if (g->online) 146 | { 147 | for (int i = 0; i < g->w.num_players; i++) 148 | { 149 | g->w.players[i].prev_position = g->w.players[i].position; 150 | } 151 | 152 | FD_ZERO(&g->read_fds); 153 | FD_SET(g->server_socket, &g->read_fds); 154 | 155 | select(g->server_socket + 1, &g->read_fds, NULL, NULL, &g->tv); 156 | 157 | if (FD_ISSET(g->server_socket, &g->read_fds)) 158 | { 159 | int data_size = 0; 160 | int data_position = 0; 161 | if (SOCKET_VALID(data_size = recv_all(g->server_socket, g->buffer, DATA_BUFFER_SIZE))) 162 | { 163 | if (data_size == 0) 164 | { 165 | printf("Disconnected from the server.\n"); 166 | g->online = 0; 167 | #ifdef _WIN32 168 | closesocket(g->server_socket); 169 | WSACleanup(); 170 | #else 171 | close(g->server_socket); 172 | #endif 173 | } 174 | else 175 | { 176 | while (data_position < data_size) 177 | { 178 | switch (g->buffer[data_position]) 179 | { 180 | case SET_BLOCK_ID: 181 | { 182 | set_block_packet *packet = (set_block_packet *) (g->buffer + data_position); 183 | packet->x = ntohs(packet->x); 184 | packet->y = ntohs(packet->y); 185 | packet->z = ntohs(packet->z); 186 | world_set_block(&g->w, packet->x, packet->y, packet->z, packet->block); 187 | data_position += sizeof(set_block_packet); 188 | } 189 | break; 190 | case SPAWN_PLAYER_ID: 191 | { 192 | spawn_player_packet *packet = (spawn_player_packet *) (g->buffer + data_position); 193 | g->w.players[g->w.num_players].id = packet->player_id; 194 | packet->nickname[30] = '\0'; 195 | strcpy(g->w.players[g->w.num_players].nickname, packet->nickname); 196 | printf("%s joined the game.\n", g->w.players[g->w.num_players].nickname); 197 | g->w.num_players++; 198 | data_position += sizeof(spawn_player_packet); 199 | } 200 | break; 201 | case DESPAWN_PLAYER_ID: 202 | { 203 | despawn_player_packet *packet = (despawn_player_packet *) (g->buffer + data_position); 204 | int index = 0; 205 | for (int i = 0; i < MAX_PLAYERS - 1; i++) 206 | { 207 | if (g->w.players[i].id == packet->player_id) 208 | { 209 | index = i; 210 | break; 211 | } 212 | } 213 | printf("%s left the game.\n", g->w.players[index].nickname); 214 | for (int i = index; i < MAX_PLAYERS - 2; i++) 215 | { 216 | g->w.players[i] = g->w.players[i + 1]; 217 | } 218 | g->w.num_players--; 219 | data_position += sizeof(despawn_player_packet); 220 | } 221 | break; 222 | case POSITION_UPDATE_ID: 223 | { 224 | position_update_packet *packet = (position_update_packet *) (g->buffer + data_position); 225 | for (int i = 0; i < g->w.num_players; i++) 226 | { 227 | if (g->w.players[i].id == packet->player_id) 228 | { 229 | g->w.players[i].position = (vec3) 230 | { 231 | (short) ntohs(packet->x) / 32.0f, 232 | (short) ntohs(packet->y) / 32.0f + g->w.player.box.size.y * 0.5f, 233 | (short) ntohs(packet->z) / 32.0f 234 | }; 235 | break; 236 | } 237 | } 238 | data_position += sizeof(position_update_packet); 239 | } 240 | break; 241 | case CHUNK_DATA_ID: 242 | { 243 | chunk_data_packet *packet = (chunk_data_packet *) (g->buffer + data_position); 244 | packet->complete = ntohs(packet->complete); 245 | packet->length = ntohs(packet->length); 246 | 247 | size_t chunk_x = packet->x + WORLD_SIZE / 2; 248 | size_t chunk_z = packet->z + WORLD_SIZE / 2; 249 | struct Chunk *c = &g->w.chunks[chunk_x * WORLD_SIZE + chunk_z]; 250 | 251 | g->inf_stream.zalloc = Z_NULL; 252 | g->inf_stream.zfree = Z_NULL; 253 | g->inf_stream.opaque = Z_NULL; 254 | 255 | g->inf_stream.avail_in = sizeof(packet->data); 256 | g->inf_stream.next_in = packet->data; 257 | g->inf_stream.avail_out = sizeof(c->blocks) - packet->complete; 258 | g->inf_stream.next_out = (char *) c->blocks + packet->complete; 259 | 260 | inflateInit(&g->inf_stream); 261 | inflate(&g->inf_stream, Z_NO_FLUSH); 262 | inflateEnd(&g->inf_stream); 263 | 264 | if (packet->complete + packet->length == CHUNK_SIZE * WORLD_HEIGHT * CHUNK_SIZE) 265 | { 266 | c->dirty = 1; 267 | g->w.chunks[(chunk_x == 0 ? chunk_x : chunk_x - 1) * WORLD_SIZE + chunk_z].dirty = 1; 268 | g->w.chunks[(chunk_x == WORLD_SIZE - 1 ? chunk_x : chunk_x + 1) * WORLD_SIZE + chunk_z].dirty = 1; 269 | g->w.chunks[chunk_x * WORLD_SIZE + (chunk_z == 0 ? chunk_z : chunk_z - 1)].dirty = 1; 270 | g->w.chunks[chunk_x * WORLD_SIZE + (chunk_z == WORLD_SIZE - 1 ? chunk_z : chunk_z + 1)].dirty = 1; 271 | } 272 | 273 | data_position += sizeof(chunk_data_packet); 274 | } 275 | break; 276 | } 277 | } 278 | } 279 | } 280 | } 281 | 282 | if (g->w.block_changed) 283 | { 284 | set_block_packet packet; 285 | packet.id = SET_BLOCK_ID; 286 | packet.x = htons((short) g->w.selected_block_x); 287 | packet.y = htons((short) g->w.selected_block_y); 288 | packet.z = htons((short) g->w.selected_block_z); 289 | packet.block = g->w.new_block; 290 | 291 | send(g->server_socket, &packet, sizeof(packet), 0); 292 | } 293 | 294 | position_update_packet packet; 295 | packet.id = POSITION_UPDATE_ID; 296 | packet.player_id = 255; 297 | packet.x = htons(g->w.player.position.x * 32.0f); 298 | packet.y = htons(g->w.player.position.y * 32.0f); 299 | packet.z = htons(g->w.player.position.z * 32.0f); 300 | send(g->server_socket, &packet, sizeof(packet), 0); 301 | } 302 | else 303 | { 304 | g->w.num_players = 0; 305 | } 306 | } 307 | 308 | void game_draw(game *g, double delta_time, double time_since_tick) 309 | { 310 | char text[128]; 311 | sprintf(text, "b\n%d fps\n", (int) roundf(1.0f / delta_time)); 312 | if (g->w.noclip_mode) 313 | strcat(text, "Noclip mode\n"); 314 | else if (g->w.fly_mode) 315 | strcat(text, "Fly mode\n"); 316 | gui_set_text(g->debug_text, text, 8.0f); 317 | g->debug_text->position = (vec2) { 318 | -g->window_width / 2.0f / g->gui.scale + 2.0f, 319 | g->window_height / 2.0f / g->gui.scale - 10.0f, 320 | }; 321 | 322 | world_draw(&g->w, delta_time, time_since_tick); 323 | gui_draw(&g->gui); 324 | } -------------------------------------------------------------------------------- /src/game.h: -------------------------------------------------------------------------------- 1 | #ifndef GAME_H 2 | #define GAME_H 3 | 4 | #include "world.h" 5 | #include "gui.h" 6 | #include "miniz.h" 7 | 8 | typedef struct 9 | { 10 | GLFWwindow *window; 11 | float window_width; 12 | float window_height; 13 | 14 | struct World w; 15 | gui gui; 16 | 17 | gui_text *debug_text; 18 | 19 | int online; 20 | char *buffer; 21 | z_stream inf_stream; 22 | struct sockaddr_in server_addr; 23 | SOCKET server_socket; 24 | char player_nickname[31]; 25 | fd_set read_fds; 26 | struct timeval tv; 27 | } game; 28 | 29 | void game_init(game *g, GLFWwindow *window); 30 | void game_destroy(game *g); 31 | void game_handle_input(game *g, input *i); 32 | void game_tick(game *g); 33 | void game_draw(game *g, double delta_time, double time_since_tick); 34 | 35 | #endif -------------------------------------------------------------------------------- /src/gui.c: -------------------------------------------------------------------------------- 1 | #include "gui.h" 2 | 3 | #include "util.h" 4 | #include "block_data.h" 5 | 6 | #include "mesh.h" 7 | 8 | #include 9 | #include 10 | 11 | static gui_vertex gui_buffer_data[4096]; 12 | 13 | void init_gui_object(GLuint *vao, GLuint *vbo, gui *g) 14 | { 15 | glGenBuffers(1, vbo); 16 | glBindBuffer(GL_ARRAY_BUFFER, *vbo); 17 | 18 | glGenVertexArrays(1, vao); 19 | glBindVertexArray(*vao); 20 | glEnableVertexAttribArray(g->gui_shader.position_location); 21 | glVertexAttribPointer(g->gui_shader.position_location, 2, GL_FLOAT, GL_FALSE, sizeof(gui_vertex), NULL); 22 | glEnableVertexAttribArray(g->gui_shader.tex_coord_location); 23 | glVertexAttribPointer(g->gui_shader.tex_coord_location, 2, GL_FLOAT, GL_FALSE, sizeof(gui_vertex), (GLvoid *) sizeof(vec2)); 24 | glEnableVertexAttribArray(g->gui_shader.tex_id_location); 25 | glVertexAttribPointer(g->gui_shader.tex_id_location, 1, GL_FLOAT, GL_FALSE, sizeof(gui_vertex), (GLvoid *) (sizeof(vec2) * 2)); 26 | glEnableVertexAttribArray(g->gui_shader.color_location); 27 | glVertexAttribPointer(g->gui_shader.color_location, 3, GL_FLOAT, GL_FALSE, sizeof(gui_vertex), (GLvoid *) (sizeof(vec2) * 2 + sizeof(float))); 28 | } 29 | 30 | void destroy_gui_object(GLuint *vao, GLuint *vbo) 31 | { 32 | glDeleteBuffers(1, vbo); 33 | glDeleteVertexArrays(1, vao); 34 | } 35 | 36 | void sprite_init(gui_sprite *sprite, gui *g, vec2 tex_position) 37 | { 38 | init_gui_object(&sprite->vao, &sprite->vbo, g); 39 | 40 | gui_vertex buffer_data[4] = 41 | { 42 | {{-sprite->size.x * 0.5f, -sprite->size.y * 0.5f}, {tex_position.x / 256.0f, (tex_position.y + sprite->size.y) / 256.0f}, 0, VEC3_ONE}, 43 | {{sprite->size.x * 0.5f, -sprite->size.y * 0.5f}, {(tex_position.x + sprite->size.x) / 256.0f, (tex_position.y + sprite->size.y) / 256.0f}, 0, VEC3_ONE}, 44 | {{sprite->size.x * 0.5f, sprite->size.y * 0.5f}, {(tex_position.x + sprite->size.x) / 256.0f, tex_position.y / 256.0f}, 0, VEC3_ONE}, 45 | {{-sprite->size.x * 0.5f, sprite->size.y * 0.5f}, {tex_position.x / 256.0f, tex_position.y / 256.0f}, 0, VEC3_ONE}, 46 | }; 47 | 48 | glBufferData(GL_ARRAY_BUFFER, sizeof(buffer_data), buffer_data, GL_STATIC_DRAW); 49 | } 50 | 51 | void sprite_draw(gui_sprite *sprite, gui *g) 52 | { 53 | translate_v2(&TEMP_MAT, &sprite->position); 54 | 55 | glUniformMatrix4fv(g->gui_shader.model_location, 1, GL_FALSE, TEMP_MAT.value); 56 | 57 | glBindVertexArray(sprite->vao); 58 | glDrawArrays(GL_TRIANGLE_FAN, 0, 4); 59 | } 60 | 61 | void sprite_destroy(gui_sprite *sprite) 62 | { 63 | destroy_gui_object(&sprite->vao, &sprite->vbo); 64 | } 65 | 66 | gui_text *gui_create_text(gui *g) 67 | { 68 | gui_text *text = &g->texts[g->num_texts]; 69 | init_gui_object(&text->vao, &text->vbo, g); 70 | text->vert_count = 0; 71 | g->num_texts++; 72 | return text; 73 | } 74 | 75 | void gui_set_text(gui_text *text, const char *s, float scale) 76 | { 77 | strcpy(text->text, s); 78 | text->vert_count = make_text(gui_buffer_data, text->text, scale, &text->size); 79 | glBindBuffer(GL_ARRAY_BUFFER, text->vbo); 80 | glBufferData(GL_ARRAY_BUFFER, sizeof(gui_vertex) * text->vert_count, gui_buffer_data, GL_DYNAMIC_DRAW); 81 | } 82 | 83 | void gui_init(gui *g, struct World *w) 84 | { 85 | glLogicOp(GL_INVERT); 86 | 87 | g->w = w; 88 | 89 | g->gui_shader.program = load_program("res/shaders/gui.vsh", "res/shaders/gui.fsh"); 90 | g->gui_shader.position_location = glGetAttribLocation(g->gui_shader.program, "position"); 91 | g->gui_shader.tex_coord_location = glGetAttribLocation(g->gui_shader.program, "tex_coord"); 92 | g->gui_shader.tex_id_location = glGetAttribLocation(g->gui_shader.program, "tex_id"); 93 | g->gui_shader.color_location = glGetAttribLocation(g->gui_shader.program, "color"); 94 | g->gui_shader.projection_location = glGetUniformLocation(g->gui_shader.program, "projection"); 95 | g->gui_shader.model_location = glGetUniformLocation(g->gui_shader.program, "model"); 96 | g->gui_shader.texture_location = glGetUniformLocation(g->gui_shader.program, "gui_textures"); 97 | 98 | glGenTextures(1, &g->widgets_texture); 99 | glActiveTexture(GL_TEXTURE1); 100 | glBindTexture(GL_TEXTURE_2D, g->widgets_texture); 101 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 102 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 103 | load_png_texture("res/textures/widgets.png"); 104 | 105 | glGenTextures(1, &g->ascii_texture); 106 | glActiveTexture(GL_TEXTURE2); 107 | glBindTexture(GL_TEXTURE_2D, g->ascii_texture); 108 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 109 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 110 | load_png_texture("res/textures/ascii.png"); 111 | 112 | g->num_texts = 0; 113 | 114 | g->player_id_text = gui_create_text(g); 115 | 116 | g->crosshair_sprite.position = (vec2) {0.0f, 0.0f}; 117 | g->crosshair_sprite.size = (vec2) {15.0f, 15.0f}; 118 | sprite_init(&g->crosshair_sprite, g, (vec2) {240.0f, 0.0f}); 119 | 120 | g->hotbar_sprite.size = (vec2) {182.0f, 22.0f}; 121 | sprite_init(&g->hotbar_sprite, g, (vec2) {0.0f, 0.0f}); 122 | 123 | g->hotbar_selection_sprite.size = (vec2) {24.0f, 24.0f}; 124 | sprite_init(&g->hotbar_selection_sprite, g, (vec2) {0.0f, 22.0f}); 125 | 126 | glGenBuffers(256, g->hotbar_item_vbos); 127 | glGenVertexArrays(256, g->hotbar_item_vaos); 128 | 129 | block_vertex hotbar_item_buffer[36]; 130 | block_id neighbours[6] = { AIR }; 131 | 132 | for (int i = 0; i < 256; i++) 133 | { 134 | glBindBuffer(GL_ARRAY_BUFFER, g->hotbar_item_vbos[i]); 135 | glBindVertexArray(g->hotbar_item_vaos[i]); 136 | 137 | glEnableVertexAttribArray(w->blocks_shader.position_location); 138 | glVertexAttribPointer(w->blocks_shader.position_location, 3, GL_FLOAT, GL_FALSE, sizeof(block_vertex), NULL); 139 | glEnableVertexAttribArray(w->blocks_shader.normal_location); 140 | glVertexAttribPointer(w->blocks_shader.normal_location, 3, GL_FLOAT, GL_FALSE, sizeof(block_vertex), (GLvoid *) sizeof(vec3)); 141 | glEnableVertexAttribArray(w->blocks_shader.tex_coord_location); 142 | glVertexAttribPointer(w->blocks_shader.tex_coord_location, 2, GL_FLOAT, GL_FALSE, sizeof(block_vertex), (GLvoid *) (sizeof(vec3) * 2)); 143 | 144 | memset(hotbar_item_buffer, 0, sizeof(hotbar_item_buffer)); 145 | make_block(hotbar_item_buffer, (vec3) {0.0f, 0.0f, 0.0f}, (block_id) i, neighbours); 146 | glBufferData(GL_ARRAY_BUFFER, 36 * sizeof(block_vertex), hotbar_item_buffer, GL_STATIC_DRAW); 147 | } 148 | } 149 | 150 | void gui_handle_input(gui *g, input *i) 151 | { 152 | g->window_width = i->window_width; 153 | g->window_height = i->window_height; 154 | 155 | g->scale = 2 + g->window_height / 720; 156 | } 157 | 158 | void gui_draw(gui *g) 159 | { 160 | glDisable(GL_DEPTH_TEST); 161 | 162 | glUseProgram(g->w->blocks_shader.program); 163 | 164 | mat4 model; 165 | identity(&model); 166 | 167 | vec3 translation = {0.5f, -0.5f, -0.6f}; 168 | vec3 block_scale = {0.4f, 0.4f, 0.4f}; 169 | 170 | scale(&TEMP_MAT, &block_scale); 171 | multiply(&model, &TEMP_MAT, &model); 172 | 173 | rotate(&TEMP_MAT, &AXIS_UP, RADIANS(45.0f)); 174 | multiply(&model, &TEMP_MAT, &model); 175 | 176 | translate(&TEMP_MAT, &translation); 177 | multiply(&model, &TEMP_MAT, &model); 178 | 179 | rotate(&TEMP_MAT, &AXIS_RIGHT, RADIANS(-g->w->camera_rotation.x)); 180 | multiply(&model, &TEMP_MAT, &model); 181 | rotate(&TEMP_MAT, &AXIS_UP, RADIANS(-g->w->camera_rotation.y)); 182 | multiply(&model, &TEMP_MAT, &model); 183 | 184 | translate(&TEMP_MAT, &g->w->camera_position); 185 | multiply(&model, &TEMP_MAT, &model); 186 | 187 | glUniformMatrix4fv(g->w->blocks_shader.model_location, 1, GL_FALSE, model.value); 188 | 189 | glBindVertexArray(g->hotbar_item_vaos[g->w->selected_block]); 190 | glDrawArrays(GL_TRIANGLES, 0, 36); 191 | 192 | glUseProgram(g->gui_shader.program); 193 | GLint texture_locations[] = { 1, 2 }; 194 | glUniform1iv(g->gui_shader.texture_location, 2, texture_locations); 195 | 196 | identity(&TEMP_MAT); 197 | multiply(&TEMP_MAT, &g->w->world_view, &TEMP_MAT); 198 | multiply(&TEMP_MAT, &g->w->world_projection, &TEMP_MAT); 199 | glUniformMatrix4fv(g->gui_shader.projection_location, 1, GL_FALSE, TEMP_MAT.value); 200 | 201 | for (int i = 0; i < g->w->num_players; i++) 202 | { 203 | char buffer[32]; 204 | strcpy(buffer, g->w->players[i].nickname); 205 | gui_set_text(g->player_id_text, buffer, 1.0f / 8.0f); 206 | identity(&model); 207 | rotate(&TEMP_MAT, &AXIS_RIGHT, RADIANS(-g->w->camera_rotation.x)); 208 | multiply(&model, &TEMP_MAT, &model); 209 | translation = (vec3) 210 | { 211 | -g->player_id_text->size.x / 2.0f, 212 | 1.1f, 213 | 0.0f 214 | }; 215 | translate(&TEMP_MAT, &translation); 216 | multiply(&model, &TEMP_MAT, &model); 217 | rotate(&TEMP_MAT, &AXIS_UP, RADIANS(-g->w->camera_rotation.y)); 218 | multiply(&model, &TEMP_MAT, &model); 219 | translate(&TEMP_MAT, &g->w->players[i].smoothed_position); 220 | multiply(&model, &TEMP_MAT, &model); 221 | glUniformMatrix4fv(g->gui_shader.model_location, 1, GL_FALSE, model.value); 222 | glBindVertexArray(g->player_id_text->vao); 223 | glDrawArrays(GL_TRIANGLES, 0, g->player_id_text->vert_count); 224 | } 225 | 226 | ortho(&TEMP_MAT, -g->window_width / 2 / g->scale, g->window_width / 2 / g->scale, -g->window_height / 2 / g->scale, g->window_height / 2 / g->scale, -1.0f, 1.0f); 227 | glUniformMatrix4fv(g->gui_shader.projection_location, 1, GL_FALSE, TEMP_MAT.value); 228 | 229 | glEnable(GL_COLOR_LOGIC_OP); 230 | sprite_draw(&g->crosshair_sprite, g); 231 | glDisable(GL_COLOR_LOGIC_OP); 232 | 233 | g->hotbar_sprite.position.y = -g->window_height / 2.0f / g->scale + g->hotbar_sprite.size.y / 2.0f; 234 | sprite_draw(&g->hotbar_sprite, g); 235 | g->hotbar_selection_sprite.position.y = -g->window_height / 2.0f / g->scale + g->hotbar_sprite.size.y / 2.0f; 236 | g->hotbar_selection_sprite.position.x = (g->w->selected_block - 1) % 9 * 20.0f - 4 * 20.0f; 237 | sprite_draw(&g->hotbar_selection_sprite, g); 238 | 239 | for (int i = 1; i < g->num_texts; i++) 240 | { 241 | gui_text *text = &g->texts[i]; 242 | translate_v2(&TEMP_MAT, &text->position); 243 | glUniformMatrix4fv(g->gui_shader.model_location, 1, GL_FALSE, TEMP_MAT.value); 244 | glBindVertexArray(text->vao); 245 | glDrawArrays(GL_TRIANGLES, 0, text->vert_count); 246 | } 247 | 248 | glUseProgram(g->w->blocks_shader.program); 249 | 250 | ortho(&TEMP_MAT, -g->window_width / 20 / g->scale, g->window_width / 20 / g->scale, -g->window_height / 20 / g->scale, g->window_height / 20 / g->scale, -1.0f, 2.0f); 251 | glUniformMatrix4fv(g->w->blocks_shader.projection_location, 1, GL_FALSE, TEMP_MAT.value); 252 | 253 | identity(&TEMP_MAT); 254 | glUniformMatrix4fv(g->w->blocks_shader.view_location, 1, GL_FALSE, TEMP_MAT.value); 255 | 256 | for (int i = 0; i < 9; i++) 257 | { 258 | identity(&model); 259 | rotate(&TEMP_MAT, &AXIS_UP, RADIANS(-45.0f)); 260 | multiply(&model, &TEMP_MAT, &model); 261 | rotate(&TEMP_MAT, &AXIS_RIGHT, RADIANS(-30.0f)); 262 | multiply(&model, &TEMP_MAT, &model); 263 | vec2 translation = {i * 2.0f - 8.0f, -g->window_height / g->scale / 20.0f + 1.1f}; 264 | translate_v2(&TEMP_MAT, &translation); 265 | multiply(&model, &TEMP_MAT, &model); 266 | 267 | glUniformMatrix4fv(g->w->blocks_shader.model_location, 1, GL_FALSE, model.value); 268 | glBindVertexArray(g->hotbar_item_vaos[i + 1 + GET_CURRENT_HOTBAR(g->w) * 9]); 269 | glDrawArrays(GL_TRIANGLES, 0, 36); 270 | } 271 | } 272 | 273 | void gui_destroy(gui *g) 274 | { 275 | sprite_destroy(&g->crosshair_sprite); 276 | sprite_destroy(&g->hotbar_sprite); 277 | sprite_destroy(&g->hotbar_selection_sprite); 278 | for (int i = 0; i < g->num_texts; i++) 279 | { 280 | destroy_gui_object(&g->texts[i].vao, &g->texts[i].vbo); 281 | } 282 | glDeleteBuffers(256, g->hotbar_item_vbos); 283 | glDeleteVertexArrays(256, g->hotbar_item_vaos); 284 | glDeleteTextures(1, &g->widgets_texture); 285 | glDeleteTextures(1, &g->ascii_texture); 286 | glDeleteProgram(g->gui_shader.program); 287 | } -------------------------------------------------------------------------------- /src/gui.h: -------------------------------------------------------------------------------- 1 | #ifndef GUI_H 2 | #define GUI_H 3 | #include "world.h" 4 | 5 | typedef struct 6 | { 7 | GLuint vao; 8 | GLuint vbo; 9 | vec2 position; 10 | vec2 size; 11 | } gui_sprite; 12 | 13 | typedef struct 14 | { 15 | GLuint vao; 16 | GLuint vbo; 17 | GLsizei vert_count; 18 | vec2 position; 19 | vec2 size; 20 | char text[256]; 21 | } gui_text; 22 | 23 | typedef struct 24 | { 25 | float window_width; 26 | float window_height; 27 | int scale; 28 | 29 | struct World *w; 30 | 31 | shader gui_shader; 32 | 33 | GLuint widgets_texture; 34 | GLuint ascii_texture; 35 | 36 | gui_sprite crosshair_sprite; 37 | gui_sprite hotbar_sprite; 38 | gui_sprite hotbar_selection_sprite; 39 | 40 | gui_text *player_id_text; 41 | gui_text texts[32]; 42 | unsigned int num_texts; 43 | 44 | GLuint hotbar_item_vaos[256]; 45 | GLuint hotbar_item_vbos[256]; 46 | } gui; 47 | 48 | void gui_init(gui *g, struct World* w); 49 | void gui_handle_input(gui *g, input* i); 50 | void gui_draw(gui *g); 51 | void gui_destroy(gui *g); 52 | 53 | gui_text *gui_create_text(gui *g); 54 | void gui_set_text(gui_text *text, const char *s, float scale); 55 | 56 | #endif -------------------------------------------------------------------------------- /src/input.c: -------------------------------------------------------------------------------- 1 | #include "input.h" 2 | 3 | #include "game.h" 4 | 5 | void framebuffer_size_callback(GLFWwindow *window, int x, int y) 6 | { 7 | input *i = (input *) glfwGetWindowUserPointer(window); 8 | i->window_width = x; 9 | i->window_height = y; 10 | } 11 | 12 | void cursor_pos_callback(GLFWwindow* window, double mouse_x, double mouse_y) 13 | { 14 | input *i = (input *) glfwGetWindowUserPointer(window); 15 | i->mouse_pos = (vec2) {mouse_x, mouse_y}; 16 | } 17 | 18 | void mouse_button_callback(GLFWwindow* window, int button, int action, int mods) 19 | { 20 | input *i = (input *) glfwGetWindowUserPointer(window); 21 | if (action == GLFW_PRESS) 22 | { 23 | i->mouse_buttons_down[button] = 1; 24 | i->mouse_buttons[button] = 1; 25 | } 26 | else if (action == GLFW_RELEASE) 27 | { 28 | i->mouse_buttons_up[button] = 1; 29 | i->mouse_buttons[button] = 0; 30 | } 31 | } 32 | 33 | void scroll_callback(GLFWwindow *window, double x_offset, double y_offset) 34 | { 35 | input *i = (input *) glfwGetWindowUserPointer(window); 36 | i->scroll_delta = y_offset; 37 | } 38 | 39 | void key_callback(GLFWwindow *window, int key, int scancode, int action, int mods) 40 | { 41 | input *i = (input *) glfwGetWindowUserPointer(window); 42 | if (action == GLFW_PRESS) 43 | { 44 | i->keys_down[key] = 1; 45 | i->keys[key] = 1; 46 | } 47 | else if (action == GLFW_RELEASE) 48 | { 49 | i->keys_up[key] = 1; 50 | i->keys[key] = 0; 51 | } 52 | } 53 | 54 | void input_init(input *i, GLFWwindow *window) 55 | { 56 | glfwSetScrollCallback(window, &scroll_callback); 57 | glfwSetKeyCallback(window, &key_callback); 58 | glfwSetCursorPosCallback(window, &cursor_pos_callback); 59 | glfwSetMouseButtonCallback(window, &mouse_button_callback); 60 | glfwSetFramebufferSizeCallback(window, &framebuffer_size_callback); 61 | 62 | int width, height; 63 | glfwGetWindowSize(window, &width, &height); 64 | i->window_width = width; 65 | i->window_height = height; 66 | 67 | for (int j = 0; j < 512; j++) 68 | { 69 | i->keys[j] = 0; 70 | i->keys_down[j] = 0; 71 | i->keys_up[j] = 0; 72 | } 73 | 74 | for (int j = 0; j < 3; j++) 75 | { 76 | i->mouse_buttons[j] = 0; 77 | i->mouse_buttons_down[j] = 0; 78 | i->mouse_buttons_up[j] = 0; 79 | } 80 | i->mouse_locked = 0; 81 | 82 | i->mouse_sensitivity = 0.1f; 83 | } 84 | 85 | void input_poll_events(input *i) 86 | { 87 | i->scroll_delta = 0.0; 88 | for (int j = 0; j < 512; j++) 89 | { 90 | i->keys_down[j] = 0; 91 | i->keys_up[j] = 0; 92 | } 93 | for (int j = 0; j < 3; j++) 94 | { 95 | i->mouse_buttons_down[j] = 0; 96 | i->mouse_buttons_up[j] = 0; 97 | } 98 | glfwPollEvents(); 99 | subtract_v2(&i->mouse_delta, &i->last_mouse_pos, &i->mouse_pos); 100 | i->last_mouse_pos = i->mouse_pos; 101 | } 102 | 103 | void input_lock_mouse(input *i, GLFWwindow *window) 104 | { 105 | glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); 106 | glfwSetCursorPos(window, 0.0, 0.0); 107 | i->last_mouse_pos.x = 0.0f; 108 | i->last_mouse_pos.y = 0.0f; 109 | i->mouse_pos.x = 0.0f; 110 | i->mouse_pos.y = 0.0f; 111 | i->mouse_delta.x = 0.0f; 112 | i->mouse_delta.y = 0.0f; 113 | i->mouse_locked = 1; 114 | i->mouse_buttons_down[GLFW_MOUSE_BUTTON_LEFT] = 0; 115 | i->mouse_buttons[GLFW_MOUSE_BUTTON_LEFT] = 0; 116 | } 117 | 118 | void input_unlock_mouse(input *i, GLFWwindow *window) 119 | { 120 | glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); 121 | glfwSetCursorPos(window, i->window_width / 2.0, i->window_height / 2.0); 122 | i->mouse_locked = 0; 123 | } -------------------------------------------------------------------------------- /src/input.h: -------------------------------------------------------------------------------- 1 | #ifndef INPUT_H 2 | #define INPUT_H 3 | 4 | #include 5 | #include 6 | 7 | #include "mvmath.h" 8 | 9 | typedef struct 10 | { 11 | float window_width; 12 | float window_height; 13 | 14 | int keys[512]; 15 | int keys_down[512]; 16 | int keys_up[512]; 17 | 18 | float mouse_sensitivity; 19 | 20 | int mouse_buttons[3]; 21 | int mouse_buttons_down[3]; 22 | int mouse_buttons_up[3]; 23 | int mouse_locked; 24 | double scroll_delta; 25 | 26 | vec2 mouse_pos; 27 | vec2 last_mouse_pos; 28 | vec2 mouse_delta; 29 | } input; 30 | 31 | void input_init(input *i, GLFWwindow *window); 32 | void input_poll_events(input *i); 33 | 34 | void input_lock_mouse(input *i, GLFWwindow *window); 35 | void input_unlock_mouse(input *i, GLFWwindow *window); 36 | 37 | #endif -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | #include "game.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #define FPS 120.0 9 | 10 | int main(int argc, char **argv) 11 | { 12 | if (!glfwInit()) 13 | { 14 | printf("Couldn't initialize GLFW.\n"); 15 | return -1; 16 | } 17 | 18 | glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); 19 | glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2); 20 | glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); 21 | glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE); 22 | 23 | GLFWwindow *window = glfwCreateWindow(1280, 720, "kunevi", NULL, NULL); 24 | if (!window) 25 | { 26 | printf("Couldn't create the window.\n"); 27 | glfwTerminate(); 28 | return -1; 29 | } 30 | 31 | glfwMakeContextCurrent(window); 32 | glfwSwapInterval(0); 33 | 34 | if(!gladLoadGLLoader((GLADloadproc) glfwGetProcAddress)) 35 | { 36 | printf("Couldn't initialize GLAD.\n"); 37 | return -1; 38 | } 39 | 40 | game g; 41 | input i; 42 | 43 | g.server_addr.sin_port = htons(25565); 44 | g.online = 0; 45 | strcpy(g.player_nickname, "Unnamed"); 46 | 47 | glfwSetWindowUserPointer(window, &i); 48 | 49 | #ifdef _WIN32 50 | WSADATA wsa_data; 51 | WSAStartup(MAKEWORD(1,1), &wsa_data); 52 | #endif 53 | 54 | if (argc / 2 >= 1) 55 | { 56 | for (int i = 0; i < argc; i++) 57 | { 58 | if (strcmp(argv[i], "--ip") == 0) 59 | { 60 | struct addrinfo hints, *res; 61 | 62 | memset (&hints, 0, sizeof (hints)); 63 | hints.ai_family = AF_UNSPEC; 64 | hints.ai_socktype = SOCK_STREAM; 65 | hints.ai_protocol = IPPROTO_TCP; 66 | 67 | if (getaddrinfo(argv[i + 1], NULL, &hints, &res) == 0) 68 | { 69 | g.server_addr.sin_addr = ((struct sockaddr_in *) res->ai_addr)->sin_addr; 70 | g.online = 1; 71 | } 72 | else 73 | { 74 | printf("Couldn't resolve the server hostname.\n"); 75 | } 76 | } 77 | else if (strcmp(argv[i], "--port") == 0) 78 | { 79 | g.server_addr.sin_port = htons((unsigned short) atoi(argv[i + 1])); 80 | } 81 | else if (strcmp(argv[i], "--nick") == 0) 82 | { 83 | strcpy(g.player_nickname, argv[i + 1]); 84 | } 85 | } 86 | } 87 | 88 | game_init(&g, window); 89 | input_init(&i, window); 90 | 91 | 92 | const double tick_interval = 1.0 / 20.0; 93 | const double frame_interval = 1.0 / FPS; 94 | double last_time = 0.0; 95 | double current_time = 0.0; 96 | double delta_time = 0.0; 97 | double tick_accumulator = tick_interval; 98 | 99 | int first_frame = 1; 100 | 101 | while (!glfwWindowShouldClose(window)) 102 | { 103 | current_time = glfwGetTime(); 104 | if (first_frame) 105 | { 106 | last_time = current_time - frame_interval; 107 | first_frame = 0; 108 | } 109 | delta_time = current_time - last_time; 110 | 111 | if (delta_time < frame_interval && !first_frame) 112 | { 113 | usleep(frame_interval * 1000000 - delta_time * 1000000); 114 | current_time = glfwGetTime(); 115 | delta_time = current_time - last_time; 116 | } 117 | 118 | tick_accumulator += delta_time; 119 | 120 | while (tick_accumulator >= tick_interval) 121 | { 122 | game_tick(&g); 123 | tick_accumulator -= tick_interval; 124 | } 125 | 126 | input_poll_events(&i); 127 | game_handle_input(&g, &i); 128 | 129 | game_draw(&g, delta_time, tick_accumulator); 130 | 131 | glfwSwapBuffers(window); 132 | last_time = current_time; 133 | } 134 | 135 | game_destroy(&g); 136 | 137 | glfwDestroyWindow(window); 138 | glfwTerminate(); 139 | 140 | #ifdef _WIN32 141 | WSACleanup(); 142 | #endif 143 | 144 | return 0; 145 | } -------------------------------------------------------------------------------- /src/mesh.c: -------------------------------------------------------------------------------- 1 | #include "mesh.h" 2 | 3 | #include 4 | #include 5 | 6 | int make_block(block_vertex *data, vec3 position, block_id block, block_id neighbours[6]) 7 | { 8 | static vec3 cube_positions[][4] = { 9 | //front face 10 | { 11 | {-0.5f,-0.5f, 0.5f}, 12 | { 0.5f,-0.5f, 0.5f}, 13 | { 0.5f, 0.5f, 0.5f}, 14 | {-0.5f, 0.5f, 0.5f}, 15 | }, 16 | //back face 17 | { 18 | { 0.5f,-0.5f,-0.5f}, 19 | {-0.5f,-0.5f,-0.5f}, 20 | {-0.5f, 0.5f,-0.5f}, 21 | { 0.5f, 0.5f,-0.5f}, 22 | }, 23 | //right face 24 | { 25 | { 0.5f,-0.5f, 0.5f}, 26 | { 0.5f,-0.5f,-0.5f}, 27 | { 0.5f, 0.5f,-0.5f}, 28 | { 0.5f, 0.5f, 0.5f}, 29 | }, 30 | //left face 31 | { 32 | {-0.5f,-0.5f,-0.5f}, 33 | {-0.5f,-0.5f, 0.5f}, 34 | {-0.5f, 0.5f, 0.5f}, 35 | {-0.5f, 0.5f,-0.5f}, 36 | }, 37 | //top face 38 | { 39 | {-0.5f, 0.5f, 0.5f}, 40 | { 0.5f, 0.5f, 0.5f}, 41 | { 0.5f, 0.5f,-0.5f}, 42 | {-0.5f, 0.5f,-0.5f}, 43 | }, 44 | //bottom face 45 | { 46 | {-0.5f,-0.5f,-0.5f}, 47 | { 0.5f,-0.5f,-0.5f}, 48 | { 0.5f,-0.5f, 0.5f}, 49 | {-0.5f,-0.5f, 0.5f}, 50 | }, 51 | }; 52 | static const vec3 cube_normals[] = { 53 | //front face 54 | { 0.0f, 0.0f,-1.0f}, 55 | //back face 56 | { 0.0f, 0.0f, 1.0f}, 57 | //right face 58 | { 1.0f, 0.0f, 0.0f}, 59 | //left face 60 | {-1.0f, 0.0f, 0.0f}, 61 | //top face 62 | { 0.0f, 1.0f, 0.0f}, 63 | //bottom face 64 | { 0.0f,-1.0f, 0.0f}, 65 | }; 66 | static const vec2 cube_tex_coords[][4] = { 67 | //front face 68 | { 69 | {0.0f, 1.0f}, 70 | {1.0f, 1.0f}, 71 | {1.0f, 0.0f}, 72 | {0.0f, 0.0f}, 73 | }, 74 | //back face 75 | { 76 | {0.0f, 1.0f}, 77 | {1.0f, 1.0f}, 78 | {1.0f, 0.0f}, 79 | {0.0f, 0.0f}, 80 | }, 81 | //right face 82 | { 83 | {0.0f, 1.0f}, 84 | {1.0f, 1.0f}, 85 | {1.0f, 0.0f}, 86 | {0.0f, 0.0f}, 87 | }, 88 | //left face 89 | { 90 | {1.0f, 1.0f}, 91 | {0.0f, 1.0f}, 92 | {0.0f, 0.0f}, 93 | {1.0f, 0.0f}, 94 | }, 95 | //top face 96 | { 97 | {0.0f, 1.0f}, 98 | {1.0f, 1.0f}, 99 | {1.0f, 0.0f}, 100 | {0.0f, 0.0f}, 101 | }, 102 | //bottom face 103 | { 104 | {0.0f, 1.0f}, 105 | {1.0f, 1.0f}, 106 | {1.0f, 0.0f}, 107 | {0.0f, 0.0f}, 108 | }, 109 | }; 110 | GLushort cube_indices[] = { 111 | 0, 1, 2, 2, 3, 0, 112 | }; 113 | block_vertex *d = data; 114 | int vert_count = 0; 115 | 116 | if (block == SAPLING || block == ROSE || block == BUTTERCUP) 117 | { 118 | static vec3 cross_positions[][4] = { 119 | { 120 | {-0.5f,-0.5f,-0.5f}, 121 | { 0.5f,-0.5f, 0.5f}, 122 | { 0.5f, 0.5f, 0.5f}, 123 | {-0.5f, 0.5f,-0.5f}, 124 | }, 125 | { 126 | {-0.5f,-0.5f, 0.5f}, 127 | { 0.5f,-0.5f,-0.5f}, 128 | { 0.5f, 0.5f,-0.5f}, 129 | {-0.5f, 0.5f, 0.5f}, 130 | }, 131 | }; 132 | 133 | static const vec2 cross_tex_coords[][4] = { 134 | { 135 | {0.0f, 1.0f}, 136 | {1.0f, 1.0f}, 137 | {1.0f, 0.0f}, 138 | {0.0f, 0.0f}, 139 | }, 140 | { 141 | {1.0f, 1.0f}, 142 | {0.0f, 1.0f}, 143 | {0.0f, 0.0f}, 144 | {1.0f, 0.0f}, 145 | }, 146 | }; 147 | 148 | GLushort cross_indices[] = { 149 | 0, 1, 2, 2, 3, 0, 150 | }; 151 | 152 | for (int i = 0; i < 2; i++) 153 | { 154 | for (int j = 0; j < 6; j++) 155 | { 156 | float tex_x = blocks[block].face_tiles[0] % 16; 157 | float tex_y = blocks[block].face_tiles[0] / 16; 158 | 159 | add_v3(&d->position, &position, &cross_positions[i][cross_indices[j]]); 160 | d->normal = (vec3) {0.0f, 1.0f, 0.0f}; 161 | d->tex_coord.x = tex_x / 16.0f + cross_tex_coords[0][cross_indices[j]].x / 16.0f; 162 | d->tex_coord.y = tex_y / 16.0f + cross_tex_coords[0][cross_indices[j]].y / 16.0f; 163 | d++; 164 | vert_count++; 165 | } 166 | for (int j = 5; j >= 0; j--) 167 | { 168 | float tex_x = blocks[block].face_tiles[0] % 16; 169 | float tex_y = blocks[block].face_tiles[0] / 16; 170 | 171 | add_v3(&d->position, &position, &cross_positions[i][cross_indices[j]]); 172 | d->normal = (vec3) {0.0f, 1.0f, 0.0f}; 173 | d->tex_coord.x = tex_x / 16.0f + cross_tex_coords[1][cross_indices[j]].x / 16.0f; 174 | d->tex_coord.y = tex_y / 16.0f + cross_tex_coords[1][cross_indices[j]].y / 16.0f; 175 | d++; 176 | vert_count++; 177 | } 178 | } 179 | } 180 | else 181 | { 182 | for (int i = 0; i < 6; i++) 183 | { 184 | if (!block_is_opaque(neighbours[i]) && (block != neighbours[i] || !block_connects(block))) 185 | { 186 | float tex_x = blocks[block].face_tiles[i] % 16; 187 | float tex_y = blocks[block].face_tiles[i] / 16; 188 | for (int j = 0; j < 6; j++) 189 | { 190 | add_v3(&d->position, &position, &cube_positions[i][cube_indices[j]]); 191 | d->normal = cube_normals[i]; 192 | d->tex_coord.x = tex_x / 16.0f + cube_tex_coords[i][cube_indices[j]].x / 16.0f; 193 | d->tex_coord.y = tex_y / 16.0f + cube_tex_coords[i][cube_indices[j]].y / 16.0f; 194 | d++; 195 | vert_count++; 196 | } 197 | } 198 | } 199 | } 200 | return vert_count; 201 | } 202 | 203 | void make_frame(vec3 *data, vec3 *position, bounding_box *box) 204 | { 205 | static const vec3 positions[] = 206 | { 207 | {-0.5f, 0.5f, 0.5f}, 208 | { 0.5f, 0.5f, 0.5f}, 209 | { 0.5f, 0.5f,-0.5f}, 210 | {-0.5f, 0.5f,-0.5f}, 211 | 212 | {-0.5f,-0.5f, 0.5f}, 213 | { 0.5f,-0.5f, 0.5f}, 214 | { 0.5f,-0.5f,-0.5f}, 215 | {-0.5f,-0.5f,-0.5f}, 216 | }; 217 | 218 | static const GLushort indices[] = 219 | { 220 | 0, 1, 221 | 1, 2, 222 | 2, 3, 223 | 3, 0, 224 | 225 | 0, 4, 226 | 1, 5, 227 | 2, 6, 228 | 3, 7, 229 | 230 | 4, 5, 231 | 5, 6, 232 | 6, 7, 233 | 7, 4, 234 | }; 235 | 236 | for (int i = 0; i < 24; i++) 237 | { 238 | data->x = positions[indices[i]].x * box->size.x + position->x; 239 | data->y = positions[indices[i]].y * box->size.y + position->y; 240 | data->z = positions[indices[i]].z * box->size.z + position->z; 241 | data++; 242 | } 243 | } 244 | 245 | int make_text(gui_vertex *data, const char *text, float scale, vec2 *size) 246 | { 247 | static const vec2 positions[] = 248 | { 249 | {0.0f, 0.0f}, 250 | {1.0f, 0.0f}, 251 | {1.0f, 1.0f}, 252 | {0.0f, 1.0f}, 253 | }; 254 | 255 | static const vec2 tex_coords[] = 256 | { 257 | {0.0f, 1.0f}, 258 | {1.0f, 1.0f}, 259 | {1.0f, 0.0f}, 260 | {0.0f, 0.0f}, 261 | }; 262 | static const GLushort indices[] = 263 | { 264 | 0, 1, 2, 2, 3, 0 265 | }; 266 | static const int char_widths[256] = 267 | { 268 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 269 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 270 | 6, 2, 5, 6, 6, 6, 6, 3, 5, 5, 5, 6, 2, 6, 2, 6, 271 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 272 | 7, 6, 6, 6, 6, 6, 6, 6, 6, 4, 6, 6, 6, 6, 6, 6, 273 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 274 | 6, 6, 6, 6, 6, 6, 5, 6, 6, 2, 6, 5, 3, 6, 6, 6, 275 | 6, 6, 6, 6, 4, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 276 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 277 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 278 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 279 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 280 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 281 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 282 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 283 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 284 | }; 285 | 286 | int vert_count = 0; 287 | 288 | float x = 0; 289 | float y = 0; 290 | 291 | for (int i = 0; text[i] != '\0'; i++) 292 | { 293 | if (text[i] == '\n') 294 | { 295 | y -= scale * 9.0f / 8.0f; 296 | x = 0; 297 | continue; 298 | } 299 | if (text[i] == ' ') 300 | { 301 | x += scale / 2.0f; 302 | continue; 303 | } 304 | for (int j = 0; j < 6; j++) 305 | { 306 | data->position.x = positions[indices[j]].x * scale + x + scale / 8.0f; 307 | data->position.y = positions[indices[j]].y * scale + y - scale / 8.0f; 308 | data->tex_coord.x = (text[i] % 16 + tex_coords[indices[j]].x) / 16.0f; 309 | data->tex_coord.y = (text[i] / 16 + tex_coords[indices[j]].y) / 16.0f; 310 | data->tex_id = 1; 311 | data->color = (vec3) {0.3f, 0.3f, 0.3f}; 312 | data++; 313 | vert_count++; 314 | } 315 | for (int j = 0; j < 6; j++) 316 | { 317 | data->position.x = positions[indices[j]].x * scale + x; 318 | data->position.y = positions[indices[j]].y * scale + y; 319 | data->tex_coord.x = (text[i] % 16 + tex_coords[indices[j]].x) / 16.0f; 320 | data->tex_coord.y = (text[i] / 16 + tex_coords[indices[j]].y) / 16.0f; 321 | data->tex_id = 1; 322 | data->color = VEC3_ONE; 323 | data++; 324 | vert_count++; 325 | } 326 | x += char_widths[text[i]] * scale / 8.0f; 327 | } 328 | size->x = x; 329 | size->y = y; 330 | return vert_count; 331 | } -------------------------------------------------------------------------------- /src/mesh.h: -------------------------------------------------------------------------------- 1 | #ifndef MESH_H 2 | #define MESH_H 3 | 4 | #include "mvmath.h" 5 | #include "block_data.h" 6 | 7 | typedef struct 8 | { 9 | vec3 position; 10 | vec3 normal; 11 | vec2 tex_coord; 12 | } block_vertex; 13 | 14 | typedef struct 15 | { 16 | vec2 position; 17 | vec2 tex_coord; 18 | float tex_id; 19 | vec3 color; 20 | } gui_vertex; 21 | 22 | int make_block(block_vertex *data, vec3 position, block_id block, block_id neighbours[6]); 23 | void make_frame(vec3 *data, vec3 *position, bounding_box* box); 24 | int make_text(gui_vertex* data, const char* text, float scale, vec2 *size); 25 | 26 | #endif -------------------------------------------------------------------------------- /src/mvmath.c: -------------------------------------------------------------------------------- 1 | #include "mvmath.h" 2 | #include 3 | 4 | void normalize(vec3 *v) 5 | { 6 | float d = sqrtf((v->x) * (v->x) + (v->y) * (v->y) + (v->z) * (v->z)); 7 | v->x /= d; v->y /= d; v->z /= d; 8 | } 9 | 10 | vec3 subtract_v3(vec3 v1, vec3 v2) { 11 | vec3 result; 12 | result.x = v1.x - v2.x; 13 | result.y = v1.y - v2.y; 14 | result.z = v1.z - v2.z; 15 | return result; 16 | } 17 | 18 | ivec3 subtract_v3i(ivec3 v1, ivec3 v2) { 19 | ivec3 result; 20 | result.x = v1.x - v2.x; 21 | result.y = v1.y - v2.y; 22 | result.z = v1.z - v2.z; 23 | return result; 24 | } 25 | 26 | vec2 subtract_v2r(vec2 v1, vec2 v2) { 27 | vec2 result; 28 | result.x = v1.x - v2.x; 29 | result.y = v1.y - v2.y; 30 | return result; 31 | }; 32 | 33 | float 34 | vec3_dot(vec3 a, vec3 b) { 35 | return a.x * b.x + a.y * b.y + a.z * b.z; 36 | } 37 | 38 | float 39 | vec3_norm2(vec3 v){ 40 | return vec3_dot(v, v); 41 | } 42 | 43 | float 44 | vec3_norm(vec3 v) { 45 | return sqrtf(vec3_norm2(v)); 46 | } 47 | 48 | float 49 | vec2_dot(vec2 a, vec2 b) { 50 | return a.x * b.x + a.y * b.y; 51 | } 52 | 53 | float 54 | vec2_norm2(vec2 v){ 55 | return vec2_dot(v, v); 56 | } 57 | 58 | float 59 | vec2_norm(vec2 v) { 60 | return sqrtf(vec2_norm2(v)); 61 | } 62 | 63 | void identity(mat4 *m) 64 | { 65 | m->value[0] = 1.0f; m->value[1] = 0.0f; m->value[2] = 0.0f; m->value[3] = 0.0f; 66 | m->value[4] = 0.0f; m->value[5] = 1.0f; m->value[6] = 0.0f; m->value[7] = 0.0f; 67 | m->value[8] = 0.0f; m->value[9] = 0.0f; m->value[10] = 1.0f; m->value[11] = 0.0f; 68 | m->value[12] = 0.0f; m->value[13] = 0.0f; m->value[14] = 0.0f; m->value[15] = 1.0f; 69 | } 70 | 71 | void translate(mat4 *m, vec3 *v) 72 | { 73 | m->value[0] = 1.0f; m->value[1] = 0.0f; m->value[2] = 0.0f; m->value[3] = 0.0f; 74 | m->value[4] = 0.0f; m->value[5] = 1.0f; m->value[6] = 0.0f; m->value[7] = 0.0f; 75 | m->value[8] = 0.0f; m->value[9] = 0.0f; m->value[10] = 1.0f; m->value[11] = 0.0f; 76 | m->value[12] = v->x; m->value[13] = v->y; m->value[14] = v->z; m->value[15] = 1.0f; 77 | } 78 | 79 | void translate_v2(mat4 *m, vec2 *v) 80 | { 81 | m->value[0] = 1.0f; m->value[1] = 0.0f; m->value[2] = 0.0f; m->value[3] = 0.0f; 82 | m->value[4] = 0.0f; m->value[5] = 1.0f; m->value[6] = 0.0f; m->value[7] = 0.0f; 83 | m->value[8] = 0.0f; m->value[9] = 0.0f; m->value[10] = 1.0f; m->value[11] = 0.0f; 84 | m->value[12] = v->x; m->value[13] = v->y; m->value[14] = 0.0f; m->value[15] = 1.0f; 85 | } 86 | 87 | void add_v3(vec3 *v, vec3 *v1, vec3 *v2) 88 | { 89 | v->x = v1->x + v2->x; 90 | v->y = v1->y + v2->y; 91 | v->z = v1->z + v2->z; 92 | } 93 | 94 | void add_v2(vec2 *v, vec2 *v1, vec2 *v2) 95 | { 96 | v->x = v1->x + v2->x; 97 | v->y = v1->y + v2->y; 98 | } 99 | 100 | void subtract_v2(vec2 *v, vec2 *v1, vec2 *v2) 101 | { 102 | v->x = v1->x - v2->x; 103 | v->y = v1->y - v2->y; 104 | } 105 | 106 | void rotate(mat4 *matrix, vec3 *axis, float angle) 107 | { 108 | normalize(axis); 109 | float s = sinf(angle); 110 | float c = cosf(angle); 111 | float m = 1.0f - c; 112 | matrix->value[0] = m * axis->x * axis->x + c; 113 | matrix->value[1] = m * axis->x * axis->y - axis->z * s; 114 | matrix->value[2] = m * axis->z * axis->x + axis->y * s; 115 | matrix->value[3] = 0.0f; 116 | matrix->value[4] = m * axis->x * axis->y + axis->z * s; 117 | matrix->value[5] = m * axis->y * axis->y + c; 118 | matrix->value[6] = m * axis->y * axis->z - axis->x * s; 119 | matrix->value[7] = 0.0f; 120 | matrix->value[8] = m * axis->z * axis->x - axis->y * s; 121 | matrix->value[9] = m * axis->y * axis->z + axis->x * s; 122 | matrix->value[10] = m * axis->z * axis->z + c; 123 | matrix->value[11] = 0.0f; 124 | matrix->value[12] = 0.0f; 125 | matrix->value[13] = 0.0f; 126 | matrix->value[14] = 0.0f; 127 | matrix->value[15] = 1.0f; 128 | } 129 | 130 | void multiply(mat4 *m, mat4 *m1, mat4 *m2) 131 | { 132 | mat4 result; 133 | for (int c = 0; c < 4; c++) { 134 | for (int r = 0; r < 4; r++) { 135 | int index = c * 4 + r; 136 | float total = 0; 137 | for (int i = 0; i < 4; i++) { 138 | int p = i * 4 + r; 139 | int q = c * 4 + i; 140 | total += m1->value[p] * m2->value[q]; 141 | } 142 | result.value[index] = total; 143 | } 144 | } 145 | *m = result; 146 | } 147 | 148 | void scale(mat4 *m, vec3 *v) { 149 | m->value[0] = v->x; m->value[1] = 0.0f; m->value[2] = 0.0f; m->value[3] = 0.0f; 150 | m->value[4] = 0.0f; m->value[5] = v->y; m->value[6] = 0.0f; m->value[7] = 0.0f; 151 | m->value[8] = 0.0f; m->value[9] = 0.0f; m->value[10] = v->z; m->value[11] = 0.0f; 152 | m->value[12] = 0.0f; m->value[13] = 0.0f; m->value[14] = 0.0f; m->value[15] = 1.0f; 153 | } 154 | 155 | void multiply_v3f(vec3 *v, vec3 *v1, float v2) 156 | { 157 | v->x = v1->x * v2; 158 | v->y = v1->y * v2; 159 | v->z = v1->z * v2; 160 | } 161 | 162 | void multiply_v2f(vec2 *v, vec2 *v1, float v2) 163 | { 164 | v->x = v1->x * v2; 165 | v->y = v1->y * v2; 166 | } 167 | 168 | void frustum(mat4 *m, float left, float right, float bottom, float top, float znear, float zfar) 169 | { 170 | float temp, temp2, temp3, temp4; 171 | temp = 2.0f * znear; 172 | temp2 = right - left; 173 | temp3 = top - bottom; 174 | temp4 = zfar - znear; 175 | m->value[0] = temp / temp2; 176 | m->value[1] = 0.0f; 177 | m->value[2] = 0.0f; 178 | m->value[3] = 0.0f; 179 | m->value[4] = 0.0f; 180 | m->value[5] = temp / temp3; 181 | m->value[6] = 0.0f; 182 | m->value[7] = 0.0f; 183 | m->value[8] = (right + left) / temp2; 184 | m->value[9] = (top + bottom) / temp3; 185 | m->value[10] = (-zfar - znear) / temp4; 186 | m->value[11] = -1.0f; 187 | m->value[12] = 0.0f; 188 | m->value[13] = 0.0f; 189 | m->value[14] = (-temp * zfar) / temp4; 190 | m->value[15] = 0.0f; 191 | } 192 | 193 | void perspective(mat4 *m, float fov, float aspect, float znear, float zfar) 194 | { 195 | float ymax, xmax; 196 | ymax = znear * tanf(fov * PI / 360.0f); 197 | xmax = ymax * aspect; 198 | frustum(m, -xmax, xmax, -ymax, ymax, znear, zfar); 199 | } 200 | 201 | void ortho(mat4 *m, float left, float right, float bottom, float top, float near, float far) 202 | { 203 | m->value[0] = 2 / (right - left); 204 | m->value[1] = 0; 205 | m->value[2] = 0; 206 | m->value[3] = 0; 207 | m->value[4] = 0; 208 | m->value[5] = 2 / (top - bottom); 209 | m->value[6] = 0; 210 | m->value[7] = 0; 211 | m->value[8] = 0; 212 | m->value[9] = 0; 213 | m->value[10] = -2 / (far - near); 214 | m->value[11] = 0; 215 | m->value[12] = -(right + left) / (right - left); 216 | m->value[13] = -(top + bottom) / (top - bottom); 217 | m->value[14] = -(far + near) / (far - near); 218 | m->value[15] = 1; 219 | } 220 | 221 | float lerp(float a, float b, float t) { 222 | return a + t * (b - a); 223 | } 224 | 225 | void v3_lerp(vec3* v, vec3 *a, vec3 *b, float t) { 226 | v->x = lerp(a->x, b->x, t); 227 | v->y = lerp(a->y, b->y, t); 228 | v->z = lerp(a->z, b->z, t); 229 | } 230 | 231 | int max(int a, int b) { 232 | return (a > b ? a : b); 233 | } -------------------------------------------------------------------------------- /src/mvmath.h: -------------------------------------------------------------------------------- 1 | #ifndef MVMATH_H 2 | #define MVMATH_H 3 | 4 | #define PI 3.14159265359f 5 | #define DEGREES(radians) ((radians) * 180.0f / PI) 6 | #define RADIANS(degrees) ((degrees) * PI / 180.0f) 7 | 8 | typedef struct 9 | { 10 | float value[16]; 11 | } mat4; 12 | 13 | typedef struct 14 | { 15 | float x; 16 | float y; 17 | float z; 18 | } vec3; 19 | 20 | typedef struct 21 | { 22 | int x; 23 | int y; 24 | int z; 25 | } ivec3; 26 | 27 | typedef struct 28 | { 29 | float x; 30 | float y; 31 | } vec2; 32 | 33 | typedef struct 34 | { 35 | int x; 36 | int y; 37 | } ivec2; 38 | 39 | #define VEC3S2V(v) (vec3){v.x, v.y, v.z} 40 | #define VEC2S2V(v) (vec2){v.x, v.y} 41 | #define VEC3I2V(v) (ivec3){v.x, v.y, v.z} 42 | #define VEC2I2V(v) (ivec2){v.x, v.y} 43 | 44 | static vec3 AXIS_UP = {0.0f, 1.0f, 0.0f}; 45 | static vec3 AXIS_RIGHT = {1.0f, 0.0f, 0.0f}; 46 | static vec3 AXIS_FRONT = {0.0f, 0.0f, 1.0f}; 47 | static vec3 VEC3_ONE = {1.0f, 1.0f, 1.0f}; 48 | 49 | static mat4 TEMP_MAT; 50 | 51 | void normalize(vec3 *v); 52 | 53 | void identity(mat4 *m); 54 | void translate(mat4 *m, vec3 *v); 55 | void translate_v2(mat4 *m, vec2 *v); 56 | void rotate(mat4 *m, vec3 *axis, float angle); 57 | 58 | void add_v3(vec3 *v, vec3 *v1, vec3 *v2); 59 | void add_v2(vec2 *v, vec2 *v1, vec2 *v2); 60 | 61 | void subtract_v2(vec2 *v, vec2 *v1, vec2 *v2); 62 | vec2 subtract_v2r(vec2 v1, vec2 v2); 63 | vec3 subtract_v3(vec3 v1, vec3 v2); 64 | ivec3 subtract_v3i(ivec3 v1, ivec3 v2); 65 | 66 | float vec3_norm(vec3 v); 67 | float vec2_norm(vec2 v); 68 | 69 | void multiply(mat4 *m, mat4 *m1, mat4 *m2); 70 | void scale(mat4 *m, vec3* v); 71 | void multiply_v3f(vec3 *v, vec3 *v1, float v2); 72 | void multiply_v2f(vec2 *v, vec2 *v1, float v2); 73 | 74 | void frustum(mat4 *m, float left, float right, float bottom, float top, float znear, float zfar); 75 | void perspective(mat4 *m, float fov, float aspect, float znear, float zfar); 76 | void ortho(mat4 *m, float left, float right, float bottom, float top, float near, float far); 77 | 78 | float lerp(float a, float b, float t); 79 | void v3_lerp(vec3 *v, vec3 *a, vec3 *b, float t); 80 | 81 | int max(int a, int b); 82 | 83 | #endif -------------------------------------------------------------------------------- /src/noise.c: -------------------------------------------------------------------------------- 1 | #include "noise.h" 2 | #include "string.h" 3 | 4 | 5 | f32 octave_compute(struct Octave *p, f32 seed, f32 x, f32 z) { 6 | f32 u = 1.0f, v = 0.0f; 7 | for (int i = 0; i < p->n; i++) { 8 | v += noise3(x / u, z / u, seed + i + (p->o * 32)) * u; 9 | u *= 2.0f; 10 | } 11 | return v; 12 | } 13 | 14 | struct Noise octave(s32 n, s32 o) { 15 | struct Noise result = { .compute = (FNoise) octave_compute }; 16 | struct Octave params = { n, o }; 17 | memcpy(&result.params, ¶ms, sizeof(struct Octave)); 18 | return result; 19 | } 20 | 21 | f32 combined_compute(struct Combined *p, f32 seed, f32 x, f32 z) { 22 | return p->n->compute(&p->n->params, seed, x + p->m->compute(&p->m->params, seed, x, z), z); 23 | } 24 | 25 | struct Noise combined(struct Noise *n, struct Noise *m) { 26 | struct Noise result = { .compute = (FNoise) combined_compute }; 27 | struct Combined params = { n, m }; 28 | memcpy(&result.params, ¶ms, sizeof(struct Combined)); 29 | return result; 30 | } 31 | -------------------------------------------------------------------------------- /src/noise.h: -------------------------------------------------------------------------------- 1 | #ifndef NOISE_H 2 | #define NOISE_H 3 | 4 | #include "world.h" 5 | #include "chunk.h" 6 | 7 | #include 8 | #include 9 | 10 | typedef uint8_t u8; 11 | typedef uint16_t u16; 12 | typedef uint32_t u32; 13 | typedef uint64_t u64; 14 | 15 | typedef int8_t s8; 16 | typedef int16_t s16; 17 | typedef int32_t s32; 18 | typedef int64_t s64; 19 | 20 | typedef float f32; 21 | typedef double f64; 22 | 23 | #define SRAND(seed) srand(seed) 24 | #define RAND(min, max) ((rand() % (max - min + 1)) + min) 25 | #define RANDCHANCE(chance) ((RAND(0, 100) / 100.0f) <= chance) 26 | 27 | #define WATER_LEVEL 64 28 | 29 | typedef f32 (*FNoise)(void *p, f32 s, f32 x, f32 z); 30 | 31 | struct Noise { 32 | u8 params[512]; // either Octave or Combined 33 | FNoise compute; 34 | }; 35 | 36 | // Octave noise with n octaves and seed offset o 37 | // Maximum amplitude is 2^0 + 2^1 + 2^2 ... 2^n = 2^(n+1) - 1 38 | // i.e. for octave 8, values range between [-511, 511] 39 | struct Octave { 40 | s32 n, o; 41 | }; 42 | 43 | // Combined noise where compute(x, z) = n.compute(x + m.compute(x, z), z) 44 | struct Combined { 45 | struct Noise *n, *m; 46 | }; 47 | 48 | f32 octave_compute(struct Octave *p, f32 seed, f32 x, f32 z) ; 49 | struct Noise octave(s32 n, s32 o); 50 | f32 combined_compute(struct Combined *p, f32 seed, f32 x, f32 z); 51 | struct Noise combined(struct Noise *n, struct Noise *m); 52 | 53 | #endif 54 | -------------------------------------------------------------------------------- /src/server/main.c: -------------------------------------------------------------------------------- 1 | #include "server.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | int main(int argc, char **argv) { 10 | printf("b server\n"); 11 | 12 | server s; 13 | unsigned short port = 25565; 14 | if(argc / 2 >= 1) { 15 | for(int i = 0; i < argc; i++) { 16 | if(strcmp(argv[i], "--p") == 0) { 17 | port = (unsigned short)argv[i+1]; 18 | } 19 | } 20 | } 21 | 22 | server_init(&s, port); 23 | 24 | struct timeval current_tv; 25 | gettimeofday(¤t_tv, NULL); 26 | 27 | unsigned int last_time = 0; 28 | unsigned int current_time = 0; 29 | unsigned int delta_time = 0; 30 | unsigned int tick_accumulator = 0; 31 | const unsigned int tick_interval = 1000000 / 20; 32 | 33 | int first_tick = 1; 34 | 35 | struct timeval tv; 36 | fd_set read_fds; 37 | 38 | tv.tv_sec = 0; 39 | tv.tv_usec = 0; 40 | 41 | while(1) { 42 | gettimeofday(¤t_tv, NULL); 43 | current_time = 1000000 * current_tv.tv_sec + current_tv.tv_usec; 44 | if(first_tick) { 45 | last_time = current_time - tick_interval; 46 | first_tick = 0; 47 | } 48 | 49 | delta_time = current_time - last_time; 50 | 51 | if(delta_time < tick_interval && !first_tick) { 52 | usleep(tick_interval - delta_time); 53 | gettimeofday(¤t_tv, NULL); 54 | current_time = 1000000 * current_tv.tv_sec + current_tv.tv_usec; 55 | delta_time = current_time - last_time; 56 | } 57 | 58 | last_time = current_time; 59 | 60 | tick_accumulator += delta_time; 61 | 62 | server_tick(&s); 63 | 64 | #ifndef _WIN32 65 | FD_ZERO(&read_fds); 66 | FD_SET(0, &read_fds); 67 | 68 | select(1, &read_fds, NULL, NULL, &tv); 69 | 70 | if (FD_ISSET(0, &read_fds)) 71 | break; 72 | #endif 73 | 74 | FD_ZERO(&read_fds); 75 | FD_SET(0, &read_fds); 76 | 77 | select(1, &read_fds, NULL, NULL, &tv); 78 | 79 | if(FD_ISSET(0, &read_fds)) { 80 | break; 81 | } 82 | } 83 | 84 | printf("closing\n"); 85 | server_destroy(&s); 86 | } -------------------------------------------------------------------------------- /src/server/server.c: -------------------------------------------------------------------------------- 1 | #include "server.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | void server_init(server *s, unsigned short port) 10 | { 11 | #ifdef _WIN32 12 | WSADATA wsa_data; 13 | WSAStartup(MAKEWORD(1,1), &wsa_data); 14 | #endif 15 | 16 | s->buffer = malloc(DATA_BUFFER_SIZE); 17 | s->chunks = malloc(WORLD_SIZE * WORLD_SIZE * sizeof(chunk)); 18 | memset(s->chunks, AIR, WORLD_SIZE * WORLD_SIZE * sizeof(chunk)); 19 | memset(s->ids_used, 0, sizeof(s->ids_used)); 20 | 21 | static const int GRASS_LEVEL = 40; 22 | 23 | for (int chunk_x = 0; chunk_x < WORLD_SIZE; chunk_x++) 24 | { 25 | for (int chunk_z = 0; chunk_z < WORLD_SIZE; chunk_z++) 26 | { 27 | chunk *c = &s->chunks[chunk_x * WORLD_SIZE + chunk_z]; 28 | 29 | for (int x = 0; x < CHUNK_SIZE; x++) 30 | { 31 | for (int y = 0; y < WORLD_HEIGHT; y++) 32 | { 33 | for (int z = 0; z < CHUNK_SIZE; z++) 34 | { 35 | if (y > GRASS_LEVEL) 36 | c->blocks[x][y][z] = AIR; 37 | else if (y == GRASS_LEVEL) 38 | c->blocks[x][y][z] = GRASS; 39 | else if (y == 0) 40 | c->blocks[x][y][z] = BEDROCK; 41 | else if (y < 25) 42 | c->blocks[x][y][z] = STONE; 43 | else if (y < GRASS_LEVEL) 44 | c->blocks[x][y][z] = DIRT; 45 | } 46 | } 47 | } 48 | } 49 | } 50 | 51 | FD_ZERO(&s->sockets); 52 | 53 | struct sockaddr_in addrport; 54 | s->listener = socket(PF_INET, SOCK_STREAM, 0); 55 | addrport.sin_family = AF_INET; 56 | addrport.sin_port = htons(port); 57 | addrport.sin_addr.s_addr = htonl(INADDR_ANY); 58 | 59 | if (SOCKET_VALID(bind(s->listener, (struct sockaddr *) &addrport, sizeof(addrport)))) 60 | { 61 | if (SOCKET_VALID(listen(s->listener, 10))) 62 | { 63 | printf("Server listening for connections on port %d\n", port); 64 | FD_SET(s->listener, &s->sockets); 65 | s->max_fd = s->listener; 66 | #ifndef _WIN32 67 | signal(SIGPIPE, SIG_IGN); 68 | #endif 69 | } 70 | else 71 | printf("Server socket can't listen for connections.\n"); 72 | } 73 | else 74 | { 75 | printf("Couldn't bind the socket.\n"); 76 | } 77 | 78 | s->num_players = 0; 79 | } 80 | 81 | int find_player_by_socket(server *s, SOCKET socket) 82 | { 83 | int index = -1; 84 | for (int i = 0; i < s->num_players; i++) 85 | { 86 | if (s->players[i].socket == socket) 87 | { 88 | index = i; 89 | break; 90 | } 91 | } 92 | return index; 93 | } 94 | 95 | 96 | void spawn_player(server *s, SOCKET player_socket, const char *nickname) 97 | { 98 | player *new_player = &s->players[s->num_players]; 99 | memset(new_player, 0, sizeof(player)); 100 | 101 | new_player->socket = player_socket; 102 | strcpy(new_player->nickname, nickname); 103 | 104 | for (unsigned char i = 0; i < 255; i++) 105 | { 106 | if (!s->ids_used[i]) 107 | { 108 | new_player->id = i; 109 | s->ids_used[i] = 1; 110 | break; 111 | } 112 | } 113 | 114 | spawn_player_packet packet; 115 | packet.id = SPAWN_PLAYER_ID; 116 | packet.player_id = new_player->id; 117 | strcpy(packet.nickname, new_player->nickname); 118 | for (int i = 0; i < s->num_players; i++) 119 | { 120 | send(s->players[i].socket, &packet, sizeof(packet), 0); 121 | } 122 | 123 | for (int i = 0; i < s->num_players; i++) 124 | { 125 | packet.player_id = s->players[i].id; 126 | strcpy(packet.nickname, s->players[i].nickname); 127 | send(player_socket, &packet, sizeof(packet), 0); 128 | } 129 | 130 | printf("%s joined the game.\n", new_player->nickname); 131 | 132 | s->num_players++; 133 | } 134 | 135 | void despawn_player(server *s, int player_index) 136 | { 137 | player *p = &s->players[player_index]; 138 | 139 | printf("%s left the game.\n", p->nickname); 140 | close(p->socket); 141 | FD_CLR(p->socket, &s->sockets); 142 | 143 | despawn_player_packet packet; 144 | packet.id = DESPAWN_PLAYER_ID; 145 | packet.player_id = p->id; 146 | 147 | for (int i = player_index; i < s->num_players - 1; i++) 148 | { 149 | s->players[i] = s->players[i + 1]; 150 | } 151 | s->num_players--; 152 | 153 | for (int i = 0; i < s->num_players; i++) 154 | { 155 | send(s->players[i].socket, &packet, sizeof(packet), 0); 156 | } 157 | 158 | s->ids_used[p->id] = 0; 159 | } 160 | 161 | void server_tick(server *s) 162 | { 163 | fd_set read_fds = s->sockets; 164 | struct timeval tv; 165 | tv.tv_sec = 0; 166 | tv.tv_usec = 0; 167 | if (SOCKET_VALID(select(s->max_fd + 1, &read_fds, NULL, NULL, &tv))) 168 | { 169 | for (int i = 0; i <= s->max_fd; i++) 170 | { 171 | if (FD_ISSET(i, &read_fds)) 172 | { 173 | if (i == s->listener) 174 | { 175 | struct sockaddr_storage client_addr; 176 | socklen_t addr_len = sizeof(client_addr); 177 | SOCKET new_fd = accept(s->listener, (struct sockaddr *) &client_addr, &addr_len); 178 | FD_SET(new_fd, &s->sockets); 179 | if (new_fd > s->max_fd) s->max_fd = new_fd; 180 | } 181 | else 182 | { 183 | int data_size = 0; 184 | int data_position = 0; 185 | if (SOCKET_VALID(data_size = recv(i, s->buffer, DATA_BUFFER_SIZE, 0))) 186 | { 187 | if (data_size == 0) 188 | { 189 | int index = find_player_by_socket(s, i); 190 | if (index != -1) 191 | { 192 | despawn_player(s, index); 193 | } 194 | } 195 | else 196 | { 197 | while (data_position < data_size) 198 | { 199 | switch (s->buffer[data_position]) 200 | { 201 | case PLAYER_IDENTIFICATION_ID: 202 | { 203 | player_identification_packet *packet = (player_identification_packet *) (s->buffer + data_position); 204 | if (find_player_by_socket(s, i) == -1) 205 | { 206 | packet->nickname[30] = '\0'; 207 | spawn_player(s, i, packet->nickname); 208 | } 209 | data_position += sizeof(player_identification_packet); 210 | } 211 | break; 212 | case SET_BLOCK_ID: 213 | { 214 | set_block_packet *packet = (set_block_packet *) (s->buffer + data_position); 215 | 216 | short x = ntohs(packet->x); 217 | short y = ntohs(packet->y); 218 | short z = ntohs(packet->z); 219 | 220 | if (y >= WORLD_HEIGHT || y < 0 || 221 | x >= CHUNK_SIZE * WORLD_SIZE / 2 || x <= -CHUNK_SIZE * WORLD_SIZE / 2 || 222 | z >= CHUNK_SIZE * WORLD_SIZE / 2 || z <= -CHUNK_SIZE * WORLD_SIZE / 2) 223 | { 224 | int index = find_player_by_socket(s, i); 225 | if (index != -1) 226 | { 227 | printf("%s has tried to set a block at invalid coordinates, kicking him...\n", s->players[index].nickname); 228 | despawn_player(s, index); 229 | } 230 | } 231 | else 232 | { 233 | for (int j = 0; j <= s->max_fd; j++) 234 | { 235 | if (j != s->listener && j != i) 236 | { 237 | send(j, packet, sizeof(set_block_packet), 0); 238 | } 239 | } 240 | 241 | size_t chunk_x = CHUNK_FROM_WORLD_COORDS(x); 242 | size_t chunk_z = CHUNK_FROM_WORLD_COORDS(z); 243 | chunk *c = &s->chunks[chunk_x * WORLD_SIZE + chunk_z]; 244 | 245 | size_t block_x = WORLD_TO_CHUNK(x); 246 | size_t block_z = WORLD_TO_CHUNK(z); 247 | c->blocks[block_x][y][block_z] = packet->block; 248 | 249 | printf("The block at %d/%d/%d has been set to %d\n", x, y, z, packet->block); 250 | } 251 | data_position += sizeof(set_block_packet); 252 | } 253 | break; 254 | case POSITION_UPDATE_ID: 255 | { 256 | position_update_packet *packet = (position_update_packet *) (s->buffer + data_position); 257 | for (int j = 0; j < s->num_players; j++) 258 | { 259 | if (s->players[j].socket == i) 260 | { 261 | s->players[j].x = ntohs(packet->x); 262 | s->players[j].y = ntohs(packet->y); 263 | s->players[j].z = ntohs(packet->z); 264 | break; 265 | } 266 | } 267 | data_position += sizeof(position_update_packet); 268 | } 269 | } 270 | } 271 | } 272 | } 273 | } 274 | } 275 | } 276 | for (int i = 0; i < s->num_players; i++) 277 | { 278 | player *p = &s->players[i]; 279 | int bytes_to_send = 0; 280 | for (int j = 0; j < s->num_players; j++) 281 | { 282 | if (j != i) 283 | { 284 | position_update_packet *packet = (position_update_packet *) (s->buffer + bytes_to_send); 285 | packet->id = POSITION_UPDATE_ID; 286 | packet->player_id = s->players[j].id; 287 | packet->x = htons(s->players[j].x); 288 | packet->y = htons(s->players[j].y); 289 | packet->z = htons(s->players[j].z); 290 | bytes_to_send += sizeof(position_update_packet); 291 | } 292 | } 293 | chunk *chunk_to_send = NULL; 294 | size_t chunk_to_send_x; 295 | size_t chunk_to_send_z; 296 | unsigned short chunk_to_send_complete; 297 | float min_distance = -1.0f; 298 | 299 | for (int x = 0; x < WORLD_SIZE; x++) 300 | { 301 | for (int z = 0; z < WORLD_SIZE; z++) 302 | { 303 | if (p->chunk_data_sent[x][z] < CHUNK_SIZE * WORLD_HEIGHT * CHUNK_SIZE) 304 | { 305 | chunk *c = &s->chunks[x * WORLD_SIZE + z]; 306 | 307 | float distance_x = abs(x - CHUNK_FROM_WORLD_COORDS((int) floorf(s->players[i].x / 32.0f))); 308 | float distance_z = abs(z - CHUNK_FROM_WORLD_COORDS((int) floorf(s->players[i].z / 32.0f))); 309 | float distance = sqrtf(distance_x * distance_x + distance_z * distance_z); 310 | if (distance <= 12.0f && (distance < min_distance || min_distance < 0.0f)) 311 | { 312 | min_distance = distance; 313 | chunk_to_send = c; 314 | chunk_to_send_x = x; 315 | chunk_to_send_z = z; 316 | chunk_to_send_complete = p->chunk_data_sent[x][z]; 317 | } 318 | } 319 | } 320 | } 321 | 322 | if (chunk_to_send) 323 | { 324 | chunk_data_packet *packet = (chunk_data_packet *) (s->buffer + bytes_to_send); 325 | packet->id = CHUNK_DATA_ID; 326 | packet->x = chunk_to_send_x - WORLD_SIZE / 2; 327 | packet->z = chunk_to_send_z - WORLD_SIZE / 2; 328 | 329 | s->def_stream.zalloc = Z_NULL; 330 | s->def_stream.zfree = Z_NULL; 331 | s->def_stream.opaque = Z_NULL; 332 | deflateInit(&s->def_stream, 3); 333 | 334 | s->def_stream.avail_in = sizeof(chunk_to_send->blocks) - chunk_to_send_complete; 335 | s->def_stream.next_in = (Bytef *) ((char *) chunk_to_send->blocks + chunk_to_send_complete); 336 | s->def_stream.avail_out = sizeof(packet->data); 337 | s->def_stream.next_out = (Bytef *) packet->data; 338 | 339 | deflate(&s->def_stream, Z_FINISH); 340 | deflateEnd(&s->def_stream); 341 | 342 | packet->length = htons(s->def_stream.total_in); 343 | packet->complete = htons(chunk_to_send_complete); 344 | 345 | s->players[i].chunk_data_sent[chunk_to_send_x][chunk_to_send_z] += s->def_stream.total_in; 346 | bytes_to_send += sizeof(chunk_data_packet); 347 | } 348 | send(s->players[i].socket, s->buffer, bytes_to_send, 0); 349 | } 350 | } 351 | } 352 | 353 | void server_destroy(server *s) 354 | { 355 | #ifdef _WIN32 356 | closesocket(s->listener); 357 | WSACleanup(); 358 | #else 359 | close(s->listener); 360 | #endif 361 | free(s->buffer); 362 | free(s->chunks); 363 | } -------------------------------------------------------------------------------- /src/server/server.h: -------------------------------------------------------------------------------- 1 | #ifndef SERVER_H 2 | #define SERVER_H 3 | 4 | #include "../sockets.h" 5 | 6 | #include "miniz.h" 7 | 8 | #define CHUNK_SIZE 16 9 | #define WORLD_HEIGHT 128 10 | #define WORLD_SIZE 32 11 | 12 | #define WORLD_TO_CHUNK(x) (x < 0 ? x % CHUNK_SIZE == 0 ? 0 : CHUNK_SIZE + x % CHUNK_SIZE : x % CHUNK_SIZE) 13 | #define CHUNK_FROM_WORLD_COORDS(x) ((x / CHUNK_SIZE < 0 ? x + 1 : x) / CHUNK_SIZE + WORLD_SIZE / 2 - (x < 0 ? 1 : 0)) 14 | 15 | typedef struct 16 | { 17 | block_id blocks[CHUNK_SIZE][WORLD_HEIGHT][CHUNK_SIZE]; 18 | } chunk; 19 | 20 | typedef struct 21 | { 22 | short x; 23 | short y; 24 | short z; 25 | unsigned short chunk_data_sent[WORLD_SIZE][WORLD_SIZE]; 26 | unsigned char id; 27 | char nickname[31]; 28 | SOCKET socket; 29 | } player; 30 | 31 | typedef struct 32 | { 33 | fd_set sockets; 34 | SOCKET listener; 35 | SOCKET max_fd; 36 | 37 | chunk *chunks; 38 | 39 | player players[MAX_PLAYERS]; 40 | unsigned int num_players; 41 | int ids_used[256]; 42 | 43 | char *buffer; 44 | z_stream def_stream; 45 | } server; 46 | 47 | void server_init(server *s, unsigned short port); 48 | void server_tick(server *s); 49 | void server_destroy(server *s); 50 | 51 | #endif -------------------------------------------------------------------------------- /src/shader.h: -------------------------------------------------------------------------------- 1 | #ifndef SHADER_H 2 | #define SHADER_H 3 | 4 | #include 5 | 6 | typedef struct 7 | { 8 | GLuint program; 9 | GLuint position_location; 10 | GLuint normal_location; 11 | GLuint tex_id_location; 12 | GLuint tex_coord_location; 13 | GLuint model_location; 14 | GLuint view_location; 15 | GLuint color_location; 16 | GLuint projection_location; 17 | GLuint texture_location; 18 | } shader; 19 | 20 | #endif -------------------------------------------------------------------------------- /src/sky.c: -------------------------------------------------------------------------------- 1 | #include "sky.h" 2 | #include "stdio.h" 3 | 4 | const vec3 DAY_COLOR = {0.6f, 0.7f, 1.0f}; 5 | const vec3 NIGHT_COLOR = {0.0f, 0.0f, 0.0f}; 6 | 7 | void sky_init(struct Sky *sky) 8 | { 9 | sky->curr_color = DAY_COLOR; 10 | sky->ticks = 0; 11 | }; 12 | 13 | // 0 -> 2400 = day 14 | // 2400 -> 0 = night 15 | void sky_tick(struct Sky *sky) 16 | { 17 | sky->ticks++; 18 | // if its night (ticks > 2400) then we want to gradually switch into day, then reset ticks 19 | if (sky->ticks > 2400) 20 | { 21 | sky->curr_color.x = lerp(NIGHT_COLOR.x, DAY_COLOR.x, (float)(sky->ticks - 2400) / 2400.0f); 22 | sky->curr_color.y = lerp(NIGHT_COLOR.y, DAY_COLOR.y, (float)(sky->ticks - 2400) / 2400.0f); 23 | sky->curr_color.z = lerp(NIGHT_COLOR.z, DAY_COLOR.z, (float)(sky->ticks - 2400) / 2400.0f); 24 | if (sky->ticks > 4800) 25 | { 26 | sky->ticks = 0; 27 | 28 | // reset the color to day 29 | sky->curr_color = DAY_COLOR; 30 | } 31 | } 32 | else 33 | { 34 | sky->curr_color.x = lerp(DAY_COLOR.x, NIGHT_COLOR.x, (float)sky->ticks / 2400.0f); 35 | sky->curr_color.y = lerp(DAY_COLOR.y, NIGHT_COLOR.y, (float)sky->ticks / 2400.0f); 36 | sky->curr_color.z = lerp(DAY_COLOR.z, NIGHT_COLOR.z, (float)sky->ticks / 2400.0f); 37 | } 38 | }; 39 | 40 | void sky_render(struct Sky *sky) 41 | { 42 | glClearColor(sky->curr_color.x, sky->curr_color.y, sky->curr_color.z, 1.0f); 43 | } -------------------------------------------------------------------------------- /src/sky.h: -------------------------------------------------------------------------------- 1 | #ifndef SKY_H 2 | #define SKY_H 3 | 4 | #include "mvmath.h" 5 | #include 6 | 7 | struct Sky { 8 | vec3 curr_color; 9 | int ticks; 10 | }; 11 | 12 | void sky_init(struct Sky *sky); 13 | void sky_tick(struct Sky *sky); 14 | void sky_render(struct Sky *sky); 15 | 16 | #endif -------------------------------------------------------------------------------- /src/sockets.h: -------------------------------------------------------------------------------- 1 | #ifndef SOCKETS_H 2 | #define SOCKETS_H 3 | 4 | #ifdef _WIN32 5 | #if (_WIN32_WINNT < 0x0501) 6 | #define _WIN32_WINNT 0x0501 7 | #endif 8 | #include 9 | #include 10 | #define SOCKET_VALID(s) ((s) != INVALID_SOCKET) 11 | #else 12 | #include 13 | #include 14 | #include 15 | #include 16 | #define SOCKET_VALID(s) ((s) != -1) 17 | typedef int SOCKET; 18 | #endif 19 | 20 | #include "block.h" 21 | 22 | #define MAX_PLAYERS 10 23 | #define DATA_BUFFER_SIZE 32768 24 | 25 | #define SET_BLOCK_ID 0 26 | #define SPAWN_PLAYER_ID 1 27 | #define DESPAWN_PLAYER_ID 2 28 | #define POSITION_UPDATE_ID 3 29 | #define CHUNK_DATA_ID 4 30 | #define PLAYER_IDENTIFICATION_ID 5 31 | 32 | typedef struct 33 | { 34 | unsigned char id; 35 | char nickname[31]; 36 | } player_identification_packet; 37 | 38 | typedef struct 39 | { 40 | unsigned char id; 41 | block_id block; 42 | short x; 43 | short y; 44 | short z; 45 | } set_block_packet; 46 | 47 | typedef struct 48 | { 49 | unsigned char id; 50 | unsigned char player_id; 51 | char nickname[31]; 52 | } spawn_player_packet; 53 | 54 | typedef struct 55 | { 56 | unsigned char id; 57 | unsigned char player_id; 58 | } despawn_player_packet; 59 | 60 | typedef struct 61 | { 62 | unsigned char id; 63 | unsigned char player_id; 64 | short x; 65 | short y; 66 | short z; 67 | short prev_x; 68 | short prev_y; 69 | short prev_z; 70 | } position_update_packet; 71 | 72 | typedef struct 73 | { 74 | unsigned char id; 75 | char x; 76 | char z; 77 | unsigned short length; 78 | unsigned short complete; 79 | char data[1024]; 80 | } chunk_data_packet; 81 | 82 | #endif -------------------------------------------------------------------------------- /src/util.c: -------------------------------------------------------------------------------- 1 | #include "util.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | char *load_file(const char *path) 9 | { 10 | FILE *file = fopen(path, "rb"); 11 | if (!file) 12 | { 13 | fprintf(stderr, "fopen %s failed: %d %s\n", path, errno, strerror(errno)); 14 | exit(1); 15 | } 16 | fseek(file, 0, SEEK_END); 17 | int length = ftell(file); 18 | rewind(file); 19 | char *data = calloc(length + 1, sizeof(char)); 20 | fread(data, 1, length, file); 21 | fclose(file); 22 | return data; 23 | } 24 | 25 | GLuint make_shader(GLenum type, const char *source) 26 | { 27 | GLuint shader = glCreateShader(type); 28 | glShaderSource(shader, 1, &source, NULL); 29 | glCompileShader(shader); 30 | GLint status; 31 | glGetShaderiv(shader, GL_COMPILE_STATUS, &status); 32 | if (status == GL_FALSE) { 33 | GLint length; 34 | glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &length); 35 | GLchar *info = calloc(length, sizeof(GLchar)); 36 | glGetShaderInfoLog(shader, length, NULL, info); 37 | fprintf(stderr, "glCompileShader failed:\n%s\n", info); 38 | free(info); 39 | } 40 | return shader; 41 | } 42 | 43 | GLuint load_shader(GLenum type, const char *path) 44 | { 45 | char *data = load_file(path); 46 | GLuint result = make_shader(type, data); 47 | free(data); 48 | return result; 49 | } 50 | 51 | GLuint make_program(GLuint shader1, GLuint shader2) 52 | { 53 | GLuint program = glCreateProgram(); 54 | glAttachShader(program, shader1); 55 | glAttachShader(program, shader2); 56 | glLinkProgram(program); 57 | GLint status; 58 | glGetProgramiv(program, GL_LINK_STATUS, &status); 59 | if (status == GL_FALSE) { 60 | GLint length; 61 | glGetProgramiv(program, GL_INFO_LOG_LENGTH, &length); 62 | GLchar *info = calloc(length, sizeof(GLchar)); 63 | glGetProgramInfoLog(program, length, NULL, info); 64 | fprintf(stderr, "glLinkProgram failed: %s\n", info); 65 | free(info); 66 | } 67 | glDetachShader(program, shader1); 68 | glDetachShader(program, shader2); 69 | glDeleteShader(shader1); 70 | glDeleteShader(shader2); 71 | return program; 72 | } 73 | 74 | GLuint load_program(const char *path1, const char *path2) 75 | { 76 | GLuint shader1 = load_shader(GL_VERTEX_SHADER, path1); 77 | GLuint shader2 = load_shader(GL_FRAGMENT_SHADER, path2); 78 | GLuint program = make_program(shader1, shader2); 79 | return program; 80 | } 81 | 82 | void load_png_texture(const char *file_name) 83 | { 84 | unsigned int error; 85 | unsigned char *data; 86 | unsigned int width, height; 87 | error = lodepng_decode32_file(&data, &width, &height, file_name); 88 | if (error) { 89 | fprintf(stderr, "load_png_texture %s failed, error %u: %s\n", file_name, error, lodepng_error_text(error)); 90 | exit(1); 91 | } 92 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei) width, (GLsizei) height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); 93 | free(data); 94 | } -------------------------------------------------------------------------------- /src/util.h: -------------------------------------------------------------------------------- 1 | #ifndef UTIL_H 2 | #define UTIL_H 3 | 4 | #include 5 | #include 6 | 7 | char *load_file(const char *path); 8 | GLuint make_shader(GLenum type, const char *source); 9 | GLuint load_shader(GLenum type, const char *path); 10 | GLuint make_program(GLuint shader1, GLuint shader2); 11 | GLuint load_program(const char *path1, const char *path2); 12 | void load_png_texture(const char *file_name); 13 | 14 | #endif -------------------------------------------------------------------------------- /src/world.c: -------------------------------------------------------------------------------- 1 | #include "world.h" 2 | 3 | #include "util.h" 4 | #include "block_data.h" 5 | #include "worldgen.h" 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | void calculate_selected_block(struct World *w, float radius) 14 | { 15 | vec3 direction = 16 | { 17 | .x = -sinf(RADIANS(w->camera_rotation.y)) * cosf(RADIANS(w->camera_rotation.x)), 18 | .z = -cosf(RADIANS(w->camera_rotation.y)) * cosf(RADIANS(w->camera_rotation.x)), 19 | .y = sinf(RADIANS(w->camera_rotation.x))}; 20 | 21 | w->selected_block_x = roundf(w->camera_position.x); 22 | w->selected_block_z = roundf(w->camera_position.z); 23 | w->selected_block_y = roundf(w->camera_position.y); 24 | 25 | int step_x = direction.x > 0.0f ? 1 : direction.x < 0.0f ? -1 26 | : 0; 27 | int step_z = direction.z > 0.0f ? 1 : direction.z < 0.0f ? -1 28 | : 0; 29 | int step_y = direction.y > 0.0f ? 1 : direction.y < 0.0f ? -1 30 | : 0; 31 | 32 | float t_max_x = (direction.x > 0.0f ? roundf(w->camera_position.x) + 0.5f - w->camera_position.x : roundf(w->camera_position.x) - 0.5f - w->camera_position.x) / direction.x; 33 | float t_max_z = (direction.z > 0.0f ? roundf(w->camera_position.z) + 0.5f - w->camera_position.z : roundf(w->camera_position.z) - 0.5f - w->camera_position.z) / direction.z; 34 | float t_max_y = (direction.y > 0.0f ? roundf(w->camera_position.y) + 0.5f - w->camera_position.y : roundf(w->camera_position.y) - 0.5f - w->camera_position.y) / direction.y; 35 | 36 | float t_delta_x = (float)step_x / direction.x; 37 | float t_delta_z = (float)step_z / direction.z; 38 | float t_delta_y = (float)step_y / direction.y; 39 | 40 | while (1) 41 | { 42 | if (t_max_x < t_max_y) 43 | { 44 | if (t_max_x < t_max_z) 45 | { 46 | if (t_max_x > radius) 47 | { 48 | w->block_in_range = 0; 49 | return; 50 | } 51 | w->selected_block_x += step_x; 52 | t_max_x += t_delta_x; 53 | 54 | w->selected_face_x = -step_x; 55 | w->selected_face_y = 0; 56 | w->selected_face_z = 0; 57 | } 58 | else 59 | { 60 | if (t_max_z > radius) 61 | { 62 | w->block_in_range = 0; 63 | return; 64 | } 65 | w->selected_block_z += step_z; 66 | t_max_z += t_delta_z; 67 | 68 | w->selected_face_x = 0; 69 | w->selected_face_y = 0; 70 | w->selected_face_z = -step_z; 71 | } 72 | } 73 | else 74 | { 75 | if (t_max_y < t_max_z) 76 | { 77 | if (t_max_y > radius) 78 | { 79 | w->block_in_range = 0; 80 | return; 81 | } 82 | w->selected_block_y += step_y; 83 | t_max_y += t_delta_y; 84 | 85 | w->selected_face_x = 0; 86 | w->selected_face_y = -step_y; 87 | w->selected_face_z = 0; 88 | } 89 | else 90 | { 91 | if (t_max_z > radius) 92 | { 93 | w->block_in_range = 0; 94 | return; 95 | } 96 | w->selected_block_z += step_z; 97 | t_max_z += t_delta_z; 98 | 99 | w->selected_face_x = 0; 100 | w->selected_face_y = 0; 101 | w->selected_face_z = -step_z; 102 | } 103 | } 104 | if (world_get_block(w, w->selected_block_x, w->selected_block_y, w->selected_block_z) != AIR) 105 | { 106 | w->block_in_range = 1; 107 | return; 108 | } 109 | } 110 | } 111 | 112 | void world_init(struct World *w) 113 | { 114 | w->player.box = (bounding_box){{0.6f, 1.8f, 0.6f}}; 115 | w->player.position = (vec3){0.0f, WORLD_HEIGHT, 0.0f}; 116 | w->player.velocity = (vec3){0.0f}; 117 | w->player.move_direction = (vec3){0.0f}; 118 | w->player.jumping = 0; 119 | w->player.on_ground = 0; 120 | w->selected_block = 1; 121 | w->destroying_block = 0; 122 | w->placing_block = 0; 123 | w->camera_rotation = (vec2){0.0f}; 124 | w->seed = RAND(0, ULONG_MAX - 1); 125 | 126 | w->blocks_shader.program = load_program("res/shaders/blocks.vsh", "res/shaders/blocks.fsh"); 127 | w->blocks_shader.position_location = glGetAttribLocation(w->blocks_shader.program, "position"); 128 | w->blocks_shader.normal_location = glGetAttribLocation(w->blocks_shader.program, "normal"); 129 | w->blocks_shader.tex_coord_location = glGetAttribLocation(w->blocks_shader.program, "tex_coord"); 130 | w->blocks_shader.model_location = glGetUniformLocation(w->blocks_shader.program, "model"); 131 | w->blocks_shader.view_location = glGetUniformLocation(w->blocks_shader.program, "view"); 132 | w->blocks_shader.projection_location = glGetUniformLocation(w->blocks_shader.program, "projection"); 133 | w->blocks_shader.texture_location = glGetUniformLocation(w->blocks_shader.program, "blocks_texture"); 134 | 135 | w->lines_shader.program = load_program("res/shaders/lines.vsh", "res/shaders/lines.fsh"); 136 | w->lines_shader.position_location = glGetAttribLocation(w->lines_shader.program, "position"); 137 | w->lines_shader.view_location = glGetUniformLocation(w->lines_shader.program, "view"); 138 | w->lines_shader.projection_location = glGetUniformLocation(w->lines_shader.program, "projection"); 139 | 140 | sky_init(&w->sky); 141 | 142 | w->chunk_data_buffer = malloc(36 * CHUNK_SIZE * CHUNK_SIZE * WORLD_HEIGHT * sizeof(block_vertex)); 143 | 144 | w->chunks = malloc(WORLD_SIZE * WORLD_SIZE * sizeof(struct Chunk)); 145 | 146 | for (int x = 0; x < WORLD_SIZE; x++) 147 | { 148 | for (int z = 0; z < WORLD_SIZE; z++) 149 | { 150 | chunk_init(&w->chunks[x * WORLD_SIZE + z], w, x - WORLD_SIZE / 2, z - WORLD_SIZE / 2, &w->blocks_shader); 151 | } 152 | } 153 | 154 | glGenTextures(1, &w->blocks_texture); 155 | glActiveTexture(GL_TEXTURE0); 156 | glBindTexture(GL_TEXTURE_2D, w->blocks_texture); 157 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 158 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 159 | load_png_texture("res/textures/terrain.png"); 160 | 161 | glGenVertexArrays(1, &w->frame_vao); 162 | glBindVertexArray(w->frame_vao); 163 | 164 | glGenBuffers(1, &w->frame_vbo); 165 | glBindBuffer(GL_ARRAY_BUFFER, w->frame_vbo); 166 | glBufferData(GL_ARRAY_BUFFER, sizeof(vec3) * 24, NULL, GL_STREAM_DRAW); 167 | glVertexAttribPointer(w->lines_shader.position_location, 3, GL_FLOAT, GL_FALSE, sizeof(vec3), NULL); 168 | glEnableVertexAttribArray(w->lines_shader.position_location); 169 | 170 | w->num_players = 0; 171 | w->fly_mode = 0; 172 | w->noclip_mode = 0; 173 | } 174 | 175 | void world_handle_input(struct World *w, input *i) 176 | { 177 | w->window_width = i->window_width; 178 | w->window_height = i->window_height; 179 | 180 | if (i->mouse_locked) 181 | { 182 | w->camera_rotation.x += i->mouse_delta.y * i->mouse_sensitivity; 183 | w->camera_rotation.y += i->mouse_delta.x * i->mouse_sensitivity; 184 | 185 | w->camera_rotation.x = w->camera_rotation.x > 89.0f ? 89.0f : w->camera_rotation.x; 186 | w->camera_rotation.x = w->camera_rotation.x < -89.0f ? -89.0f : w->camera_rotation.x; 187 | 188 | w->player.move_direction = (vec3){0.0f}; 189 | 190 | w->player.jumping = i->keys[GLFW_KEY_SPACE]; 191 | 192 | if (i->keys[GLFW_KEY_W]) 193 | { 194 | w->player.move_direction.x -= sinf(RADIANS(w->camera_rotation.y)); 195 | w->player.move_direction.z -= cosf(RADIANS(w->camera_rotation.y)); 196 | } 197 | if (i->keys[GLFW_KEY_A]) 198 | { 199 | w->player.move_direction.x -= cosf(RADIANS(w->camera_rotation.y)); 200 | w->player.move_direction.z += sinf(RADIANS(w->camera_rotation.y)); 201 | } 202 | if (i->keys[GLFW_KEY_S]) 203 | { 204 | w->player.move_direction.x += sinf(RADIANS(w->camera_rotation.y)); 205 | w->player.move_direction.z += cosf(RADIANS(w->camera_rotation.y)); 206 | } 207 | if (i->keys[GLFW_KEY_D]) 208 | { 209 | w->player.move_direction.x += cosf(RADIANS(w->camera_rotation.y)); 210 | w->player.move_direction.z -= sinf(RADIANS(w->camera_rotation.y)); 211 | } 212 | if (i->keys_down[GLFW_KEY_F] && !w->noclip_mode) 213 | { 214 | w->fly_mode = !w->fly_mode; 215 | } 216 | if (i->keys_down[GLFW_KEY_N]) 217 | { 218 | w->fly_mode = 1; 219 | w->noclip_mode = !w->noclip_mode; 220 | } 221 | if (w->player.move_direction.x != 0.0f || w->player.move_direction.z != 0.0f) 222 | { 223 | normalize(&w->player.move_direction); 224 | } 225 | if (w->fly_mode) 226 | { 227 | if (i->keys[GLFW_KEY_SPACE]) 228 | { 229 | w->player.move_direction.y += 1.0f; 230 | } 231 | if (i->keys[GLFW_KEY_LEFT_SHIFT]) 232 | { 233 | w->player.move_direction.y -= 1.0f; 234 | } 235 | } 236 | 237 | if (i->keys_down[GLFW_KEY_1]) 238 | w->selected_block = GET_CURRENT_HOTBAR(w) * 9 + 1; 239 | if (i->keys_down[GLFW_KEY_2]) 240 | w->selected_block = GET_CURRENT_HOTBAR(w) * 9 + 2; 241 | if (i->keys_down[GLFW_KEY_3]) 242 | w->selected_block = GET_CURRENT_HOTBAR(w) * 9 + 3; 243 | if (i->keys_down[GLFW_KEY_4]) 244 | w->selected_block = GET_CURRENT_HOTBAR(w) * 9 + 4; 245 | if (i->keys_down[GLFW_KEY_5]) 246 | w->selected_block = GET_CURRENT_HOTBAR(w) * 9 + 5; 247 | if (i->keys_down[GLFW_KEY_6]) 248 | w->selected_block = GET_CURRENT_HOTBAR(w) * 9 + 6; 249 | if (i->keys_down[GLFW_KEY_7]) 250 | w->selected_block = GET_CURRENT_HOTBAR(w) * 9 + 7; 251 | if (i->keys_down[GLFW_KEY_8]) 252 | w->selected_block = GET_CURRENT_HOTBAR(w) * 9 + 8; 253 | if (i->keys_down[GLFW_KEY_9]) 254 | w->selected_block = GET_CURRENT_HOTBAR(w) * 9 + 9; 255 | 256 | if (i->scroll_delta < 0.0) 257 | { 258 | w->selected_block++; 259 | } 260 | else if (i->scroll_delta > 0.0) 261 | { 262 | w->selected_block--; 263 | } 264 | if (w->selected_block == 0) 265 | w->selected_block = 3 * 9; 266 | else if (w->selected_block == 3 * 9 + 1) 267 | w->selected_block = 1; 268 | 269 | if (i->mouse_buttons_down[GLFW_MOUSE_BUTTON_LEFT]) 270 | { 271 | w->destroying_block = 1; 272 | } 273 | if (i->mouse_buttons_down[GLFW_MOUSE_BUTTON_RIGHT]) 274 | { 275 | w->placing_block = 1; 276 | } 277 | if (i->mouse_buttons_down[GLFW_MOUSE_BUTTON_MIDDLE]) 278 | { 279 | block_id selected_block = world_get_block(w, w->selected_block_x, w->selected_block_y, w->selected_block_z); 280 | if (selected_block != AIR) 281 | w->selected_block = selected_block; 282 | } 283 | } 284 | } 285 | 286 | void world_tick(struct World *w) 287 | { 288 | sky_tick(&w->sky); 289 | 290 | if (w->fly_mode) 291 | { 292 | w->player.velocity.x *= 0.85f; 293 | w->player.velocity.z *= 0.85f; 294 | w->player.velocity.y *= 0.6f; 295 | } 296 | else 297 | { 298 | w->player.velocity.y -= 0.08f; 299 | w->player.velocity.y *= 0.98f; 300 | 301 | if (w->player.jumping && w->player.on_ground) 302 | w->player.velocity.y += 0.5f; 303 | 304 | w->player.velocity.x *= w->player.on_ground ? 0.6f : 0.91f; 305 | w->player.velocity.z *= w->player.on_ground ? 0.6f : 0.91f; 306 | } 307 | 308 | w->block_changed = 0; 309 | if (w->block_in_range) 310 | { 311 | if (w->destroying_block) 312 | { 313 | world_set_block(w, w->selected_block_x, w->selected_block_y, w->selected_block_z, AIR); 314 | w->block_changed = 1; 315 | w->new_block = AIR; 316 | } 317 | if (w->placing_block) 318 | { 319 | w->selected_block_x += w->selected_face_x; 320 | w->selected_block_y += w->selected_face_y; 321 | w->selected_block_z += w->selected_face_z; 322 | 323 | vec3 position = {w->selected_block_x, w->selected_block_y - 0.5f, w->selected_block_z}; 324 | bounding_box_update(&block_box, &position); 325 | 326 | if (!block_is_obstacle(w->selected_block) || !is_colliding(&block_box, &w->player.box)) 327 | { 328 | world_set_block(w, w->selected_block_x, w->selected_block_y, w->selected_block_z, w->selected_block); 329 | w->block_changed = 1; 330 | w->new_block = w->selected_block; 331 | } 332 | } 333 | } 334 | w->destroying_block = 0; 335 | w->placing_block = 0; 336 | 337 | vec3 velocity_change = {0.0f}; 338 | if (w->fly_mode) 339 | { 340 | velocity_change.x = w->player.move_direction.x * 0.1f; 341 | velocity_change.z = w->player.move_direction.z * 0.1f; 342 | velocity_change.y = w->player.move_direction.y * 0.2f; 343 | } 344 | else 345 | multiply_v3f(&velocity_change, &w->player.move_direction, w->player.on_ground ? 0.1f : 0.02f); 346 | add_v3(&w->player.velocity, &w->player.velocity, &velocity_change); 347 | } 348 | 349 | void world_draw(struct World *w, double delta_time, double time_since_tick) 350 | { 351 | double tick_delta_time = delta_time * 20.0f; 352 | 353 | bounding_box_update(&w->player.box, &w->player.position); 354 | 355 | vec3 player_delta; 356 | multiply_v3f(&player_delta, &w->player.velocity, tick_delta_time); 357 | 358 | if (w->noclip_mode) 359 | { 360 | add_v3(&w->player.position, &w->player.position, &player_delta); 361 | } 362 | else 363 | { 364 | entity_move(&w->player, w, &player_delta); 365 | 366 | w->player.on_ground = 0; 367 | for (int x = roundf(w->player.box.min.x); x <= roundf(w->player.box.max.x); x++) 368 | { 369 | for (int z = roundf(w->player.box.min.z); z <= roundf(w->player.box.max.z); z++) 370 | { 371 | if (world_get_block(w, x, roundf(w->player.position.y - 0.5f), z) == AIR) 372 | continue; 373 | vec3 block_position = {x, roundf(w->player.position.y) - 1.5f, z}; 374 | bounding_box_update(&block_box, &block_position); 375 | if (is_touching(&w->player.box, &block_box)) 376 | { 377 | w->player.on_ground = 1; 378 | break; 379 | } 380 | } 381 | if (w->player.on_ground) 382 | break; 383 | } 384 | 385 | multiply_v3f(&w->player.velocity, &player_delta, 1.0f / tick_delta_time); 386 | } 387 | 388 | w->camera_position.x = w->player.position.x; 389 | w->camera_position.z = w->player.position.z; 390 | w->camera_position.y = w->player.position.y + 1.62f; 391 | 392 | calculate_selected_block(w, 5.0f); 393 | 394 | sky_render(&w->sky); 395 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 396 | 397 | glEnable(GL_DEPTH_TEST); 398 | 399 | vec3 view_translation; 400 | multiply_v3f(&view_translation, &w->camera_position, -1.0f); 401 | translate(&w->world_view, &view_translation); 402 | rotate(&TEMP_MAT, &AXIS_UP, RADIANS(w->camera_rotation.y)); 403 | multiply(&w->world_view, &TEMP_MAT, &w->world_view); 404 | rotate(&TEMP_MAT, &AXIS_RIGHT, RADIANS(w->camera_rotation.x)); 405 | multiply(&w->world_view, &TEMP_MAT, &w->world_view); 406 | 407 | perspective(&w->world_projection, 85.0f, w->window_width / w->window_height, 0.05f, 1000.0f); 408 | 409 | glUseProgram(w->blocks_shader.program); 410 | glUniform1i(w->blocks_shader.texture_location, 0); 411 | 412 | glUniformMatrix4fv(w->blocks_shader.projection_location, 1, GL_FALSE, w->world_projection.value); 413 | glUniformMatrix4fv(w->blocks_shader.view_location, 1, GL_FALSE, w->world_view.value); 414 | 415 | for (int i = 0; i < 4; i++) 416 | { 417 | struct Chunk *chunk_to_update = NULL; 418 | float min_distance = -1.0f; 419 | 420 | for (int x = 0; x < WORLD_SIZE; x++) 421 | { 422 | for (int z = 0; z < WORLD_SIZE; z++) 423 | { 424 | struct Chunk *c = &w->chunks[x * WORLD_SIZE + z]; 425 | 426 | if (c->dirty) 427 | { 428 | float distance_x = abs(x - CHUNK_FROM_WORLD_COORDS((int)roundf(w->player.position.x))); 429 | float distance_z = abs(z - CHUNK_FROM_WORLD_COORDS((int)roundf(w->player.position.z))); 430 | float distance = sqrtf(distance_x * distance_x + distance_z * distance_z); 431 | if (distance < min_distance || min_distance < 0.0f) 432 | { 433 | min_distance = distance; 434 | chunk_to_update = c; 435 | } 436 | } 437 | } 438 | } 439 | 440 | if (chunk_to_update) 441 | { 442 | glBindBuffer(GL_ARRAY_BUFFER, chunk_to_update->vbo); 443 | chunk_build_buffer(chunk_to_update, w->chunk_data_buffer); 444 | } 445 | else 446 | break; 447 | } 448 | 449 | for (int x = -WORLD_SIZE / 2; x < WORLD_SIZE - WORLD_SIZE / 2; x++) 450 | { 451 | for (int z = -WORLD_SIZE / 2; z < WORLD_SIZE - WORLD_SIZE / 2; z++) 452 | { 453 | struct Chunk *c = &w->chunks[(x + WORLD_SIZE / 2) * WORLD_SIZE + z + WORLD_SIZE / 2]; 454 | vec3 chunk_translation = {x * CHUNK_SIZE, 0.0f, z * CHUNK_SIZE}; 455 | translate(&w->blocks_model, &chunk_translation); 456 | glUniformMatrix4fv(w->blocks_shader.model_location, 1, GL_FALSE, w->blocks_model.value); 457 | glBindVertexArray(c->vao); 458 | glDrawArrays(GL_TRIANGLES, 0, c->vert_count); 459 | } 460 | } 461 | 462 | glUseProgram(w->lines_shader.program); 463 | 464 | glUniformMatrix4fv(w->lines_shader.projection_location, 1, GL_FALSE, w->world_projection.value); 465 | glUniformMatrix4fv(w->lines_shader.view_location, 1, GL_FALSE, w->world_view.value); 466 | 467 | glBindBuffer(GL_ARRAY_BUFFER, w->frame_vbo); 468 | glBindVertexArray(w->frame_vao); 469 | 470 | vec3 data[24]; 471 | 472 | for (int i = 0; i < w->num_players; i++) 473 | { 474 | network_player *player = &w->players[i]; 475 | v3_lerp(&player->smoothed_position, &player->prev_position, &player->position, time_since_tick * 20.0f); 476 | 477 | make_frame(data, &player->smoothed_position, &w->player.box); 478 | 479 | glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(data), data); 480 | glDrawArrays(GL_LINES, 0, 24); 481 | } 482 | 483 | if (w->block_in_range) 484 | { 485 | vec3 position = 486 | { 487 | w->selected_block_x, 488 | w->selected_block_y, 489 | w->selected_block_z}; 490 | make_frame(data, &position, &block_box); 491 | 492 | glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(data), data); 493 | glDrawArrays(GL_LINES, 0, 24); 494 | } 495 | 496 | glUseProgram(w->blocks_shader.program); 497 | 498 | for (int x = -WORLD_SIZE / 2; x < WORLD_SIZE - WORLD_SIZE / 2; x++) 499 | { 500 | for (int z = -WORLD_SIZE / 2; z < WORLD_SIZE - WORLD_SIZE / 2; z++) 501 | { 502 | struct Chunk *c = &w->chunks[(x + WORLD_SIZE / 2) * WORLD_SIZE + z + WORLD_SIZE / 2]; 503 | vec3 chunk_translation = {x * CHUNK_SIZE, 0.0f, z * CHUNK_SIZE}; 504 | translate(&w->blocks_model, &chunk_translation); 505 | glUniformMatrix4fv(w->blocks_shader.model_location, 1, GL_FALSE, w->blocks_model.value); 506 | glBindVertexArray(c->vao); 507 | glDrawArrays(GL_TRIANGLES, c->water_offset, c->water_count); 508 | } 509 | } 510 | } 511 | 512 | void world_destroy(struct World *w) 513 | { 514 | for (int x = 0; x < WORLD_SIZE; x++) 515 | { 516 | for (int z = 0; z < WORLD_SIZE; z++) 517 | { 518 | chunk_destroy(&w->chunks[x * WORLD_SIZE + z]); 519 | } 520 | } 521 | free(w->chunks); 522 | free(w->chunk_data_buffer); 523 | 524 | glDeleteBuffers(1, &w->frame_vbo); 525 | glDeleteVertexArrays(1, &w->frame_vao); 526 | 527 | glDeleteTextures(1, &w->blocks_texture); 528 | glDeleteProgram(w->blocks_shader.program); 529 | } 530 | 531 | block_id world_get_block(struct World *w, int x, int y, int z) 532 | { 533 | size_t chunk_x = CHUNK_FROM_WORLD_COORDS(x); 534 | size_t chunk_z = CHUNK_FROM_WORLD_COORDS(z); 535 | if (chunk_x < 0 || chunk_x >= WORLD_SIZE || chunk_z < 0 || chunk_z >= WORLD_SIZE || y < 0 || y >= WORLD_HEIGHT) 536 | return AIR; 537 | else 538 | return w->chunks[chunk_x * WORLD_SIZE + chunk_z].blocks[WORLD_TO_CHUNK(x)][y][WORLD_TO_CHUNK(z)]; 539 | } 540 | 541 | void world_set_block(struct World *w, int x, int y, int z, block_id new_block) 542 | { 543 | size_t chunk_x = CHUNK_FROM_WORLD_COORDS(x); 544 | size_t chunk_z = CHUNK_FROM_WORLD_COORDS(z); 545 | if (!(chunk_x < 0 || chunk_x >= WORLD_SIZE || chunk_z < 0 || chunk_z >= WORLD_SIZE || y < 0 || y >= WORLD_HEIGHT)) 546 | { 547 | struct Chunk *c = &w->chunks[chunk_x * WORLD_SIZE + chunk_z]; 548 | 549 | size_t block_x = WORLD_TO_CHUNK(x); 550 | size_t block_z = WORLD_TO_CHUNK(z); 551 | block_id *b = &c->blocks[block_x][y][block_z]; 552 | 553 | if(*b != new_block) { 554 | *b = new_block; 555 | if (block_x == 0) w->chunks[(chunk_x == 0 ? chunk_x : chunk_x - 1) * WORLD_SIZE + chunk_z].dirty = 1; 556 | else if (block_x == CHUNK_SIZE - 1) w->chunks[(chunk_x == WORLD_SIZE - 1 ? chunk_x : chunk_x + 1) * WORLD_SIZE + chunk_z].dirty = 1; 557 | if (block_z == 0) w->chunks[chunk_x * WORLD_SIZE + (chunk_z == 0 ? chunk_z : chunk_z - 1)].dirty = 1; 558 | else if (block_z == CHUNK_SIZE - 1) w->chunks[chunk_x * WORLD_SIZE + (chunk_z == WORLD_SIZE - 1 ? chunk_z : chunk_z + 1)].dirty = 1; 559 | c->dirty = 1; 560 | } 561 | } 562 | } 563 | 564 | // world_get_chunk 565 | struct Chunk *world_get_chunk(struct World *w, int x, int z) 566 | { 567 | const int max = WORLD_SIZE / 2; 568 | 569 | if (x < -max || x >= max || z < -max || z >= max) 570 | { 571 | return NULL; 572 | } 573 | 574 | x += max; 575 | z += max; 576 | return &w->chunks[x * WORLD_SIZE + z]; 577 | } -------------------------------------------------------------------------------- /src/world.h: -------------------------------------------------------------------------------- 1 | #ifndef WORLD_H 2 | #define WORLD_H 3 | 4 | #include "chunk.h" 5 | #include "entity.h" 6 | #include "input.h" 7 | #include "shader.h" 8 | #include "sockets.h" 9 | #include "noise.h" 10 | #include "sky.h" 11 | 12 | #define WORLD_SIZE 32 13 | #define ULONG_MAX 0xFFFFFFFFUL 14 | 15 | #define WORLD_TO_CHUNK(x) (x < 0 ? x % CHUNK_SIZE == 0 ? 0 : CHUNK_SIZE + x % CHUNK_SIZE : x % CHUNK_SIZE) 16 | #define CHUNK_FROM_WORLD_COORDS(x) ((x / CHUNK_SIZE < 0 ? x + 1 : x) / CHUNK_SIZE + WORLD_SIZE / 2 - (x < 0 ? 1 : 0)) 17 | #define GET_CURRENT_HOTBAR(w) (((w)->selected_block - 1) / 9) 18 | 19 | typedef struct 20 | { 21 | vec3 position; 22 | vec3 smoothed_position; 23 | vec3 prev_position; 24 | unsigned char id; 25 | char nickname[31]; 26 | } network_player; 27 | 28 | struct World 29 | { 30 | struct Chunk *chunks; 31 | 32 | float window_width; 33 | float window_height; 34 | 35 | network_player players[MAX_PLAYERS]; 36 | unsigned char num_players; 37 | entity player; 38 | int fly_mode; 39 | int noclip_mode; 40 | 41 | uint64_t seed; 42 | struct Sky sky; 43 | 44 | vec3 camera_position; 45 | vec2 camera_rotation; 46 | int destroying_block; 47 | int placing_block; 48 | block_id selected_block; 49 | int selected_block_x; 50 | int selected_block_y; 51 | int selected_block_z; 52 | int selected_face_x; 53 | int selected_face_y; 54 | int selected_face_z; 55 | int block_in_range; 56 | 57 | int block_changed; 58 | block_id new_block; 59 | 60 | block_vertex *chunk_data_buffer; 61 | 62 | mat4 blocks_model; 63 | mat4 world_view; 64 | mat4 world_projection; 65 | 66 | GLuint blocks_texture; 67 | shader blocks_shader; 68 | 69 | shader lines_shader; 70 | 71 | GLuint frame_vao; 72 | GLuint frame_vbo; 73 | }; 74 | 75 | void world_init(struct World *w); 76 | void world_generate(struct World *w); 77 | void world_handle_input(struct World *w, input *i); 78 | void world_tick(struct World *w); 79 | void world_draw(struct World *w, double delta_time, double time_since_tick); 80 | void world_destroy(struct World *w); 81 | 82 | block_id world_get_block(struct World *w, int x, int y, int z); 83 | void world_set_block(struct World *w, int x, int y, int z, block_id new_block); 84 | 85 | struct Chunk* world_get_chunk(struct World *w, int x, int z); 86 | 87 | #endif -------------------------------------------------------------------------------- /src/worldgen.c: -------------------------------------------------------------------------------- 1 | #include "worldgen.h" 2 | #include "noise.h" 3 | #include "mvmath.h" 4 | 5 | enum Biome 6 | { 7 | OCEAN, 8 | PLAINS, 9 | BEACH, 10 | MOUNTAIN 11 | }; 12 | 13 | typedef void (*FSet)(struct Chunk *, int, int, int, block_id); 14 | typedef block_id (*FGet)(struct Chunk *, int, int, int); 15 | 16 | // make a function that takes c, r, v and returns a float in a RADIAL3I way 17 | 18 | #define RADIAL2I(c, r, v) \ 19 | (vec2_norm(subtract_v2r((c), (v))) / vec2_norm(r)) 20 | 21 | #define RADIAL3I(c, r, v) \ 22 | (vec3_norm(subtract_v3((c), (v))) / vec3_norm(r)) 23 | 24 | block_id _get(struct Chunk *chunk, int x, int y, int z) 25 | { 26 | if (chunk_in_bounds(x, y, z)) 27 | { 28 | return chunk_get_block(chunk, x, y, z); 29 | } 30 | else 31 | { 32 | return world_get_block(chunk->world, x + chunk->x * CHUNK_SIZE, y, z + chunk->z * CHUNK_SIZE); 33 | return 0; 34 | } 35 | } 36 | 37 | void _set(struct Chunk *chunk, int x, int y, int z, block_id b) 38 | { 39 | if (chunk_in_bounds(x, y, z)) 40 | { 41 | chunk_set_block(chunk, x, y, z, b); 42 | } 43 | else 44 | { 45 | world_set_block(chunk->world, x + chunk->x * CHUNK_SIZE, y, z + chunk->z * CHUNK_SIZE, b); 46 | } 47 | } 48 | 49 | void tree(struct Chunk *chunk, FGet get, FSet set, int x, int y, int z) 50 | { 51 | block_id under = get(chunk, x, y - 1, z); // get the block under spawn 52 | if (under != GRASS && under != DIRT) 53 | return; // if it's not grass or dirt, don't spawn 54 | 55 | int h = RAND(3, 5); // generate a random height of logs 56 | 57 | for (int i = y; i < (y + h); i++) 58 | { 59 | set(chunk, x, i, z, WOOD); // set the logs 60 | } 61 | 62 | int lh = RAND(2, 3); // generate a random height of leaves 63 | 64 | for (int xx = (x - 2); xx <= (x + 2); xx++) 65 | { 66 | for (int zz = (z - 2); zz <= (z + 2); zz++) 67 | { 68 | for (int yy = (y + h); yy <= (y + h + 1); yy++) 69 | { 70 | int c = 0; 71 | c += xx == (x - 2) || xx == (x + 2); // if it's on the edge of the leaves 72 | c += zz == (z - 2) || zz == (z + 2); // if it's on the edge of the leaves 73 | int corner = c == 2; // if it's a corner 74 | 75 | if ((!(xx == x && zz == z) || yy > (y + h)) && 76 | !(corner && yy == (y + h + 1) && RANDCHANCE(0.4))) 77 | { 78 | set(chunk, xx, yy, zz, LEAVES); 79 | } // set the leaves 80 | } 81 | } 82 | } 83 | 84 | for (int xx = (x - 1); xx <= (x + 1); xx++) 85 | { 86 | for (int zz = (z - 1); zz <= (z + 1); zz++) 87 | { 88 | for (int yy = (y + h + 2); yy <= (y + h + lh); yy++) 89 | { 90 | int c = 0; 91 | c += xx == (x - 1) || xx == (x + 1); 92 | c += zz == (z - 1) || zz == (z + 1); 93 | int corner = c == 2; 94 | 95 | if (!(corner && yy == (y + h + lh) && RANDCHANCE(0.8))) 96 | { 97 | set(chunk, xx, yy, zz, LEAVES); 98 | } 99 | } 100 | } 101 | } 102 | } 103 | 104 | void orevein(struct Chunk *chunk, FGet get, FSet set, int x, int y, int z, block_id block) 105 | { 106 | int h = RAND(1, y - 4); 107 | 108 | if (h < 0 || h > y - 4) 109 | return; 110 | 111 | int s; 112 | 113 | switch (block) 114 | { 115 | case IRON_ORE: 116 | s = RAND(3, 6); 117 | break; 118 | case GOLD_ORE: 119 | s = RAND(2, 4); 120 | break; 121 | case COAL_ORE: 122 | s = RAND(4, 8); 123 | break; 124 | default: 125 | s = RAND(1, 3); 126 | break; 127 | } 128 | 129 | int l = RAND(s - 1, s + 1); 130 | int w = RAND(s - 1, s + 1); 131 | int i = RAND(s - 1, s + 1); 132 | 133 | for (int xx = (x - l); xx <= (x + l); xx++) 134 | { 135 | for (int zz = (z - w); zz <= (z + w); zz++) 136 | { 137 | for (int yy = (y - i); yy <= (y + i); yy++) 138 | { 139 | vec3 c = {x, y, z}; 140 | vec3 r = {l + 1, i + 1, w + 1}; 141 | vec3 v = {xx, yy, zz}; 142 | 143 | float d = 1.0f - RADIAL3I( 144 | c, r, v); 145 | 146 | if (get(chunk, xx, yy, zz) == STONE && RANDCHANCE(0.2 + d * 0.7)) 147 | { 148 | set(chunk, xx, yy, zz, block); 149 | } 150 | } 151 | } 152 | } 153 | } 154 | 155 | void flowers(struct Chunk *chunk, FGet get, FSet set, int x, int y, int z) 156 | { 157 | block_id flower = RANDCHANCE(0.6) ? ROSE : BUTTERCUP; 158 | 159 | int s = RAND(2, 6); 160 | int l = RAND(s - 1, s + 1); 161 | int h = RAND(s - 1, s + 1); 162 | 163 | for (int xx = (x - l); xx <= (x + l); xx++) 164 | { 165 | for (int zz = (z - h); zz <= (z + h); zz++) 166 | { 167 | if (get(chunk, xx, y, zz) == GRASS && RANDCHANCE(0.5)) 168 | { 169 | set(chunk, xx, y + 1, zz, flower); 170 | } 171 | } 172 | } 173 | } 174 | 175 | void lavapool(struct Chunk *chunk, FGet get, FSet set, int x, int y, int z) 176 | { 177 | int h = y - 1; 178 | 179 | int s = RAND(1, 5); 180 | int l = RAND(s - 1, s + 1); 181 | int w = RAND(s - 1, s + 1); 182 | 183 | for (int xx = (x - l); xx <= (x + l); xx++) 184 | { 185 | for (int zz = (z - w); zz <= (z + w); zz++) 186 | { 187 | vec2 c = {x, z}; 188 | vec2 r = {l + 1, w + 1}; 189 | vec2 v = {xx, zz}; 190 | 191 | float d = 1.0f - RADIAL2I( 192 | c, r, v 193 | ); 194 | 195 | int allow = 1; // all border blocks have to be solid or lava to be place lava 196 | 197 | for(int i = -1; i <= 1; i++) { 198 | for(int j = -1; j <= 1; j++) { 199 | block_id block = get(chunk, xx + i, h, zz + j); 200 | if(block != STILL_LAVA && !block_is_obstacle(block)) { 201 | allow = 0; 202 | break; 203 | } 204 | } 205 | } 206 | 207 | if(!allow) continue; 208 | 209 | if(RANDCHANCE(0.2 + d * 0.95)) { 210 | set(chunk, xx, h, zz, STILL_LAVA); 211 | } 212 | } 213 | } 214 | } 215 | 216 | int hash(int x, int y, int z) 217 | { 218 | int h = 0; 219 | h ^= x + 0x9e3779b9 + (h << 6) + (h >> 2); 220 | h ^= y + 0x9e3779b9 + (h << 6) + (h >> 2); 221 | h ^= z + 0x9e3779b9 + (h << 6) + (h >> 2); 222 | return h; 223 | } 224 | 225 | // Generate chunk block positions using noise 226 | void worldgen_noise(struct Chunk *chunk) 227 | { 228 | SRAND(chunk->world->seed + hash(chunk->x, 1, chunk->z)); 229 | 230 | // Base noise 231 | struct Noise n = octave(6, 0); 232 | 233 | // ore noise 234 | struct Noise m = octave(6, 1); 235 | 236 | // Different offsets of octave noise functions 237 | struct Noise os[] = { 238 | octave(8, 1), 239 | octave(8, 2), 240 | octave(8, 3), 241 | octave(8, 4), 242 | octave(8, 5), 243 | octave(8, 6), 244 | }; 245 | 246 | // Two separate combined noise functions, each combining two different 247 | // octave noise functions 248 | struct Noise cs[] = { 249 | combined(&os[0], &os[1]), 250 | combined(&os[2], &os[3]), 251 | combined(&os[4], &os[5]), 252 | }; 253 | 254 | for (int x = 0; x < CHUNK_SIZE; x++) 255 | { 256 | for (int z = 0; z < CHUNK_SIZE; z++) 257 | { 258 | int wx = chunk->x * CHUNK_SIZE + x; 259 | int wz = chunk->z * CHUNK_SIZE + z; 260 | 261 | // Sample each combined noise function for high/low results 262 | const f32 base_scale = 1.3f; 263 | int hr; 264 | int hl = (cs[0].compute(&cs[0].params, chunk->world->seed, wx * base_scale, wz * base_scale) / 6.0f) - 4.0f; 265 | int hh = (cs[1].compute(&cs[1].params, chunk->world->seed, wx * base_scale, wz * base_scale) / 5.0f) + 6.0f; 266 | 267 | // Sample the "biome" noise 268 | f32 t = n.compute(&n.params, chunk->world->seed, wx, wz); 269 | f32 r = m.compute(&m.params, chunk->world->seed, wx / 4.0f, wz / 4.0f) / 32.0f; 270 | 271 | if (t > 0) 272 | { 273 | hr = hl; 274 | } 275 | else 276 | { 277 | hr = max(hh, hl); 278 | } 279 | 280 | // offset by water level and determine biome 281 | int h = hr + WATER_LEVEL; 282 | 283 | // beach is anything close-ish to water in biome AND height 284 | enum Biome biome; 285 | if (h < WATER_LEVEL) 286 | { 287 | biome = OCEAN; 288 | } 289 | else if (t < 0.08f && h < WATER_LEVEL + 2) 290 | { 291 | biome = BEACH; 292 | } 293 | else if (0) 294 | { 295 | biome = MOUNTAIN; 296 | } 297 | else 298 | { 299 | biome = PLAINS; 300 | } 301 | 302 | if (biome == MOUNTAIN) 303 | { 304 | h += (r + (-t / 12.0f)) * 2 + 2; 305 | } 306 | 307 | s32 d = r * 1.4f + 5.0f; // dirt or sand depth 308 | 309 | block_id top_block; 310 | switch (biome) 311 | { 312 | case OCEAN: 313 | if (r > 0.8f) 314 | { 315 | top_block = GRAVEL; 316 | } 317 | else if (r > 0.3f) 318 | { 319 | top_block = SAND; 320 | } 321 | else 322 | { 323 | top_block = DIRT; 324 | } 325 | break; 326 | case BEACH: 327 | top_block = SAND; 328 | break; 329 | case PLAINS: 330 | top_block = (t > 4.0f && r > 0.78f) ? GRAVEL : GRASS; 331 | break; 332 | case MOUNTAIN: 333 | if (r > 0.8f) 334 | { 335 | top_block = GRAVEL; 336 | } 337 | else if (r > 0.7f) 338 | { 339 | top_block = DIRT; 340 | } 341 | else 342 | { 343 | top_block = STONE; 344 | } 345 | break; 346 | } 347 | 348 | for (int y = 0; y < h; y++) 349 | { 350 | block_id block; 351 | if (y == (h - 1)) 352 | { 353 | block = top_block; 354 | } 355 | else if (y > (h - d)) 356 | { 357 | if (top_block == GRASS) 358 | { 359 | block = DIRT; 360 | } 361 | else 362 | { 363 | block = top_block; 364 | } 365 | } 366 | else 367 | { 368 | block = STONE; 369 | } 370 | 371 | chunk_set_block(chunk, x, y, z, block); 372 | } 373 | 374 | for (int y = h; y < WATER_LEVEL; y++) 375 | { 376 | chunk_set_block(chunk, x, y, z, STILL_WATER); 377 | } 378 | 379 | if (biome == PLAINS && RANDCHANCE(0.005)) 380 | { 381 | tree(chunk, _get, _set, x, h, z); 382 | } 383 | 384 | if (biome == PLAINS && RANDCHANCE(0.0085)) 385 | { 386 | flowers(chunk, _get, _set, x, h, z); 387 | } 388 | 389 | if (biome != OCEAN && h <= (WATER_LEVEL + 3) && t < 0.1f && RANDCHANCE(0.001)) { 390 | lavapool(chunk, _get, _set, x, h, z); 391 | } 392 | 393 | if (RANDCHANCE(0.02)) 394 | { 395 | orevein(chunk, _get, _set, x, h, z, IRON_ORE); 396 | } 397 | 398 | if (RANDCHANCE(0.02)) 399 | { 400 | orevein(chunk, _get, _set, x, h, z, GOLD_ORE); 401 | } 402 | 403 | if (RANDCHANCE(0.02)) 404 | { 405 | orevein(chunk, _get, _set, x, h, z, COAL_ORE); 406 | } 407 | } 408 | } 409 | } 410 | 411 | void worldgen_init(struct World *world) 412 | { 413 | for (int x = 0; x < WORLD_SIZE; x++) 414 | { 415 | for (int z = 0; z < WORLD_SIZE; z++) 416 | { 417 | struct Chunk *c = &world->chunks[x * WORLD_SIZE + z]; 418 | 419 | // for (int x = 0; x < CHUNK_SIZE; x++) 420 | // { 421 | // for (int y = 0; y < WORLD_HEIGHT; y++) 422 | // { 423 | // for (int z = 0; z < CHUNK_SIZE; z++) 424 | // { 425 | // if (y > GRASS_LEVEL) 426 | // c->blocks[x][y][z] = AIR; 427 | // else if (y == GRASS_LEVEL) 428 | // c->blocks[x][y][z] = GRASS; 429 | // else if (y == 0) 430 | // c->blocks[x][y][z] = BEDROCK; 431 | // else if (y < 25) 432 | // c->blocks[x][y][z] = STONE; 433 | // else if (y < GRASS_LEVEL) 434 | // c->blocks[x][y][z] = DIRT; 435 | // } 436 | // } 437 | // } 438 | 439 | worldgen_noise(c); 440 | } 441 | } 442 | } -------------------------------------------------------------------------------- /src/worldgen.h: -------------------------------------------------------------------------------- 1 | #ifndef WORLDGEN_H 2 | #define WORLDGEN_H 3 | 4 | #include "chunk.h" 5 | #include "world.h" 6 | #include "time.h" 7 | 8 | void worldgen_noise(struct Chunk *chunk); 9 | void worldgen_init(struct World *world); 10 | 11 | #endif --------------------------------------------------------------------------------