├── .gitignore ├── bin ├── config.json ├── imgui.ini └── shaders │ ├── block.frag │ └── block.vert ├── terracotta ├── block │ ├── BlockModel.cpp │ ├── BlockFace.h │ ├── BlockModel.h │ ├── BlockState.h │ ├── BlockVariant.h │ ├── BlockFace.cpp │ ├── BlockState.cpp │ └── BlockElement.h ├── math │ ├── TypeUtil.h │ ├── Plane.h │ ├── volumes │ │ ├── Frustum.h │ │ └── Frustum.cpp │ └── Plane.cpp ├── Transform.h ├── render │ ├── ChunkMesh.h │ ├── Shader.h │ ├── ChunkMesh.cpp │ ├── Shader.cpp │ └── ChunkMeshGenerator.h ├── ChatWindow.h ├── assets │ ├── zip │ │ ├── ZipArchive.h │ │ └── ZipArchive.cpp │ ├── TextureArray.h │ ├── AssetCache.h │ ├── AssetLoader.h │ ├── AssetCache.cpp │ ├── TextureArray.cpp │ └── AssetLoader.cpp ├── Collision.h ├── PriorityQueue.h ├── lib │ └── imgui │ │ ├── LICENSE.txt │ │ ├── imgui_impl_glfw.h │ │ ├── imgui_impl_opengl3.h │ │ ├── imconfig.h │ │ ├── imgui_impl_glfw.cpp │ │ ├── imstb_rectpack.h │ │ └── imgui_impl_opengl3.cpp ├── GameWindow.h ├── Camera.h ├── ChatWindow.cpp ├── Game.h ├── GameWindow.cpp ├── Camera.cpp ├── World.h ├── Chunk.cpp ├── Chunk.h ├── Collision.cpp ├── World.cpp ├── Game.cpp └── main.cpp ├── README.md ├── LICENSE ├── cmake └── modules │ ├── Findmclib.cmake │ ├── FindGLFW3.cmake │ └── FindGLM.cmake └── CMakeLists.txt /.gitignore: -------------------------------------------------------------------------------- 1 | Debug 2 | Release 3 | build 4 | buildx64 5 | *.opensdf 6 | *.sdf 7 | *.suo 8 | *.so 9 | *.o 10 | .vs 11 | *.sln 12 | *.vcxproj 13 | *.vcxproj.user 14 | *.vspx 15 | -------------------------------------------------------------------------------- /bin/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "login": { 3 | "username": "terracotta", 4 | "password": "", 5 | "server": "127.0.0.1", 6 | "port": 25565 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /terracotta/block/BlockModel.cpp: -------------------------------------------------------------------------------- 1 | #include "BlockModel.h" 2 | 3 | #include 4 | 5 | namespace terra { 6 | namespace block { 7 | 8 | 9 | } // ns block 10 | } // ns terra 11 | -------------------------------------------------------------------------------- /bin/imgui.ini: -------------------------------------------------------------------------------- 1 | [Window][Debug##Default] 2 | Pos=60,60 3 | Size=400,400 4 | Collapsed=0 5 | 6 | [Window][chat_input] 7 | Pos=3,501 8 | Size=400,32 9 | Collapsed=0 10 | 11 | [Window][chat] 12 | Pos=4,342 13 | Size=418,157 14 | Collapsed=0 15 | 16 | -------------------------------------------------------------------------------- /terracotta/math/TypeUtil.h: -------------------------------------------------------------------------------- 1 | #ifndef TERRACOTTA_MATH_TYPEUTIL_H_ 2 | #define TERRACOTTA_MATH_TYPEUTIL_H_ 3 | 4 | #include 5 | #include 6 | 7 | namespace terra { 8 | namespace math { 9 | 10 | inline glm::vec3 VecToGLM(mc::Vector3d v) { return glm::vec3(v.x, v.y, v.z); } 11 | inline glm::vec3 VecToGLM(mc::Vector3i v) { return glm::vec3(v.x, v.y, v.z); } 12 | 13 | } // ns math 14 | } // ns terra 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /bin/shaders/block.frag: -------------------------------------------------------------------------------- 1 | #version 330 2 | 3 | in vec2 TexCoord; 4 | flat in uint texIndex; 5 | in vec3 varyingTint; 6 | 7 | uniform sampler2DArray texarray; 8 | uniform vec3 tint; 9 | 10 | out vec4 color; 11 | 12 | void main() { 13 | vec4 sample = texture(texarray, vec3(TexCoord, texIndex)); 14 | 15 | if (sample.a <= 0.6) { 16 | discard; 17 | } 18 | 19 | vec3 diffuseSample = sample.xyz; 20 | 21 | diffuseSample = diffuseSample * varyingTint; 22 | 23 | color = vec4(diffuseSample, 1.0); 24 | } 25 | -------------------------------------------------------------------------------- /terracotta/block/BlockFace.h: -------------------------------------------------------------------------------- 1 | #ifndef TERRACOTTA_BLOCK_BLOCKFACE_H_ 2 | #define TERRACOTTA_BLOCK_BLOCKFACE_H_ 3 | 4 | #include 5 | 6 | namespace terra { 7 | namespace block { 8 | 9 | enum class BlockFace { 10 | North, 11 | East, 12 | South, 13 | West, 14 | Up, 15 | Down, 16 | 17 | None 18 | }; 19 | 20 | const std::string& to_string(BlockFace face); 21 | BlockFace face_from_string(const std::string& str); 22 | BlockFace get_opposite_face(BlockFace face); 23 | 24 | } // ns block 25 | } // ns terra 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /terracotta/Transform.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef TERRACOTTA_TRANSFORM_H_ 4 | #define TERRACOTTA_TRANSFORM_H_ 5 | 6 | #include 7 | #include 8 | 9 | namespace terra { 10 | 11 | struct Transform { 12 | mc::Vector3d position; 13 | mc::Vector3d velocity; 14 | mc::Vector3d input_velocity; 15 | mc::Vector3d acceleration; 16 | mc::Vector3d input_acceleration; 17 | float max_speed; 18 | float orientation; 19 | float rotation; 20 | 21 | mc::AABB bounding_box; 22 | }; 23 | 24 | } // namespace terra 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /terracotta/block/BlockModel.h: -------------------------------------------------------------------------------- 1 | #ifndef TERRACOTTA_BLOCK_BLOCKMODEL_H_ 2 | #define TERRACOTTA_BLOCK_BLOCKMODEL_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include "BlockFace.h" 10 | #include "BlockElement.h" 11 | 12 | namespace terra { 13 | namespace block { 14 | 15 | class BlockModel { 16 | public: 17 | std::vector& GetElements() { return m_Elements; } 18 | void AddElement(BlockElement element) { m_Elements.push_back(element); } 19 | 20 | private: 21 | std::vector m_Elements; 22 | }; 23 | 24 | } // ns block 25 | } // ns terra 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /terracotta/render/ChunkMesh.h: -------------------------------------------------------------------------------- 1 | #ifndef TERRACOTTA_RENDER_CHUNKMESH_H_ 2 | #define TERRACOTTA_RENDER_CHUNKMESH_H_ 3 | 4 | #include "Shader.h" 5 | #include 6 | 7 | namespace terra { 8 | namespace render { 9 | 10 | class ChunkMesh { 11 | public: 12 | ChunkMesh(unsigned int vao, unsigned int vbo, GLsizei vertex_count); 13 | ChunkMesh(const ChunkMesh& other); 14 | ChunkMesh& operator=(const ChunkMesh& other); 15 | 16 | void Render(unsigned int model_uniform); 17 | void Destroy(); 18 | 19 | private: 20 | unsigned int m_VAO; 21 | unsigned int m_VBO; 22 | GLsizei m_VertexCount; 23 | }; 24 | 25 | } // ns render 26 | } // ns terra 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /bin/shaders/block.vert: -------------------------------------------------------------------------------- 1 | #version 330 2 | 3 | layout (location = 0) in vec3 position; 4 | layout (location = 1) in vec2 texCoord; 5 | layout (location = 2) in uint inTexIndex; 6 | layout (location = 3) in vec3 tint; 7 | layout (location = 4) in uint ambientOcclusion; 8 | 9 | out vec2 TexCoord; 10 | flat out uint texIndex; 11 | out vec3 varyingTint; 12 | 13 | uniform mat4 model; 14 | uniform mat4 view; 15 | uniform mat4 projection; 16 | 17 | void main() { 18 | vec4 worldPos = model * vec4(position, 1.0f); 19 | vec4 viewPos = view * worldPos; 20 | 21 | gl_Position = projection * viewPos; 22 | 23 | TexCoord = texCoord; 24 | texIndex = inTexIndex; 25 | varyingTint = tint * (0.55 + float(ambientOcclusion) * 0.15); 26 | } 27 | -------------------------------------------------------------------------------- /terracotta/render/Shader.h: -------------------------------------------------------------------------------- 1 | #ifndef TERRACOTTA_RENDER_SHADER_H_ 2 | #define TERRACOTTA_RENDER_SHADER_H_ 3 | 4 | #include 5 | 6 | namespace terra { 7 | namespace render { 8 | 9 | class Shader { 10 | public: 11 | Shader(); 12 | Shader(const Shader& other) = delete; 13 | Shader(Shader&& other) = delete; 14 | Shader& operator=(const Shader& other) = delete; 15 | Shader& operator=(Shader&& other) = delete; 16 | 17 | bool Initialize(const char* vertexPath, const char* fragmentPath); 18 | 19 | void Use(); 20 | void Stop(); 21 | 22 | GLuint GetUniform(const char* name); 23 | 24 | private: 25 | GLuint m_Program; 26 | }; 27 | 28 | } // ns render 29 | } // ns terra 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /terracotta/ChatWindow.h: -------------------------------------------------------------------------------- 1 | #ifndef TERRACOTTA_CHAT_WINDOW_H_ 2 | #define TERRACOTTA_CHAT_WINDOW_H_ 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | namespace terra { 11 | 12 | class ChatWindow : public mc::protocol::packets::PacketHandler { 13 | public: 14 | ChatWindow(mc::protocol::packets::PacketDispatcher* dispatcher, mc::core::Connection* connection); 15 | ~ChatWindow(); 16 | 17 | void HandlePacket(mc::protocol::packets::in::ChatPacket* packet); 18 | void Render(); 19 | 20 | private: 21 | mc::core::Connection* m_Connection; 22 | char m_InputText[256]; 23 | std::deque m_ChatBuffer; 24 | }; 25 | 26 | } // ns terra 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /terracotta/assets/zip/ZipArchive.h: -------------------------------------------------------------------------------- 1 | #ifndef TERRACOTTA_UTIL_ZIPARCHIVE_H_ 2 | #define TERRACOTTA_UTIL_ZIPARCHIVE_H_ 3 | 4 | #include 5 | #include 6 | 7 | #include "miniz.h" 8 | 9 | namespace terra { 10 | namespace assets { 11 | 12 | class ZipArchive { 13 | public: 14 | ZipArchive(); 15 | 16 | bool Open(const char *archivePath); 17 | void Close(); 18 | 19 | // Reads a file from the archive into the heap. 20 | char* ReadFile(const char* filename, size_t* size); 21 | void FreeFileData(char* data); 22 | 23 | // Lists all of the files in the archive that contain search in their name. 24 | std::vector ListFiles(const char* search); 25 | 26 | private: 27 | mz_zip_archive m_Archive; 28 | }; 29 | 30 | } // ns assets 31 | } // ns terra 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Terracotta 2 | This is a toy Minecraft client that I'm using to learn OpenGL. I have no experience, so this is probably really bad. 3 | 4 | ![Terracotta Image](https://i.imgur.com/0ZZfQ3w.png) 5 | 6 | ## Running 7 | This makes use of the Minecraft assets stored in the 1.13.2 jar. 8 | Copy the 1.13.2.jar, bin/blocks.json, bin/shaders, and mclib.dll over to the folder with terracotta.exe for it to work correctly. 9 | 10 | It can currently connect to Minecraft servers and attempts to render blocks. 11 | 12 | ## Videos 13 | [Basic rendering](https://gfycat.com/dimillinformedharrier) - Renders most basic blocks. Entities are rendered with a black box. 14 | 15 | ## Building 16 | ### Dependencies 17 | - C++14 compiler 18 | - CMake 19 | - libglew-dev 20 | - libglfw3-dev 21 | - libglm-dev 22 | - [mclib](https://github.com/plushmonkey/mclib) 23 | -------------------------------------------------------------------------------- /terracotta/math/Plane.h: -------------------------------------------------------------------------------- 1 | #ifndef TERRACOTTA_MATH_PLANE_H_ 2 | #define TERRACOTTA_MATH_PLANE_H_ 3 | 4 | #include 5 | #include 6 | 7 | namespace terra { 8 | namespace math { 9 | 10 | class Plane { 11 | public: 12 | Plane() { } 13 | Plane(glm::vec3 normal, float distance); 14 | // Construct plane from three noncollinear points 15 | Plane(glm::vec3 p1, glm::vec3 p2, glm::vec3 p3); 16 | 17 | const glm::vec3& GetNormal() const { return m_Normal; } 18 | float GetDistance() const { return m_Distance; } 19 | 20 | float PointDistance(const glm::vec3& q) const; 21 | float PointDistance(mc::Vector3i q) const; 22 | float PointDistance(mc::Vector3d q) const; 23 | 24 | private: 25 | glm::vec3 m_Normal; 26 | float m_Distance; 27 | }; 28 | 29 | } // ns math 30 | } // ns terra 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /terracotta/block/BlockState.h: -------------------------------------------------------------------------------- 1 | #ifndef TERRACOTTA_BLOCK_BLOCKSTATE_H_ 2 | #define TERRACOTTA_BLOCK_BLOCKSTATE_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | namespace terra { 11 | namespace block { 12 | 13 | class BlockState { 14 | public: 15 | BlockState(u32 id) : m_Id(id) { } 16 | 17 | u32 GetId() const { return m_Id; } 18 | const std::string& GetProperty(const std::string& property) const; 19 | const std::string& GetVariant() const { return m_Variant; } 20 | 21 | static std::unique_ptr FromJSON(const mc::json& json); 22 | 23 | private: 24 | u32 m_Id; 25 | std::string m_Variant; 26 | std::unordered_map m_Properties; 27 | }; 28 | 29 | } // ns block 30 | } // ns terra 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /terracotta/assets/TextureArray.h: -------------------------------------------------------------------------------- 1 | #ifndef TERRACOTTA_ASSETS_TEXTUREARRAY_H_ 2 | #define TERRACOTTA_ASSETS_TEXTUREARRAY_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | namespace terra { 11 | namespace assets { 12 | 13 | using TextureHandle = uint32_t; 14 | 15 | class TextureArray { 16 | public: 17 | TextureArray(); 18 | 19 | TextureHandle Append(const std::string& filename, const std::string& texture); 20 | 21 | bool GetTexture(const std::string& filename, TextureHandle* handle); 22 | bool IsTransparent(TextureHandle handle) const; 23 | 24 | void Generate(); 25 | void Bind(); 26 | private: 27 | unsigned int m_TextureId; 28 | 29 | std::vector m_TextureData; 30 | std::unordered_map m_Textures; 31 | 32 | bool m_Transparency[2048]; 33 | }; 34 | 35 | } // ns assets 36 | } // ns terra 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /terracotta/math/volumes/Frustum.h: -------------------------------------------------------------------------------- 1 | #ifndef TERRACOTTA_MATH_VOLUMES_FRUSTUM_H_ 2 | #define TERRACOTTA_MATH_VOLUMES_FRUSTUM_H_ 3 | 4 | #include 5 | #include 6 | #include "../Plane.h" 7 | 8 | namespace terra { 9 | namespace math { 10 | namespace volumes { 11 | 12 | class Frustum { 13 | public: 14 | Frustum(glm::vec3 position, glm::vec3 forward, float near, float far, float fov, float ratio, glm::vec3 up, glm::vec3 right); 15 | 16 | bool Intersects(const mc::AABB& aabb) const; 17 | bool Intersects(glm::vec3 v) const; 18 | bool Intersects(mc::Vector3i v) const; 19 | bool Intersects(mc::Vector3d v) const; 20 | 21 | private: 22 | glm::vec3 m_Position; 23 | glm::vec3 m_Forward; 24 | float m_Near; 25 | float m_Far; 26 | float m_NearWidth; 27 | float m_NearHeight; 28 | float m_FarWidth; 29 | float m_FarHeight; 30 | 31 | Plane m_Planes[6]; 32 | }; 33 | 34 | } // ns volumes 35 | } // ns math 36 | } // ns terra 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /terracotta/math/Plane.cpp: -------------------------------------------------------------------------------- 1 | #include "Plane.h" 2 | 3 | namespace terra { 4 | namespace math { 5 | 6 | Plane::Plane(glm::vec3 normal, float distance) 7 | : m_Normal(normal), 8 | m_Distance(distance) 9 | { 10 | 11 | } 12 | 13 | Plane::Plane(glm::vec3 p1, glm::vec3 p2, glm::vec3 p3) { 14 | m_Normal = glm::normalize(glm::cross(p2 - p1, p3 - p1)); 15 | m_Distance = glm::dot(m_Normal, p1); 16 | } 17 | 18 | float Plane::PointDistance(const glm::vec3& q) const { 19 | return (glm::dot(m_Normal, q) - m_Distance) / glm::dot(m_Normal, m_Normal); 20 | } 21 | 22 | float Plane::PointDistance(mc::Vector3i q) const { 23 | using T = mc::Vector3i::value_type; 24 | 25 | mc::Vector3i n(static_cast(m_Normal.x), static_cast(m_Normal.y), static_cast(m_Normal.z)); 26 | 27 | return static_cast((n.Dot(q) - m_Distance) / n.Dot(n)); 28 | } 29 | 30 | float Plane::PointDistance(mc::Vector3d q) const { 31 | mc::Vector3d n(m_Normal.x, m_Normal.y, m_Normal.z); 32 | 33 | return static_cast((n.Dot(q) - m_Distance) / n.Dot(n)); 34 | } 35 | 36 | } // ns math 37 | } // ns terra 38 | -------------------------------------------------------------------------------- /terracotta/Collision.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef TERRACOTTA_COLLISION_H_ 4 | #define TERRACOTTA_COLLISION_H_ 5 | 6 | #include "Transform.h" 7 | #include "World.h" 8 | 9 | namespace terra { 10 | 11 | class Collision { 12 | private: 13 | mc::Vector3d m_Position; 14 | mc::Vector3d m_Normal; 15 | 16 | public: 17 | Collision() noexcept { } 18 | Collision(mc::Vector3d position, mc::Vector3d normal) noexcept : m_Position(position), m_Normal(normal) { } 19 | 20 | mc::Vector3d GetPosition() const noexcept { return m_Position; } 21 | mc::Vector3d GetNormal() const noexcept { return m_Normal; } 22 | }; 23 | 24 | class CollisionDetector { 25 | private: 26 | terra::World* m_World; 27 | 28 | std::vector GetSurroundingLocations(mc::AABB bounds); 29 | 30 | public: 31 | CollisionDetector(terra::World* world) noexcept : m_World(world) { } 32 | 33 | bool DetectCollision(mc::Vector3d from, mc::Vector3d rayVector, Collision* collision) const; 34 | 35 | void ResolveCollisions(Transform* transform, double dt, bool* onGround); 36 | }; 37 | 38 | } // namespace terra 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /terracotta/PriorityQueue.h: -------------------------------------------------------------------------------- 1 | #ifndef TERRACOTTA_PRIORITY_QUEUE_H_ 2 | #define TERRACOTTA_PRIORITY_QUEUE_H_ 3 | 4 | #include 5 | #include 6 | 7 | namespace terra { 8 | 9 | template > 10 | class PriorityQueue { 11 | private: 12 | Container m_Container; 13 | Comparator m_Comp; 14 | 15 | public: 16 | PriorityQueue() = default; 17 | PriorityQueue(const Comparator& cmp) : m_Comp(cmp) { } 18 | 19 | void Push(T item) { 20 | m_Container.push_back(item); 21 | std::push_heap(m_Container.begin(), m_Container.end(), m_Comp); 22 | } 23 | 24 | T Pop() { 25 | T item = m_Container.front(); 26 | std::pop_heap(m_Container.begin(), m_Container.end(), m_Comp); 27 | m_Container.pop_back(); 28 | return item; 29 | } 30 | 31 | void Update() { 32 | std::make_heap(m_Container.begin(), m_Container.end(), m_Comp); 33 | } 34 | 35 | bool Empty() const { return m_Container.empty(); } 36 | Container& GetData() { return m_Container; } 37 | }; 38 | 39 | } // ns terra 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019 plushmonkey (plushmonkey.ss@gmail.com) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /terracotta/lib/imgui/LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014-2019 Omar Cornut 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /terracotta/block/BlockVariant.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef TERRACOTTA_BLOCK_BLOCKVARIANT_H_ 4 | #define TERRACOTTA_BLOCK_BLOCKVARIANT_H_ 5 | 6 | #include "BlockModel.h" 7 | 8 | #include 9 | #include 10 | 11 | namespace terra { 12 | namespace block { 13 | 14 | class BlockVariant { 15 | public: 16 | BlockVariant(BlockModel* model, const std::string& properties, mc::block::BlockPtr block) : m_Model(model), m_Properties(properties), m_Block(block), m_Rotations(0, 0, 0), m_LockUV(true), m_HasRotation(false) { } 17 | 18 | BlockModel* GetModel() { return m_Model; } 19 | mc::block::BlockPtr GetBlock() { return m_Block; } 20 | const std::string& GetProperties() const { return m_Properties; } 21 | 22 | const glm::vec3& GetRotations() const { return m_Rotations; } 23 | void SetRotation(const glm::vec3& rotations) { 24 | m_Rotations = rotations; 25 | m_HasRotation = m_Rotations.x != 0 || m_Rotations.y != 0 || m_Rotations.z != 0; 26 | } 27 | 28 | bool HasRotation() { 29 | return m_HasRotation; 30 | } 31 | 32 | private: 33 | std::string m_Properties; 34 | BlockModel* m_Model; 35 | mc::block::BlockPtr m_Block; 36 | glm::vec3 m_Rotations; 37 | bool m_LockUV; 38 | bool m_HasRotation; 39 | }; 40 | 41 | } // ns block 42 | } // ns terra 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /terracotta/block/BlockFace.cpp: -------------------------------------------------------------------------------- 1 | #include "BlockFace.h" 2 | #include 3 | #include 4 | 5 | namespace terra { 6 | namespace block { 7 | 8 | const std::string face_strings[] = { 9 | "North", "East", "South", "West", "Up", "Down" 10 | }; 11 | 12 | const std::string& to_string(BlockFace face) { 13 | return face_strings[static_cast(face)]; 14 | } 15 | 16 | BlockFace face_from_string(const std::string& str) { 17 | std::string find; 18 | 19 | find.resize(str.size()); 20 | 21 | std::transform(str.begin(), str.end(), find.begin(), tolower); 22 | 23 | find[0] = toupper(find[0]); 24 | 25 | for (std::size_t i = 0; i < 6; ++i) { 26 | if (face_strings[i] == find) { 27 | return static_cast(i); 28 | } 29 | } 30 | 31 | return BlockFace::None; 32 | } 33 | 34 | BlockFace get_opposite_face(BlockFace face) { 35 | if (face == BlockFace::East) { 36 | return BlockFace::West; 37 | } else if (face == BlockFace::West) { 38 | return BlockFace::East; 39 | } else if (face == BlockFace::North) { 40 | return BlockFace::South; 41 | } else if (face == BlockFace::South) { 42 | return BlockFace::North; 43 | } else if (face == BlockFace::Up) { 44 | return BlockFace::Down; 45 | } else if (face == BlockFace::Down) { 46 | return BlockFace::Up; 47 | } 48 | 49 | return BlockFace::None; 50 | } 51 | 52 | } // ns block 53 | } // ns terra 54 | -------------------------------------------------------------------------------- /cmake/modules/Findmclib.cmake: -------------------------------------------------------------------------------- 1 | # Locate the mclib library 2 | # 3 | # This module defines the following variables: 4 | # 5 | # MCLIB_LIBRARY the name of the library; 6 | # MCLIB_INCLUDE_DIR where to find mclib include files. 7 | # MCLIB_FOUND true if both the MCLIB_LIBRARY and MCLIB_INCLUDE_DIR have been found. 8 | # 9 | # To help locate the library and include file, you can define a 10 | # variable called MCLIB_ROOT which points to the root of the mclib library 11 | # installation. 12 | 13 | set( _mclib_HEADER_SEARCH_DIRS 14 | "/usr/include" 15 | "/usr/local/include" 16 | "${CMAKE_SOURCE_DIR}/includes" 17 | "C:/Program Files (x86)/mclib/include" ) 18 | set( _mclib_LIB_SEARCH_DIRS 19 | "/usr/lib" 20 | "/usr/local/lib" 21 | "${CMAKE_SOURCE_DIR}/lib" 22 | "C:/Program Files (x86)/mclib/lib" ) 23 | 24 | # Check environment for root search directory 25 | set( _mclib_ENV_ROOT $ENV{MCLIB_ROOT} ) 26 | if( NOT MCLIB_ROOT AND _mclib_ENV_ROOT ) 27 | set(MCILB_ROOT ${_mclib_ENV_ROOT} ) 28 | endif() 29 | 30 | # Put user specified location at beginning of search 31 | if( MCLIB_ROOT ) 32 | list( INSERT _mclib_HEADER_SEARCH_DIRS 0 "${MCLIB_ROOT}/include" ) 33 | list( INSERT _mclib_LIB_SEARCH_DIRS 0 "${MCLIB_ROOT}/lib" ) 34 | endif() 35 | 36 | # Search for the header 37 | FIND_PATH(MCLIB_INCLUDE_DIR "mclib/mclib.h" 38 | PATHS ${_mclib_HEADER_SEARCH_DIRS} ) 39 | 40 | # Search for the library 41 | FIND_LIBRARY(MCLIB_LIBRARY NAMES mclib 42 | PATHS ${_mclib_LIB_SEARCH_DIRS} ) 43 | INCLUDE(FindPackageHandleStandardArgs) 44 | FIND_PACKAGE_HANDLE_STANDARD_ARGS(MCLIB DEFAULT_MSG 45 | MCLIB_LIBRARY MCLIB_INCLUDE_DIR) -------------------------------------------------------------------------------- /terracotta/render/ChunkMesh.cpp: -------------------------------------------------------------------------------- 1 | #include "ChunkMesh.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include "../assets/AssetCache.h" 7 | #include 8 | 9 | namespace terra { 10 | namespace render { 11 | 12 | ChunkMesh::ChunkMesh(unsigned int vao, unsigned int vbo, GLsizei vertex_count) 13 | : m_VAO(vao), 14 | m_VBO(vbo), 15 | m_VertexCount(vertex_count) 16 | { 17 | 18 | } 19 | 20 | ChunkMesh::ChunkMesh(const ChunkMesh& other) { 21 | this->m_VAO = other.m_VAO; 22 | this->m_VBO = other.m_VBO; 23 | this->m_VertexCount = other.m_VertexCount; 24 | } 25 | 26 | ChunkMesh& ChunkMesh::operator=(const ChunkMesh& other) { 27 | this->m_VAO = other.m_VAO; 28 | this->m_VBO = other.m_VBO; 29 | this->m_VertexCount = other.m_VertexCount; 30 | 31 | return *this; 32 | } 33 | 34 | void ChunkMesh::Render(unsigned int model_uniform) { 35 | static const glm::mat4 modelMatrix(1.0); 36 | 37 | glBindVertexArray(m_VAO); 38 | g_AssetCache->GetTextures().Bind(); 39 | 40 | glUniformMatrix4fv(model_uniform, 1, GL_FALSE, glm::value_ptr(modelMatrix)); 41 | 42 | glDrawArrays(GL_TRIANGLES, 0, m_VertexCount); 43 | 44 | GLenum error; 45 | 46 | while ((error = glGetError()) != GL_NO_ERROR) { 47 | std::cout << "OpenGL error rendering: " << error << std::endl; 48 | } 49 | } 50 | 51 | void ChunkMesh::Destroy() { 52 | glBindVertexArray(m_VAO); 53 | glDeleteBuffers(1, &m_VBO); 54 | glDeleteVertexArrays(1, &m_VAO); 55 | } 56 | 57 | } // ns render 58 | } // ns terra 59 | -------------------------------------------------------------------------------- /terracotta/block/BlockState.cpp: -------------------------------------------------------------------------------- 1 | #include "BlockState.h" 2 | 3 | #include 4 | #include 5 | 6 | namespace terra { 7 | namespace block { 8 | 9 | static const std::string kNullProperty = ""; 10 | 11 | const std::string& BlockState::GetProperty(const std::string& property) const { 12 | auto iter = m_Properties.find(property); 13 | 14 | if (iter == m_Properties.end()) { 15 | return kNullProperty; 16 | } 17 | 18 | return iter->second; 19 | } 20 | 21 | std::unique_ptr BlockState::FromJSON(const mc::json& json) { 22 | u32 id = json.value("id", 0); 23 | std::unique_ptr state = std::make_unique(id); 24 | 25 | mc::json properties_node = json.value("properties", mc::json()); 26 | 27 | std::vector property_keys; 28 | 29 | if (properties_node.is_object()) { 30 | for (auto& kv : properties_node.items()) { 31 | std::string property = kv.key(); 32 | std::string value = kv.value().get(); 33 | 34 | state->m_Properties[property] = value; 35 | property_keys.push_back(property); 36 | } 37 | } 38 | 39 | std::sort(property_keys.begin(), property_keys.end()); 40 | 41 | for (std::size_t i = 0; i < property_keys.size(); ++i) { 42 | auto& property_key = property_keys[i]; 43 | 44 | if (i != 0) { 45 | state->m_Variant += ","; 46 | } 47 | 48 | state->m_Variant += property_key + "=" + state->m_Properties[property_key]; 49 | } 50 | 51 | return state; 52 | } 53 | 54 | } // ns block 55 | } // ns terra 56 | -------------------------------------------------------------------------------- /cmake/modules/FindGLFW3.cmake: -------------------------------------------------------------------------------- 1 | # Locate the glfw3 library 2 | # 3 | # This module defines the following variables: 4 | # 5 | # GLFW3_LIBRARY the name of the library; 6 | # GLFW3_INCLUDE_DIR where to find glfw include files. 7 | # GLFW3_FOUND true if both the GLFW3_LIBRARY and GLFW3_INCLUDE_DIR have been found. 8 | # 9 | # To help locate the library and include file, you can define a 10 | # variable called GLFW3_ROOT which points to the root of the glfw library 11 | # installation. 12 | # 13 | # default search dirs 14 | # 15 | # Cmake file from: https://github.com/daw42/glslcookbook 16 | 17 | set( _glfw3_HEADER_SEARCH_DIRS 18 | "/usr/include" 19 | "/usr/local/include" 20 | "${CMAKE_SOURCE_DIR}/includes" 21 | "C:/Program Files (x86)/glfw/include" ) 22 | set( _glfw3_LIB_SEARCH_DIRS 23 | "/usr/lib" 24 | "/usr/local/lib" 25 | "${CMAKE_SOURCE_DIR}/lib" 26 | "C:/Program Files (x86)/glfw/lib" 27 | "C:/Program Files (x86)/glfw/lib-msvc110" ) 28 | 29 | # Check environment for root search directory 30 | set( _glfw3_ENV_ROOT $ENV{GLFW3_ROOT} ) 31 | if( NOT GLFW3_ROOT AND _glfw3_ENV_ROOT ) 32 | set(GLFW3_ROOT ${_glfw3_ENV_ROOT} ) 33 | endif() 34 | 35 | # Put user specified location at beginning of search 36 | if( GLFW3_ROOT ) 37 | list( INSERT _glfw3_HEADER_SEARCH_DIRS 0 "${GLFW3_ROOT}/include" ) 38 | list( INSERT _glfw3_LIB_SEARCH_DIRS 0 "${GLFW3_ROOT}/lib" ) 39 | endif() 40 | 41 | # Search for the header 42 | FIND_PATH(GLFW3_INCLUDE_DIR "GLFW/glfw3.h" 43 | PATHS ${_glfw3_HEADER_SEARCH_DIRS} ) 44 | 45 | # Search for the library 46 | FIND_LIBRARY(GLFW3_LIBRARY NAMES glfw3 glfw 47 | PATHS ${_glfw3_LIB_SEARCH_DIRS} ) 48 | INCLUDE(FindPackageHandleStandardArgs) 49 | FIND_PACKAGE_HANDLE_STANDARD_ARGS(GLFW3 DEFAULT_MSG 50 | GLFW3_LIBRARY GLFW3_INCLUDE_DIR) -------------------------------------------------------------------------------- /terracotta/GameWindow.h: -------------------------------------------------------------------------------- 1 | #ifndef TERRACOTTA_GAME_WINDOW_H_ 2 | #define TERRACOTTA_GAME_WINDOW_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | namespace terra { 9 | 10 | using MouseSetCallback = std::function; 11 | using MouseChangeCallback = std::function; 12 | using MouseButtonCallback = std::function; 13 | using MouseScrollCallback = std::function; 14 | 15 | class GameWindow { 16 | public: 17 | GameWindow(GLFWwindow* window); 18 | 19 | void OnKeyChange(int key, int code, int action, int mode); 20 | void OnMouseMove(double x, double y); 21 | void OnMouseButton(int button, int action, int mods); 22 | void OnMouseScroll(double offset_x, double offset_y); 23 | 24 | void RegisterMouseSet(MouseSetCallback callback) { 25 | m_MouseSetCallbacks.push_back(callback); 26 | } 27 | 28 | void RegisterMouseChange(MouseChangeCallback callback) { 29 | m_MouseChangeCallbacks.push_back(callback); 30 | } 31 | 32 | void RegisterMouseButton(MouseButtonCallback callback) { 33 | m_MouseButtonCallbacks.push_back(callback); 34 | } 35 | 36 | void RegisterMouseScroll(MouseScrollCallback callback) { 37 | m_MouseScrollCallbacks.push_back(callback); 38 | } 39 | 40 | bool IsKeyDown(int code); 41 | private: 42 | GLFWwindow* m_Window; 43 | std::vector m_MouseSetCallbacks; 44 | std::vector m_MouseChangeCallbacks; 45 | std::vector m_MouseButtonCallbacks; 46 | std::vector m_MouseScrollCallbacks; 47 | 48 | bool m_Keys[1024]; 49 | float m_LastMouseX; 50 | float m_LastMouseY; 51 | bool m_FirstMouse; 52 | bool m_UpdateMouse; 53 | }; 54 | 55 | } // ns terra 56 | 57 | #endif 58 | -------------------------------------------------------------------------------- /terracotta/Camera.h: -------------------------------------------------------------------------------- 1 | #ifndef TERRACOTTA_CAMERA_H_ 2 | #define TERRACOTTA_CAMERA_H_ 3 | 4 | #include 5 | #include 6 | #include "math/volumes/Frustum.h" 7 | 8 | #undef near 9 | #undef far 10 | 11 | namespace terra { 12 | 13 | enum class CameraMovement { 14 | Forward, 15 | Backward, 16 | Left, 17 | Right, 18 | Raise, 19 | Lower 20 | }; 21 | 22 | class Camera { 23 | public: 24 | Camera(glm::vec3 position, float fov, float ratio, float near, float far, glm::vec3 worldUp = glm::vec3(0.0f, 1.0f, 0.0f), float yaw = -1.5708, float pitch = 0.0f); 25 | 26 | void ProcessMovement(CameraMovement direction, float dt); 27 | void ProcessRotation(float xoffset, float yoffset); 28 | void ProcessZoom(float yoffset); 29 | 30 | const glm::vec3& GetPosition() const { return m_Position; } 31 | glm::vec3 GetFront() const { return m_Front; } 32 | glm::vec3 GetRight() const { return m_Right; } 33 | float GetZoom() const { return m_Zoom; } 34 | glm::mat4 GetViewMatrix() const; 35 | glm::mat4 GetPerspectiveMatrix() const; 36 | 37 | void SetPosition(glm::vec3 pos) { m_Position = pos; } 38 | 39 | float GetYaw() const { return m_Yaw; } 40 | float GetPitch() const { return m_Pitch; } 41 | 42 | math::volumes::Frustum GetFrustum() const; 43 | 44 | void SetMouseSensitivity(float sensitivity) { m_MouseSensitivity = sensitivity; } 45 | 46 | void SetFov(float fov) { m_Fov = fov; } 47 | private: 48 | glm::vec3 m_Position; 49 | glm::vec3 m_Front; 50 | glm::vec3 m_WorldUp; 51 | glm::vec3 m_Up; 52 | glm::vec3 m_Right; 53 | 54 | float m_Yaw; 55 | float m_Pitch; 56 | float m_Near; 57 | float m_Far; 58 | float m_Fov; 59 | float m_AspectRatio; 60 | 61 | float m_MovementSpeed; 62 | float m_MouseSensitivity; 63 | float m_Zoom; 64 | 65 | void UpdateVectors(); 66 | }; 67 | 68 | } // ns terra 69 | 70 | #endif 71 | -------------------------------------------------------------------------------- /terracotta/assets/zip/ZipArchive.cpp: -------------------------------------------------------------------------------- 1 | #include "ZipArchive.h" 2 | 3 | namespace terra { 4 | namespace assets { 5 | 6 | ZipArchive::ZipArchive() { 7 | 8 | } 9 | 10 | bool ZipArchive::Open(const char *archivePath) { 11 | mz_zip_zero_struct(&m_Archive); 12 | 13 | if (!mz_zip_reader_init_file_v2(&m_Archive, archivePath, MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY, 0, 0)) { 14 | return false; 15 | } 16 | 17 | return true; 18 | } 19 | 20 | void ZipArchive::Close() { 21 | mz_zip_reader_end(&m_Archive); 22 | 23 | mz_zip_zero_struct(&m_Archive); 24 | } 25 | 26 | // Reads a file from the archive into the heap. 27 | char* ZipArchive::ReadFile(const char* filename, size_t* size) { 28 | unsigned int index; 29 | void* p = nullptr; 30 | 31 | if (mz_zip_reader_locate_file_v2(&m_Archive, filename, nullptr, 0, &index)) { 32 | p = mz_zip_reader_extract_to_heap(&m_Archive, index, size, 0); 33 | } 34 | 35 | return static_cast(p); 36 | } 37 | 38 | void ZipArchive::FreeFileData(char* data) { 39 | mz_free(data); 40 | } 41 | 42 | // Lists all of the files in the archive that contain search in their name. 43 | std::vector ZipArchive::ListFiles(const char* search) { 44 | std::vector results; 45 | unsigned int count = mz_zip_reader_get_num_files(&m_Archive); 46 | 47 | mz_zip_archive_file_stat stat; 48 | 49 | if (!mz_zip_reader_file_stat(&m_Archive, 0, &stat)) { 50 | return results; 51 | } 52 | 53 | for (unsigned int i = 0; i < count; ++i) { 54 | if (!mz_zip_reader_file_stat(&m_Archive, i, &stat)) continue; 55 | if (mz_zip_reader_is_file_a_directory(&m_Archive, i)) continue; 56 | 57 | const char* current = stat.m_filename; 58 | 59 | if (search == nullptr || strstr(current, search) != nullptr) { 60 | results.emplace_back(current); 61 | } 62 | } 63 | 64 | return results; 65 | } 66 | 67 | } // ns assets 68 | } // ns terra 69 | -------------------------------------------------------------------------------- /terracotta/assets/AssetCache.h: -------------------------------------------------------------------------------- 1 | #ifndef TERRACOTTA_ASSETS_ASSETCACHE_H_ 2 | #define TERRACOTTA_ASSETS_ASSETCACHE_H_ 3 | 4 | #include "TextureArray.h" 5 | 6 | #include 7 | #include 8 | #include 9 | #include "../block/BlockState.h" 10 | #include "../block/BlockModel.h" 11 | #include "../block/BlockVariant.h" 12 | 13 | #include 14 | 15 | namespace terra { 16 | namespace assets { 17 | 18 | class ZipArchive; 19 | 20 | class AssetCache { 21 | public: 22 | AssetCache() { } 23 | 24 | ~AssetCache() { } 25 | block::BlockState* GetBlockState(u32 block_id) const; 26 | void AddBlockState(std::unique_ptr state); 27 | void SetMaxBlockId(std::size_t id); 28 | 29 | TextureArray& GetTextures() { return m_TextureArray; } 30 | TextureHandle AddTexture(const std::string& path, const std::string& data); 31 | 32 | void AddVariantModel(std::unique_ptr variant); 33 | block::BlockVariant* GetVariant(mc::block::BlockPtr block); 34 | 35 | std::vector GetBlockModels(const std::string& find); 36 | block::BlockModel* GetBlockModel(const std::string& path); 37 | void AddBlockModel(const std::string& path, std::unique_ptr model); 38 | 39 | private: 40 | block::BlockVariant* GetVariantFromProperties(const std::string& block_name, const std::string& properties); 41 | 42 | // Maps block id to the BlockState 43 | std::vector> m_BlockStates; 44 | std::vector m_VariantCache; 45 | 46 | // Maps model path to a BlockModel 47 | std::unordered_map> m_BlockModels; 48 | // Block name -> (list of Variants) 49 | std::unordered_map>> m_BlockVariants; 50 | 51 | TextureArray m_TextureArray; 52 | }; 53 | 54 | } // assets 55 | } // terra 56 | 57 | extern std::unique_ptr g_AssetCache; 58 | 59 | #endif 60 | -------------------------------------------------------------------------------- /terracotta/assets/AssetLoader.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef TERRACOTTA_ASSETS_ASSETLOADER_H_ 4 | #define TERRACOTTA_ASSETS_ASSETLOADER_H_ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "TextureArray.h" 12 | #include "../block/BlockFace.h" 13 | #include "../block/BlockElement.h" 14 | 15 | namespace terra { 16 | 17 | namespace block { 18 | 19 | class BlockModel; 20 | 21 | } // ns block 22 | 23 | namespace assets { 24 | 25 | // Stores the intermediate faces of a block. The textures are resolved later and stored into a final RenderableFace. 26 | struct IntermediateFace { 27 | block::BlockFace face; 28 | block::BlockFace cull_face; 29 | std::string texture; 30 | int tint_index; 31 | glm::vec2 uv_from; 32 | glm::vec2 uv_to; 33 | }; 34 | 35 | // Stores the intermediate element for a BlockModel. The faces are resolved later and stored in a final BlockElement. 36 | struct IntermediateElement { 37 | glm::vec3 from; 38 | glm::vec3 to; 39 | bool shade; 40 | block::ElementRotation rotation; 41 | 42 | std::vector faces; 43 | }; 44 | 45 | class ZipArchive; 46 | class AssetCache; 47 | 48 | class AssetLoader { 49 | public: 50 | AssetLoader(AssetCache& cache); 51 | 52 | bool LoadArchive(const std::string& archive_path, const std::string& blocks_path); 53 | 54 | private: 55 | using TextureMap = std::unordered_map; 56 | 57 | bool LoadBlockStates(const std::string& filename); 58 | std::size_t LoadBlockModels(terra::assets::ZipArchive& archive); 59 | void LoadBlockVariants(terra::assets::ZipArchive& archive); 60 | bool LoadTexture(terra::assets::ZipArchive& archive, const std::string& path, TextureHandle* handle); 61 | std::unique_ptr LoadBlockModel(terra::assets::ZipArchive& archive, const std::string& path, TextureMap& texture_map, std::vector& intermediates); 62 | 63 | AssetCache& m_Cache; 64 | }; 65 | 66 | } // ns assets 67 | } // ns terra 68 | 69 | #endif 70 | 71 | -------------------------------------------------------------------------------- /terracotta/block/BlockElement.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef TERRACOTTA_BLOCK_BLOCKELEMENT_H_ 4 | #define TERRACOTTA_BLOCK_BLOCKELEMENT_H_ 5 | 6 | #include "BlockFace.h" 7 | #include 8 | #include 9 | #include "../assets/TextureArray.h" 10 | 11 | namespace terra { 12 | namespace block { 13 | 14 | struct RenderableFace { 15 | assets::TextureHandle texture; 16 | int tint_index; 17 | BlockFace face; 18 | BlockFace cull_face; 19 | glm::vec2 uv_from; 20 | glm::vec2 uv_to; 21 | }; 22 | 23 | struct ElementRotation { 24 | glm::vec3 origin; 25 | glm::vec3 axis; 26 | float angle; 27 | bool rescale; 28 | 29 | ElementRotation() : origin(0, 0, 0), axis(0, 0, 0), angle(0), rescale(false) { } 30 | }; 31 | 32 | class BlockElement { 33 | public: 34 | BlockElement(glm::vec3 from, glm::vec3 to) : m_From(from), m_To(to), m_Shade(true) { 35 | m_Faces.resize(6); 36 | 37 | for (RenderableFace& renderable : m_Faces) { 38 | renderable.face = BlockFace::None; 39 | } 40 | 41 | m_FullExtent = m_From == glm::vec3(0, 0, 0) && m_To == glm::vec3(1, 1, 1); 42 | } 43 | 44 | std::vector& GetFaces() { return m_Faces; } 45 | 46 | const RenderableFace& GetFace(BlockFace face) const { return m_Faces[static_cast(face)]; } 47 | void AddFace(RenderableFace face) { m_Faces[static_cast(face.face)] = face; } 48 | 49 | const glm::vec3& GetFrom() const { return m_From; } 50 | const glm::vec3& GetTo() const { return m_To; } 51 | bool IsFullExtent() const { return m_FullExtent; } 52 | 53 | void SetShouldShade(bool shade) { m_Shade = shade; } 54 | bool ShouldShade() const { return m_Shade; } 55 | 56 | ElementRotation& GetRotation() { return m_Rotation; } 57 | const ElementRotation& GetRotation() const { return m_Rotation; } 58 | 59 | private: 60 | std::vector m_Faces; 61 | glm::vec3 m_From; 62 | glm::vec3 m_To; 63 | bool m_FullExtent; 64 | bool m_Shade; 65 | ElementRotation m_Rotation; 66 | }; 67 | 68 | } // ns block 69 | } // ns terra 70 | 71 | #endif 72 | 73 | -------------------------------------------------------------------------------- /terracotta/ChatWindow.cpp: -------------------------------------------------------------------------------- 1 | #include "ChatWindow.h" 2 | 3 | #include "lib/imgui/imgui.h" 4 | #include 5 | #include 6 | #include 7 | 8 | namespace { 9 | 10 | constexpr std::size_t kChatBufferLines = 50; 11 | 12 | } // ns 13 | 14 | namespace terra { 15 | 16 | ChatWindow::ChatWindow(mc::protocol::packets::PacketDispatcher* dispatcher, mc::core::Connection* connection) 17 | : mc::protocol::packets::PacketHandler(dispatcher), m_Connection(connection) 18 | { 19 | memset(m_InputText, 0, IM_ARRAYSIZE(m_InputText)); 20 | using namespace mc::protocol; 21 | 22 | dispatcher->RegisterHandler(State::Play, play::Chat, this); 23 | } 24 | 25 | ChatWindow::~ChatWindow() { 26 | GetDispatcher()->UnregisterHandler(this); 27 | } 28 | 29 | void ChatWindow::Render() { 30 | ImGui::SetNextWindowBgAlpha(0.3f); 31 | ImGuiWindowFlags flags = ImGuiWindowFlags_NoTitleBar; 32 | ImGui::Begin("chat", 0, flags); 33 | 34 | for (auto&& str : m_ChatBuffer) { 35 | ImGui::TextWrapped("%s", str.c_str()); 36 | } 37 | 38 | ImGui::SetScrollHereY(1.0f); 39 | 40 | ImGui::End(); 41 | 42 | int width = static_cast(ImGui::GetWindowContentRegionWidth()); 43 | flags = ImGuiWindowFlags_NoDecoration; 44 | ImGui::SetNextWindowBgAlpha(0.5f); 45 | ImGui::SetNextWindowSize(ImVec2(400, ImGui::GetTextLineHeightWithSpacing() + 5)); 46 | ImGui::Begin("chat_input", 0, flags); 47 | 48 | ImGui::PushItemWidth(400); 49 | if (ImGui::InputText("", m_InputText, IM_ARRAYSIZE(m_InputText), ImGuiInputTextFlags_EnterReturnsTrue)) { 50 | mc::protocol::packets::out::ChatPacket packet(m_InputText); 51 | 52 | m_Connection->SendPacket(&packet); 53 | 54 | memset(m_InputText, 0, IM_ARRAYSIZE(m_InputText)); 55 | } 56 | ImGui::PopItemWidth(); 57 | ImGui::End(); 58 | } 59 | 60 | void ChatWindow::HandlePacket(mc::protocol::packets::in::ChatPacket* packet) { 61 | auto&& data = packet->GetChatData(); 62 | std::string str = mc::util::ParseChatNode(data); 63 | str = mc::util::StripChatMessage(str); 64 | 65 | m_ChatBuffer.push_back(str); 66 | 67 | if (m_ChatBuffer.size() > kChatBufferLines) { 68 | m_ChatBuffer.pop_front(); 69 | } 70 | } 71 | 72 | } // ns terra 73 | -------------------------------------------------------------------------------- /terracotta/Game.h: -------------------------------------------------------------------------------- 1 | #ifndef TERRACOTTA_GAME_H_ 2 | #define TERRACOTTA_GAME_H_ 3 | 4 | #include 5 | 6 | #include "Camera.h" 7 | #include "GameWindow.h" 8 | #include "Transform.h" 9 | #include "Collision.h" 10 | 11 | #include 12 | #include 13 | 14 | #include 15 | 16 | namespace terra { 17 | 18 | class World; 19 | 20 | class Player { 21 | public: 22 | Player(terra::World* world); 23 | 24 | void Update(float dt); 25 | 26 | Transform& GetTransform() noexcept { return m_Transform; } 27 | 28 | bool OnGround(); 29 | bool IsSneaking() const { return m_Sneaking; } 30 | void SetSneaking(bool sneaking) { m_Sneaking = sneaking; } 31 | 32 | CollisionDetector& GetCollisionDetector() { return m_CollisionDetector; } 33 | private: 34 | bool m_OnGround; 35 | bool m_Sneaking; 36 | Transform m_Transform; 37 | CollisionDetector m_CollisionDetector; 38 | }; 39 | 40 | class Game : public mc::protocol::packets::PacketHandler, mc::core::PlayerListener { 41 | public: 42 | Game(mc::protocol::packets::PacketDispatcher* dispatcher, GameWindow& window, const Camera& camera); 43 | 44 | void OnMouseChange(double x, double y); 45 | void OnMouseScroll(double offset_x, double offset_y); 46 | void OnMousePress(int button, int action, int mods); 47 | 48 | void OnClientSpawn(mc::core::PlayerPtr player); 49 | 50 | void HandlePacket(mc::protocol::packets::in::UpdateHealthPacket* packet); 51 | void HandlePacket(mc::protocol::packets::in::EntityVelocityPacket* packet); 52 | void HandlePacket(mc::protocol::packets::in::SpawnPositionPacket* packet); 53 | 54 | void Update(); 55 | void UpdateClient(); 56 | 57 | void CreatePlayer(terra::World* world); 58 | 59 | Camera& GetCamera() { return m_Camera; } 60 | mc::Vector3d GetPosition(); 61 | mc::core::Client& GetNetworkClient() { return m_NetworkClient; } 62 | 63 | private: 64 | mc::core::Client m_NetworkClient; 65 | GameWindow& m_Window; 66 | std::unique_ptr m_Player; 67 | Camera m_Camera; 68 | float m_DeltaTime; 69 | float m_LastFrame; 70 | float m_LastPositionTime; 71 | bool m_Sprinting; 72 | }; 73 | 74 | } // ns terra 75 | 76 | #endif 77 | -------------------------------------------------------------------------------- /terracotta/lib/imgui/imgui_impl_glfw.h: -------------------------------------------------------------------------------- 1 | // dear imgui: Platform Binding for GLFW 2 | // This needs to be used along with a Renderer (e.g. OpenGL3, Vulkan..) 3 | // (Info: GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.) 4 | 5 | // Implemented features: 6 | // [X] Platform: Clipboard support. 7 | // [X] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. 8 | // [x] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. FIXME: 3 cursors types are missing from GLFW. 9 | // [X] Platform: Keyboard arrays indexed using GLFW_KEY_* codes, e.g. ImGui::IsKeyPressed(GLFW_KEY_SPACE). 10 | 11 | // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. 12 | // If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp. 13 | // https://github.com/ocornut/imgui 14 | 15 | // About GLSL version: 16 | // The 'glsl_version' initialization parameter defaults to "#version 150" if NULL. 17 | // Only override if your GL version doesn't handle this GLSL version. Keep NULL if unsure! 18 | 19 | #pragma once 20 | 21 | struct GLFWwindow; 22 | 23 | IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForOpenGL(GLFWwindow* window, bool install_callbacks); 24 | IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForVulkan(GLFWwindow* window, bool install_callbacks); 25 | IMGUI_IMPL_API void ImGui_ImplGlfw_Shutdown(); 26 | IMGUI_IMPL_API void ImGui_ImplGlfw_NewFrame(); 27 | 28 | // InitXXX function with 'install_callbacks=true': install GLFW callbacks. They will call user's previously installed callbacks, if any. 29 | // InitXXX function with 'install_callbacks=false': do not install GLFW callbacks. You will need to call them yourself from your own GLFW callbacks. 30 | IMGUI_IMPL_API void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow* window, int button, int action, int mods); 31 | IMGUI_IMPL_API void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yoffset); 32 | IMGUI_IMPL_API void ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods); 33 | IMGUI_IMPL_API void ImGui_ImplGlfw_CharCallback(GLFWwindow* window, unsigned int c); 34 | -------------------------------------------------------------------------------- /cmake/modules/FindGLM.cmake: -------------------------------------------------------------------------------- 1 | # FindGLM - attempts to locate the glm matrix/vector library. 2 | # 3 | # This module defines the following variables (on success): 4 | # GLM_INCLUDE_DIRS - where to find glm/glm.hpp 5 | # GLM_FOUND - if the library was successfully located 6 | # 7 | # It is trying a few standard installation locations, but can be customized 8 | # with the following variables: 9 | # GLM_ROOT_DIR - root directory of a glm installation 10 | # Headers are expected to be found in either: 11 | # /glm/glm.hpp OR 12 | # /include/glm/glm.hpp 13 | # This variable can either be a cmake or environment 14 | # variable. Note however that changing the value 15 | # of the environment varible will NOT result in 16 | # re-running the header search and therefore NOT 17 | # adjust the variables set by this module. 18 | #============================================================================= 19 | # Copyright 2012 Carsten Neumann 20 | # 21 | # Distributed under the OSI-approved BSD License (the "License"); 22 | # see accompanying file Copyright.txt for details. 23 | # 24 | # This software is distributed WITHOUT ANY WARRANTY; without even the 25 | # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 26 | # See the License for more information. 27 | #============================================================================= 28 | # (To distribute this file outside of CMake, substitute the full 29 | # License text for the above reference.) 30 | # default search dirs 31 | 32 | SET(_glm_HEADER_SEARCH_DIRS 33 | "/usr/include" 34 | "/usr/local/include" 35 | "${CMAKE_SOURCE_DIR}/includes" 36 | "C:/Program Files (x86)/glm" ) 37 | # check environment variable 38 | SET(_glm_ENV_ROOT_DIR "$ENV{GLM_ROOT_DIR}") 39 | IF(NOT GLM_ROOT_DIR AND _glm_ENV_ROOT_DIR) 40 | SET(GLM_ROOT_DIR "${_glm_ENV_ROOT_DIR}") 41 | ENDIF(NOT GLM_ROOT_DIR AND _glm_ENV_ROOT_DIR) 42 | # put user specified location at beginning of search 43 | IF(GLM_ROOT_DIR) 44 | SET(_glm_HEADER_SEARCH_DIRS "${GLM_ROOT_DIR}" 45 | "${GLM_ROOT_DIR}/include" 46 | ${_glm_HEADER_SEARCH_DIRS}) 47 | ENDIF(GLM_ROOT_DIR) 48 | # locate header 49 | FIND_PATH(GLM_INCLUDE_DIR "glm/glm.hpp" 50 | PATHS ${_glm_HEADER_SEARCH_DIRS}) 51 | INCLUDE(FindPackageHandleStandardArgs) 52 | FIND_PACKAGE_HANDLE_STANDARD_ARGS(GLM DEFAULT_MSG 53 | GLM_INCLUDE_DIR) 54 | IF(GLM_FOUND) 55 | SET(GLM_INCLUDE_DIRS "${GLM_INCLUDE_DIR}") 56 | MESSAGE(STATUS "GLM_INCLUDE_DIR = ${GLM_INCLUDE_DIR}") 57 | ENDIF(GLM_FOUND) -------------------------------------------------------------------------------- /terracotta/lib/imgui/imgui_impl_opengl3.h: -------------------------------------------------------------------------------- 1 | // dear imgui: Renderer for OpenGL3 / OpenGL ES2 / OpenGL ES3 (modern OpenGL with shaders / programmatic pipeline) 2 | // This needs to be used along with a Platform Binding (e.g. GLFW, SDL, Win32, custom..) 3 | // (Note: We are using GL3W as a helper library to access OpenGL functions since there is no standard header to access modern OpenGL functions easily. Alternatives are GLEW, Glad, etc..) 4 | 5 | // Implemented features: 6 | // [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. 7 | 8 | // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. 9 | // If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp. 10 | // https://github.com/ocornut/imgui 11 | 12 | // About OpenGL function loaders: 13 | // About OpenGL function loaders: modern OpenGL doesn't have a standard header file and requires individual function pointers to be loaded manually. 14 | // Helper libraries are often used for this purpose! Here we are supporting a few common ones: gl3w, glew, glad. 15 | // You may use another loader/header of your choice (glext, glLoadGen, etc.), or chose to manually implement your own. 16 | 17 | // About GLSL version: 18 | // The 'glsl_version' initialization parameter should be NULL (default) or a "#version XXX" string. 19 | // On computer platform the GLSL version default to "#version 130". On OpenGL ES 3 platform it defaults to "#version 300 es" 20 | // Only override if your GL version doesn't handle this GLSL version. See GLSL version table at the top of imgui_impl_opengl3.cpp. 21 | 22 | #pragma once 23 | 24 | // Set default OpenGL loader to be gl3w 25 | #if !defined(IMGUI_IMPL_OPENGL_LOADER_GL3W) \ 26 | && !defined(IMGUI_IMPL_OPENGL_LOADER_GLEW) \ 27 | && !defined(IMGUI_IMPL_OPENGL_LOADER_GLAD) \ 28 | && !defined(IMGUI_IMPL_OPENGL_LOADER_CUSTOM) 29 | #define IMGUI_IMPL_OPENGL_LOADER_GL3W 30 | #endif 31 | 32 | IMGUI_IMPL_API bool ImGui_ImplOpenGL3_Init(const char* glsl_version = NULL); 33 | IMGUI_IMPL_API void ImGui_ImplOpenGL3_Shutdown(); 34 | IMGUI_IMPL_API void ImGui_ImplOpenGL3_NewFrame(); 35 | IMGUI_IMPL_API void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data); 36 | 37 | // Called by Init/NewFrame/Shutdown 38 | IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateFontsTexture(); 39 | IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyFontsTexture(); 40 | IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateDeviceObjects(); 41 | IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyDeviceObjects(); 42 | -------------------------------------------------------------------------------- /terracotta/GameWindow.cpp: -------------------------------------------------------------------------------- 1 | #include "GameWindow.h" 2 | #include 3 | #include 4 | 5 | namespace terra { 6 | 7 | GameWindow::GameWindow(GLFWwindow* window) : m_Window(window), m_UpdateMouse(true), m_FirstMouse(true) { 8 | memset(m_Keys, 0, sizeof(m_Keys)); 9 | } 10 | 11 | bool GameWindow::IsKeyDown(int code) { 12 | return m_Keys[code]; 13 | } 14 | 15 | void GameWindow::OnKeyChange(int key, int code, int action, int mode) { 16 | if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) { 17 | m_UpdateMouse = !m_UpdateMouse; 18 | 19 | if (m_UpdateMouse) { 20 | glfwSetInputMode(m_Window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); 21 | } else { 22 | glfwSetInputMode(m_Window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); 23 | } 24 | } 25 | 26 | if (key == GLFW_KEY_1 && action == GLFW_PRESS) { 27 | glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); 28 | } 29 | 30 | if (key == GLFW_KEY_2 && action == GLFW_PRESS) { 31 | glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); 32 | } 33 | 34 | if ((key == GLFW_KEY_3 || key == GLFW_KEY_T) && action == GLFW_PRESS) { 35 | m_UpdateMouse = false; 36 | glfwSetInputMode(m_Window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); 37 | } 38 | 39 | if (key == GLFW_KEY_4 && action == GLFW_PRESS) { 40 | m_UpdateMouse = true; 41 | m_FirstMouse = true; 42 | glfwSetInputMode(m_Window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); 43 | } 44 | 45 | if (action == GLFW_PRESS && m_UpdateMouse) { 46 | m_Keys[key] = true; 47 | } else if (action == GLFW_RELEASE) { 48 | m_Keys[key] = false; 49 | } 50 | } 51 | 52 | void GameWindow::OnMouseMove(double x, double y) { 53 | if (!m_UpdateMouse) return; 54 | 55 | for (auto&& cb : m_MouseSetCallbacks) { 56 | cb(x, y); 57 | } 58 | 59 | if (m_FirstMouse) { 60 | m_LastMouseX = (float)x; 61 | m_LastMouseY = (float)y; 62 | m_FirstMouse = false; 63 | return; 64 | } 65 | 66 | float offset_x = ((float)x - m_LastMouseX); 67 | float offset_y = (m_LastMouseY - (float)y); 68 | 69 | m_LastMouseX = (float)x; 70 | m_LastMouseY = (float)y; 71 | 72 | for (auto&& cb : m_MouseChangeCallbacks) { 73 | cb(offset_x, offset_y); 74 | } 75 | } 76 | 77 | void GameWindow::OnMouseButton(int button, int action, int mods) { 78 | if (!m_UpdateMouse) return; 79 | 80 | for (auto&& cb : m_MouseButtonCallbacks) { 81 | cb(button, action, mods); 82 | } 83 | } 84 | 85 | void GameWindow::OnMouseScroll(double offset_x, double offset_y) { 86 | if (!m_UpdateMouse) return; 87 | 88 | for (auto&& cb : m_MouseScrollCallbacks) { 89 | cb(offset_x, offset_y); 90 | } 91 | } 92 | 93 | } // ns terra 94 | -------------------------------------------------------------------------------- /terracotta/Camera.cpp: -------------------------------------------------------------------------------- 1 | #include "Camera.h" 2 | #include 3 | #include 4 | 5 | namespace terra { 6 | 7 | Camera::Camera(glm::vec3 position, float fov, float ratio, float near, float far, glm::vec3 worldUp, float yaw, float pitch) 8 | : m_Position(position), 9 | m_WorldUp(worldUp), 10 | m_Yaw(yaw), 11 | m_Pitch(pitch), 12 | m_Near(near), 13 | m_Far(far), 14 | m_Fov(fov), 15 | m_AspectRatio(ratio), 16 | m_MovementSpeed(5.0f), 17 | m_MouseSensitivity(0.005f), 18 | m_Zoom(45.0f) 19 | { 20 | UpdateVectors(); 21 | } 22 | 23 | math::volumes::Frustum Camera::GetFrustum() const { 24 | return math::volumes::Frustum(m_Position, m_Front, m_Near, m_Far, m_Fov, m_AspectRatio, m_Up, m_Right); 25 | } 26 | 27 | void Camera::ProcessMovement(CameraMovement direction, float dt) { 28 | float speed = m_MovementSpeed * dt; 29 | 30 | if (direction == CameraMovement::Forward) { 31 | m_Position += m_Front * speed; 32 | } else if (direction == CameraMovement::Backward) { 33 | m_Position -= m_Front * speed; 34 | } else if (direction == CameraMovement::Left) { 35 | m_Position -= m_Right * speed; 36 | } else if (direction == CameraMovement::Right) { 37 | m_Position += m_Right * speed; 38 | } else if (direction == CameraMovement::Raise) { 39 | m_Position += m_WorldUp * speed; 40 | } else if (direction == CameraMovement::Lower) { 41 | m_Position -= m_WorldUp * speed; 42 | } 43 | } 44 | 45 | void Camera::ProcessRotation(float xoffset, float yoffset) { 46 | m_Yaw += xoffset * m_MouseSensitivity; 47 | m_Pitch += yoffset * m_MouseSensitivity; 48 | 49 | const float kMaxPitch = (float)glm::radians(89.0); 50 | 51 | m_Pitch = std::min(std::max(m_Pitch, -kMaxPitch), kMaxPitch); 52 | 53 | UpdateVectors(); 54 | } 55 | 56 | void Camera::ProcessZoom(float yoffset) { 57 | const float kMinZoom = 1.0f; 58 | const float kMaxZoom = 60.0f; 59 | 60 | if (m_Zoom >= kMinZoom && m_Zoom <= kMaxZoom) { 61 | m_Zoom -= yoffset; 62 | } 63 | 64 | m_Zoom = std::min(std::max(m_Zoom, kMinZoom), kMaxZoom); 65 | } 66 | 67 | glm::mat4 Camera::GetViewMatrix() const { 68 | return glm::lookAt(m_Position, m_Position + m_Front, m_Up); 69 | } 70 | 71 | glm::mat4 Camera::GetPerspectiveMatrix() const { 72 | return glm::perspective(m_Fov, m_AspectRatio, m_Near, m_Far); 73 | } 74 | 75 | void Camera::UpdateVectors() { 76 | m_Front.x = std::cos(m_Yaw) * std::cos(m_Pitch); 77 | m_Front.y = std::sin(m_Pitch); 78 | m_Front.z = std::sin(m_Yaw) * std::cos(m_Pitch); 79 | m_Front = glm::normalize(m_Front); 80 | 81 | m_Right = glm::normalize(glm::cross(m_Front, m_WorldUp)); 82 | m_Up = glm::normalize(glm::cross(m_Right, m_Front)); 83 | } 84 | 85 | } // ns terra 86 | -------------------------------------------------------------------------------- /terracotta/assets/AssetCache.cpp: -------------------------------------------------------------------------------- 1 | #include "AssetCache.h" 2 | #include "zip/ZipArchive.h" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "stb_image.h" 11 | 12 | namespace terra { 13 | namespace assets { 14 | 15 | void AssetCache::SetMaxBlockId(std::size_t id) { 16 | m_BlockStates.resize(id + 1); 17 | m_VariantCache.resize(id + 1); 18 | 19 | std::fill(m_VariantCache.begin(), m_VariantCache.end(), nullptr); 20 | } 21 | 22 | terra::block::BlockState* AssetCache::GetBlockState(u32 block_id) const { 23 | if (block_id >= m_BlockStates.size()) return nullptr; 24 | 25 | return m_BlockStates[block_id].get(); 26 | } 27 | 28 | block::BlockModel* AssetCache::GetBlockModel(const std::string& path) { 29 | auto iter = m_BlockModels.find(path); 30 | 31 | if (iter == m_BlockModels.end()) return nullptr; 32 | 33 | return iter->second.get(); 34 | } 35 | 36 | std::vector AssetCache::GetBlockModels(const std::string& find) { 37 | std::vector result; 38 | 39 | for (auto& kv : m_BlockModels) { 40 | if (kv.first.find(find) != std::string::npos) { 41 | result.push_back(kv.second.get()); 42 | } 43 | } 44 | 45 | return result; 46 | } 47 | 48 | void AssetCache::AddBlockState(std::unique_ptr state) { 49 | m_BlockStates[state->GetId()] = std::move(state); 50 | } 51 | 52 | TextureHandle AssetCache::AddTexture(const std::string& path, const std::string& data) { 53 | return m_TextureArray.Append(path, data); 54 | } 55 | 56 | void AssetCache::AddBlockModel(const std::string& path, std::unique_ptr model) { 57 | m_BlockModels[path] = std::move(model); 58 | } 59 | 60 | block::BlockVariant* AssetCache::GetVariantFromProperties(const std::string& block_name, const std::string& properties) { 61 | auto block_iter = m_BlockVariants.find(block_name); 62 | 63 | if (block_iter == m_BlockVariants.end()) return nullptr; 64 | 65 | for (auto&& variant : block_iter->second) { 66 | if (properties.find(variant->GetProperties()) != std::string::npos) { 67 | return variant.get(); 68 | } 69 | } 70 | 71 | return nullptr; 72 | } 73 | 74 | void AssetCache::AddVariantModel(std::unique_ptr variant) { 75 | m_BlockVariants[variant->GetBlock()->GetName()].push_back(std::move(variant)); 76 | } 77 | 78 | block::BlockVariant* AssetCache::GetVariant(mc::block::BlockPtr block) { 79 | block::BlockVariant* variant = m_VariantCache[block->GetType()]; 80 | 81 | if (variant == nullptr) { 82 | variant = GetVariantFromProperties(block->GetName(), GetBlockState(block->GetType())->GetVariant()); 83 | 84 | m_VariantCache[block->GetType()] = variant; 85 | } 86 | 87 | return variant; 88 | } 89 | 90 | } // ns assets 91 | } // ns terra 92 | -------------------------------------------------------------------------------- /terracotta/World.h: -------------------------------------------------------------------------------- 1 | #ifndef TERRACOTTA_WORLD_H_ 2 | #define TERRACOTTA_WORLD_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "Chunk.h" 9 | 10 | #include 11 | 12 | namespace terra { 13 | 14 | using ChunkCoord = std::pair; 15 | 16 | } // ns terra 17 | 18 | namespace std { 19 | template <> struct hash { 20 | std::size_t operator()(const terra::ChunkCoord& s) const noexcept { 21 | std::size_t seed = 3; 22 | 23 | seed ^= s.first + 0x9e3779b9 + (seed << 6) + (seed >> 2); 24 | seed ^= s.second + 0x9e3779b9 + (seed << 6) + (seed >> 2); 25 | 26 | return seed; 27 | } 28 | }; 29 | } // ns std 30 | 31 | namespace terra { 32 | 33 | class WorldListener { 34 | public: 35 | // yIndex is the chunk section index of the column, 0 means bottom chunk, 15 means top 36 | virtual void OnChunkLoad(ChunkPtr chunk, const ChunkColumnMetadata& meta, u16 yIndex) { } 37 | virtual void OnChunkUnload(ChunkColumnPtr chunk) { } 38 | virtual void OnBlockChange(mc::Vector3i position, mc::block::BlockPtr newBlock, mc::block::BlockPtr oldBlock) { } 39 | }; 40 | 41 | class World : public mc::protocol::packets::PacketHandler, public mc::util::ObserverSubject { 42 | public: 43 | World(mc::protocol::packets::PacketDispatcher* dispatcher); 44 | ~World(); 45 | 46 | World(const World& rhs) = delete; 47 | World& operator=(const World& rhs) = delete; 48 | World(World&& rhs) = delete; 49 | World& operator=(World&& rhs) = delete; 50 | 51 | void HandlePacket(mc::protocol::packets::in::ChunkDataPacket* packet); 52 | void HandlePacket(mc::protocol::packets::in::UnloadChunkPacket* packet); 53 | void HandlePacket(mc::protocol::packets::in::MultiBlockChangePacket* packet); 54 | void HandlePacket(mc::protocol::packets::in::BlockChangePacket* packet); 55 | void HandlePacket(mc::protocol::packets::in::ExplosionPacket* packet); 56 | void HandlePacket(mc::protocol::packets::in::UpdateBlockEntityPacket* packet); 57 | void HandlePacket(mc::protocol::packets::in::RespawnPacket* packet); 58 | 59 | /** 60 | * Pos can be any world position inside of the chunk 61 | */ 62 | ChunkColumnPtr GetChunk(const mc::Vector3i& pos) const; 63 | 64 | mc::block::BlockPtr GetBlock(const mc::Vector3i& pos) const; 65 | 66 | mc::block::BlockEntityPtr GetBlockEntity(mc::Vector3i pos) const; 67 | // Gets all of the known block entities in loaded chunks 68 | std::vector GetBlockEntities() const; 69 | 70 | const std::unordered_map::const_iterator begin() const { return m_Chunks.begin(); } 71 | const std::unordered_map::const_iterator end() const { return m_Chunks.end(); } 72 | 73 | private: 74 | std::unordered_map m_Chunks; 75 | 76 | bool SetBlock(mc::Vector3i position, u32 blockData); 77 | }; 78 | 79 | } // ns terra 80 | 81 | #endif 82 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.1) 2 | 3 | project(Terracotta) 4 | 5 | set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/modules/") 6 | set(CMAKE_BUILD_TYPE Release) 7 | set(CMAKE_CXX_STANDARD 14) 8 | 9 | include(GNUInstallDirs) 10 | 11 | add_executable(terracotta 12 | terracotta/assets/AssetCache.cpp 13 | terracotta/assets/AssetCache.h 14 | terracotta/assets/AssetLoader.cpp 15 | terracotta/assets/AssetLoader.h 16 | terracotta/assets/stb_image.h 17 | terracotta/assets/TextureArray.cpp 18 | terracotta/assets/TextureArray.h 19 | terracotta/assets/zip/miniz.cpp 20 | terracotta/assets/zip/miniz.h 21 | terracotta/assets/zip/ZipArchive.cpp 22 | terracotta/assets/zip/ZipArchive.h 23 | terracotta/block/BlockElement.h 24 | terracotta/block/BlockFace.cpp 25 | terracotta/block/BlockFace.h 26 | terracotta/block/BlockModel.cpp 27 | terracotta/block/BlockModel.h 28 | terracotta/block/BlockState.cpp 29 | terracotta/block/BlockState.h 30 | terracotta/block/BlockVariant.h 31 | terracotta/Camera.cpp 32 | terracotta/Camera.h 33 | terracotta/ChatWindow.cpp 34 | terracotta/ChatWindow.h 35 | terracotta/Chunk.cpp 36 | terracotta/Chunk.h 37 | terracotta/Collision.h 38 | terracotta/Collision.cpp 39 | terracotta/Game.cpp 40 | terracotta/Game.h 41 | terracotta/GameWindow.cpp 42 | terracotta/GameWindow.h 43 | terracotta/lib/imgui/imconfig.h 44 | terracotta/lib/imgui/imgui.cpp 45 | terracotta/lib/imgui/imgui.h 46 | terracotta/lib/imgui/imgui_draw.cpp 47 | terracotta/lib/imgui/imgui_impl_glfw.cpp 48 | terracotta/lib/imgui/imgui_impl_glfw.h 49 | terracotta/lib/imgui/imgui_impl_opengl3.cpp 50 | terracotta/lib/imgui/imgui_impl_opengl3.h 51 | terracotta/lib/imgui/imgui_internal.h 52 | terracotta/lib/imgui/imgui_widgets.cpp 53 | terracotta/lib/imgui/imstb_rectpack.h 54 | terracotta/lib/imgui/imstb_textedit.h 55 | terracotta/lib/imgui/imstb_truetype.h 56 | terracotta/main.cpp 57 | terracotta/math/Plane.cpp 58 | terracotta/math/Plane.h 59 | terracotta/math/TypeUtil.h 60 | terracotta/math/volumes/Frustum.cpp 61 | terracotta/math/volumes/Frustum.h 62 | terracotta/PriorityQueue.h 63 | terracotta/Transform.h 64 | terracotta/render/ChunkMesh.cpp 65 | terracotta/render/ChunkMesh.h 66 | terracotta/render/ChunkMeshGenerator.cpp 67 | terracotta/render/ChunkMeshGenerator.h 68 | terracotta/render/Shader.cpp 69 | terracotta/render/Shader.h 70 | terracotta/World.cpp 71 | terracotta/World.h 72 | ) 73 | 74 | add_definitions(-DGLEW_STATIC -DIMGUI_IMPL_OPENGL_LOADER_GLEW) 75 | 76 | find_package(glfw3 REQUIRED) 77 | find_package(GLEW REQUIRED) 78 | find_package(GLM REQUIRED) 79 | find_package(mclib REQUIRED) 80 | 81 | if (WIN32) 82 | set(GL_LIBRARY opengl32) 83 | else() 84 | set(GL_LIBRARY GL) 85 | set(OTHER_LIBS pthread glfw) 86 | endif() 87 | 88 | target_include_directories(terracotta PRIVATE ${MCLIB_INCLUDE_DIR} ${GLFW3_INCLUDE_DIR} ${GLM_INCLUDE_DIRS} ${GLEW_INCLUDE_DIRS}) 89 | target_link_libraries(terracotta PRIVATE ${GL_LIBRARY} ${MCLIB_LIBRARY} ${GLFW3_LIBRARY} ${GLEW_LIBRARY} ${OTHER_LIBS}) 90 | -------------------------------------------------------------------------------- /terracotta/Chunk.cpp: -------------------------------------------------------------------------------- 1 | #include "Chunk.h" 2 | 3 | #include 4 | 5 | namespace terra { 6 | 7 | Chunk::Chunk() { 8 | auto block = mc::block::BlockRegistry::GetInstance()->GetBlock(0); 9 | 10 | for (int y = 0; y < 16; ++y) { 11 | for (int z = 0; z < 16; ++z) { 12 | for (int x = 0; x < 16; ++x) { 13 | m_Blocks[y * 16 * 16 + z * 16 + x] = block; 14 | } 15 | } 16 | } 17 | } 18 | 19 | Chunk::Chunk(const mc::world::Chunk& other) { 20 | static const mc::block::BlockPtr air = mc::block::BlockRegistry::GetInstance()->GetBlock(0); 21 | 22 | for (int y = 0; y < 16; ++y) { 23 | for (int z = 0; z < 16; ++z) { 24 | for (int x = 0; x < 16; ++x) { 25 | mc::block::BlockPtr block = other.GetBlock(mc::Vector3i(x, y, z)); 26 | 27 | if (block == nullptr) { 28 | block = air; 29 | } 30 | 31 | m_Blocks[y * 16 * 16 + z * 16 + x] = block; 32 | } 33 | } 34 | } 35 | } 36 | 37 | mc::block::BlockPtr Chunk::GetBlock(const mc::Vector3i& chunkPosition) const { 38 | std::size_t index = chunkPosition.y * 16 * 16 + chunkPosition.z * 16 + chunkPosition.x; 39 | 40 | if (chunkPosition.x < 0 || chunkPosition.x > 15 || chunkPosition.y < 0 || chunkPosition.y > 15 || chunkPosition.z < 0 || chunkPosition.z > 15) { 41 | return nullptr; 42 | } 43 | 44 | return m_Blocks[index]; 45 | } 46 | 47 | void Chunk::SetBlock(mc::Vector3i chunkPosition, mc::block::BlockPtr block) { 48 | m_Blocks[chunkPosition.y * 16 * 16 + chunkPosition.z * 16 + chunkPosition.x] = block; 49 | } 50 | 51 | ChunkColumn::ChunkColumn(const mc::world::ChunkColumn& rhs) 52 | : m_Metadata(terra::ChunkColumnMetadata(rhs.GetMetadata())) 53 | { 54 | for (int i = 0; i < ChunksPerColumn; ++i) { 55 | m_Chunks[i] = nullptr; 56 | 57 | if (rhs[i] != nullptr) { 58 | m_Chunks[i] = std::make_shared(*rhs[i]); 59 | } 60 | } 61 | } 62 | 63 | ChunkColumn::ChunkColumn(ChunkColumnMetadata metadata) 64 | : m_Metadata(metadata) 65 | { 66 | for (std::size_t i = 0; i < m_Chunks.size(); ++i) 67 | m_Chunks[i] = nullptr; 68 | } 69 | 70 | mc::block::BlockPtr ChunkColumn::GetBlock(const mc::Vector3i& position) { 71 | s32 chunkIndex = (s64)std::floor(position.y / 16.0); 72 | mc::Vector3i relativePosition(position.x, position.y % 16, position.z); 73 | 74 | static const mc::block::BlockPtr air = mc::block::BlockRegistry::GetInstance()->GetBlock(0); 75 | 76 | if (chunkIndex < 0 || chunkIndex > 15 || !m_Chunks[chunkIndex]) return air; 77 | 78 | return m_Chunks[chunkIndex]->GetBlock(relativePosition); 79 | } 80 | 81 | mc::block::BlockEntityPtr ChunkColumn::GetBlockEntity(mc::Vector3i worldPos) { 82 | auto iter = m_BlockEntities.find(worldPos); 83 | if (iter == m_BlockEntities.end()) return nullptr; 84 | return iter->second; 85 | } 86 | 87 | std::vector ChunkColumn::GetBlockEntities() { 88 | std::vector blockEntities; 89 | 90 | for (auto iter = m_BlockEntities.begin(); iter != m_BlockEntities.end(); ++iter) 91 | blockEntities.push_back(iter->second); 92 | 93 | return blockEntities; 94 | } 95 | 96 | } // ns terra 97 | -------------------------------------------------------------------------------- /terracotta/render/Shader.cpp: -------------------------------------------------------------------------------- 1 | #include "Shader.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | bool CreateShader(GLenum type, const char* source, GLuint* shaderOut) { 9 | GLuint shader = glCreateShader(type); 10 | 11 | glShaderSource(shader, 1, &source, nullptr); 12 | glCompileShader(shader); 13 | 14 | GLint success; 15 | GLchar info_log[512]; 16 | glGetShaderiv(shader, GL_COMPILE_STATUS, &success); 17 | if (!success) { 18 | glGetShaderInfoLog(shader, sizeof(info_log), nullptr, info_log); 19 | std::cout << "Shader error: " << std::endl << info_log << std::endl; 20 | return false; 21 | } 22 | 23 | *shaderOut = shader; 24 | return true; 25 | } 26 | 27 | bool CreateProgram(GLuint vertexShader, GLuint fragmentShader, GLuint* programOut) { 28 | GLuint program = glCreateProgram(); 29 | 30 | glAttachShader(program, vertexShader); 31 | glAttachShader(program, fragmentShader); 32 | glLinkProgram(program); 33 | 34 | GLint success; 35 | GLchar info_log[512]; 36 | glGetProgramiv(program, GL_LINK_STATUS, &success); 37 | 38 | glDeleteShader(vertexShader); 39 | glDeleteShader(fragmentShader); 40 | 41 | if (!success) { 42 | glGetProgramInfoLog(program, sizeof(info_log), nullptr, info_log); 43 | std::cout << "Link error: " << std::endl << info_log << std::endl; 44 | return false; 45 | } 46 | 47 | *programOut = program; 48 | return true; 49 | } 50 | 51 | namespace terra { 52 | namespace render { 53 | 54 | Shader::Shader() { 55 | 56 | } 57 | 58 | bool Shader::Initialize(const char* vPath, const char* fPath) { 59 | std::string vertex_source, fragment_source; 60 | std::ifstream vertex_file, fragment_file; 61 | 62 | vertex_file.exceptions(std::ifstream::badbit); 63 | fragment_file.exceptions(std::ifstream::badbit); 64 | try { 65 | vertex_file.open(vPath); 66 | fragment_file.open(fPath); 67 | 68 | std::stringstream vertex_stream, fragment_stream; 69 | vertex_stream << vertex_file.rdbuf(); 70 | fragment_stream << fragment_file.rdbuf(); 71 | 72 | vertex_file.close(); 73 | fragment_file.close(); 74 | 75 | vertex_source = vertex_stream.str(); 76 | fragment_source = fragment_stream.str(); 77 | } catch (std::ifstream::failure& e) { 78 | std::cerr << "Read error: " << e.what() << std::endl; 79 | return false; 80 | } 81 | 82 | GLuint vertex_shader, fragment_shader; 83 | if (!CreateShader(GL_VERTEX_SHADER, vertex_source.c_str(), &vertex_shader)) { 84 | return false; 85 | } 86 | 87 | if (!CreateShader(GL_FRAGMENT_SHADER, fragment_source.c_str(), &fragment_shader)) { 88 | glDeleteShader(vertex_shader); 89 | return false; 90 | } 91 | 92 | return CreateProgram(vertex_shader, fragment_shader, &m_Program); 93 | } 94 | 95 | GLuint Shader::GetUniform(const char* name) { 96 | return glGetUniformLocation(m_Program, name); 97 | } 98 | 99 | void Shader::Use() { 100 | glUseProgram(m_Program); 101 | } 102 | 103 | void Shader::Stop() { 104 | glUseProgram(0); 105 | } 106 | 107 | } // ns render 108 | } // ns terra 109 | -------------------------------------------------------------------------------- /terracotta/math/volumes/Frustum.cpp: -------------------------------------------------------------------------------- 1 | #include "Frustum.h" 2 | #include "../TypeUtil.h" 3 | 4 | namespace terra { 5 | namespace math { 6 | namespace volumes { 7 | 8 | Frustum::Frustum(glm::vec3 position, glm::vec3 forward, float near, float far, float fov, float ratio, glm::vec3 up, glm::vec3 right) 9 | : m_Position(position), 10 | m_Forward(forward), 11 | m_Near(near), 12 | m_Far(far) 13 | { 14 | m_NearHeight = 2 * std::tan(fov / 2.0f) * m_Near; 15 | m_NearWidth = m_NearHeight * ratio; 16 | 17 | m_FarHeight = 2 * std::tan(fov / 2.0f) * m_Far; 18 | m_FarWidth = m_FarHeight * ratio; 19 | 20 | glm::vec3 nc = position + forward * near; 21 | glm::vec3 fc = position + forward * far; 22 | 23 | float hnh = m_NearHeight / 2.0f; 24 | float hnw = m_NearWidth / 2.0f; 25 | float hfh = m_FarHeight / 2.0f; 26 | float hfw = m_FarWidth / 2.0f; 27 | 28 | glm::vec3 ntl = nc + up * hnh - right * hnw; 29 | glm::vec3 ntr = nc + up * hnh + right * hnw; 30 | glm::vec3 nbl = nc - up * hnh - right * hnw; 31 | glm::vec3 nbr = nc - up * hnh + right * hnw; 32 | 33 | glm::vec3 ftl = fc + up * hfh - right * hfw; 34 | glm::vec3 ftr = fc + up * hfh + right * hfw; 35 | glm::vec3 fbl = fc - up * hfh - right * hfw; 36 | glm::vec3 fbr = fc - up * hfh + right * hfw; 37 | 38 | m_Planes[0] = Plane(ntr, ntl, ftl); 39 | m_Planes[1] = Plane(nbl, nbr, fbr); 40 | m_Planes[2] = Plane(ntl, nbl, fbl); 41 | m_Planes[3] = Plane(nbr, ntr, fbr); 42 | m_Planes[4] = Plane(ntl, ntr, nbr); 43 | m_Planes[5] = Plane(ftr, ftl, fbl); 44 | } 45 | 46 | bool Frustum::Intersects(glm::vec3 v) const { 47 | for (int i = 0; i < 6; ++i) { 48 | if (m_Planes[i].PointDistance(v) < 0) 49 | return false; 50 | } 51 | 52 | return true; 53 | } 54 | 55 | bool Frustum::Intersects(mc::Vector3i v) const { 56 | for (int i = 0; i < 6; ++i) { 57 | if (m_Planes[i].PointDistance(v) < 0) 58 | return false; 59 | } 60 | 61 | return true; 62 | } 63 | 64 | bool Frustum::Intersects(mc::Vector3d v) const { 65 | for (int i = 0; i < 6; ++i) { 66 | if (m_Planes[i].PointDistance(v) < 0) 67 | return false; 68 | } 69 | 70 | return true; 71 | } 72 | 73 | glm::vec3 GetVertex(const mc::AABB& aabb, int index) { 74 | mc::Vector3d v; 75 | mc::Vector3d diff = aabb.max - aabb.min; 76 | 77 | if (index == 0) { 78 | v = aabb.min; 79 | } else if (index == 1) { 80 | v = aabb.min + mc::Vector3d(diff.x, 0, 0); 81 | } else if (index == 2) { 82 | v = aabb.min + mc::Vector3d(diff.x, diff.y, 0); 83 | } else if (index == 3) { 84 | v = aabb.min + mc::Vector3d(0, diff.y, 0); 85 | } else if (index == 4) { 86 | v = aabb.min + mc::Vector3d(0, diff.y, diff.z); 87 | } else if (index == 5) { 88 | v = aabb.min + mc::Vector3d(0, 0, diff.z); 89 | } else if (index == 6) { 90 | v = aabb.min + mc::Vector3d(diff.x, 0, diff.z); 91 | } else if (index == 7) { 92 | v = aabb.max; 93 | } 94 | 95 | return VecToGLM(v); 96 | } 97 | 98 | bool Frustum::Intersects(const mc::AABB& aabb) const { 99 | for (int i = 0; i < 6; ++i) { 100 | int out = 0; 101 | int in = 0; 102 | 103 | for (int k = 0; k < 8 && (in == 0 || out == 0); ++k) { 104 | glm::vec3 v = GetVertex(aabb, k); 105 | 106 | if (m_Planes[i].PointDistance(v) < 0) { 107 | ++out; 108 | } else { 109 | ++in; 110 | } 111 | } 112 | 113 | if (in == 0) { 114 | return false; 115 | } 116 | } 117 | 118 | return true; 119 | } 120 | 121 | } // ns volumes 122 | } // ns math 123 | } // ns terra 124 | -------------------------------------------------------------------------------- /terracotta/Chunk.h: -------------------------------------------------------------------------------- 1 | #ifndef TERRACOTTA_CHUNK_H_ 2 | #define TERRACOTTA_CHUNK_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | namespace terra { 15 | 16 | struct ChunkColumnMetadata { 17 | s32 x; 18 | s32 z; 19 | u16 sectionmask; 20 | bool continuous; 21 | bool skylight; 22 | 23 | ChunkColumnMetadata(const mc::world::ChunkColumnMetadata& metadata) 24 | : x(metadata.x), 25 | z(metadata.z), 26 | sectionmask(metadata.sectionmask), 27 | continuous(metadata.continuous), 28 | skylight(metadata.skylight) 29 | { 30 | 31 | } 32 | }; 33 | 34 | /** 35 | * A 16x16x16 area. A ChunkColumn is made up of 16 of these 36 | */ 37 | class Chunk { 38 | private: 39 | std::array m_Blocks; 40 | 41 | public: 42 | Chunk(); 43 | 44 | Chunk(const Chunk& other) = delete; 45 | Chunk& operator=(const Chunk& other) = delete; 46 | 47 | Chunk(const mc::world::Chunk& other); 48 | 49 | /** 50 | * Position is relative to this chunk position 51 | */ 52 | mc::block::BlockPtr GetBlock(const mc::Vector3i& chunkPosition) const; 53 | 54 | /** 55 | * Position is relative to this chunk position 56 | */ 57 | void SetBlock(mc::Vector3i chunkPosition, mc::block::BlockPtr block); 58 | }; 59 | 60 | typedef std::shared_ptr ChunkPtr; 61 | 62 | /** 63 | * Stores a 16x256x16 area. Uses chunks (16x16x16) to store the data vertically. 64 | * A null chunk is fully air. 65 | */ 66 | class ChunkColumn { 67 | public: 68 | enum { ChunksPerColumn = 16 }; 69 | 70 | typedef std::array::iterator iterator; 71 | typedef std::array::reference reference; 72 | typedef std::array::const_iterator const_iterator; 73 | typedef std::array::const_reference const_reference; 74 | 75 | private: 76 | std::array m_Chunks; 77 | ChunkColumnMetadata m_Metadata; 78 | std::map m_BlockEntities; 79 | 80 | public: 81 | ChunkColumn(ChunkColumnMetadata metadata); 82 | 83 | ChunkColumn(const ChunkColumn& rhs) = default; 84 | ChunkColumn& operator=(const ChunkColumn& rhs) = default; 85 | ChunkColumn(ChunkColumn&& rhs) = default; 86 | ChunkColumn& operator=(ChunkColumn&& rhs) = default; 87 | 88 | ChunkColumn(const mc::world::ChunkColumn& rhs); 89 | 90 | iterator begin() { 91 | return m_Chunks.begin(); 92 | } 93 | 94 | iterator end() { 95 | return m_Chunks.end(); 96 | } 97 | 98 | reference operator[](std::size_t index) { 99 | return m_Chunks[index]; 100 | } 101 | 102 | const_iterator begin() const { 103 | return m_Chunks.begin(); 104 | } 105 | 106 | const_iterator end() const { 107 | return m_Chunks.end(); 108 | } 109 | 110 | const_reference operator[](std::size_t index) const { 111 | return m_Chunks[index]; 112 | } 113 | 114 | void AddBlockEntity(mc::block::BlockEntityPtr blockEntity) { 115 | m_BlockEntities.insert(std::make_pair(blockEntity->GetPosition(), blockEntity)); 116 | } 117 | 118 | void RemoveBlockEntity(mc::Vector3i pos) { 119 | m_BlockEntities.erase(pos); 120 | } 121 | 122 | /** 123 | * Position is relative to this ChunkColumn position. 124 | */ 125 | mc::block::BlockPtr GetBlock(const mc::Vector3i& position); 126 | const ChunkColumnMetadata& GetMetadata() const { return m_Metadata; } 127 | 128 | mc::block::BlockEntityPtr GetBlockEntity(mc::Vector3i worldPos); 129 | std::vector GetBlockEntities(); 130 | }; 131 | 132 | typedef std::shared_ptr ChunkColumnPtr; 133 | 134 | } // ns terra 135 | 136 | #endif 137 | -------------------------------------------------------------------------------- /terracotta/render/ChunkMeshGenerator.h: -------------------------------------------------------------------------------- 1 | #ifndef TERRACOTTA_RENDER_CHUNKMESHGENERATOR_H_ 2 | #define TERRACOTTA_RENDER_CHUNKMESHGENERATOR_H_ 3 | 4 | #include "ChunkMesh.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "../World.h" 12 | #include "../PriorityQueue.h" 13 | #include "../block/BlockFace.h" 14 | #include "../block/BlockVariant.h" 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | namespace std { 21 | template <> struct hash { 22 | std::size_t operator()(const mc::Vector3i& s) const noexcept { 23 | std::size_t seed = 3; 24 | for (int i = 0; i < 3; ++i) { 25 | seed ^= s[i] + 0x9e3779b9 + (seed << 6) + (seed >> 2); 26 | } 27 | return seed; 28 | } 29 | }; 30 | } 31 | 32 | namespace terra { 33 | namespace block { 34 | 35 | class BlockModel; 36 | 37 | } // ns block 38 | 39 | namespace render { 40 | 41 | struct Vertex { 42 | glm::vec3 position; 43 | glm::vec2 uv; 44 | u32 texture_index; 45 | glm::vec3 tint; 46 | unsigned char ambient_occlusion; 47 | 48 | Vertex(glm::vec3 pos, glm::vec2 uv, u32 tex_index, glm::vec3 tint, int ambient_occlusion) 49 | : position(pos), uv(uv), texture_index(tex_index), tint(tint), ambient_occlusion(static_cast(ambient_occlusion)) 50 | { 51 | } 52 | }; 53 | 54 | struct ChunkMeshBuildContext { 55 | // Store the chunk data and a border around the chunk 56 | mc::block::BlockPtr chunk_data[18 * 18 * 18]; 57 | mc::Vector3i world_position; 58 | 59 | mc::block::BlockPtr GetBlock(const mc::Vector3i& world_pos) { 60 | mc::Vector3i::value_type x = world_pos.x - world_position.x + 1; 61 | mc::Vector3i::value_type y = world_pos.y - world_position.y + 1; 62 | mc::Vector3i::value_type z = world_pos.z - world_position.z + 1; 63 | 64 | return chunk_data[y * 18 * 18 + z * 18 + x]; 65 | } 66 | }; 67 | 68 | class ChunkMeshGenerator : public terra::WorldListener { 69 | public: 70 | using iterator = std::unordered_map>::iterator; 71 | 72 | ChunkMeshGenerator(terra::World* world, const glm::vec3& camera_position); 73 | ~ChunkMeshGenerator(); 74 | 75 | void OnBlockChange(mc::Vector3i position, mc::block::BlockPtr newBlock, mc::block::BlockPtr oldBlock) override; 76 | void OnChunkLoad(terra::ChunkPtr chunk, const terra::ChunkColumnMetadata& meta, u16 index_y) override; 77 | void OnChunkUnload(terra::ChunkColumnPtr chunk) override; 78 | 79 | void GenerateMesh(s64 chunk_x, s64 chunk_y, s64 chunk_z); 80 | void GenerateMesh(ChunkMeshBuildContext& context); 81 | void DestroyChunk(s64 chunk_x, s64 chunk_y, s64 chunk_z); 82 | 83 | iterator begin() { return m_ChunkMeshes.begin(); } 84 | iterator end() { return m_ChunkMeshes.end(); } 85 | 86 | void ProcessChunks(); 87 | 88 | private: 89 | struct ChunkMeshBuildComparator { 90 | const glm::vec3& position; 91 | 92 | ChunkMeshBuildComparator(const glm::vec3& position) : position(position) { } 93 | 94 | bool operator()(const std::shared_ptr& first_ctx, const std::shared_ptr& second_ctx) { 95 | mc::Vector3i first = first_ctx->world_position; 96 | mc::Vector3i second = second_ctx->world_position; 97 | 98 | glm::vec3 f(first.x, 0, first.z); 99 | glm::vec3 s(second.x, 0, second.z); 100 | 101 | glm::vec3 a = f - position; 102 | glm::vec3 b = s - position; 103 | 104 | return glm::dot(a, a) > glm::dot(b, b); 105 | } 106 | }; 107 | 108 | struct VertexPush { 109 | mc::Vector3i pos; 110 | std::unique_ptr> vertices; 111 | 112 | VertexPush(const mc::Vector3i& pos, std::unique_ptr> vertices) : pos(pos), vertices(std::move(vertices)) { } 113 | }; 114 | 115 | int GetAmbientOcclusion(ChunkMeshBuildContext& context, const mc::Vector3i& side1, const mc::Vector3i& side2, const mc::Vector3i& corner); 116 | bool IsOccluding(terra::block::BlockVariant* from_variant, terra::block::BlockFace face, mc::block::BlockPtr test_block); 117 | void WorkerUpdate(); 118 | void EnqueueBuildWork(long chunk_x, int chunk_y, long chunk_z); 119 | 120 | std::mutex m_QueueMutex; 121 | PriorityQueue, ChunkMeshBuildComparator> m_ChunkBuildQueue; 122 | std::deque m_ChunkPushQueue; 123 | std::condition_variable m_BuildCV; 124 | 125 | terra::World* m_World; 126 | 127 | std::mutex m_PushMutex; 128 | std::vector> m_VertexPushes; 129 | 130 | std::unordered_map> m_ChunkMeshes; 131 | 132 | bool m_Working; 133 | std::vector m_Workers; 134 | }; 135 | 136 | } // ns render 137 | } // ns terra 138 | 139 | #endif 140 | -------------------------------------------------------------------------------- /terracotta/lib/imgui/imconfig.h: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | // COMPILE-TIME OPTIONS FOR DEAR IMGUI 3 | // Runtime options (clipboard callbacks, enabling various features, etc.) can generally be set via the ImGuiIO structure. 4 | // You can use ImGui::SetAllocatorFunctions() before calling ImGui::CreateContext() to rewire memory allocation functions. 5 | //----------------------------------------------------------------------------- 6 | // A) You may edit imconfig.h (and not overwrite it when updating imgui, or maintain a patch/branch with your modifications to imconfig.h) 7 | // B) or add configuration directives in your own file and compile with #define IMGUI_USER_CONFIG "myfilename.h" 8 | // If you do so you need to make sure that configuration settings are defined consistently _everywhere_ dear imgui is used, which include 9 | // the imgui*.cpp files but also _any_ of your code that uses imgui. This is because some compile-time options have an affect on data structures. 10 | // Defining those options in imconfig.h will ensure every compilation unit gets to see the same data structure layouts. 11 | // Call IMGUI_CHECKVERSION() from your .cpp files to verify that the data structures your files are using are matching the ones imgui.cpp is using. 12 | //----------------------------------------------------------------------------- 13 | 14 | #pragma once 15 | 16 | //---- Define assertion handler. Defaults to calling assert(). 17 | //#define IM_ASSERT(_EXPR) MyAssert(_EXPR) 18 | //#define IM_ASSERT(_EXPR) ((void)(_EXPR)) // Disable asserts 19 | 20 | //---- Define attributes of all API symbols declarations, e.g. for DLL under Windows. 21 | //#define IMGUI_API __declspec( dllexport ) 22 | //#define IMGUI_API __declspec( dllimport ) 23 | 24 | //---- Don't define obsolete functions/enums names. Consider enabling from time to time after updating to avoid using soon-to-be obsolete function/names. 25 | //#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS 26 | 27 | //---- Don't implement demo windows functionality (ShowDemoWindow()/ShowStyleEditor()/ShowUserGuide() methods will be empty) 28 | //---- It is very strongly recommended to NOT disable the demo windows during development. Please read the comments in imgui_demo.cpp. 29 | //#define IMGUI_DISABLE_DEMO_WINDOWS 30 | 31 | //---- Don't implement some functions to reduce linkage requirements. 32 | //#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS // [Win32] Don't implement default clipboard handler. Won't use and link with OpenClipboard/GetClipboardData/CloseClipboard etc. 33 | //#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] Don't implement default IME handler. Won't use and link with ImmGetContext/ImmSetCompositionWindow. 34 | //#define IMGUI_DISABLE_WIN32_FUNCTIONS // [Win32] Won't use and link with any Win32 function. 35 | //#define IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS // Don't implement ImFormatString/ImFormatStringV so you can implement them yourself if you don't want to link with vsnprintf. 36 | //#define IMGUI_DISABLE_MATH_FUNCTIONS // Don't implement ImFabs/ImSqrt/ImPow/ImFmod/ImCos/ImSin/ImAcos/ImAtan2 wrapper so you can implement them yourself. Declare your prototypes in imconfig.h. 37 | //#define IMGUI_DISABLE_DEFAULT_ALLOCATORS // Don't implement default allocators calling malloc()/free() to avoid linking with them. You will need to call ImGui::SetAllocatorFunctions(). 38 | 39 | //---- Include imgui_user.h at the end of imgui.h as a convenience 40 | //#define IMGUI_INCLUDE_IMGUI_USER_H 41 | 42 | //---- Pack colors to BGRA8 instead of RGBA8 (to avoid converting from one to another) 43 | //#define IMGUI_USE_BGRA_PACKED_COLOR 44 | 45 | //---- Avoid multiple STB libraries implementations, or redefine path/filenames to prioritize another version 46 | // By default the embedded implementations are declared static and not available outside of imgui cpp files. 47 | //#define IMGUI_STB_TRUETYPE_FILENAME "my_folder/stb_truetype.h" 48 | //#define IMGUI_STB_RECT_PACK_FILENAME "my_folder/stb_rect_pack.h" 49 | //#define IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION 50 | //#define IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION 51 | 52 | //---- Define constructor and implicit cast operators to convert back<>forth between your math types and ImVec2/ImVec4. 53 | // This will be inlined as part of ImVec2 and ImVec4 class declarations. 54 | /* 55 | #define IM_VEC2_CLASS_EXTRA \ 56 | ImVec2(const MyVec2& f) { x = f.x; y = f.y; } \ 57 | operator MyVec2() const { return MyVec2(x,y); } 58 | 59 | #define IM_VEC4_CLASS_EXTRA \ 60 | ImVec4(const MyVec4& f) { x = f.x; y = f.y; z = f.z; w = f.w; } \ 61 | operator MyVec4() const { return MyVec4(x,y,z,w); } 62 | */ 63 | 64 | //---- Use 32-bit vertex indices (default is 16-bit) to allow meshes with more than 64K vertices. Render function needs to support it. 65 | //#define ImDrawIdx unsigned int 66 | 67 | //---- Tip: You can add extra functions within the ImGui:: namespace, here or in your own headers files. 68 | /* 69 | namespace ImGui 70 | { 71 | void MyFunction(const char* name, const MyMatrix44& v); 72 | } 73 | */ 74 | -------------------------------------------------------------------------------- /terracotta/Collision.cpp: -------------------------------------------------------------------------------- 1 | #include "Collision.h" 2 | #include "Transform.h" 3 | 4 | #include 5 | 6 | namespace terra { 7 | 8 | using mc::Vector3d; 9 | using mc::Vector3i; 10 | using mc::AABB; 11 | using mc::Ray; 12 | 13 | template 14 | inline T Sign(T val) { 15 | return std::signbit(val) ? static_cast(-1) : static_cast(1); 16 | } 17 | 18 | inline Vector3d BasisAxis(int basisIndex) { 19 | static const Vector3d axes[3] = { Vector3d(1, 0, 0), Vector3d(0, 1, 0), Vector3d(0, 0, 1) }; 20 | return axes[basisIndex]; 21 | } 22 | 23 | Vector3d GetClosestFaceNormal(const Vector3d& pos, AABB bounds) { 24 | Vector3d center = bounds.min + (bounds.max - bounds.min) / 2; 25 | Vector3d dim = bounds.max - bounds.min; 26 | Vector3d offset = pos - center; 27 | 28 | double minDist = std::numeric_limits::max(); 29 | Vector3d normal; 30 | 31 | for (int i = 0; i < 3; ++i) { 32 | double dist = dim[i] - std::abs(offset[i]); 33 | if (dist < minDist) { 34 | minDist = dist; 35 | normal = BasisAxis(i) * Sign(offset[i]); 36 | } 37 | } 38 | 39 | return normal; 40 | } 41 | 42 | 43 | bool CollisionDetector::DetectCollision(Vector3d from, Vector3d rayVector, Collision* collision) const { 44 | static const std::vector directions = { 45 | Vector3d(0, 0, 0), Vector3d(1, 0, 0), Vector3d(-1, 0, 0), Vector3d(0, 1, 0), Vector3d(0, -1, 0), Vector3d(0, 0, 1), Vector3d(0, 0, -1) 46 | }; 47 | 48 | Vector3d direction = Vector3Normalize(rayVector); 49 | double length = rayVector.Length(); 50 | 51 | Ray ray(from, direction); 52 | 53 | if (collision) 54 | * collision = Collision(); 55 | 56 | for (double i = 0; i < length; ++i) { 57 | Vector3d position = from + direction * i; 58 | 59 | // Look for collisions in any blocks surrounding the ray 60 | for (Vector3d checkDirection : directions) { 61 | Vector3d checkPos = position + checkDirection; 62 | mc::block::BlockPtr block = m_World->GetBlock(mc::ToVector3i(checkPos)); 63 | 64 | if (block && block->IsSolid()) { 65 | AABB bounds = block->GetBoundingBox(checkPos); 66 | double distance; 67 | 68 | if (bounds.Intersects(ray, &distance)) { 69 | if (distance < 0 || distance > length) continue; 70 | 71 | Vector3d collisionHit = from + direction * distance; 72 | Vector3d normal = GetClosestFaceNormal(collisionHit, bounds); 73 | 74 | if (collision) 75 | * collision = Collision(collisionHit, normal); 76 | 77 | return true; 78 | } 79 | } 80 | } 81 | } 82 | 83 | return false; 84 | } 85 | 86 | std::vector CollisionDetector::GetSurroundingLocations(AABB bounds) { 87 | std::vector locs; 88 | 89 | s32 radius = 2; 90 | for (s32 y = (s32)bounds.min.y - radius; y < (s32)bounds.max.y + radius; ++y) { 91 | for (s32 z = (s32)bounds.min.z - radius; z < (s32)bounds.max.z + radius; ++z) { 92 | for (s32 x = (s32)bounds.min.x - radius; x < (s32)bounds.max.x + radius; ++x) { 93 | locs.emplace_back(x, y, z); 94 | } 95 | } 96 | } 97 | 98 | return locs; 99 | } 100 | 101 | void CollisionDetector::ResolveCollisions(Transform* transform, double dt, bool* onGround) { 102 | const s32 MaxIterations = 10; 103 | bool collisions = true; 104 | 105 | Vector3d velocity = transform->velocity; 106 | Vector3d input_velocity = transform->input_velocity; 107 | 108 | for (s32 iteration = 0; iteration < MaxIterations && collisions; ++iteration) { 109 | Vector3d position = transform->position; 110 | 111 | collisions = false; 112 | 113 | for (std::size_t i = 0; i < 3; ++i) { 114 | AABB playerBounds = transform->bounding_box; 115 | 116 | if (iteration == 0) 117 | position[i] += velocity[i] * dt + input_velocity[i] * dt; 118 | 119 | playerBounds.min += position; 120 | playerBounds.max += position; 121 | 122 | std::vector surrounding = GetSurroundingLocations(playerBounds); 123 | 124 | for (Vector3i checkPos : surrounding) { 125 | mc::block::BlockPtr block = m_World->GetBlock(checkPos); 126 | 127 | if (block && block->IsSolid()) { 128 | AABB blockBounds = block->GetBoundingBox(checkPos); 129 | 130 | if (playerBounds.Intersects(blockBounds)) { 131 | if (onGround != nullptr && i == 1 && checkPos.y < transform->position.y) { 132 | *onGround = true; 133 | } 134 | 135 | velocity[i] = 0; 136 | input_velocity[i] = 0; 137 | 138 | double penetrationDepth; 139 | 140 | if (playerBounds.min[i] < blockBounds.min[i]) { 141 | penetrationDepth = playerBounds.max[i] - blockBounds.min[i]; 142 | } else { 143 | penetrationDepth = playerBounds.min[i] - blockBounds.max[i]; 144 | } 145 | 146 | position[i] -= penetrationDepth; 147 | collisions = true; 148 | break; 149 | } 150 | } 151 | } 152 | } 153 | 154 | transform->position = position; 155 | } 156 | 157 | transform->velocity = velocity; 158 | transform->input_velocity = input_velocity; 159 | } 160 | 161 | } // namespace terra 162 | -------------------------------------------------------------------------------- /terracotta/assets/TextureArray.cpp: -------------------------------------------------------------------------------- 1 | #include "TextureArray.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace terra { 8 | namespace assets { 9 | 10 | struct Mipmap { 11 | unsigned char* data; 12 | std::size_t dimension; 13 | 14 | Mipmap(unsigned char* data, std::size_t dimension) : data(data), dimension(dimension) { } 15 | 16 | int Sample(std::size_t x, std::size_t y, std::size_t color_offset) { 17 | return data[(y * dimension + x) * 4 + color_offset]; 18 | } 19 | }; 20 | 21 | float GetColorGamma(int a, int b, int c, int d) { 22 | float an = a / 255.0f; 23 | float bn = b / 255.0f; 24 | float cn = c / 255.0f; 25 | float dn = d / 255.0f; 26 | 27 | return (std::pow(an, 2.2f) + std::pow(bn, 2.2f) + std::pow(cn, 2.2f) + std::pow(dn, 2.2f)) / 4.0f; 28 | } 29 | 30 | // Blend four samples into a final result after doing gamma conversions 31 | int GammaBlend(int a, int b, int c, int d) { 32 | float result = std::pow(GetColorGamma(a, b, c, d), 1.0f / 2.2f); 33 | 34 | return static_cast(255.0f * result); 35 | } 36 | 37 | // Performs basic pixel averaging filter for generating mipmap. 38 | void BoxFilterMipmap(std::vector& previous, std::vector& data, std::size_t dim) { 39 | std::size_t size_per_tex = dim * dim * 4; 40 | std::size_t count = data.size() / size_per_tex; 41 | std::size_t prev_dim = dim * 2; 42 | 43 | unsigned int* pixel = (unsigned int*)data.data(); 44 | for (std::size_t i = 0; i < count; ++i) { 45 | unsigned char* prev_tex = previous.data() + i * (prev_dim * prev_dim * 4); 46 | 47 | Mipmap source(prev_tex, prev_dim); 48 | 49 | for (std::size_t y = 0; y < dim; ++y) { 50 | for (std::size_t x = 0; x < dim; ++x) { 51 | int red, green, blue, alpha; 52 | 53 | const std::size_t red_index = 0; 54 | const std::size_t green_index = 1; 55 | const std::size_t blue_index = 2; 56 | const std::size_t alpha_index = 3; 57 | 58 | red = GammaBlend(source.Sample(x * 2, y * 2, red_index), source.Sample(x * 2 + 1, y * 2, red_index), 59 | source.Sample(x * 2, y * 2 + 1, red_index), source.Sample(x * 2 + 1, y * 2 + 1, red_index)); 60 | 61 | green = GammaBlend(source.Sample(x * 2, y * 2, green_index), source.Sample(x * 2 + 1, y * 2, green_index), 62 | source.Sample(x * 2, y * 2 + 1, green_index), source.Sample(x * 2 + 1, y * 2 + 1, green_index)); 63 | 64 | blue = GammaBlend(source.Sample(x * 2, y * 2, blue_index), source.Sample(x * 2 + 1, y * 2, blue_index), 65 | source.Sample(x * 2, y * 2 + 1, blue_index), source.Sample(x * 2 + 1, y * 2 + 1, blue_index)); 66 | 67 | alpha = GammaBlend(source.Sample(x * 2, y * 2, alpha_index), source.Sample(x * 2 + 1, y * 2, alpha_index), 68 | source.Sample(x * 2, y * 2 + 1, alpha_index), source.Sample(x * 2 + 1, y * 2 + 1, alpha_index)); 69 | 70 | // AA BB GG RR 71 | *pixel = ((alpha & 0xFF) << 24) | ((blue & 0xFF) << 16) | ((green & 0xFF) << 8) | (red & 0xFF); 72 | ++pixel; 73 | } 74 | } 75 | } 76 | } 77 | 78 | TextureArray::TextureArray() { 79 | glActiveTexture(GL_TEXTURE0); 80 | glGenTextures(1, &m_TextureId); 81 | glBindTexture(GL_TEXTURE_2D_ARRAY, m_TextureId); 82 | 83 | memset(m_Transparency, 0, sizeof(m_Transparency)); 84 | } 85 | 86 | TextureHandle TextureArray::Append(const std::string& filename, const std::string& texture) { 87 | auto iter = m_Textures.find(filename); 88 | 89 | if (iter != m_Textures.end()) { 90 | return iter->second; 91 | } 92 | 93 | TextureHandle handle = static_cast(m_Textures.size()); 94 | 95 | m_Textures[filename] = handle; 96 | m_TextureData.insert(m_TextureData.end(), texture.c_str(), texture.c_str() + texture.size()); 97 | 98 | for (std::size_t i = 4; i < texture.size(); i += 4) { 99 | char alpha = texture[i]; 100 | 101 | if (texture[i] == 0) { 102 | m_Transparency[handle] = true; 103 | break; 104 | } 105 | } 106 | 107 | return handle; 108 | } 109 | 110 | void TextureArray::Generate() { 111 | glBindTexture(GL_TEXTURE_2D_ARRAY, m_TextureId); 112 | 113 | glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 114 | glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 115 | 116 | glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); 117 | glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 118 | 119 | int levels = 5; 120 | 121 | glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_BASE_LEVEL, 0); 122 | glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAX_LEVEL, levels - 1); 123 | 124 | GLsizei size = static_cast(m_Textures.size()); 125 | 126 | glTexStorage3D(GL_TEXTURE_2D_ARRAY, levels, GL_RGBA8, 16, 16, size); 127 | glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, 0, 16, 16, size, GL_RGBA, GL_UNSIGNED_BYTE, &m_TextureData[0]); 128 | 129 | std::vector mipmap_data(m_TextureData); 130 | 131 | GLsizei dim = 16; 132 | for (int i = 1; i < levels; ++i) { 133 | dim /= 2; 134 | 135 | if (dim < 1) dim = 1; 136 | 137 | std::size_t data_size = dim * dim * 4 * size; 138 | std::vector previous(mipmap_data); 139 | mipmap_data = std::vector(data_size); 140 | 141 | BoxFilterMipmap(previous, mipmap_data, dim); 142 | 143 | glTexSubImage3D(GL_TEXTURE_2D_ARRAY, i, 0, 0, 0, dim, dim, size, GL_RGBA, GL_UNSIGNED_BYTE, mipmap_data.data()); 144 | } 145 | 146 | m_TextureData.clear(); 147 | std::vector().swap(m_TextureData); 148 | } 149 | 150 | void TextureArray::Bind() { 151 | glBindTexture(GL_TEXTURE_2D_ARRAY, m_TextureId); 152 | } 153 | 154 | bool TextureArray::GetTexture(const std::string& filename, TextureHandle* handle) { 155 | auto iter = m_Textures.find(filename); 156 | 157 | if (iter == m_Textures.end()) { 158 | return false; 159 | } 160 | 161 | *handle = iter->second; 162 | return true; 163 | } 164 | 165 | bool TextureArray::IsTransparent(TextureHandle handle) const { 166 | return m_Transparency[handle]; 167 | } 168 | 169 | } // ns assets 170 | } // ns terra 171 | -------------------------------------------------------------------------------- /terracotta/World.cpp: -------------------------------------------------------------------------------- 1 | #include "World.h" 2 | 3 | namespace terra { 4 | 5 | World::World(mc::protocol::packets::PacketDispatcher* dispatcher) 6 | : mc::protocol::packets::PacketHandler(dispatcher) 7 | { 8 | dispatcher->RegisterHandler(mc::protocol::State::Play, mc::protocol::play::MultiBlockChange, this); 9 | dispatcher->RegisterHandler(mc::protocol::State::Play, mc::protocol::play::BlockChange, this); 10 | dispatcher->RegisterHandler(mc::protocol::State::Play, mc::protocol::play::ChunkData, this); 11 | dispatcher->RegisterHandler(mc::protocol::State::Play, mc::protocol::play::UnloadChunk, this); 12 | dispatcher->RegisterHandler(mc::protocol::State::Play, mc::protocol::play::Explosion, this); 13 | dispatcher->RegisterHandler(mc::protocol::State::Play, mc::protocol::play::UpdateBlockEntity, this); 14 | dispatcher->RegisterHandler(mc::protocol::State::Play, mc::protocol::play::Respawn, this); 15 | } 16 | 17 | World::~World() { 18 | GetDispatcher()->UnregisterHandler(this); 19 | } 20 | 21 | bool World::SetBlock(mc::Vector3i position, u32 blockData) { 22 | ChunkColumnPtr chunk = GetChunk(position); 23 | if (!chunk) return false; 24 | 25 | mc::Vector3i relative(position); 26 | 27 | relative.x %= 16; 28 | relative.z %= 16; 29 | 30 | if (relative.x < 0) 31 | relative.x += 16; 32 | if (relative.z < 0) 33 | relative.z += 16; 34 | 35 | std::size_t index = (std::size_t)position.y / 16; 36 | if ((*chunk)[index] == nullptr) { 37 | ChunkPtr section = std::make_shared(); 38 | 39 | (*chunk)[index] = section; 40 | } 41 | 42 | relative.y %= 16; 43 | (*chunk)[index]->SetBlock(relative, mc::block::BlockRegistry::GetInstance()->GetBlock(blockData)); 44 | return true; 45 | } 46 | 47 | void World::HandlePacket(mc::protocol::packets::in::ExplosionPacket* packet) { 48 | mc::Vector3d position = packet->GetPosition(); 49 | 50 | for (mc::Vector3s offset : packet->GetAffectedBlocks()) { 51 | mc::Vector3d absolute = position + ToVector3d(offset); 52 | 53 | mc::block::BlockPtr oldBlock = GetBlock(mc::ToVector3i(absolute)); 54 | 55 | // Set all affected blocks to air 56 | SetBlock(ToVector3i(absolute), 0); 57 | 58 | mc::block::BlockPtr newBlock = mc::block::BlockRegistry::GetInstance()->GetBlock(0); 59 | NotifyListeners(&WorldListener::OnBlockChange, ToVector3i(absolute), newBlock, oldBlock); 60 | } 61 | } 62 | 63 | void World::HandlePacket(mc::protocol::packets::in::ChunkDataPacket* packet) { 64 | mc::world::ChunkColumnPtr lib_column = packet->GetChunkColumn(); 65 | 66 | ChunkColumnPtr col = std::make_shared(*lib_column); 67 | const ChunkColumnMetadata& meta = col->GetMetadata(); 68 | ChunkCoord key(meta.x, meta.z); 69 | 70 | if (meta.continuous && meta.sectionmask == 0) { 71 | m_Chunks[key] = nullptr; 72 | return; 73 | } 74 | 75 | auto iter = m_Chunks.find(key); 76 | 77 | if (!meta.continuous) { 78 | // This isn't an entire column of chunks, so just update the existing chunk column with the provided chunks. 79 | for (s16 i = 0; i < ChunkColumn::ChunksPerColumn; ++i) { 80 | // The section mask says whether or not there is data in this chunk. 81 | if (meta.sectionmask & (1 << i)) { 82 | (*iter->second)[i] = (*col)[i]; 83 | } 84 | } 85 | } else { 86 | // This is an entire column of chunks, so just replace the entire column with the new one. 87 | m_Chunks[key] = col; 88 | } 89 | 90 | 91 | for (s32 i = 0; i < ChunkColumn::ChunksPerColumn; ++i) { 92 | ChunkPtr chunk = (*col)[i]; 93 | 94 | NotifyListeners(&WorldListener::OnChunkLoad, chunk, meta, i); 95 | } 96 | } 97 | 98 | void World::HandlePacket(mc::protocol::packets::in::MultiBlockChangePacket* packet) { 99 | mc::Vector3i chunkStart(packet->GetChunkX() * 16, 0, packet->GetChunkZ() * 16); 100 | auto iter = m_Chunks.find(ChunkCoord(packet->GetChunkX(), packet->GetChunkZ())); 101 | if (iter == m_Chunks.end()) 102 | return; 103 | 104 | ChunkColumnPtr chunk = iter->second; 105 | if (!chunk) 106 | return; 107 | 108 | const auto& changes = packet->GetBlockChanges(); 109 | for (const auto& change : changes) { 110 | mc::Vector3i relative(change.x, change.y, change.z); 111 | 112 | std::size_t index = change.y / 16; 113 | mc::block::BlockPtr oldBlock = mc::block::BlockRegistry::GetInstance()->GetBlock(0); 114 | if ((*chunk)[index] == nullptr) { 115 | ChunkPtr section = std::make_shared(); 116 | 117 | (*chunk)[index] = section; 118 | } else { 119 | oldBlock = chunk->GetBlock(relative); 120 | } 121 | 122 | mc::block::BlockPtr newBlock = mc::block::BlockRegistry::GetInstance()->GetBlock(change.blockData); 123 | 124 | mc::Vector3i blockChangePos = chunkStart + relative; 125 | 126 | relative.y %= 16; 127 | if (newBlock->GetType() != oldBlock->GetType()) { 128 | chunk->RemoveBlockEntity(chunkStart + relative); 129 | (*chunk)[index]->SetBlock(relative, newBlock); 130 | NotifyListeners(&WorldListener::OnBlockChange, blockChangePos, newBlock, oldBlock); 131 | } 132 | } 133 | } 134 | 135 | void World::HandlePacket(mc::protocol::packets::in::BlockChangePacket* packet) { 136 | mc::block::BlockPtr newBlock = mc::block::BlockRegistry::GetInstance()->GetBlock((u16)packet->GetBlockId()); 137 | auto pos = packet->GetPosition(); 138 | mc::block::BlockPtr oldBlock = GetBlock(pos); 139 | 140 | SetBlock(packet->GetPosition(), packet->GetBlockId()); 141 | 142 | NotifyListeners(&WorldListener::OnBlockChange, packet->GetPosition(), newBlock, oldBlock); 143 | 144 | ChunkColumnPtr col = GetChunk(pos); 145 | if (col) { 146 | col->RemoveBlockEntity(packet->GetPosition()); 147 | } 148 | } 149 | 150 | void World::HandlePacket(mc::protocol::packets::in::UpdateBlockEntityPacket* packet) { 151 | mc::Vector3i pos = packet->GetPosition(); 152 | 153 | ChunkColumnPtr col = GetChunk(pos); 154 | 155 | if (!col) return; 156 | 157 | col->RemoveBlockEntity(pos); 158 | 159 | mc::block::BlockEntityPtr entity = packet->GetBlockEntity(); 160 | if (entity) 161 | col->AddBlockEntity(entity); 162 | } 163 | 164 | void World::HandlePacket(mc::protocol::packets::in::UnloadChunkPacket* packet) { 165 | ChunkCoord coord(packet->GetChunkX(), packet->GetChunkZ()); 166 | 167 | auto iter = m_Chunks.find(coord); 168 | 169 | if (iter == m_Chunks.end()) 170 | return; 171 | 172 | ChunkColumnPtr chunk = iter->second; 173 | NotifyListeners(&WorldListener::OnChunkUnload, chunk); 174 | 175 | m_Chunks.erase(iter); 176 | } 177 | 178 | // Clear all chunks because the server will resend the chunks after this. 179 | void World::HandlePacket(mc::protocol::packets::in::RespawnPacket* packet) { 180 | for (auto entry : m_Chunks) { 181 | ChunkColumnPtr chunk = entry.second; 182 | 183 | NotifyListeners(&WorldListener::OnChunkUnload, chunk); 184 | } 185 | 186 | m_Chunks.clear(); 187 | } 188 | 189 | ChunkColumnPtr World::GetChunk(const mc::Vector3i& pos) const { 190 | s32 chunk_x = (s32)std::floor(pos.x / 16.0); 191 | s32 chunk_z = (s32)std::floor(pos.z / 16.0); 192 | 193 | ChunkCoord key(chunk_x, chunk_z); 194 | 195 | auto iter = m_Chunks.find(key); 196 | 197 | if (iter == m_Chunks.end()) return nullptr; 198 | 199 | return iter->second; 200 | } 201 | 202 | mc::block::BlockPtr World::GetBlock(const mc::Vector3i& pos) const { 203 | ChunkColumnPtr col = GetChunk(pos); 204 | 205 | if (!col) return mc::block::BlockRegistry::GetInstance()->GetBlock(0); 206 | 207 | s64 chunk_x = pos.x % 16; 208 | s64 chunk_z = pos.z % 16; 209 | 210 | if (chunk_x < 0) 211 | chunk_x += 16; 212 | if (chunk_z < 0) 213 | chunk_z += 16; 214 | 215 | return col->GetBlock(mc::Vector3i(chunk_x, pos.y, chunk_z)); 216 | } 217 | 218 | mc::block::BlockEntityPtr World::GetBlockEntity(mc::Vector3i pos) const { 219 | ChunkColumnPtr col = GetChunk(pos); 220 | 221 | if (!col) return nullptr; 222 | 223 | return col->GetBlockEntity(pos); 224 | } 225 | 226 | std::vector World::GetBlockEntities() const { 227 | std::vector blockEntities; 228 | 229 | for (auto iter = m_Chunks.begin(); iter != m_Chunks.end(); ++iter) { 230 | if (iter->second == nullptr) continue; 231 | std::vector chunkBlockEntities = iter->second->GetBlockEntities(); 232 | if (chunkBlockEntities.empty()) continue; 233 | blockEntities.insert(blockEntities.end(), chunkBlockEntities.begin(), chunkBlockEntities.end()); 234 | } 235 | 236 | return blockEntities; 237 | } 238 | 239 | } // ns terra 240 | -------------------------------------------------------------------------------- /terracotta/lib/imgui/imgui_impl_glfw.cpp: -------------------------------------------------------------------------------- 1 | // dear imgui: Platform Binding for GLFW 2 | // This needs to be used along with a Renderer (e.g. OpenGL3, Vulkan..) 3 | // (Info: GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.) 4 | // (Requires: GLFW 3.1+) 5 | 6 | // Implemented features: 7 | // [X] Platform: Clipboard support. 8 | // [X] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. 9 | // [x] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. FIXME: 3 cursors types are missing from GLFW. 10 | // [X] Platform: Keyboard arrays indexed using GLFW_KEY_* codes, e.g. ImGui::IsKeyPressed(GLFW_KEY_SPACE). 11 | 12 | // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. 13 | // If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp. 14 | // https://github.com/ocornut/imgui 15 | 16 | // CHANGELOG 17 | // (minor and older changes stripped away, please see git history for details) 18 | // 2018-11-30: Misc: Setting up io.BackendPlatformName so it can be displayed in the About Window. 19 | // 2018-11-07: Inputs: When installing our GLFW callbacks, we save user's previously installed ones - if any - and chain call them. 20 | // 2018-08-01: Inputs: Workaround for Emscripten which doesn't seem to handle focus related calls. 21 | // 2018-06-29: Inputs: Added support for the ImGuiMouseCursor_Hand cursor. 22 | // 2018-06-08: Misc: Extracted imgui_impl_glfw.cpp/.h away from the old combined GLFW+OpenGL/Vulkan examples. 23 | // 2018-03-20: Misc: Setup io.BackendFlags ImGuiBackendFlags_HasMouseCursors flag + honor ImGuiConfigFlags_NoMouseCursorChange flag. 24 | // 2018-02-20: Inputs: Added support for mouse cursors (ImGui::GetMouseCursor() value, passed to glfwSetCursor()). 25 | // 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves. 26 | // 2018-02-06: Inputs: Added mapping for ImGuiKey_Space. 27 | // 2018-01-25: Inputs: Added gamepad support if ImGuiConfigFlags_NavEnableGamepad is set. 28 | // 2018-01-25: Inputs: Honoring the io.WantSetMousePos by repositioning the mouse (when using navigation and ImGuiConfigFlags_NavMoveMouse is set). 29 | // 2018-01-20: Inputs: Added Horizontal Mouse Wheel support. 30 | // 2018-01-18: Inputs: Added mapping for ImGuiKey_Insert. 31 | // 2017-08-25: Inputs: MousePos set to -FLT_MAX,-FLT_MAX when mouse is unavailable/missing (instead of -1,-1). 32 | // 2016-10-15: Misc: Added a void* user_data parameter to Clipboard function handlers. 33 | 34 | #include "imgui.h" 35 | #include "imgui_impl_glfw.h" 36 | 37 | // GLFW 38 | #include 39 | #ifdef _WIN32 40 | #undef APIENTRY 41 | #define GLFW_EXPOSE_NATIVE_WIN32 42 | #include // for glfwGetWin32Window 43 | #endif 44 | #define GLFW_HAS_WINDOW_TOPMOST (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3200) // 3.2+ GLFW_FLOATING 45 | #define GLFW_HAS_WINDOW_HOVERED (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3300) // 3.3+ GLFW_HOVERED 46 | #define GLFW_HAS_WINDOW_ALPHA (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3300) // 3.3+ glfwSetWindowOpacity 47 | #define GLFW_HAS_PER_MONITOR_DPI (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3300) // 3.3+ glfwGetMonitorContentScale 48 | #define GLFW_HAS_VULKAN (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3200) // 3.2+ glfwCreateWindowSurface 49 | 50 | // Data 51 | enum GlfwClientApi 52 | { 53 | GlfwClientApi_Unknown, 54 | GlfwClientApi_OpenGL, 55 | GlfwClientApi_Vulkan 56 | }; 57 | static GLFWwindow* g_Window = NULL; 58 | static GlfwClientApi g_ClientApi = GlfwClientApi_Unknown; 59 | static double g_Time = 0.0; 60 | static bool g_MouseJustPressed[5] = { false, false, false, false, false }; 61 | static GLFWcursor* g_MouseCursors[ImGuiMouseCursor_COUNT] = { 0 }; 62 | 63 | // Chain GLFW callbacks: our callbacks will call the user's previously installed callbacks, if any. 64 | static GLFWmousebuttonfun g_PrevUserCallbackMousebutton = NULL; 65 | static GLFWscrollfun g_PrevUserCallbackScroll = NULL; 66 | static GLFWkeyfun g_PrevUserCallbackKey = NULL; 67 | static GLFWcharfun g_PrevUserCallbackChar = NULL; 68 | 69 | static const char* ImGui_ImplGlfw_GetClipboardText(void* user_data) 70 | { 71 | return glfwGetClipboardString((GLFWwindow*)user_data); 72 | } 73 | 74 | static void ImGui_ImplGlfw_SetClipboardText(void* user_data, const char* text) 75 | { 76 | glfwSetClipboardString((GLFWwindow*)user_data, text); 77 | } 78 | 79 | void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow* window, int button, int action, int mods) 80 | { 81 | if (g_PrevUserCallbackMousebutton != NULL) 82 | g_PrevUserCallbackMousebutton(window, button, action, mods); 83 | 84 | if (action == GLFW_PRESS && button >= 0 && button < IM_ARRAYSIZE(g_MouseJustPressed)) 85 | g_MouseJustPressed[button] = true; 86 | } 87 | 88 | void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yoffset) 89 | { 90 | if (g_PrevUserCallbackScroll != NULL) 91 | g_PrevUserCallbackScroll(window, xoffset, yoffset); 92 | 93 | ImGuiIO& io = ImGui::GetIO(); 94 | io.MouseWheelH += (float)xoffset; 95 | io.MouseWheel += (float)yoffset; 96 | } 97 | 98 | void ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods) 99 | { 100 | if (g_PrevUserCallbackKey != NULL) 101 | g_PrevUserCallbackKey(window, key, scancode, action, mods); 102 | 103 | ImGuiIO& io = ImGui::GetIO(); 104 | if (action == GLFW_PRESS) 105 | io.KeysDown[key] = true; 106 | if (action == GLFW_RELEASE) 107 | io.KeysDown[key] = false; 108 | 109 | // Modifiers are not reliable across systems 110 | io.KeyCtrl = io.KeysDown[GLFW_KEY_LEFT_CONTROL] || io.KeysDown[GLFW_KEY_RIGHT_CONTROL]; 111 | io.KeyShift = io.KeysDown[GLFW_KEY_LEFT_SHIFT] || io.KeysDown[GLFW_KEY_RIGHT_SHIFT]; 112 | io.KeyAlt = io.KeysDown[GLFW_KEY_LEFT_ALT] || io.KeysDown[GLFW_KEY_RIGHT_ALT]; 113 | io.KeySuper = io.KeysDown[GLFW_KEY_LEFT_SUPER] || io.KeysDown[GLFW_KEY_RIGHT_SUPER]; 114 | } 115 | 116 | void ImGui_ImplGlfw_CharCallback(GLFWwindow* window, unsigned int c) 117 | { 118 | if (g_PrevUserCallbackChar != NULL) 119 | g_PrevUserCallbackChar(window, c); 120 | 121 | ImGuiIO& io = ImGui::GetIO(); 122 | if (c > 0 && c < 0x10000) 123 | io.AddInputCharacter((unsigned short)c); 124 | } 125 | 126 | static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, GlfwClientApi client_api) 127 | { 128 | g_Window = window; 129 | g_Time = 0.0; 130 | 131 | // Setup back-end capabilities flags 132 | ImGuiIO& io = ImGui::GetIO(); 133 | io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional) 134 | io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) 135 | io.BackendPlatformName = "imgui_impl_glfw"; 136 | 137 | // Keyboard mapping. ImGui will use those indices to peek into the io.KeysDown[] array. 138 | io.KeyMap[ImGuiKey_Tab] = GLFW_KEY_TAB; 139 | io.KeyMap[ImGuiKey_LeftArrow] = GLFW_KEY_LEFT; 140 | io.KeyMap[ImGuiKey_RightArrow] = GLFW_KEY_RIGHT; 141 | io.KeyMap[ImGuiKey_UpArrow] = GLFW_KEY_UP; 142 | io.KeyMap[ImGuiKey_DownArrow] = GLFW_KEY_DOWN; 143 | io.KeyMap[ImGuiKey_PageUp] = GLFW_KEY_PAGE_UP; 144 | io.KeyMap[ImGuiKey_PageDown] = GLFW_KEY_PAGE_DOWN; 145 | io.KeyMap[ImGuiKey_Home] = GLFW_KEY_HOME; 146 | io.KeyMap[ImGuiKey_End] = GLFW_KEY_END; 147 | io.KeyMap[ImGuiKey_Insert] = GLFW_KEY_INSERT; 148 | io.KeyMap[ImGuiKey_Delete] = GLFW_KEY_DELETE; 149 | io.KeyMap[ImGuiKey_Backspace] = GLFW_KEY_BACKSPACE; 150 | io.KeyMap[ImGuiKey_Space] = GLFW_KEY_SPACE; 151 | io.KeyMap[ImGuiKey_Enter] = GLFW_KEY_ENTER; 152 | io.KeyMap[ImGuiKey_Escape] = GLFW_KEY_ESCAPE; 153 | io.KeyMap[ImGuiKey_A] = GLFW_KEY_A; 154 | io.KeyMap[ImGuiKey_C] = GLFW_KEY_C; 155 | io.KeyMap[ImGuiKey_V] = GLFW_KEY_V; 156 | io.KeyMap[ImGuiKey_X] = GLFW_KEY_X; 157 | io.KeyMap[ImGuiKey_Y] = GLFW_KEY_Y; 158 | io.KeyMap[ImGuiKey_Z] = GLFW_KEY_Z; 159 | 160 | io.SetClipboardTextFn = ImGui_ImplGlfw_SetClipboardText; 161 | io.GetClipboardTextFn = ImGui_ImplGlfw_GetClipboardText; 162 | io.ClipboardUserData = g_Window; 163 | #if defined(_WIN32) 164 | io.ImeWindowHandle = (void*)glfwGetWin32Window(g_Window); 165 | #endif 166 | 167 | g_MouseCursors[ImGuiMouseCursor_Arrow] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); 168 | g_MouseCursors[ImGuiMouseCursor_TextInput] = glfwCreateStandardCursor(GLFW_IBEAM_CURSOR); 169 | g_MouseCursors[ImGuiMouseCursor_ResizeAll] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); // FIXME: GLFW doesn't have this. 170 | g_MouseCursors[ImGuiMouseCursor_ResizeNS] = glfwCreateStandardCursor(GLFW_VRESIZE_CURSOR); 171 | g_MouseCursors[ImGuiMouseCursor_ResizeEW] = glfwCreateStandardCursor(GLFW_HRESIZE_CURSOR); 172 | g_MouseCursors[ImGuiMouseCursor_ResizeNESW] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); // FIXME: GLFW doesn't have this. 173 | g_MouseCursors[ImGuiMouseCursor_ResizeNWSE] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); // FIXME: GLFW doesn't have this. 174 | g_MouseCursors[ImGuiMouseCursor_Hand] = glfwCreateStandardCursor(GLFW_HAND_CURSOR); 175 | 176 | // Chain GLFW callbacks: our callbacks will call the user's previously installed callbacks, if any. 177 | g_PrevUserCallbackMousebutton = NULL; 178 | g_PrevUserCallbackScroll = NULL; 179 | g_PrevUserCallbackKey = NULL; 180 | g_PrevUserCallbackChar = NULL; 181 | if (install_callbacks) 182 | { 183 | g_PrevUserCallbackMousebutton = glfwSetMouseButtonCallback(window, ImGui_ImplGlfw_MouseButtonCallback); 184 | g_PrevUserCallbackScroll = glfwSetScrollCallback(window, ImGui_ImplGlfw_ScrollCallback); 185 | g_PrevUserCallbackKey = glfwSetKeyCallback(window, ImGui_ImplGlfw_KeyCallback); 186 | g_PrevUserCallbackChar = glfwSetCharCallback(window, ImGui_ImplGlfw_CharCallback); 187 | } 188 | 189 | g_ClientApi = client_api; 190 | return true; 191 | } 192 | 193 | bool ImGui_ImplGlfw_InitForOpenGL(GLFWwindow* window, bool install_callbacks) 194 | { 195 | return ImGui_ImplGlfw_Init(window, install_callbacks, GlfwClientApi_OpenGL); 196 | } 197 | 198 | bool ImGui_ImplGlfw_InitForVulkan(GLFWwindow* window, bool install_callbacks) 199 | { 200 | return ImGui_ImplGlfw_Init(window, install_callbacks, GlfwClientApi_Vulkan); 201 | } 202 | 203 | void ImGui_ImplGlfw_Shutdown() 204 | { 205 | for (ImGuiMouseCursor cursor_n = 0; cursor_n < ImGuiMouseCursor_COUNT; cursor_n++) 206 | { 207 | glfwDestroyCursor(g_MouseCursors[cursor_n]); 208 | g_MouseCursors[cursor_n] = NULL; 209 | } 210 | g_ClientApi = GlfwClientApi_Unknown; 211 | } 212 | 213 | static void ImGui_ImplGlfw_UpdateMousePosAndButtons() 214 | { 215 | // Update buttons 216 | ImGuiIO& io = ImGui::GetIO(); 217 | for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) 218 | { 219 | // If a mouse press event came, always pass it as "mouse held this frame", so we don't miss click-release events that are shorter than 1 frame. 220 | io.MouseDown[i] = g_MouseJustPressed[i] || glfwGetMouseButton(g_Window, i) != 0; 221 | g_MouseJustPressed[i] = false; 222 | } 223 | 224 | // Update mouse position 225 | const ImVec2 mouse_pos_backup = io.MousePos; 226 | io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX); 227 | #ifdef __EMSCRIPTEN__ 228 | const bool focused = true; // Emscripten 229 | #else 230 | const bool focused = glfwGetWindowAttrib(g_Window, GLFW_FOCUSED) != 0; 231 | #endif 232 | if (focused) 233 | { 234 | if (io.WantSetMousePos) 235 | { 236 | glfwSetCursorPos(g_Window, (double)mouse_pos_backup.x, (double)mouse_pos_backup.y); 237 | } 238 | else 239 | { 240 | double mouse_x, mouse_y; 241 | glfwGetCursorPos(g_Window, &mouse_x, &mouse_y); 242 | io.MousePos = ImVec2((float)mouse_x, (float)mouse_y); 243 | } 244 | } 245 | } 246 | 247 | static void ImGui_ImplGlfw_UpdateMouseCursor() 248 | { 249 | ImGuiIO& io = ImGui::GetIO(); 250 | if ((io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange) || glfwGetInputMode(g_Window, GLFW_CURSOR) == GLFW_CURSOR_DISABLED) 251 | return; 252 | 253 | ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor(); 254 | if (imgui_cursor == ImGuiMouseCursor_None || io.MouseDrawCursor) 255 | { 256 | // Hide OS mouse cursor if imgui is drawing it or if it wants no cursor 257 | glfwSetInputMode(g_Window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); 258 | } 259 | else 260 | { 261 | // Show OS mouse cursor 262 | // FIXME-PLATFORM: Unfocused windows seems to fail changing the mouse cursor with GLFW 3.2, but 3.3 works here. 263 | glfwSetCursor(g_Window, g_MouseCursors[imgui_cursor] ? g_MouseCursors[imgui_cursor] : g_MouseCursors[ImGuiMouseCursor_Arrow]); 264 | glfwSetInputMode(g_Window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); 265 | } 266 | } 267 | 268 | void ImGui_ImplGlfw_NewFrame() 269 | { 270 | ImGuiIO& io = ImGui::GetIO(); 271 | IM_ASSERT(io.Fonts->IsBuilt() && "Font atlas not built! It is generally built by the renderer back-end. Missing call to renderer _NewFrame() function? e.g. ImGui_ImplOpenGL3_NewFrame()."); 272 | 273 | // Setup display size (every frame to accommodate for window resizing) 274 | int w, h; 275 | int display_w, display_h; 276 | glfwGetWindowSize(g_Window, &w, &h); 277 | glfwGetFramebufferSize(g_Window, &display_w, &display_h); 278 | io.DisplaySize = ImVec2((float)w, (float)h); 279 | io.DisplayFramebufferScale = ImVec2(w > 0 ? ((float)display_w / w) : 0, h > 0 ? ((float)display_h / h) : 0); 280 | 281 | // Setup time step 282 | double current_time = glfwGetTime(); 283 | io.DeltaTime = g_Time > 0.0 ? (float)(current_time - g_Time) : (float)(1.0f/60.0f); 284 | g_Time = current_time; 285 | 286 | ImGui_ImplGlfw_UpdateMousePosAndButtons(); 287 | ImGui_ImplGlfw_UpdateMouseCursor(); 288 | 289 | // Gamepad navigation mapping [BETA] 290 | memset(io.NavInputs, 0, sizeof(io.NavInputs)); 291 | if (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) 292 | { 293 | // Update gamepad inputs 294 | #define MAP_BUTTON(NAV_NO, BUTTON_NO) { if (buttons_count > BUTTON_NO && buttons[BUTTON_NO] == GLFW_PRESS) io.NavInputs[NAV_NO] = 1.0f; } 295 | #define MAP_ANALOG(NAV_NO, AXIS_NO, V0, V1) { float v = (axes_count > AXIS_NO) ? axes[AXIS_NO] : V0; v = (v - V0) / (V1 - V0); if (v > 1.0f) v = 1.0f; if (io.NavInputs[NAV_NO] < v) io.NavInputs[NAV_NO] = v; } 296 | int axes_count = 0, buttons_count = 0; 297 | const float* axes = glfwGetJoystickAxes(GLFW_JOYSTICK_1, &axes_count); 298 | const unsigned char* buttons = glfwGetJoystickButtons(GLFW_JOYSTICK_1, &buttons_count); 299 | MAP_BUTTON(ImGuiNavInput_Activate, 0); // Cross / A 300 | MAP_BUTTON(ImGuiNavInput_Cancel, 1); // Circle / B 301 | MAP_BUTTON(ImGuiNavInput_Menu, 2); // Square / X 302 | MAP_BUTTON(ImGuiNavInput_Input, 3); // Triangle / Y 303 | MAP_BUTTON(ImGuiNavInput_DpadLeft, 13); // D-Pad Left 304 | MAP_BUTTON(ImGuiNavInput_DpadRight, 11); // D-Pad Right 305 | MAP_BUTTON(ImGuiNavInput_DpadUp, 10); // D-Pad Up 306 | MAP_BUTTON(ImGuiNavInput_DpadDown, 12); // D-Pad Down 307 | MAP_BUTTON(ImGuiNavInput_FocusPrev, 4); // L1 / LB 308 | MAP_BUTTON(ImGuiNavInput_FocusNext, 5); // R1 / RB 309 | MAP_BUTTON(ImGuiNavInput_TweakSlow, 4); // L1 / LB 310 | MAP_BUTTON(ImGuiNavInput_TweakFast, 5); // R1 / RB 311 | MAP_ANALOG(ImGuiNavInput_LStickLeft, 0, -0.3f, -0.9f); 312 | MAP_ANALOG(ImGuiNavInput_LStickRight,0, +0.3f, +0.9f); 313 | MAP_ANALOG(ImGuiNavInput_LStickUp, 1, +0.3f, +0.9f); 314 | MAP_ANALOG(ImGuiNavInput_LStickDown, 1, -0.3f, -0.9f); 315 | #undef MAP_BUTTON 316 | #undef MAP_ANALOG 317 | if (axes_count > 0 && buttons_count > 0) 318 | io.BackendFlags |= ImGuiBackendFlags_HasGamepad; 319 | else 320 | io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad; 321 | } 322 | } 323 | -------------------------------------------------------------------------------- /terracotta/Game.cpp: -------------------------------------------------------------------------------- 1 | #include "Game.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "math/Plane.h" 11 | #include "math/TypeUtil.h" 12 | #include 13 | #include 14 | 15 | namespace terra { 16 | 17 | #ifndef M_TAU 18 | #define M_TAU 3.14159 * 2 19 | #endif 20 | 21 | float clamp(float value, float min, float max) { 22 | if (value < min) return min; 23 | if (value > max) return max; 24 | return value; 25 | } 26 | 27 | Player::Player(terra::World* world) : m_CollisionDetector(world), m_OnGround(false), m_Sneaking(false), m_Transform({}) { 28 | m_Transform.bounding_box = mc::AABB(mc::Vector3d(-0.3, 0, -0.3), mc::Vector3d(0.3, 1.8, 0.3)); 29 | m_Transform.max_speed = 14.3f; 30 | } 31 | 32 | bool Player::OnGround() { 33 | return m_OnGround; 34 | } 35 | 36 | void Player::Update(float dt) { 37 | const float kMaxAcceleration = 100.0f; 38 | const double kEpsilon = 0.0001; 39 | 40 | using vec3 = mc::Vector3d; 41 | 42 | vec3 horizontal_acceleration = m_Transform.acceleration; 43 | horizontal_acceleration.y = 0; 44 | 45 | horizontal_acceleration.Truncate(kMaxAcceleration); 46 | 47 | vec3 acceleration(horizontal_acceleration.x, -38 + m_Transform.acceleration.y, horizontal_acceleration.z); 48 | 49 | m_OnGround = false; 50 | 51 | m_CollisionDetector.ResolveCollisions(&m_Transform, dt, &m_OnGround); 52 | 53 | m_Transform.velocity += acceleration * dt; 54 | m_Transform.input_velocity += m_Transform.input_acceleration * dt; 55 | m_Transform.orientation += m_Transform.rotation * dt; 56 | 57 | if (m_Transform.velocity.LengthSq() < kEpsilon) m_Transform.velocity = vec3(0, 0, 0); 58 | if (m_Transform.input_velocity.LengthSq() < kEpsilon) m_Transform.input_velocity = vec3(0, 0, 0); 59 | 60 | float drag = 0.98 - (int)m_OnGround * 0.13; 61 | m_Transform.velocity *= drag; 62 | m_Transform.input_velocity *= drag; 63 | 64 | m_Transform.orientation = clamp(m_Transform.orientation, -M_TAU, M_TAU); 65 | 66 | vec3 horizontal_velocity = m_Transform.input_velocity; 67 | horizontal_velocity.y = 0; 68 | horizontal_velocity.Truncate(m_Transform.max_speed); 69 | 70 | m_Transform.input_velocity = horizontal_velocity + vec3(0, m_Transform.input_velocity.y, 0); 71 | 72 | m_Transform.acceleration = vec3(); 73 | m_Transform.input_acceleration = vec3(); 74 | m_Transform.rotation = 0.0f; 75 | } 76 | 77 | Game::Game(mc::protocol::packets::PacketDispatcher* dispatcher, GameWindow& window, const Camera& camera) 78 | : mc::protocol::packets::PacketHandler(dispatcher), 79 | m_NetworkClient(dispatcher, mc::protocol::Version::Minecraft_1_13_2), 80 | m_Window(window), 81 | m_Camera(camera), 82 | m_Sprinting(false), 83 | m_LastPositionTime(0) 84 | { 85 | window.RegisterMouseChange(std::bind(&Game::OnMouseChange, this, std::placeholders::_1, std::placeholders::_2)); 86 | window.RegisterMouseScroll(std::bind(&Game::OnMouseScroll, this, std::placeholders::_1, std::placeholders::_2)); 87 | window.RegisterMouseButton(std::bind(&Game::OnMousePress, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); 88 | 89 | m_NetworkClient.GetPlayerController()->SetHandleFall(true); 90 | m_NetworkClient.GetConnection()->GetSettings() 91 | .SetMainHand(mc::MainHand::Right) 92 | .SetViewDistance(4); 93 | 94 | m_NetworkClient.GetPlayerManager()->RegisterListener(this); 95 | 96 | dispatcher->RegisterHandler(mc::protocol::State::Play, mc::protocol::play::UpdateHealth, this); 97 | dispatcher->RegisterHandler(mc::protocol::State::Play, mc::protocol::play::EntityVelocity, this); 98 | dispatcher->RegisterHandler(mc::protocol::State::Play, mc::protocol::play::SpawnPosition, this); 99 | } 100 | 101 | void Game::CreatePlayer(terra::World* world) { 102 | m_Player = std::make_unique(world); 103 | } 104 | 105 | mc::Vector3d Game::GetPosition() { 106 | return m_Player->GetTransform().position; 107 | } 108 | 109 | void Game::Update() { 110 | UpdateClient(); 111 | 112 | float current_frame = (float)glfwGetTime(); 113 | 114 | if (current_frame - m_LastFrame < 1.0f / 60.0f) { 115 | return; 116 | } 117 | 118 | m_DeltaTime = current_frame - m_LastFrame; 119 | m_LastFrame = current_frame; 120 | 121 | mc::Vector3d front( 122 | std::cos(m_Camera.GetYaw()) * std::cos(0), 123 | std::sin(0), 124 | std::sin(m_Camera.GetYaw()) * std::cos(0) 125 | ); 126 | 127 | mc::Vector3d direction; 128 | 129 | if (m_Window.IsKeyDown(GLFW_KEY_W)) { 130 | direction += front; 131 | 132 | if (m_Window.IsKeyDown(GLFW_KEY_LEFT_CONTROL)) { 133 | m_Sprinting = true; 134 | } 135 | } 136 | 137 | if (m_Window.IsKeyDown(GLFW_KEY_S)) { 138 | direction -= front; 139 | m_Sprinting = false; 140 | } 141 | 142 | if (m_Window.IsKeyDown(GLFW_KEY_A)) { 143 | mc::Vector3d right = mc::Vector3Normalize(front.Cross(mc::Vector3d(0, 1, 0))); 144 | 145 | direction -= right; 146 | } 147 | 148 | if (m_Window.IsKeyDown(GLFW_KEY_D)) { 149 | mc::Vector3d right = mc::Vector3Normalize(front.Cross(mc::Vector3d(0, 1, 0))); 150 | 151 | direction += right; 152 | } 153 | 154 | if (!m_Player->OnGround()) { 155 | direction *= 0.2; 156 | } 157 | 158 | if (m_Sprinting) { 159 | if (direction.LengthSq() <= 0.0) { 160 | m_Sprinting = false; 161 | } else { 162 | direction *= 1.3f; 163 | } 164 | m_Camera.SetFov(glm::radians(90.0f)); 165 | } else { 166 | m_Camera.SetFov(glm::radians(80.0f)); 167 | } 168 | 169 | if (m_Window.IsKeyDown(GLFW_KEY_SPACE) && m_Player->OnGround()) { 170 | m_Player->GetTransform().input_acceleration += mc::Vector3d(0, 6 / m_DeltaTime, 0); 171 | } 172 | 173 | m_Player->GetTransform().max_speed = 4.3f + (int)m_Sprinting * 1.3f; 174 | m_Player->GetTransform().input_acceleration += direction * 85.0f; 175 | 176 | m_Player->Update(m_DeltaTime); 177 | 178 | glm::vec3 eye = math::VecToGLM(m_Player->GetTransform().position) + glm::vec3(0, 1.6, 0); 179 | 180 | m_Camera.SetPosition(eye); 181 | 182 | constexpr float kTickTime = 1000.0f / 20.0f / 1000.0f; 183 | 184 | if (current_frame > m_LastPositionTime + kTickTime && m_NetworkClient.GetConnection()->GetProtocolState() == mc::protocol::State::Play) { 185 | float yaw = m_Camera.GetYaw() - glm::radians(90.0f); 186 | float pitch = -m_Camera.GetPitch(); 187 | 188 | mc::protocol::packets::out::PlayerPositionAndLookPacket response(m_Player->GetTransform().position, yaw * 180.0f / 3.14159f, pitch * 180.0f / 3.14159f, m_Player->OnGround()); 189 | 190 | m_NetworkClient.GetConnection()->SendPacket(&response); 191 | 192 | m_LastPositionTime = current_frame; 193 | 194 | if (m_Player->IsSneaking() && !m_Window.IsKeyDown(GLFW_KEY_LEFT_SHIFT)) { 195 | mc::protocol::packets::out::EntityActionPacket packet(0, mc::protocol::packets::out::EntityActionPacket::Action::StopSneak); 196 | 197 | m_NetworkClient.GetConnection()->SendPacket(&packet); 198 | 199 | m_Player->SetSneaking(false); 200 | } else if (!m_Player->IsSneaking() && m_Window.IsKeyDown(GLFW_KEY_LEFT_SHIFT)) { 201 | mc::protocol::packets::out::EntityActionPacket packet(0, mc::protocol::packets::out::EntityActionPacket::Action::StartSneak); 202 | 203 | m_NetworkClient.GetConnection()->SendPacket(&packet); 204 | 205 | m_Player->SetSneaking(true); 206 | } 207 | } 208 | } 209 | 210 | void Game::UpdateClient() { 211 | try { 212 | m_NetworkClient.GetConnection()->CreatePacket(); 213 | } catch (std::exception& e) { 214 | std::wcout << e.what() << std::endl; 215 | } 216 | } 217 | 218 | void Game::OnMouseChange(double offset_x, double offset_y) { 219 | m_Camera.ProcessRotation((float)offset_x, (float)offset_y); 220 | } 221 | 222 | void Game::OnMouseScroll(double offset_x, double offset_y) { 223 | m_Camera.ProcessZoom((float)offset_y); 224 | } 225 | 226 | void Game::OnClientSpawn(mc::core::PlayerPtr player) { 227 | m_Player->GetTransform().position = player->GetEntity()->GetPosition(); 228 | m_Player->GetTransform().velocity = mc::Vector3d(); 229 | m_Player->GetTransform().acceleration = mc::Vector3d(); 230 | m_Player->GetTransform().orientation = player->GetEntity()->GetYaw() * 3.14159f / 180.0f; 231 | } 232 | 233 | void Game::HandlePacket(mc::protocol::packets::in::UpdateHealthPacket* packet) { 234 | 235 | } 236 | 237 | void Game::HandlePacket(mc::protocol::packets::in::EntityVelocityPacket* packet) { 238 | mc::EntityId eid = packet->GetEntityId(); 239 | 240 | auto playerEntity = m_NetworkClient.GetEntityManager()->GetPlayerEntity(); 241 | if (playerEntity && playerEntity->GetEntityId() == eid) { 242 | mc::Vector3d newVelocity = ToVector3d(packet->GetVelocity()) * 20.0 / 8000.0; 243 | 244 | std::cout << "Applying new velocity " << newVelocity << std::endl; 245 | m_Player->GetTransform().velocity = newVelocity; 246 | } 247 | } 248 | 249 | void Game::HandlePacket(mc::protocol::packets::in::SpawnPositionPacket* packet) { 250 | s64 x = packet->GetLocation().GetX(); 251 | s64 y = packet->GetLocation().GetY(); 252 | s64 z = packet->GetLocation().GetZ(); 253 | } 254 | 255 | // TODO: Temporary fun code 256 | template 257 | inline T Sign(T val) { 258 | return std::signbit(val) ? static_cast(-1) : static_cast(1); 259 | } 260 | 261 | // TODO: Temporary fun code 262 | inline mc::Vector3d BasisAxis(int basisIndex) { 263 | static const mc::Vector3d axes[3] = { mc::Vector3d(1, 0, 0), mc::Vector3d(0, 1, 0), mc::Vector3d(0, 0, 1) }; 264 | return axes[basisIndex]; 265 | } 266 | 267 | // TODO: Temporary fun code 268 | std::pair GetClosestNormal(const mc::Vector3d& pos, mc::AABB bounds) { 269 | mc::Vector3d center = bounds.min + (bounds.max - bounds.min) / 2; 270 | mc::Vector3d dim = bounds.max - bounds.min; 271 | mc::Vector3d offset = pos - center; 272 | 273 | double minDist = std::numeric_limits::max(); 274 | mc::Vector3d normal; 275 | 276 | for (int i = 0; i < 3; ++i) { 277 | double dist = dim[i] - std::abs(offset[i]); 278 | if (dist < minDist) { 279 | minDist = dist; 280 | normal = BasisAxis(i) * Sign(offset[i]); 281 | } 282 | } 283 | 284 | mc::Face face = mc::Face::North; 285 | 286 | if (normal.x == 1) { 287 | face = mc::Face::East; 288 | } else if (normal.x == -1) { 289 | face = mc::Face::West; 290 | } else if (normal.y == 1) { 291 | face = mc::Face::Top; 292 | } else if (normal.y == -1) { 293 | face = mc::Face::Bottom; 294 | } else if (normal.z == 1) { 295 | face = mc::Face::South; 296 | } 297 | 298 | return std::make_pair<>(normal, face); 299 | } 300 | 301 | // TODO: Temporary fun code 302 | bool RayCast(mc::world::World& world, mc::Vector3d from, mc::Vector3d direction, double range, mc::Vector3d& hit, mc::Vector3d& normal, mc::Face& face) { 303 | static const std::vector directions = { 304 | mc::Vector3d(0, 0, 0), 305 | mc::Vector3d(1, 0, 0), mc::Vector3d(-1, 0, 0), 306 | mc::Vector3d(0, 1, 0), mc::Vector3d(0, -1, 0), 307 | mc::Vector3d(0, 0, 1), mc::Vector3d(0, 0, -1), 308 | mc::Vector3d(0, 1, 1), mc::Vector3d(0, 1, -1), 309 | mc::Vector3d(1, 1, 0), mc::Vector3d(1, 1, 1), mc::Vector3d(1, 1, -1), 310 | mc::Vector3d(-1, 1, 0), mc::Vector3d(-1, 1, 1), mc::Vector3d(-1, 1, -1), 311 | 312 | mc::Vector3d(0, -1, 1), mc::Vector3d(0, -1, -1), 313 | mc::Vector3d(1, -1, 0), mc::Vector3d(1, -1, 1), mc::Vector3d(1, -1, -1), 314 | mc::Vector3d(-1, -1, 0), mc::Vector3d(-1, -1, 1), mc::Vector3d(-1, -1, -1) 315 | }; 316 | 317 | mc::Ray ray(from, direction); 318 | 319 | double closest_distance = std::numeric_limits::max(); 320 | mc::Vector3d closest_pos; 321 | mc::AABB closest_aabb; 322 | bool collided = false; 323 | 324 | for (double i = 0; i < range + 1; ++i) { 325 | mc::Vector3d position = from + direction * i; 326 | 327 | for (mc::Vector3d checkDirection : directions) { 328 | mc::Vector3d checkPos = position + checkDirection; 329 | mc::block::BlockPtr block = world.GetBlock(checkPos); 330 | 331 | if (block->IsOpaque()) { 332 | mc::AABB bounds = block->GetBoundingBox(checkPos); 333 | double distance; 334 | 335 | if (bounds.Intersects(ray, &distance)) { 336 | if (distance < 0 || distance > range) continue; 337 | 338 | if (distance < closest_distance) { 339 | closest_distance = distance; 340 | closest_pos = from + direction * closest_distance; 341 | closest_aabb = bounds; 342 | collided = true; 343 | } 344 | } 345 | } 346 | } 347 | } 348 | 349 | if (collided) { 350 | hit = closest_pos; 351 | std::tie(normal, face) = GetClosestNormal(hit, closest_aabb); 352 | return true; 353 | } 354 | 355 | return false; 356 | } 357 | 358 | // TODO: Temporary fun code 359 | void Game::OnMousePress(int button, int action, int mods) { 360 | if (button == GLFW_MOUSE_BUTTON_1 && action == GLFW_PRESS) { 361 | using namespace mc::protocol::packets::out; 362 | 363 | auto& world = *m_NetworkClient.GetWorld(); 364 | mc::Vector3d position(m_Camera.GetPosition().x, m_Camera.GetPosition().y, m_Camera.GetPosition().z); 365 | mc::Vector3d forward(m_Camera.GetFront().x, m_Camera.GetFront().y, m_Camera.GetFront().z); 366 | mc::Vector3d hit; 367 | mc::Vector3d normal; 368 | mc::Face face; 369 | 370 | if (RayCast(world, position, forward, 5.0, hit, normal, face)) { 371 | 372 | { 373 | PlayerDiggingPacket::Status status = PlayerDiggingPacket::Status::StartedDigging; 374 | PlayerDiggingPacket packet(status, mc::ToVector3i(hit + forward * 0.1), mc::Face::West); 375 | 376 | m_NetworkClient.GetConnection()->SendPacket(&packet); 377 | } 378 | 379 | { 380 | PlayerDiggingPacket::Status status = PlayerDiggingPacket::Status::FinishedDigging; 381 | PlayerDiggingPacket packet(status, mc::ToVector3i(hit + forward * 0.1), mc::Face::West); 382 | 383 | m_NetworkClient.GetConnection()->SendPacket(&packet); 384 | } 385 | } 386 | 387 | AnimationPacket animation; 388 | m_NetworkClient.GetConnection()->SendPacket(&animation); 389 | 390 | } else if (button == GLFW_MOUSE_BUTTON_2 && action == GLFW_PRESS) { 391 | auto& world = *m_NetworkClient.GetWorld(); 392 | mc::Vector3d position(m_Camera.GetPosition().x, m_Camera.GetPosition().y, m_Camera.GetPosition().z); 393 | mc::Vector3d forward(m_Camera.GetFront().x, m_Camera.GetFront().y, m_Camera.GetFront().z); 394 | mc::Vector3d hit; 395 | mc::Vector3d normal; 396 | mc::Face face; 397 | 398 | if (RayCast(world, position, forward, 5.0, hit, normal, face)) { 399 | mc::inventory::Inventory& inventory = *m_NetworkClient.GetInventoryManager()->GetPlayerInventory(); 400 | 401 | auto& hotbar = m_NetworkClient.GetHotbar(); 402 | s32 slot_id = hotbar.GetSelectedSlot() + mc::inventory::Inventory::HOTBAR_SLOT_START; 403 | 404 | mc::inventory::Slot slot = inventory.GetItem(slot_id); 405 | 406 | // Item ids are separate from block ids. 407 | const u32 draw_block = 9; 408 | 409 | if (slot.GetItemId() != draw_block) { 410 | mc::protocol::packets::out::CreativeInventoryActionPacket packet(slot_id, mc::inventory::Slot(draw_block, 1, 0)); 411 | m_NetworkClient.GetConnection()->SendPacket(&packet); 412 | } 413 | 414 | mc::Vector3i target = mc::ToVector3i(hit + forward * 0.1); 415 | 416 | mc::protocol::packets::out::PlayerBlockPlacementPacket packet(target, face, mc::Hand::Main, mc::Vector3f(1.0f, 0.0f, 0.0f)); 417 | m_NetworkClient.GetConnection()->SendPacket(&packet); 418 | } 419 | } 420 | } 421 | 422 | } // ns terra 423 | -------------------------------------------------------------------------------- /terracotta/assets/AssetLoader.cpp: -------------------------------------------------------------------------------- 1 | #include "AssetLoader.h" 2 | #include "AssetCache.h" 3 | #include "zip/ZipArchive.h" 4 | #include "stb_image.h" 5 | #include "../block/BlockModel.h" 6 | #include "../block/BlockVariant.h" 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | namespace terra { 15 | namespace assets { 16 | 17 | std::string GetBlockNameFromPath(const std::string& path) { 18 | auto ext_pos = path.find_last_of('.'); 19 | auto pos = path.find_last_of('/'); 20 | auto filename = path.substr(pos + 1); 21 | auto ext_size = path.size() - ext_pos; 22 | 23 | return filename.substr(0, filename.length() - ext_size); 24 | } 25 | 26 | glm::vec3 GetVectorFromJson(const mc::json& node) { 27 | glm::vec3 result(0, 0, 0); 28 | 29 | int index = 0; 30 | 31 | for (auto& n : node) { 32 | if (n.is_number()) { 33 | result[index++] = n.get() / static_cast(16.0); 34 | } 35 | } 36 | 37 | return result; 38 | } 39 | 40 | std::pair GetUVFromJson(const mc::json& node) { 41 | glm::vec2 from(0, 0); 42 | glm::vec2 to(1, 1); 43 | 44 | int index = 0; 45 | 46 | for (auto& n : node) { 47 | if (n.is_number()) { 48 | if (index < 2) { 49 | from[index++] = n.get() / static_cast(16.0); 50 | } else { 51 | to[index - 2] = n.get() / static_cast(16.0); 52 | ++index; 53 | } 54 | } 55 | } 56 | 57 | return std::make_pair<>(from, to); 58 | } 59 | 60 | AssetLoader::AssetLoader(AssetCache& cache) 61 | : m_Cache(cache) 62 | { 63 | 64 | } 65 | 66 | bool AssetLoader::LoadArchive(const std::string& archive_path, const std::string& blocks_path) { 67 | if (!LoadBlockStates(blocks_path)) { 68 | return false; 69 | } 70 | 71 | terra::assets::ZipArchive archive; 72 | 73 | if (!archive.Open(archive_path.c_str())) { 74 | std::cerr << "Failed to open archive.\n"; 75 | return false; 76 | } 77 | 78 | if (LoadBlockModels(archive) <= 0) { 79 | return false; 80 | } 81 | 82 | LoadBlockVariants(archive); 83 | 84 | m_Cache.GetTextures().Generate(); 85 | 86 | // Fancy graphics mode seems to do something different than using cullface. 87 | for (auto& model : m_Cache.GetBlockModels("leaves")) { 88 | for (auto& element : model->GetElements()) { 89 | for (auto& face : element.GetFaces()) { 90 | face.cull_face = block::BlockFace::None; 91 | face.tint_index = 1; 92 | } 93 | } 94 | } 95 | 96 | return true; 97 | } 98 | 99 | bool AssetLoader::LoadBlockStates(const std::string& filename) { 100 | std::ifstream in(filename); 101 | 102 | if (!in) { 103 | std::cerr << "Failed to open " << filename << std::endl; 104 | return false; 105 | } 106 | 107 | std::string data; 108 | in.seekg(0, std::ios::end); 109 | std::size_t size = static_cast(in.tellg()); 110 | in.seekg(0, std::ios::beg); 111 | 112 | data.resize(size); 113 | 114 | in.read(&data[0], size); 115 | 116 | mc::json root; 117 | 118 | try { 119 | root = mc::json::parse(data); 120 | } catch (mc::json::parse_error& e) { 121 | std::cerr << e.what() << std::endl; 122 | return false; 123 | } 124 | 125 | std::size_t max_id = 0; 126 | 127 | for (auto& kv : root.items()) { 128 | mc::json states_node = kv.value().value("states", mc::json()); 129 | 130 | if (!states_node.is_array()) continue; 131 | 132 | for (auto& state_node : states_node) { 133 | u32 id = state_node.value("id", 0); 134 | 135 | if (id > max_id) { 136 | max_id = id; 137 | } 138 | } 139 | } 140 | 141 | m_Cache.SetMaxBlockId(max_id); 142 | 143 | for (auto& kv : root.items()) { 144 | std::string block_name = kv.key(); 145 | mc::json states_node = kv.value().value("states", mc::json()); 146 | 147 | if (!states_node.is_array()) continue; 148 | 149 | for (auto& state_node : states_node) { 150 | std::unique_ptr state = terra::block::BlockState::FromJSON(state_node); 151 | 152 | m_Cache.AddBlockState(std::move(state)); 153 | } 154 | } 155 | 156 | return true; 157 | } 158 | 159 | bool AssetLoader::LoadTexture(terra::assets::ZipArchive& archive, const std::string& path, TextureHandle* handle) { 160 | if (m_Cache.GetTextures().GetTexture(path, handle)) return true; 161 | 162 | std::string texture_path = "assets/minecraft/textures/" + path; 163 | 164 | int width, height, channels; 165 | std::size_t texture_size; 166 | char* texture_raw = archive.ReadFile(texture_path.c_str(), &texture_size); 167 | 168 | if (texture_raw == nullptr) return false; 169 | 170 | unsigned char* image = stbi_load_from_memory(reinterpret_cast(texture_raw), (int)texture_size, &width, &height, &channels, STBI_rgb_alpha); 171 | 172 | archive.FreeFileData(texture_raw); 173 | 174 | if (image == nullptr) return false; 175 | 176 | if (width == 16) { 177 | std::size_t size = 16 * 16 * 4; 178 | 179 | *handle = m_Cache.AddTexture(path, std::string(reinterpret_cast(image), size)); 180 | } 181 | 182 | stbi_image_free(image); 183 | 184 | return true; 185 | } 186 | 187 | std::unique_ptr AssetLoader::LoadBlockModel(terra::assets::ZipArchive& archive, const std::string& path, TextureMap& texture_map, std::vector& intermediates) { 188 | std::string block_path = "block/" + GetBlockNameFromPath(path); 189 | 190 | size_t size; 191 | char* raw = archive.ReadFile(path.c_str(), &size); 192 | 193 | if (raw) { 194 | std::string contents(raw, size); 195 | 196 | archive.FreeFileData(raw); 197 | 198 | mc::json root; 199 | try { 200 | root = mc::json::parse(contents); 201 | } catch (mc::json::parse_error& e) { 202 | std::cerr << e.what() << std::endl; 203 | return nullptr; 204 | } 205 | 206 | auto parent_node = root.value("parent", mc::json()); 207 | if (parent_node.is_string()) { 208 | std::string parent_path = "assets/minecraft/models/" + parent_node.get() + ".json"; 209 | 210 | // Load all of the texture mappings from the parent. 211 | LoadBlockModel(archive, parent_path, texture_map, intermediates); 212 | } 213 | 214 | auto model = std::make_unique(); 215 | 216 | if (model != nullptr) { 217 | // Load each texture listed as part of this model. 218 | auto textures_node = root.value("textures", mc::json()); 219 | 220 | for (auto& kv : textures_node.items()) { 221 | std::string texture_path = kv.value().get(); 222 | 223 | texture_map[kv.key()] = texture_path; 224 | } 225 | 226 | auto elements_node = root.value("elements", mc::json()); 227 | 228 | // Create each element of the model and resolve texture linking 229 | for (auto& element_node : elements_node) { 230 | glm::vec3 from = GetVectorFromJson(element_node.value("from", mc::json())); 231 | glm::vec3 to = GetVectorFromJson(element_node.value("to", mc::json())); 232 | 233 | IntermediateElement element; 234 | element.from = from; 235 | element.to = to; 236 | element.shade = element_node.value("shade", true); 237 | 238 | auto rotation_node = element_node.value("rotation", mc::json()); 239 | 240 | if (rotation_node.is_object()) { 241 | element.rotation.origin = GetVectorFromJson(rotation_node.value("origin", mc::json())); 242 | std::string axis = rotation_node.value("axis", "y"); 243 | 244 | if (axis == "x") { 245 | element.rotation.axis = glm::vec3(1, 0, 0); 246 | } else if (axis == "y") { 247 | element.rotation.axis = glm::vec3(0, 1, 0); 248 | } else { 249 | element.rotation.axis = glm::vec3(0, 0, 1); 250 | } 251 | 252 | element.rotation.angle = rotation_node.value("angle", 0.0f); 253 | element.rotation.rescale = rotation_node.value("rescale", false); 254 | } 255 | 256 | auto faces_node = element_node.value("faces", mc::json()); 257 | 258 | for (auto& kv : faces_node.items()) { 259 | block::BlockFace face = block::face_from_string(kv.key()); 260 | auto face_node = kv.value(); 261 | 262 | block::BlockFace cull_face = block::face_from_string(face_node.value("cullface", "")); 263 | 264 | auto texture_node = face_node.value("texture", mc::json()); 265 | 266 | if (texture_node.is_string()) { 267 | std::string texture = texture_node.get(); 268 | 269 | IntermediateFace renderable; 270 | 271 | renderable.texture = texture; 272 | renderable.face = face; 273 | renderable.cull_face = cull_face; 274 | renderable.tint_index = face_node.value("tintindex", -1); 275 | 276 | std::tie(renderable.uv_from, renderable.uv_to) = GetUVFromJson(face_node.value("uv", mc::json())); 277 | 278 | element.faces.push_back(renderable); 279 | } 280 | } 281 | 282 | intermediates.push_back(element); 283 | } 284 | 285 | return model; 286 | } 287 | } 288 | 289 | return nullptr; 290 | } 291 | 292 | std::size_t AssetLoader::LoadBlockModels(terra::assets::ZipArchive& archive) { 293 | std::size_t count = 0; 294 | 295 | for (std::string model_path : archive.ListFiles("assets/minecraft/models/block")) { 296 | TextureMap texture_map; 297 | std::vector intermediates; 298 | 299 | auto model = LoadBlockModel(archive, model_path, texture_map, intermediates); 300 | 301 | // Resolve intermediate textures 302 | for (const IntermediateElement& intermediate : intermediates) { 303 | block::BlockElement element(intermediate.from, intermediate.to); 304 | 305 | element.SetShouldShade(intermediate.shade); 306 | element.GetRotation() = intermediate.rotation; 307 | 308 | for (const auto& intermediate_renderable : intermediate.faces) { 309 | std::string texture = intermediate_renderable.texture; 310 | 311 | while (!texture.empty() && texture[0] == '#') { 312 | texture = texture_map[texture.substr(1)]; 313 | } 314 | 315 | TextureHandle handle; 316 | if (LoadTexture(archive, texture + ".png", &handle)) { 317 | block::RenderableFace renderable; 318 | 319 | renderable.face = intermediate_renderable.face; 320 | renderable.cull_face = intermediate_renderable.cull_face; 321 | renderable.texture = handle; 322 | renderable.tint_index = intermediate_renderable.tint_index; 323 | renderable.uv_from = intermediate_renderable.uv_from; 324 | renderable.uv_to = intermediate_renderable.uv_to; 325 | 326 | element.AddFace(renderable); 327 | } 328 | } 329 | 330 | model->AddElement(element); 331 | } 332 | 333 | std::string block_path = "block/" + GetBlockNameFromPath(model_path); 334 | 335 | m_Cache.AddBlockModel(block_path, std::move(model)); 336 | ++count; 337 | } 338 | 339 | return count; 340 | } 341 | 342 | void AssetLoader::LoadBlockVariants(terra::assets::ZipArchive& archive) { 343 | for (std::string block_state_path : archive.ListFiles("assets/minecraft/blockstates")) { 344 | size_t size; 345 | char* raw = archive.ReadFile(block_state_path.c_str(), &size); 346 | 347 | if (raw) { 348 | std::string contents(raw, size); 349 | 350 | mc::json root; 351 | try { 352 | root = mc::json::parse(contents); 353 | } catch (mc::json::parse_error& e) { 354 | std::cerr << e.what() << std::endl; 355 | continue; 356 | } 357 | 358 | mc::json variants_node = root.value("variants", mc::json()); 359 | 360 | if (!variants_node.is_object()) continue; 361 | 362 | for (auto& kv : variants_node.items()) { 363 | std::string variant_name = kv.key(); 364 | mc::json variant = kv.value(); 365 | 366 | if (variant.is_array()) { 367 | for (mc::json& variant_sub : variant) { 368 | mc::json model_node = variant_sub.value("model", mc::json()); 369 | if (model_node.is_string()) { 370 | std::string model_path = model_node.get(); 371 | 372 | block::BlockModel* model = m_Cache.GetBlockModel(model_path); 373 | 374 | if (model == nullptr) { 375 | std::cerr << "Could not find block model " << model_path << std::endl; 376 | continue; 377 | } 378 | 379 | auto pos = block_state_path.find_last_of('/'); 380 | auto filename = block_state_path.substr(pos + 1); 381 | auto state = filename.substr(0, filename.length() - 5); 382 | 383 | mc::block::BlockPtr block = mc::block::BlockRegistry::GetInstance()->GetBlock("minecraft:" + state); 384 | 385 | if (block == nullptr) { 386 | std::cerr << "Could not find block minecraft:" << state << std::endl; 387 | continue; 388 | } 389 | 390 | double x = variant_sub.value("x", 0); 391 | double y = variant_sub.value("y", 0); 392 | double z = variant_sub.value("z", 0); 393 | 394 | std::unique_ptr variant = std::make_unique(model, variant_name, block); 395 | 396 | variant->SetRotation(glm::vec3(x, y, z)); 397 | 398 | m_Cache.AddVariantModel(std::move(variant)); 399 | } 400 | } 401 | } else { 402 | mc::json model_node = variant.value("model", mc::json()); 403 | if (model_node.is_string()) { 404 | std::string model_path = model_node.get(); 405 | 406 | block::BlockModel* model = m_Cache.GetBlockModel(model_path); 407 | 408 | if (model == nullptr) { 409 | std::cerr << "Could not find block model " << model_path << std::endl; 410 | continue; 411 | } 412 | 413 | auto pos = block_state_path.find_last_of('/'); 414 | auto filename = block_state_path.substr(pos + 1); 415 | auto state = filename.substr(0, filename.length() - 5); 416 | 417 | mc::block::BlockPtr block = mc::block::BlockRegistry::GetInstance()->GetBlock("minecraft:" + state); 418 | 419 | if (block == nullptr) { 420 | std::cerr << "Could not find block minecraft:" << state << std::endl; 421 | continue; 422 | } 423 | 424 | double x = variant.value("x", 0); 425 | double y = variant.value("y", 0); 426 | double z = variant.value("z", 0); 427 | 428 | std::unique_ptr variant = std::make_unique(model, variant_name, block); 429 | 430 | variant->SetRotation(glm::vec3(x, y, z)); 431 | 432 | m_Cache.AddVariantModel(std::move(variant)); 433 | } 434 | } 435 | } 436 | } 437 | } 438 | } 439 | 440 | } // ns assets 441 | } // ns terra 442 | -------------------------------------------------------------------------------- /terracotta/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "render/Shader.h" 13 | #include "Camera.h" 14 | #include "Game.h" 15 | #include "ChatWindow.h" 16 | #include "math/TypeUtil.h" 17 | 18 | #include "render/ChunkMesh.h" 19 | #include "render/ChunkMeshGenerator.h" 20 | #include "assets/AssetCache.h" 21 | #include "assets/AssetLoader.h" 22 | #include "lib/imgui/imgui.h" 23 | #include "lib/imgui/imgui_impl_glfw.h" 24 | #include "lib/imgui/imgui_impl_opengl3.h" 25 | 26 | #include "World.h" 27 | 28 | #include 29 | 30 | #define STB_IMAGE_IMPLEMENTATION 31 | #include "assets/stb_image.h" 32 | 33 | GLuint CreateBlockVAO(); 34 | GLFWwindow* InitializeWindow(); 35 | 36 | std::unique_ptr g_GameWindow; 37 | std::unique_ptr g_AssetCache; 38 | 39 | struct CubeVertex { 40 | glm::vec3 position; 41 | glm::vec3 normal; 42 | glm::vec2 uv; 43 | u32 texture_index; 44 | glm::vec3 tint; 45 | 46 | CubeVertex(glm::vec3 pos, glm::vec3 normal, glm::vec2 uv, u32 tex_index, glm::vec3 tint) : position(pos), normal(normal), uv(uv), texture_index(tex_index), tint(tint) { } 47 | }; 48 | 49 | int main(int argc, char* argvp[]) { 50 | mc::protocol::Version version = mc::protocol::Version::Minecraft_1_13_2; 51 | mc::block::BlockRegistry::GetInstance()->RegisterVanillaBlocks(version); 52 | GLFWwindow* window = InitializeWindow(); 53 | 54 | if (window == nullptr) { 55 | return 1; 56 | } 57 | 58 | std::string server = "127.0.0.1"; 59 | u16 port = 25565; 60 | std::string username = "terracotta"; 61 | std::string password = ""; 62 | 63 | std::ifstream config_file("config.json"); 64 | 65 | if (config_file.is_open()) { 66 | mc::json config_root; 67 | 68 | try { 69 | config_root = mc::json::parse(config_file); 70 | } catch (mc::json::parse_error& e) { 71 | std::cerr << e.what() << std::endl; 72 | return 1; 73 | } 74 | 75 | mc::json login_node = config_root.value("login", mc::json()); 76 | 77 | if (login_node.is_object()) { 78 | username = login_node.value("username", ""); 79 | password = login_node.value("password", ""); 80 | server = login_node.value("server", ""); 81 | port = login_node.value("port", static_cast(25565)); 82 | } 83 | } 84 | 85 | std::cout << "Checking layers" << std::endl; 86 | 87 | GLint max_layers; 88 | glGetIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS, &max_layers); 89 | std::cout << "Layers: " << max_layers << std::endl; 90 | 91 | g_AssetCache = std::make_unique(); 92 | 93 | terra::assets::AssetLoader asset_loader(*g_AssetCache); 94 | 95 | if (!asset_loader.LoadArchive("1.13.2.jar", "blocks.json")) { 96 | std::cerr << "Failed to load assets." << std::endl; 97 | return 1; 98 | } 99 | 100 | int width, height; 101 | glfwGetFramebufferSize(window, &width, &height); 102 | 103 | glViewport(0, 0, width, height); 104 | 105 | glClearColor(184.0f / 255, 210.0f / 255, 1.0f, 1.0f); 106 | 107 | glEnable(GL_DEPTH_TEST); 108 | glEnable(GL_CULL_FACE); 109 | glEnable(GL_MULTISAMPLE); 110 | 111 | glDepthFunc(GL_LEQUAL); 112 | 113 | terra::render::Shader shader; 114 | 115 | if (!shader.Initialize("shaders/block.vert", "shaders/block.frag")) { 116 | std::cerr << "Failed to initialize shader program.\n"; 117 | return 1; 118 | } 119 | 120 | shader.Use(); 121 | 122 | GLuint model_uniform = shader.GetUniform("model"); 123 | GLuint view_uniform = shader.GetUniform("view"); 124 | GLuint proj_uniform = shader.GetUniform("projection"); 125 | GLuint sampler_uniform = shader.GetUniform("texarray"); 126 | 127 | glUniform1i(sampler_uniform, 0); 128 | 129 | GLuint block_vao = CreateBlockVAO(); 130 | 131 | float aspect_ratio = width / (float)height; 132 | float fov = glm::radians(80.0f); 133 | 134 | float view_distance = 16.0f * 16.0f; 135 | 136 | mc::protocol::packets::PacketDispatcher dispatcher; 137 | terra::Camera camera(glm::vec3(0.0f, 0.0f, 9.0f), fov, aspect_ratio, 0.1f, view_distance); 138 | terra::Game game(&dispatcher, *g_GameWindow, camera); 139 | 140 | try { 141 | std::cout << "Logging in." << std::endl; 142 | if (!game.GetNetworkClient().Login(server, port, username, password, mc::core::UpdateMethod::Manual)) { 143 | std::wcout << "Failed to login." << std::endl; 144 | return 1; 145 | } 146 | } catch (std::exception& e) { 147 | std::wcout << e.what() << std::endl; 148 | return 1; 149 | } 150 | 151 | terra::World world(game.GetNetworkClient().GetDispatcher()); 152 | 153 | auto mesh_gen = std::make_shared(&world, camera.GetPosition()); 154 | 155 | terra::ChatWindow chat(game.GetNetworkClient().GetDispatcher(), game.GetNetworkClient().GetConnection()); 156 | 157 | game.CreatePlayer(&world); 158 | 159 | while (!glfwWindowShouldClose(window)) { 160 | glfwPollEvents(); 161 | 162 | game.Update(); 163 | 164 | ImGui_ImplOpenGL3_NewFrame(); 165 | ImGui_ImplGlfw_NewFrame(); 166 | ImGui::NewFrame(); 167 | 168 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 169 | 170 | auto&& camera = game.GetCamera(); 171 | glm::mat4 viewMatrix = camera.GetViewMatrix(); 172 | terra::math::volumes::Frustum frustum = camera.GetFrustum(); 173 | 174 | shader.Use(); 175 | 176 | glUniformMatrix4fv(view_uniform, 1, GL_FALSE, glm::value_ptr(viewMatrix)); 177 | glUniformMatrix4fv(proj_uniform, 1, GL_FALSE, glm::value_ptr(camera.GetPerspectiveMatrix())); 178 | 179 | auto player_chunk = game.GetNetworkClient().GetWorld()->GetChunk(mc::ToVector3i(game.GetPosition())); 180 | if (player_chunk != nullptr) { 181 | for (auto&& kv : *mesh_gen) { 182 | terra::render::ChunkMesh* mesh = kv.second.get(); 183 | mc::Vector3i chunk_base = kv.first; 184 | 185 | mc::Vector3d min = mc::ToVector3d(chunk_base); 186 | mc::Vector3d max = mc::ToVector3d(chunk_base) + mc::Vector3d(16, 16, 16); 187 | mc::AABB chunk_bounds(min, max); 188 | 189 | if (!frustum.Intersects(chunk_bounds)) continue; 190 | 191 | mesh->Render(model_uniform); 192 | } 193 | 194 | glBindVertexArray(block_vao); 195 | 196 | auto entity_manager = game.GetNetworkClient().GetEntityManager(); 197 | for (auto f = entity_manager->begin(); f != entity_manager->end(); ++f) { 198 | auto&& entity = f->second; 199 | if (entity_manager->GetPlayerEntity() == entity) continue; 200 | mc::Vector3d position = entity->GetPosition() + mc::Vector3d(0, 0.5, 0); 201 | 202 | glm::mat4 model(1.0); 203 | model = glm::translate(model, glm::vec3(position.x, position.y + 0.5, position.z)); 204 | model = glm::rotate(model, entity->GetYaw(), glm::vec3(0, 1, 0)); 205 | model = glm::scale(model, glm::vec3(1, 2, 1)); 206 | 207 | glUniformMatrix4fv(model_uniform, 1, GL_FALSE, glm::value_ptr(model)); 208 | 209 | glDrawArrays(GL_TRIANGLES, 0, 36); 210 | } 211 | } 212 | 213 | { 214 | ImGui::SetNextWindowBgAlpha(0.3f); 215 | ImGui::SetNextWindowPos(ImVec2(0, 0), ImGuiCond_Always); 216 | ImGuiWindowFlags flags = ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav; 217 | ImGui::Begin("debug_text", 0, flags); 218 | 219 | ImGui::Text("%.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate); 220 | 221 | ImGui::End(); 222 | } 223 | 224 | chat.Render(); 225 | 226 | ImGui::Render(); 227 | ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); 228 | ImGui::EndFrame(); 229 | 230 | glBindVertexArray(0); 231 | 232 | glUseProgram(0); 233 | glfwSwapBuffers(window); 234 | 235 | mesh_gen->ProcessChunks(); 236 | } 237 | 238 | mesh_gen.reset(); 239 | 240 | return 0; 241 | } 242 | 243 | GLuint CreateBlockVAO() { 244 | GLuint vao = 0; 245 | 246 | glGenVertexArrays(1, &vao); 247 | glBindVertexArray(vao); 248 | 249 | GLfloat tex_index = 2; 250 | 251 | GLfloat vertices[] = { 252 | // Positions // Normals // Texture Coords // Texture Index // Tint 253 | -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, tex_index, 0.0, 0.0, 0.0, // Front Bottom Left 254 | 0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, tex_index, 0.0, 0.0, 0.0, // Front Bottom Right 255 | 0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, tex_index, 0.0, 0.0, 0.0,// Front Top Right 256 | 0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, tex_index, 0.0, 0.0, 0.0,// Front Top Right 257 | -0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, tex_index, 0.0, 0.0, 0.0,// Front Top Left 258 | -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, tex_index, 0.0, 0.0, 0.0,// Front Bottom Left 259 | 260 | -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, tex_index, 0.0, 0.0, 0.0,// Back Bottom Left 261 | -0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, tex_index, 0.0, 0.0, 0.0,// Back Top Left 262 | 0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, tex_index, 0.0, 0.0, 0.0,// Back Top Right 263 | 0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, tex_index, 0.0, 0.0, 0.0,// Back Top Right 264 | 0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f, tex_index, 0.0, 0.0, 0.0,// Back Bottom Right 265 | -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, tex_index, 0.0, 0.0, 0.0, // Back Bottom Left 266 | 267 | -0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, tex_index, 0.0, 0.0, 0.0, // Front Top Left 268 | -0.5f, 0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f, tex_index, 0.0, 0.0, 0.0,// Back Top Left 269 | -0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, tex_index, 0.0, 0.0, 0.0, // Back Bottom Left 270 | -0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, tex_index, 0.0, 0.0, 0.0,// Back Bottom Left 271 | -0.5f, -0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, tex_index, 0.0, 0.0, 0.0, // Front Bottom Left 272 | -0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, tex_index, 0.0, 0.0, 0.0, // Front Top Left 273 | 274 | 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, tex_index, 0.0, 0.0, 0.0,// Front Top Right 275 | 0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, tex_index, 0.0, 0.0, 0.0,// Front Bottom Right 276 | 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, tex_index, 0.0, 0.0, 0.0,// Back Bottom Right 277 | 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, tex_index, 0.0, 0.0, 0.0,// Back Bottom Right 278 | 0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, tex_index, 0.0, 0.0, 0.0,// Back Top Right 279 | 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, tex_index, 0.0, 0.0, 0.0,// Front Top Right 280 | 281 | -0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, tex_index, 0.0, 0.0, 0.0,// Back Bottom Left 282 | 0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f, tex_index, 0.0, 0.0, 0.0,// Back Bottom Right 283 | 0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, tex_index, 0.0, 0.0, 0.0,// Front Bottom Right 284 | 0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, tex_index, 0.0, 0.0, 0.0,// Front Bottom Right 285 | -0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, tex_index, 0.0, 0.0, 0.0,// Front Bottom Left 286 | -0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, tex_index, 0.0, 0.0, 0.0,// Back Bottom Left 287 | 288 | -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, tex_index, 0.0, 0.0, 0.0,// Back Top Left 289 | -0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, tex_index, 0.0, 0.0, 0.0,// Front Top Left 290 | 0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, tex_index, 0.0, 0.0, 0.0,// Front Top Right 291 | 0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, tex_index, 0.0, 0.0, 0.0,// Front Top Right 292 | 0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, tex_index, 0.0, 0.0, 0.0,// Back Top Right 293 | -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, tex_index, 0.0, 0.0, 0.0,// Back Top Left 294 | }; 295 | 296 | GLuint vbo; 297 | glGenBuffers(1, &vbo); 298 | glBindBuffer(GL_ARRAY_BUFFER, vbo); 299 | glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); 300 | 301 | // Position 302 | glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(CubeVertex), (void*)offsetof(CubeVertex, position)); 303 | glEnableVertexAttribArray(0); 304 | 305 | // TextureCoord 306 | glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(CubeVertex), (void*)offsetof(CubeVertex, uv)); 307 | glEnableVertexAttribArray(1); 308 | 309 | // TextureCoord 310 | glVertexAttribIPointer(2, 1, GL_UNSIGNED_INT, sizeof(CubeVertex), (void*)offsetof(CubeVertex, texture_index)); 311 | glEnableVertexAttribArray(2); 312 | 313 | // Tint 314 | glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, sizeof(CubeVertex), (void*)offsetof(CubeVertex, tint)); 315 | glEnableVertexAttribArray(3); 316 | 317 | return vao; 318 | } 319 | 320 | void APIENTRY OpenGLDebugOutputCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userParam) { 321 | std::cerr << "OpenGL error: " << message << std::endl; 322 | } 323 | 324 | GLFWwindow* InitializeWindow() { 325 | glfwInit(); 326 | 327 | GLFWmonitor* monitor = glfwGetPrimaryMonitor(); 328 | 329 | const GLFWvidmode* mode = glfwGetVideoMode(monitor); 330 | 331 | glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); 332 | glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); 333 | glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); 334 | glfwWindowHint(GLFW_RESIZABLE, false); 335 | glfwWindowHint(GLFW_RED_BITS, mode->redBits); 336 | glfwWindowHint(GLFW_GREEN_BITS, mode->greenBits); 337 | glfwWindowHint(GLFW_BLUE_BITS, mode->blueBits); 338 | glfwWindowHint(GLFW_REFRESH_RATE, mode->refreshRate); 339 | glfwWindowHint(GLFW_SAMPLES, 8); 340 | 341 | GLFWwindow* window = glfwCreateWindow(960, 540, "Terracotta", nullptr, nullptr); 342 | //GLFWwindow* window = glfwCreateWindow(mode->width, mode->height, "Terracotta", glfwGetPrimaryMonitor(), nullptr); 343 | 344 | if (window == nullptr) { 345 | std::cerr << "Failed to create glfw window.\n"; 346 | return nullptr; 347 | } 348 | 349 | std::cout << "Creating GameWindow" << std::endl; 350 | 351 | g_GameWindow = std::make_unique(window); 352 | 353 | std::cout << "Setting context" << std::endl; 354 | glfwMakeContextCurrent(window); 355 | std::cout << "Setting user pointer" << std::endl; 356 | glfwSetWindowUserPointer(window, g_GameWindow.get()); 357 | std::cout << "Setting input mode" << std::endl; 358 | glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); 359 | 360 | std::cout << "Setting key callback" << std::endl; 361 | glfwSetKeyCallback(window, [](GLFWwindow* window, int key, int code, int action, int mode) { 362 | terra::GameWindow* game_window = static_cast(glfwGetWindowUserPointer(window)); 363 | game_window->OnKeyChange(key, code, action, mode); 364 | }); 365 | 366 | std::cout << "Setting cursor callback" << std::endl; 367 | glfwSetCursorPosCallback(window, [](GLFWwindow* window, double x, double y) { 368 | terra::GameWindow* game_window = static_cast(glfwGetWindowUserPointer(window)); 369 | game_window->OnMouseMove(x, y); 370 | }); 371 | 372 | std::cout << "Setting mouse button callback" << std::endl; 373 | glfwSetMouseButtonCallback(window, [](GLFWwindow* window, int button, int action, int mods) { 374 | terra::GameWindow* game_window = static_cast(glfwGetWindowUserPointer(window)); 375 | game_window->OnMouseButton(button, action, mods); 376 | }); 377 | 378 | std::cout << "Setting scroll callback" << std::endl; 379 | glfwSetScrollCallback(window, [](GLFWwindow* window, double offset_x, double offset_y) { 380 | terra::GameWindow* game_window = static_cast(glfwGetWindowUserPointer(window)); 381 | game_window->OnMouseScroll(offset_x, offset_y); 382 | }); 383 | 384 | std::cout << "Setting swap interval" << std::endl; 385 | glfwSwapInterval(0); 386 | 387 | std::cout << "Initializing glew" << std::endl; 388 | glewExperimental = GL_TRUE; 389 | if (glewInit() != GLEW_OK) { 390 | std::cerr << "Failed to initialize glew\n"; 391 | return nullptr; 392 | } 393 | 394 | #ifdef _DEBUG 395 | //std::cout << "Setting debug callback" << std::endl; 396 | //glDebugMessageCallback(OpenGLDebugOutputCallback, nullptr); 397 | //glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, nullptr, GL_TRUE); 398 | #endif 399 | GLenum error; 400 | 401 | std::cout << "Checking errors" << std::endl; 402 | while ((error = glGetError()) != GL_NO_ERROR) { 403 | 404 | } 405 | 406 | IMGUI_CHECKVERSION(); 407 | ImGui::CreateContext(); 408 | ImGuiIO& io = ImGui::GetIO(); 409 | 410 | ImGui::StyleColorsDark(); 411 | 412 | ImGui_ImplGlfw_InitForOpenGL(window, true); 413 | 414 | ImGui_ImplOpenGL3_Init("#version 130"); 415 | 416 | return window; 417 | } 418 | -------------------------------------------------------------------------------- /terracotta/lib/imgui/imstb_rectpack.h: -------------------------------------------------------------------------------- 1 | // stb_rect_pack.h - v0.11 - public domain - rectangle packing 2 | // Sean Barrett 2014 3 | // 4 | // Useful for e.g. packing rectangular textures into an atlas. 5 | // Does not do rotation. 6 | // 7 | // Not necessarily the awesomest packing method, but better than 8 | // the totally naive one in stb_truetype (which is primarily what 9 | // this is meant to replace). 10 | // 11 | // Has only had a few tests run, may have issues. 12 | // 13 | // More docs to come. 14 | // 15 | // No memory allocations; uses qsort() and assert() from stdlib. 16 | // Can override those by defining STBRP_SORT and STBRP_ASSERT. 17 | // 18 | // This library currently uses the Skyline Bottom-Left algorithm. 19 | // 20 | // Please note: better rectangle packers are welcome! Please 21 | // implement them to the same API, but with a different init 22 | // function. 23 | // 24 | // Credits 25 | // 26 | // Library 27 | // Sean Barrett 28 | // Minor features 29 | // Martins Mozeiko 30 | // github:IntellectualKitty 31 | // 32 | // Bugfixes / warning fixes 33 | // Jeremy Jaussaud 34 | // 35 | // Version history: 36 | // 37 | // 0.11 (2017-03-03) return packing success/fail result 38 | // 0.10 (2016-10-25) remove cast-away-const to avoid warnings 39 | // 0.09 (2016-08-27) fix compiler warnings 40 | // 0.08 (2015-09-13) really fix bug with empty rects (w=0 or h=0) 41 | // 0.07 (2015-09-13) fix bug with empty rects (w=0 or h=0) 42 | // 0.06 (2015-04-15) added STBRP_SORT to allow replacing qsort 43 | // 0.05: added STBRP_ASSERT to allow replacing assert 44 | // 0.04: fixed minor bug in STBRP_LARGE_RECTS support 45 | // 0.01: initial release 46 | // 47 | // LICENSE 48 | // 49 | // See end of file for license information. 50 | 51 | ////////////////////////////////////////////////////////////////////////////// 52 | // 53 | // INCLUDE SECTION 54 | // 55 | 56 | #ifndef STB_INCLUDE_STB_RECT_PACK_H 57 | #define STB_INCLUDE_STB_RECT_PACK_H 58 | 59 | #define STB_RECT_PACK_VERSION 1 60 | 61 | #ifdef STBRP_STATIC 62 | #define STBRP_DEF static 63 | #else 64 | #define STBRP_DEF extern 65 | #endif 66 | 67 | #ifdef __cplusplus 68 | extern "C" { 69 | #endif 70 | 71 | typedef struct stbrp_context stbrp_context; 72 | typedef struct stbrp_node stbrp_node; 73 | typedef struct stbrp_rect stbrp_rect; 74 | 75 | #ifdef STBRP_LARGE_RECTS 76 | typedef int stbrp_coord; 77 | #else 78 | typedef unsigned short stbrp_coord; 79 | #endif 80 | 81 | STBRP_DEF int stbrp_pack_rects (stbrp_context *context, stbrp_rect *rects, int num_rects); 82 | // Assign packed locations to rectangles. The rectangles are of type 83 | // 'stbrp_rect' defined below, stored in the array 'rects', and there 84 | // are 'num_rects' many of them. 85 | // 86 | // Rectangles which are successfully packed have the 'was_packed' flag 87 | // set to a non-zero value and 'x' and 'y' store the minimum location 88 | // on each axis (i.e. bottom-left in cartesian coordinates, top-left 89 | // if you imagine y increasing downwards). Rectangles which do not fit 90 | // have the 'was_packed' flag set to 0. 91 | // 92 | // You should not try to access the 'rects' array from another thread 93 | // while this function is running, as the function temporarily reorders 94 | // the array while it executes. 95 | // 96 | // To pack into another rectangle, you need to call stbrp_init_target 97 | // again. To continue packing into the same rectangle, you can call 98 | // this function again. Calling this multiple times with multiple rect 99 | // arrays will probably produce worse packing results than calling it 100 | // a single time with the full rectangle array, but the option is 101 | // available. 102 | // 103 | // The function returns 1 if all of the rectangles were successfully 104 | // packed and 0 otherwise. 105 | 106 | struct stbrp_rect 107 | { 108 | // reserved for your use: 109 | int id; 110 | 111 | // input: 112 | stbrp_coord w, h; 113 | 114 | // output: 115 | stbrp_coord x, y; 116 | int was_packed; // non-zero if valid packing 117 | 118 | }; // 16 bytes, nominally 119 | 120 | 121 | STBRP_DEF void stbrp_init_target (stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes); 122 | // Initialize a rectangle packer to: 123 | // pack a rectangle that is 'width' by 'height' in dimensions 124 | // using temporary storage provided by the array 'nodes', which is 'num_nodes' long 125 | // 126 | // You must call this function every time you start packing into a new target. 127 | // 128 | // There is no "shutdown" function. The 'nodes' memory must stay valid for 129 | // the following stbrp_pack_rects() call (or calls), but can be freed after 130 | // the call (or calls) finish. 131 | // 132 | // Note: to guarantee best results, either: 133 | // 1. make sure 'num_nodes' >= 'width' 134 | // or 2. call stbrp_allow_out_of_mem() defined below with 'allow_out_of_mem = 1' 135 | // 136 | // If you don't do either of the above things, widths will be quantized to multiples 137 | // of small integers to guarantee the algorithm doesn't run out of temporary storage. 138 | // 139 | // If you do #2, then the non-quantized algorithm will be used, but the algorithm 140 | // may run out of temporary storage and be unable to pack some rectangles. 141 | 142 | STBRP_DEF void stbrp_setup_allow_out_of_mem (stbrp_context *context, int allow_out_of_mem); 143 | // Optionally call this function after init but before doing any packing to 144 | // change the handling of the out-of-temp-memory scenario, described above. 145 | // If you call init again, this will be reset to the default (false). 146 | 147 | 148 | STBRP_DEF void stbrp_setup_heuristic (stbrp_context *context, int heuristic); 149 | // Optionally select which packing heuristic the library should use. Different 150 | // heuristics will produce better/worse results for different data sets. 151 | // If you call init again, this will be reset to the default. 152 | 153 | enum 154 | { 155 | STBRP_HEURISTIC_Skyline_default=0, 156 | STBRP_HEURISTIC_Skyline_BL_sortHeight = STBRP_HEURISTIC_Skyline_default, 157 | STBRP_HEURISTIC_Skyline_BF_sortHeight 158 | }; 159 | 160 | 161 | ////////////////////////////////////////////////////////////////////////////// 162 | // 163 | // the details of the following structures don't matter to you, but they must 164 | // be visible so you can handle the memory allocations for them 165 | 166 | struct stbrp_node 167 | { 168 | stbrp_coord x,y; 169 | stbrp_node *next; 170 | }; 171 | 172 | struct stbrp_context 173 | { 174 | int width; 175 | int height; 176 | int align; 177 | int init_mode; 178 | int heuristic; 179 | int num_nodes; 180 | stbrp_node *active_head; 181 | stbrp_node *free_head; 182 | stbrp_node extra[2]; // we allocate two extra nodes so optimal user-node-count is 'width' not 'width+2' 183 | }; 184 | 185 | #ifdef __cplusplus 186 | } 187 | #endif 188 | 189 | #endif 190 | 191 | ////////////////////////////////////////////////////////////////////////////// 192 | // 193 | // IMPLEMENTATION SECTION 194 | // 195 | 196 | #ifdef STB_RECT_PACK_IMPLEMENTATION 197 | #ifndef STBRP_SORT 198 | #include 199 | #define STBRP_SORT qsort 200 | #endif 201 | 202 | #ifndef STBRP_ASSERT 203 | #include 204 | #define STBRP_ASSERT assert 205 | #endif 206 | 207 | #ifdef _MSC_VER 208 | #define STBRP__NOTUSED(v) (void)(v) 209 | #define STBRP__CDECL __cdecl 210 | #else 211 | #define STBRP__NOTUSED(v) (void)sizeof(v) 212 | #define STBRP__CDECL 213 | #endif 214 | 215 | enum 216 | { 217 | STBRP__INIT_skyline = 1 218 | }; 219 | 220 | STBRP_DEF void stbrp_setup_heuristic(stbrp_context *context, int heuristic) 221 | { 222 | switch (context->init_mode) { 223 | case STBRP__INIT_skyline: 224 | STBRP_ASSERT(heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight || heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight); 225 | context->heuristic = heuristic; 226 | break; 227 | default: 228 | STBRP_ASSERT(0); 229 | } 230 | } 231 | 232 | STBRP_DEF void stbrp_setup_allow_out_of_mem(stbrp_context *context, int allow_out_of_mem) 233 | { 234 | if (allow_out_of_mem) 235 | // if it's ok to run out of memory, then don't bother aligning them; 236 | // this gives better packing, but may fail due to OOM (even though 237 | // the rectangles easily fit). @TODO a smarter approach would be to only 238 | // quantize once we've hit OOM, then we could get rid of this parameter. 239 | context->align = 1; 240 | else { 241 | // if it's not ok to run out of memory, then quantize the widths 242 | // so that num_nodes is always enough nodes. 243 | // 244 | // I.e. num_nodes * align >= width 245 | // align >= width / num_nodes 246 | // align = ceil(width/num_nodes) 247 | 248 | context->align = (context->width + context->num_nodes-1) / context->num_nodes; 249 | } 250 | } 251 | 252 | STBRP_DEF void stbrp_init_target(stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes) 253 | { 254 | int i; 255 | #ifndef STBRP_LARGE_RECTS 256 | STBRP_ASSERT(width <= 0xffff && height <= 0xffff); 257 | #endif 258 | 259 | for (i=0; i < num_nodes-1; ++i) 260 | nodes[i].next = &nodes[i+1]; 261 | nodes[i].next = NULL; 262 | context->init_mode = STBRP__INIT_skyline; 263 | context->heuristic = STBRP_HEURISTIC_Skyline_default; 264 | context->free_head = &nodes[0]; 265 | context->active_head = &context->extra[0]; 266 | context->width = width; 267 | context->height = height; 268 | context->num_nodes = num_nodes; 269 | stbrp_setup_allow_out_of_mem(context, 0); 270 | 271 | // node 0 is the full width, node 1 is the sentinel (lets us not store width explicitly) 272 | context->extra[0].x = 0; 273 | context->extra[0].y = 0; 274 | context->extra[0].next = &context->extra[1]; 275 | context->extra[1].x = (stbrp_coord) width; 276 | #ifdef STBRP_LARGE_RECTS 277 | context->extra[1].y = (1<<30); 278 | #else 279 | context->extra[1].y = 65535; 280 | #endif 281 | context->extra[1].next = NULL; 282 | } 283 | 284 | // find minimum y position if it starts at x1 285 | static int stbrp__skyline_find_min_y(stbrp_context *c, stbrp_node *first, int x0, int width, int *pwaste) 286 | { 287 | stbrp_node *node = first; 288 | int x1 = x0 + width; 289 | int min_y, visited_width, waste_area; 290 | 291 | STBRP__NOTUSED(c); 292 | 293 | STBRP_ASSERT(first->x <= x0); 294 | 295 | #if 0 296 | // skip in case we're past the node 297 | while (node->next->x <= x0) 298 | ++node; 299 | #else 300 | STBRP_ASSERT(node->next->x > x0); // we ended up handling this in the caller for efficiency 301 | #endif 302 | 303 | STBRP_ASSERT(node->x <= x0); 304 | 305 | min_y = 0; 306 | waste_area = 0; 307 | visited_width = 0; 308 | while (node->x < x1) { 309 | if (node->y > min_y) { 310 | // raise min_y higher. 311 | // we've accounted for all waste up to min_y, 312 | // but we'll now add more waste for everything we've visted 313 | waste_area += visited_width * (node->y - min_y); 314 | min_y = node->y; 315 | // the first time through, visited_width might be reduced 316 | if (node->x < x0) 317 | visited_width += node->next->x - x0; 318 | else 319 | visited_width += node->next->x - node->x; 320 | } else { 321 | // add waste area 322 | int under_width = node->next->x - node->x; 323 | if (under_width + visited_width > width) 324 | under_width = width - visited_width; 325 | waste_area += under_width * (min_y - node->y); 326 | visited_width += under_width; 327 | } 328 | node = node->next; 329 | } 330 | 331 | *pwaste = waste_area; 332 | return min_y; 333 | } 334 | 335 | typedef struct 336 | { 337 | int x,y; 338 | stbrp_node **prev_link; 339 | } stbrp__findresult; 340 | 341 | static stbrp__findresult stbrp__skyline_find_best_pos(stbrp_context *c, int width, int height) 342 | { 343 | int best_waste = (1<<30), best_x, best_y = (1 << 30); 344 | stbrp__findresult fr; 345 | stbrp_node **prev, *node, *tail, **best = NULL; 346 | 347 | // align to multiple of c->align 348 | width = (width + c->align - 1); 349 | width -= width % c->align; 350 | STBRP_ASSERT(width % c->align == 0); 351 | 352 | node = c->active_head; 353 | prev = &c->active_head; 354 | while (node->x + width <= c->width) { 355 | int y,waste; 356 | y = stbrp__skyline_find_min_y(c, node, node->x, width, &waste); 357 | if (c->heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight) { // actually just want to test BL 358 | // bottom left 359 | if (y < best_y) { 360 | best_y = y; 361 | best = prev; 362 | } 363 | } else { 364 | // best-fit 365 | if (y + height <= c->height) { 366 | // can only use it if it first vertically 367 | if (y < best_y || (y == best_y && waste < best_waste)) { 368 | best_y = y; 369 | best_waste = waste; 370 | best = prev; 371 | } 372 | } 373 | } 374 | prev = &node->next; 375 | node = node->next; 376 | } 377 | 378 | best_x = (best == NULL) ? 0 : (*best)->x; 379 | 380 | // if doing best-fit (BF), we also have to try aligning right edge to each node position 381 | // 382 | // e.g, if fitting 383 | // 384 | // ____________________ 385 | // |____________________| 386 | // 387 | // into 388 | // 389 | // | | 390 | // | ____________| 391 | // |____________| 392 | // 393 | // then right-aligned reduces waste, but bottom-left BL is always chooses left-aligned 394 | // 395 | // This makes BF take about 2x the time 396 | 397 | if (c->heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight) { 398 | tail = c->active_head; 399 | node = c->active_head; 400 | prev = &c->active_head; 401 | // find first node that's admissible 402 | while (tail->x < width) 403 | tail = tail->next; 404 | while (tail) { 405 | int xpos = tail->x - width; 406 | int y,waste; 407 | STBRP_ASSERT(xpos >= 0); 408 | // find the left position that matches this 409 | while (node->next->x <= xpos) { 410 | prev = &node->next; 411 | node = node->next; 412 | } 413 | STBRP_ASSERT(node->next->x > xpos && node->x <= xpos); 414 | y = stbrp__skyline_find_min_y(c, node, xpos, width, &waste); 415 | if (y + height < c->height) { 416 | if (y <= best_y) { 417 | if (y < best_y || waste < best_waste || (waste==best_waste && xpos < best_x)) { 418 | best_x = xpos; 419 | STBRP_ASSERT(y <= best_y); 420 | best_y = y; 421 | best_waste = waste; 422 | best = prev; 423 | } 424 | } 425 | } 426 | tail = tail->next; 427 | } 428 | } 429 | 430 | fr.prev_link = best; 431 | fr.x = best_x; 432 | fr.y = best_y; 433 | return fr; 434 | } 435 | 436 | static stbrp__findresult stbrp__skyline_pack_rectangle(stbrp_context *context, int width, int height) 437 | { 438 | // find best position according to heuristic 439 | stbrp__findresult res = stbrp__skyline_find_best_pos(context, width, height); 440 | stbrp_node *node, *cur; 441 | 442 | // bail if: 443 | // 1. it failed 444 | // 2. the best node doesn't fit (we don't always check this) 445 | // 3. we're out of memory 446 | if (res.prev_link == NULL || res.y + height > context->height || context->free_head == NULL) { 447 | res.prev_link = NULL; 448 | return res; 449 | } 450 | 451 | // on success, create new node 452 | node = context->free_head; 453 | node->x = (stbrp_coord) res.x; 454 | node->y = (stbrp_coord) (res.y + height); 455 | 456 | context->free_head = node->next; 457 | 458 | // insert the new node into the right starting point, and 459 | // let 'cur' point to the remaining nodes needing to be 460 | // stiched back in 461 | 462 | cur = *res.prev_link; 463 | if (cur->x < res.x) { 464 | // preserve the existing one, so start testing with the next one 465 | stbrp_node *next = cur->next; 466 | cur->next = node; 467 | cur = next; 468 | } else { 469 | *res.prev_link = node; 470 | } 471 | 472 | // from here, traverse cur and free the nodes, until we get to one 473 | // that shouldn't be freed 474 | while (cur->next && cur->next->x <= res.x + width) { 475 | stbrp_node *next = cur->next; 476 | // move the current node to the free list 477 | cur->next = context->free_head; 478 | context->free_head = cur; 479 | cur = next; 480 | } 481 | 482 | // stitch the list back in 483 | node->next = cur; 484 | 485 | if (cur->x < res.x + width) 486 | cur->x = (stbrp_coord) (res.x + width); 487 | 488 | #ifdef _DEBUG 489 | cur = context->active_head; 490 | while (cur->x < context->width) { 491 | STBRP_ASSERT(cur->x < cur->next->x); 492 | cur = cur->next; 493 | } 494 | STBRP_ASSERT(cur->next == NULL); 495 | 496 | { 497 | int count=0; 498 | cur = context->active_head; 499 | while (cur) { 500 | cur = cur->next; 501 | ++count; 502 | } 503 | cur = context->free_head; 504 | while (cur) { 505 | cur = cur->next; 506 | ++count; 507 | } 508 | STBRP_ASSERT(count == context->num_nodes+2); 509 | } 510 | #endif 511 | 512 | return res; 513 | } 514 | 515 | static int STBRP__CDECL rect_height_compare(const void *a, const void *b) 516 | { 517 | const stbrp_rect *p = (const stbrp_rect *) a; 518 | const stbrp_rect *q = (const stbrp_rect *) b; 519 | if (p->h > q->h) 520 | return -1; 521 | if (p->h < q->h) 522 | return 1; 523 | return (p->w > q->w) ? -1 : (p->w < q->w); 524 | } 525 | 526 | static int STBRP__CDECL rect_original_order(const void *a, const void *b) 527 | { 528 | const stbrp_rect *p = (const stbrp_rect *) a; 529 | const stbrp_rect *q = (const stbrp_rect *) b; 530 | return (p->was_packed < q->was_packed) ? -1 : (p->was_packed > q->was_packed); 531 | } 532 | 533 | #ifdef STBRP_LARGE_RECTS 534 | #define STBRP__MAXVAL 0xffffffff 535 | #else 536 | #define STBRP__MAXVAL 0xffff 537 | #endif 538 | 539 | STBRP_DEF int stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int num_rects) 540 | { 541 | int i, all_rects_packed = 1; 542 | 543 | // we use the 'was_packed' field internally to allow sorting/unsorting 544 | for (i=0; i < num_rects; ++i) { 545 | rects[i].was_packed = i; 546 | #ifndef STBRP_LARGE_RECTS 547 | STBRP_ASSERT(rects[i].w <= 0xffff && rects[i].h <= 0xffff); 548 | #endif 549 | } 550 | 551 | // sort according to heuristic 552 | STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_height_compare); 553 | 554 | for (i=0; i < num_rects; ++i) { 555 | if (rects[i].w == 0 || rects[i].h == 0) { 556 | rects[i].x = rects[i].y = 0; // empty rect needs no space 557 | } else { 558 | stbrp__findresult fr = stbrp__skyline_pack_rectangle(context, rects[i].w, rects[i].h); 559 | if (fr.prev_link) { 560 | rects[i].x = (stbrp_coord) fr.x; 561 | rects[i].y = (stbrp_coord) fr.y; 562 | } else { 563 | rects[i].x = rects[i].y = STBRP__MAXVAL; 564 | } 565 | } 566 | } 567 | 568 | // unsort 569 | STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_original_order); 570 | 571 | // set was_packed flags and all_rects_packed status 572 | for (i=0; i < num_rects; ++i) { 573 | rects[i].was_packed = !(rects[i].x == STBRP__MAXVAL && rects[i].y == STBRP__MAXVAL); 574 | if (!rects[i].was_packed) 575 | all_rects_packed = 0; 576 | } 577 | 578 | // return the all_rects_packed status 579 | return all_rects_packed; 580 | } 581 | #endif 582 | 583 | /* 584 | ------------------------------------------------------------------------------ 585 | This software is available under 2 licenses -- choose whichever you prefer. 586 | ------------------------------------------------------------------------------ 587 | ALTERNATIVE A - MIT License 588 | Copyright (c) 2017 Sean Barrett 589 | Permission is hereby granted, free of charge, to any person obtaining a copy of 590 | this software and associated documentation files (the "Software"), to deal in 591 | the Software without restriction, including without limitation the rights to 592 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 593 | of the Software, and to permit persons to whom the Software is furnished to do 594 | so, subject to the following conditions: 595 | The above copyright notice and this permission notice shall be included in all 596 | copies or substantial portions of the Software. 597 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 598 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 599 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 600 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 601 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 602 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 603 | SOFTWARE. 604 | ------------------------------------------------------------------------------ 605 | ALTERNATIVE B - Public Domain (www.unlicense.org) 606 | This is free and unencumbered software released into the public domain. 607 | Anyone is free to copy, modify, publish, use, compile, sell, or distribute this 608 | software, either in source code form or as a compiled binary, for any purpose, 609 | commercial or non-commercial, and by any means. 610 | In jurisdictions that recognize copyright laws, the author or authors of this 611 | software dedicate any and all copyright interest in the software to the public 612 | domain. We make this dedication for the benefit of the public at large and to 613 | the detriment of our heirs and successors. We intend this dedication to be an 614 | overt act of relinquishment in perpetuity of all present and future rights to 615 | this software under copyright law. 616 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 617 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 618 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 619 | AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 620 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 621 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 622 | ------------------------------------------------------------------------------ 623 | */ 624 | -------------------------------------------------------------------------------- /terracotta/lib/imgui/imgui_impl_opengl3.cpp: -------------------------------------------------------------------------------- 1 | // dear imgui: Renderer for OpenGL3 / OpenGL ES2 / OpenGL ES3 (modern OpenGL with shaders / programmatic pipeline) 2 | // This needs to be used along with a Platform Binding (e.g. GLFW, SDL, Win32, custom..) 3 | // (Note: We are using GL3W as a helper library to access OpenGL functions since there is no standard header to access modern OpenGL functions easily. Alternatives are GLEW, Glad, etc..) 4 | 5 | // Implemented features: 6 | // [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. 7 | 8 | // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. 9 | // If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp. 10 | // https://github.com/ocornut/imgui 11 | 12 | // CHANGELOG 13 | // (minor and older changes stripped away, please see git history for details) 14 | // 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window. 15 | // 2018-11-13: OpenGL: Support for GL 4.5's glClipControl(GL_UPPER_LEFT). 16 | // 2018-08-29: OpenGL: Added support for more OpenGL loaders: glew and glad, with comments indicative that any loader can be used. 17 | // 2018-08-09: OpenGL: Default to OpenGL ES 3 on iOS and Android. GLSL version default to "#version 300 ES". 18 | // 2018-07-30: OpenGL: Support for GLSL 300 ES and 410 core. Fixes for Emscripten compilation. 19 | // 2018-07-10: OpenGL: Support for more GLSL versions (based on the GLSL version string). Added error output when shaders fail to compile/link. 20 | // 2018-06-08: Misc: Extracted imgui_impl_opengl3.cpp/.h away from the old combined GLFW/SDL+OpenGL3 examples. 21 | // 2018-06-08: OpenGL: Use draw_data->DisplayPos and draw_data->DisplaySize to setup projection matrix and clipping rectangle. 22 | // 2018-05-25: OpenGL: Removed unnecessary backup/restore of GL_ELEMENT_ARRAY_BUFFER_BINDING since this is part of the VAO state. 23 | // 2018-05-14: OpenGL: Making the call to glBindSampler() optional so 3.2 context won't fail if the function is a NULL pointer. 24 | // 2018-03-06: OpenGL: Added const char* glsl_version parameter to ImGui_ImplOpenGL3_Init() so user can override the GLSL version e.g. "#version 150". 25 | // 2018-02-23: OpenGL: Create the VAO in the render function so the setup can more easily be used with multiple shared GL context. 26 | // 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplSdlGL3_RenderDrawData() in the .h file so you can call it yourself. 27 | // 2018-01-07: OpenGL: Changed GLSL shader version from 330 to 150. 28 | // 2017-09-01: OpenGL: Save and restore current bound sampler. Save and restore current polygon mode. 29 | // 2017-05-01: OpenGL: Fixed save and restore of current blend func state. 30 | // 2017-05-01: OpenGL: Fixed save and restore of current GL_ACTIVE_TEXTURE. 31 | // 2016-09-05: OpenGL: Fixed save and restore of current scissor rectangle. 32 | // 2016-07-29: OpenGL: Explicitly setting GL_UNPACK_ROW_LENGTH to reduce issues because SDL changes it. (#752) 33 | 34 | //---------------------------------------- 35 | // OpenGL GLSL GLSL 36 | // version version string 37 | //---------------------------------------- 38 | // 2.0 110 "#version 110" 39 | // 2.1 120 40 | // 3.0 130 41 | // 3.1 140 42 | // 3.2 150 "#version 150" 43 | // 3.3 330 44 | // 4.0 400 45 | // 4.1 410 "#version 410 core" 46 | // 4.2 420 47 | // 4.3 430 48 | // ES 2.0 100 "#version 100" 49 | // ES 3.0 300 "#version 300 es" 50 | //---------------------------------------- 51 | 52 | #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) 53 | #define _CRT_SECURE_NO_WARNINGS 54 | #endif 55 | 56 | #include "imgui.h" 57 | #include "imgui_impl_opengl3.h" 58 | #include 59 | #if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier 60 | #include // intptr_t 61 | #else 62 | #include // intptr_t 63 | #endif 64 | #if defined(__APPLE__) 65 | #include "TargetConditionals.h" 66 | #endif 67 | 68 | // iOS, Android and Emscripten can use GL ES 3 69 | // Call ImGui_ImplOpenGL3_Init() with "#version 300 es" 70 | #if (defined(__APPLE__) && TARGET_OS_IOS) || (defined(__ANDROID__)) || (defined(__EMSCRIPTEN__)) 71 | #define USE_GL_ES3 72 | #endif 73 | 74 | #ifdef USE_GL_ES3 75 | // OpenGL ES 3 76 | #include // Use GL ES 3 77 | #else 78 | // Regular OpenGL 79 | // About OpenGL function loaders: modern OpenGL doesn't have a standard header file and requires individual function pointers to be loaded manually. 80 | // Helper libraries are often used for this purpose! Here we are supporting a few common ones: gl3w, glew, glad. 81 | // You may use another loader/header of your choice (glext, glLoadGen, etc.), or chose to manually implement your own. 82 | #if defined(IMGUI_IMPL_OPENGL_LOADER_GL3W) 83 | #include 84 | #elif defined(IMGUI_IMPL_OPENGL_LOADER_GLEW) 85 | #include 86 | #elif defined(IMGUI_IMPL_OPENGL_LOADER_GLAD) 87 | #include 88 | #else 89 | #include IMGUI_IMPL_OPENGL_LOADER_CUSTOM 90 | #endif 91 | #endif 92 | 93 | // OpenGL Data 94 | static char g_GlslVersionString[32] = ""; 95 | static GLuint g_FontTexture = 0; 96 | static GLuint g_ShaderHandle = 0, g_VertHandle = 0, g_FragHandle = 0; 97 | static int g_AttribLocationTex = 0, g_AttribLocationProjMtx = 0; 98 | static int g_AttribLocationPosition = 0, g_AttribLocationUV = 0, g_AttribLocationColor = 0; 99 | static unsigned int g_VboHandle = 0, g_ElementsHandle = 0; 100 | 101 | // Functions 102 | bool ImGui_ImplOpenGL3_Init(const char* glsl_version) 103 | { 104 | ImGuiIO& io = ImGui::GetIO(); 105 | io.BackendRendererName = "imgui_impl_opengl3"; 106 | 107 | // Store GLSL version string so we can refer to it later in case we recreate shaders. Note: GLSL version is NOT the same as GL version. Leave this to NULL if unsure. 108 | #ifdef USE_GL_ES3 109 | if (glsl_version == NULL) 110 | glsl_version = "#version 300 es"; 111 | #else 112 | if (glsl_version == NULL) 113 | glsl_version = "#version 130"; 114 | #endif 115 | IM_ASSERT((int)strlen(glsl_version) + 2 < IM_ARRAYSIZE(g_GlslVersionString)); 116 | strcpy(g_GlslVersionString, glsl_version); 117 | strcat(g_GlslVersionString, "\n"); 118 | 119 | return true; 120 | } 121 | 122 | void ImGui_ImplOpenGL3_Shutdown() 123 | { 124 | ImGui_ImplOpenGL3_DestroyDeviceObjects(); 125 | } 126 | 127 | void ImGui_ImplOpenGL3_NewFrame() 128 | { 129 | if (!g_FontTexture) 130 | ImGui_ImplOpenGL3_CreateDeviceObjects(); 131 | } 132 | 133 | // OpenGL3 Render function. 134 | // (this used to be set in io.RenderDrawListsFn and called by ImGui::Render(), but you can now call this directly from your main loop) 135 | // Note that this implementation is little overcomplicated because we are saving/setting up/restoring every OpenGL state explicitly, in order to be able to run within any OpenGL engine that doesn't do so. 136 | void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data) 137 | { 138 | // Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates) 139 | ImGuiIO& io = ImGui::GetIO(); 140 | int fb_width = (int)(draw_data->DisplaySize.x * io.DisplayFramebufferScale.x); 141 | int fb_height = (int)(draw_data->DisplaySize.y * io.DisplayFramebufferScale.y); 142 | if (fb_width <= 0 || fb_height <= 0) 143 | return; 144 | draw_data->ScaleClipRects(io.DisplayFramebufferScale); 145 | 146 | // Backup GL state 147 | GLenum last_active_texture; glGetIntegerv(GL_ACTIVE_TEXTURE, (GLint*)&last_active_texture); 148 | glActiveTexture(GL_TEXTURE0); 149 | GLint last_program; glGetIntegerv(GL_CURRENT_PROGRAM, &last_program); 150 | GLint last_texture; glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); 151 | #ifdef GL_SAMPLER_BINDING 152 | GLint last_sampler; glGetIntegerv(GL_SAMPLER_BINDING, &last_sampler); 153 | #endif 154 | GLint last_array_buffer; glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer); 155 | GLint last_vertex_array; glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array); 156 | #ifdef GL_POLYGON_MODE 157 | GLint last_polygon_mode[2]; glGetIntegerv(GL_POLYGON_MODE, last_polygon_mode); 158 | #endif 159 | GLint last_viewport[4]; glGetIntegerv(GL_VIEWPORT, last_viewport); 160 | GLint last_scissor_box[4]; glGetIntegerv(GL_SCISSOR_BOX, last_scissor_box); 161 | GLenum last_blend_src_rgb; glGetIntegerv(GL_BLEND_SRC_RGB, (GLint*)&last_blend_src_rgb); 162 | GLenum last_blend_dst_rgb; glGetIntegerv(GL_BLEND_DST_RGB, (GLint*)&last_blend_dst_rgb); 163 | GLenum last_blend_src_alpha; glGetIntegerv(GL_BLEND_SRC_ALPHA, (GLint*)&last_blend_src_alpha); 164 | GLenum last_blend_dst_alpha; glGetIntegerv(GL_BLEND_DST_ALPHA, (GLint*)&last_blend_dst_alpha); 165 | GLenum last_blend_equation_rgb; glGetIntegerv(GL_BLEND_EQUATION_RGB, (GLint*)&last_blend_equation_rgb); 166 | GLenum last_blend_equation_alpha; glGetIntegerv(GL_BLEND_EQUATION_ALPHA, (GLint*)&last_blend_equation_alpha); 167 | GLboolean last_enable_blend = glIsEnabled(GL_BLEND); 168 | GLboolean last_enable_cull_face = glIsEnabled(GL_CULL_FACE); 169 | GLboolean last_enable_depth_test = glIsEnabled(GL_DEPTH_TEST); 170 | GLboolean last_enable_scissor_test = glIsEnabled(GL_SCISSOR_TEST); 171 | bool clip_origin_lower_left = true; 172 | #ifdef GL_CLIP_ORIGIN 173 | GLenum last_clip_origin = 0; glGetIntegerv(GL_CLIP_ORIGIN, (GLint*)&last_clip_origin); // Support for GL 4.5's glClipControl(GL_UPPER_LEFT) 174 | if (last_clip_origin == GL_UPPER_LEFT) 175 | clip_origin_lower_left = false; 176 | #endif 177 | 178 | // Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, polygon fill 179 | glEnable(GL_BLEND); 180 | glBlendEquation(GL_FUNC_ADD); 181 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 182 | glDisable(GL_CULL_FACE); 183 | glDisable(GL_DEPTH_TEST); 184 | glEnable(GL_SCISSOR_TEST); 185 | #ifdef GL_POLYGON_MODE 186 | glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); 187 | #endif 188 | 189 | // Setup viewport, orthographic projection matrix 190 | // Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayMin is typically (0,0) for single viewport apps. 191 | glViewport(0, 0, (GLsizei)fb_width, (GLsizei)fb_height); 192 | float L = draw_data->DisplayPos.x; 193 | float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x; 194 | float T = draw_data->DisplayPos.y; 195 | float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y; 196 | const float ortho_projection[4][4] = 197 | { 198 | { 2.0f/(R-L), 0.0f, 0.0f, 0.0f }, 199 | { 0.0f, 2.0f/(T-B), 0.0f, 0.0f }, 200 | { 0.0f, 0.0f, -1.0f, 0.0f }, 201 | { (R+L)/(L-R), (T+B)/(B-T), 0.0f, 1.0f }, 202 | }; 203 | glUseProgram(g_ShaderHandle); 204 | glUniform1i(g_AttribLocationTex, 0); 205 | glUniformMatrix4fv(g_AttribLocationProjMtx, 1, GL_FALSE, &ortho_projection[0][0]); 206 | #ifdef GL_SAMPLER_BINDING 207 | glBindSampler(0, 0); // We use combined texture/sampler state. Applications using GL 3.3 may set that otherwise. 208 | #endif 209 | // Recreate the VAO every time 210 | // (This is to easily allow multiple GL contexts. VAO are not shared among GL contexts, and we don't track creation/deletion of windows so we don't have an obvious key to use to cache them.) 211 | GLuint vao_handle = 0; 212 | glGenVertexArrays(1, &vao_handle); 213 | glBindVertexArray(vao_handle); 214 | glBindBuffer(GL_ARRAY_BUFFER, g_VboHandle); 215 | glEnableVertexAttribArray(g_AttribLocationPosition); 216 | glEnableVertexAttribArray(g_AttribLocationUV); 217 | glEnableVertexAttribArray(g_AttribLocationColor); 218 | glVertexAttribPointer(g_AttribLocationPosition, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, pos)); 219 | glVertexAttribPointer(g_AttribLocationUV, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, uv)); 220 | glVertexAttribPointer(g_AttribLocationColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, col)); 221 | 222 | // Draw 223 | ImVec2 pos = draw_data->DisplayPos; 224 | for (int n = 0; n < draw_data->CmdListsCount; n++) 225 | { 226 | const ImDrawList* cmd_list = draw_data->CmdLists[n]; 227 | const ImDrawIdx* idx_buffer_offset = 0; 228 | 229 | glBindBuffer(GL_ARRAY_BUFFER, g_VboHandle); 230 | glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)cmd_list->VtxBuffer.Size * sizeof(ImDrawVert), (const GLvoid*)cmd_list->VtxBuffer.Data, GL_STREAM_DRAW); 231 | 232 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g_ElementsHandle); 233 | glBufferData(GL_ELEMENT_ARRAY_BUFFER, (GLsizeiptr)cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx), (const GLvoid*)cmd_list->IdxBuffer.Data, GL_STREAM_DRAW); 234 | 235 | for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) 236 | { 237 | const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i]; 238 | if (pcmd->UserCallback) 239 | { 240 | // User callback (registered via ImDrawList::AddCallback) 241 | pcmd->UserCallback(cmd_list, pcmd); 242 | } 243 | else 244 | { 245 | ImVec4 clip_rect = ImVec4(pcmd->ClipRect.x - pos.x, pcmd->ClipRect.y - pos.y, pcmd->ClipRect.z - pos.x, pcmd->ClipRect.w - pos.y); 246 | if (clip_rect.x < fb_width && clip_rect.y < fb_height && clip_rect.z >= 0.0f && clip_rect.w >= 0.0f) 247 | { 248 | // Apply scissor/clipping rectangle 249 | if (clip_origin_lower_left) 250 | glScissor((int)clip_rect.x, (int)(fb_height - clip_rect.w), (int)(clip_rect.z - clip_rect.x), (int)(clip_rect.w - clip_rect.y)); 251 | else 252 | glScissor((int)clip_rect.x, (int)clip_rect.y, (int)clip_rect.z, (int)clip_rect.w); // Support for GL 4.5's glClipControl(GL_UPPER_LEFT) 253 | 254 | // Bind texture, Draw 255 | glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->TextureId); 256 | glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer_offset); 257 | } 258 | } 259 | idx_buffer_offset += pcmd->ElemCount; 260 | } 261 | } 262 | glDeleteVertexArrays(1, &vao_handle); 263 | 264 | // Restore modified GL state 265 | glUseProgram(last_program); 266 | glBindTexture(GL_TEXTURE_2D, last_texture); 267 | #ifdef GL_SAMPLER_BINDING 268 | glBindSampler(0, last_sampler); 269 | #endif 270 | glActiveTexture(last_active_texture); 271 | glBindVertexArray(last_vertex_array); 272 | glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer); 273 | glBlendEquationSeparate(last_blend_equation_rgb, last_blend_equation_alpha); 274 | glBlendFuncSeparate(last_blend_src_rgb, last_blend_dst_rgb, last_blend_src_alpha, last_blend_dst_alpha); 275 | if (last_enable_blend) glEnable(GL_BLEND); else glDisable(GL_BLEND); 276 | if (last_enable_cull_face) glEnable(GL_CULL_FACE); else glDisable(GL_CULL_FACE); 277 | if (last_enable_depth_test) glEnable(GL_DEPTH_TEST); else glDisable(GL_DEPTH_TEST); 278 | if (last_enable_scissor_test) glEnable(GL_SCISSOR_TEST); else glDisable(GL_SCISSOR_TEST); 279 | #ifdef GL_POLYGON_MODE 280 | glPolygonMode(GL_FRONT_AND_BACK, (GLenum)last_polygon_mode[0]); 281 | #endif 282 | glViewport(last_viewport[0], last_viewport[1], (GLsizei)last_viewport[2], (GLsizei)last_viewport[3]); 283 | glScissor(last_scissor_box[0], last_scissor_box[1], (GLsizei)last_scissor_box[2], (GLsizei)last_scissor_box[3]); 284 | } 285 | 286 | bool ImGui_ImplOpenGL3_CreateFontsTexture() 287 | { 288 | // Build texture atlas 289 | ImGuiIO& io = ImGui::GetIO(); 290 | unsigned char* pixels; 291 | int width, height; 292 | io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); // Load as RGBA 32-bits (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders. If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory. 293 | 294 | // Upload texture to graphics system 295 | GLint last_texture; 296 | glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); 297 | glGenTextures(1, &g_FontTexture); 298 | glBindTexture(GL_TEXTURE_2D, g_FontTexture); 299 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 300 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 301 | glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); 302 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); 303 | 304 | // Store our identifier 305 | io.Fonts->TexID = (ImTextureID)(intptr_t)g_FontTexture; 306 | 307 | // Restore state 308 | glBindTexture(GL_TEXTURE_2D, last_texture); 309 | 310 | return true; 311 | } 312 | 313 | void ImGui_ImplOpenGL3_DestroyFontsTexture() 314 | { 315 | if (g_FontTexture) 316 | { 317 | ImGuiIO& io = ImGui::GetIO(); 318 | glDeleteTextures(1, &g_FontTexture); 319 | io.Fonts->TexID = 0; 320 | g_FontTexture = 0; 321 | } 322 | } 323 | 324 | // If you get an error please report on github. You may try different GL context version or GLSL version. See GL<>GLSL version table at the top of this file. 325 | static bool CheckShader(GLuint handle, const char* desc) 326 | { 327 | GLint status = 0, log_length = 0; 328 | glGetShaderiv(handle, GL_COMPILE_STATUS, &status); 329 | glGetShaderiv(handle, GL_INFO_LOG_LENGTH, &log_length); 330 | if ((GLboolean)status == GL_FALSE) 331 | fprintf(stderr, "ERROR: ImGui_ImplOpenGL3_CreateDeviceObjects: failed to compile %s!\n", desc); 332 | if (log_length > 0) 333 | { 334 | ImVector buf; 335 | buf.resize((int)(log_length + 1)); 336 | glGetShaderInfoLog(handle, log_length, NULL, (GLchar*)buf.begin()); 337 | fprintf(stderr, "%s\n", buf.begin()); 338 | } 339 | return (GLboolean)status == GL_TRUE; 340 | } 341 | 342 | // If you get an error please report on GitHub. You may try different GL context version or GLSL version. 343 | static bool CheckProgram(GLuint handle, const char* desc) 344 | { 345 | GLint status = 0, log_length = 0; 346 | glGetProgramiv(handle, GL_LINK_STATUS, &status); 347 | glGetProgramiv(handle, GL_INFO_LOG_LENGTH, &log_length); 348 | if ((GLboolean)status == GL_FALSE) 349 | fprintf(stderr, "ERROR: ImGui_ImplOpenGL3_CreateDeviceObjects: failed to link %s! (with GLSL '%s')\n", desc, g_GlslVersionString); 350 | if (log_length > 0) 351 | { 352 | ImVector buf; 353 | buf.resize((int)(log_length + 1)); 354 | glGetProgramInfoLog(handle, log_length, NULL, (GLchar*)buf.begin()); 355 | fprintf(stderr, "%s\n", buf.begin()); 356 | } 357 | return (GLboolean)status == GL_TRUE; 358 | } 359 | 360 | bool ImGui_ImplOpenGL3_CreateDeviceObjects() 361 | { 362 | // Backup GL state 363 | GLint last_texture, last_array_buffer, last_vertex_array; 364 | glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); 365 | glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer); 366 | glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array); 367 | 368 | // Parse GLSL version string 369 | int glsl_version = 130; 370 | sscanf(g_GlslVersionString, "#version %d", &glsl_version); 371 | 372 | const GLchar* vertex_shader_glsl_120 = 373 | "uniform mat4 ProjMtx;\n" 374 | "attribute vec2 Position;\n" 375 | "attribute vec2 UV;\n" 376 | "attribute vec4 Color;\n" 377 | "varying vec2 Frag_UV;\n" 378 | "varying vec4 Frag_Color;\n" 379 | "void main()\n" 380 | "{\n" 381 | " Frag_UV = UV;\n" 382 | " Frag_Color = Color;\n" 383 | " gl_Position = ProjMtx * vec4(Position.xy,0,1);\n" 384 | "}\n"; 385 | 386 | const GLchar* vertex_shader_glsl_130 = 387 | "uniform mat4 ProjMtx;\n" 388 | "in vec2 Position;\n" 389 | "in vec2 UV;\n" 390 | "in vec4 Color;\n" 391 | "out vec2 Frag_UV;\n" 392 | "out vec4 Frag_Color;\n" 393 | "void main()\n" 394 | "{\n" 395 | " Frag_UV = UV;\n" 396 | " Frag_Color = Color;\n" 397 | " gl_Position = ProjMtx * vec4(Position.xy,0,1);\n" 398 | "}\n"; 399 | 400 | const GLchar* vertex_shader_glsl_300_es = 401 | "precision mediump float;\n" 402 | "layout (location = 0) in vec2 Position;\n" 403 | "layout (location = 1) in vec2 UV;\n" 404 | "layout (location = 2) in vec4 Color;\n" 405 | "uniform mat4 ProjMtx;\n" 406 | "out vec2 Frag_UV;\n" 407 | "out vec4 Frag_Color;\n" 408 | "void main()\n" 409 | "{\n" 410 | " Frag_UV = UV;\n" 411 | " Frag_Color = Color;\n" 412 | " gl_Position = ProjMtx * vec4(Position.xy,0,1);\n" 413 | "}\n"; 414 | 415 | const GLchar* vertex_shader_glsl_410_core = 416 | "layout (location = 0) in vec2 Position;\n" 417 | "layout (location = 1) in vec2 UV;\n" 418 | "layout (location = 2) in vec4 Color;\n" 419 | "uniform mat4 ProjMtx;\n" 420 | "out vec2 Frag_UV;\n" 421 | "out vec4 Frag_Color;\n" 422 | "void main()\n" 423 | "{\n" 424 | " Frag_UV = UV;\n" 425 | " Frag_Color = Color;\n" 426 | " gl_Position = ProjMtx * vec4(Position.xy,0,1);\n" 427 | "}\n"; 428 | 429 | const GLchar* fragment_shader_glsl_120 = 430 | "#ifdef GL_ES\n" 431 | " precision mediump float;\n" 432 | "#endif\n" 433 | "uniform sampler2D Texture;\n" 434 | "varying vec2 Frag_UV;\n" 435 | "varying vec4 Frag_Color;\n" 436 | "void main()\n" 437 | "{\n" 438 | " gl_FragColor = Frag_Color * texture2D(Texture, Frag_UV.st);\n" 439 | "}\n"; 440 | 441 | const GLchar* fragment_shader_glsl_130 = 442 | "uniform sampler2D Texture;\n" 443 | "in vec2 Frag_UV;\n" 444 | "in vec4 Frag_Color;\n" 445 | "out vec4 Out_Color;\n" 446 | "void main()\n" 447 | "{\n" 448 | " Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n" 449 | "}\n"; 450 | 451 | const GLchar* fragment_shader_glsl_300_es = 452 | "precision mediump float;\n" 453 | "uniform sampler2D Texture;\n" 454 | "in vec2 Frag_UV;\n" 455 | "in vec4 Frag_Color;\n" 456 | "layout (location = 0) out vec4 Out_Color;\n" 457 | "void main()\n" 458 | "{\n" 459 | " Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n" 460 | "}\n"; 461 | 462 | const GLchar* fragment_shader_glsl_410_core = 463 | "in vec2 Frag_UV;\n" 464 | "in vec4 Frag_Color;\n" 465 | "uniform sampler2D Texture;\n" 466 | "layout (location = 0) out vec4 Out_Color;\n" 467 | "void main()\n" 468 | "{\n" 469 | " Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n" 470 | "}\n"; 471 | 472 | // Select shaders matching our GLSL versions 473 | const GLchar* vertex_shader = NULL; 474 | const GLchar* fragment_shader = NULL; 475 | if (glsl_version < 130) 476 | { 477 | vertex_shader = vertex_shader_glsl_120; 478 | fragment_shader = fragment_shader_glsl_120; 479 | } 480 | else if (glsl_version == 410) 481 | { 482 | vertex_shader = vertex_shader_glsl_410_core; 483 | fragment_shader = fragment_shader_glsl_410_core; 484 | } 485 | else if (glsl_version == 300) 486 | { 487 | vertex_shader = vertex_shader_glsl_300_es; 488 | fragment_shader = fragment_shader_glsl_300_es; 489 | } 490 | else 491 | { 492 | vertex_shader = vertex_shader_glsl_130; 493 | fragment_shader = fragment_shader_glsl_130; 494 | } 495 | 496 | // Create shaders 497 | const GLchar* vertex_shader_with_version[2] = { g_GlslVersionString, vertex_shader }; 498 | g_VertHandle = glCreateShader(GL_VERTEX_SHADER); 499 | glShaderSource(g_VertHandle, 2, vertex_shader_with_version, NULL); 500 | glCompileShader(g_VertHandle); 501 | CheckShader(g_VertHandle, "vertex shader"); 502 | 503 | const GLchar* fragment_shader_with_version[2] = { g_GlslVersionString, fragment_shader }; 504 | g_FragHandle = glCreateShader(GL_FRAGMENT_SHADER); 505 | glShaderSource(g_FragHandle, 2, fragment_shader_with_version, NULL); 506 | glCompileShader(g_FragHandle); 507 | CheckShader(g_FragHandle, "fragment shader"); 508 | 509 | g_ShaderHandle = glCreateProgram(); 510 | glAttachShader(g_ShaderHandle, g_VertHandle); 511 | glAttachShader(g_ShaderHandle, g_FragHandle); 512 | glLinkProgram(g_ShaderHandle); 513 | CheckProgram(g_ShaderHandle, "shader program"); 514 | 515 | g_AttribLocationTex = glGetUniformLocation(g_ShaderHandle, "Texture"); 516 | g_AttribLocationProjMtx = glGetUniformLocation(g_ShaderHandle, "ProjMtx"); 517 | g_AttribLocationPosition = glGetAttribLocation(g_ShaderHandle, "Position"); 518 | g_AttribLocationUV = glGetAttribLocation(g_ShaderHandle, "UV"); 519 | g_AttribLocationColor = glGetAttribLocation(g_ShaderHandle, "Color"); 520 | 521 | // Create buffers 522 | glGenBuffers(1, &g_VboHandle); 523 | glGenBuffers(1, &g_ElementsHandle); 524 | 525 | ImGui_ImplOpenGL3_CreateFontsTexture(); 526 | 527 | // Restore modified GL state 528 | glBindTexture(GL_TEXTURE_2D, last_texture); 529 | glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer); 530 | glBindVertexArray(last_vertex_array); 531 | 532 | return true; 533 | } 534 | 535 | void ImGui_ImplOpenGL3_DestroyDeviceObjects() 536 | { 537 | if (g_VboHandle) glDeleteBuffers(1, &g_VboHandle); 538 | if (g_ElementsHandle) glDeleteBuffers(1, &g_ElementsHandle); 539 | g_VboHandle = g_ElementsHandle = 0; 540 | 541 | if (g_ShaderHandle && g_VertHandle) glDetachShader(g_ShaderHandle, g_VertHandle); 542 | if (g_VertHandle) glDeleteShader(g_VertHandle); 543 | g_VertHandle = 0; 544 | 545 | if (g_ShaderHandle && g_FragHandle) glDetachShader(g_ShaderHandle, g_FragHandle); 546 | if (g_FragHandle) glDeleteShader(g_FragHandle); 547 | g_FragHandle = 0; 548 | 549 | if (g_ShaderHandle) glDeleteProgram(g_ShaderHandle); 550 | g_ShaderHandle = 0; 551 | 552 | ImGui_ImplOpenGL3_DestroyFontsTexture(); 553 | } 554 | --------------------------------------------------------------------------------