diff --git a/CMakeLists.txt b/CMakeLists.txt index 919cd5f..b8ec234 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -92,7 +92,10 @@ if(BUILD_CLIENT) src/impl/tcpsocket.cpp src/impl/sha1.cpp src/impl/packet_stream.cpp + src/impl/mesh.cpp + src/impl/atlas.cpp src/impl/voxel.cpp + src/impl/block.cpp src/lua_bindings/util.cpp src/lua_bindings/init.cpp src/lua_bindings/misc.cpp @@ -132,7 +135,10 @@ if(BUILD_SERVER) src/impl/module.cpp src/impl/sha1.cpp src/impl/packet_stream.cpp + src/impl/mesh.cpp + src/impl/atlas.cpp src/impl/voxel.cpp + src/impl/block.cpp ) if(WIN32) set(SERVER_CORE_SRCS ${SERVER_CORE_SRCS} src/impl/windows/file_watch.cpp) diff --git a/doc/design.txt b/doc/design.txt index be3fbc3..d4e237f 100644 --- a/doc/design.txt +++ b/doc/design.txt @@ -153,6 +153,12 @@ A way to define voxel properties: - interface::VoxelInstance - The voxel world is handled by builtin/voxelworld on the server, and is built in to the client. VoxelRegistry is part of builtin/voxelworld on the server. +- How are voxel texture definitions stored? + - They are references to one of many texture atlases (many are needed in + case of high-resolution textures) + - They are an index into a texture atlas id + - A texture atlas definition is a list of texture resource names + - One texture atlas contains textures of one resolution A way to define block properties: - interface::-based because the client has to support blocks directly: diff --git a/src/impl/atlas.cpp b/src/impl/atlas.cpp new file mode 100644 index 0000000..a240163 --- /dev/null +++ b/src/impl/atlas.cpp @@ -0,0 +1,129 @@ +// http://www.apache.org/licenses/LICENSE-2.0 +// Copyright 2014 Perttu Ahola +#include "interface/atlas.h" +#include +#include +#include + +namespace interface { + +struct CTextureAtlasRegistry: public TextureAtlasRegistry +{ + magic::Context *m_context; + sv_ m_defs; + sv_ m_cache; + + CTextureAtlasRegistry(magic::Context *context): + m_context(context) + {} + + const AtlasSegmentReference add_segment( + const AtlasSegmentDefinition &segment_def) + { + throw Exception("Not implemented"); + // Get Texture2D resource + magic::ResourceCache *magic_cache = + m_context->GetSubsystem(); + magic::Texture2D *texture = magic_cache->GetResource( + segment_def.resource_name); + // Get resolution of texture + magic::IntVector2 texture_size(texture->GetWidth(), texture->GetHeight()); + // Try to find a texture atlas for this texture size + TextureAtlasDefinition *atlas_def = NULL; + for(TextureAtlasDefinition &def0 : m_defs){ + if(def0.segment_resolution == texture_size){ + size_t max = def0.total_segments.x_ * def0.total_segments.y_; + if(atlas_def->segments.size() >= max) + continue; // Full + atlas_def = &def0; + break; + } + } + // If not found, create a texture atlas for this texture size + if(!atlas_def){ + // Create a new texture atlas + m_defs.resize(m_defs.size()+1); + atlas_def = &m_defs[m_defs.size()-1]; + atlas_def->id = m_defs.size()-1; + // Calculate segment resolution + atlas_def->segment_resolution = magic::IntVector2( + texture_size.x_ / segment_def.total_segments.x_, + texture_size.y_ / segment_def.total_segments.y_ + ); + // Calculate total segments based on segment resolution + int max_res = 2048; + atlas_def->total_segments = magic::IntVector2( + max_res / atlas_def->segment_resolution.x_, + max_res / atlas_def->segment_resolution.y_, + ); + // Create texture for new atlas + // TODO: Extend cache to fit this atlas if it doesn't yet + // TODO: Set texture of cached atlas + } + // TODO: Add this definition to the atlas + } + + const TextureAtlasDefinition* get_atlas_definition(uint atlas_id) + { + if(atlas_id >= m_defs.size()) + return NULL; + return &m_defs[atlas_id]; + } + + const AtlasSegmentDefinition* get_segment_definition( + const AtlasSegmentReference &ref) + { + const TextureAtlasDefinition *atlas = get_atlas_definition(ref.atlas_id); + if(!atlas) + return NULL; + if(ref.segment_id >= atlas->segments.size()) + return NULL; + return &atlas->segments[ref.segment_id]; + } + + void update_segment_cache(AtlasSegmentCache &cache, + const AtlasSegmentDefinition &def, const TextureAtlasDefinition &atlas) + { + // TODO + // TODO: Get Texture2D resource + // TODO: Check that resolution of texture matches with atlas + // TODO: + } + + const AtlasSegmentCache* get_texture(const AtlasSegmentReference &ref) + { + if(ref.atlas_id >= m_cache.size()){ + if(ref.atlas_id >= m_defs.size()) + return NULL; + // New atlases are left with valid=false + m_cache.resize(m_defs.size()); + } + TextureAtlasCache &cache = m_cache[ref.atlas_id]; + if(!cache.valid){ + const auto &id = ref.atlas_id; + cache.segment_resolution = m_defs[id].segment_resolution; + cache.total_segments = m_defs[id].total_segments; + // New segments are left with valid=false + cache.segments.resize(m_defs[id].segments.size()); + cache.valid = true; + } + if(ref.segment_id >= cache.segments.size()) + return NULL; + AtlasSegmentCache &seg_cache = cache.segments[ref.segment_id]; + if(seg_cache.valid) + return &seg_cache; + TextureAtlasDefinition &atlas_def = m_defs[ref.atlas_id]; + const AtlasSegmentDefinition &seg_def = atlas_def.segments[ref.segment_id]; + update_segment_cache(seg_cache, seg_def, atlas_def); + seg_cache.valid = true; + return &seg_cache; + } +}; + +TextureAtlasRegistry* createTextureAtlasRegistry(magic::Context *context) +{ + return new CTextureAtlasRegistry(context); +} + +} +// vim: set noet ts=4 sw=4: diff --git a/src/impl/block.cpp b/src/impl/block.cpp new file mode 100644 index 0000000..d52412a --- /dev/null +++ b/src/impl/block.cpp @@ -0,0 +1,48 @@ +// http://www.apache.org/licenses/LICENSE-2.0 +// Copyright 2014 Perttu Ahola +#include "interface/block.h" + +namespace interface { + +struct CBlockRegistry: public BlockRegistry +{ + VoxelRegistry *m_voxel_reg; + sv_ m_defs; + + CBlockRegistry(VoxelRegistry *voxel_reg): + m_voxel_reg(voxel_reg) + {} + + BlockTypeId add_block_with_predefined_segments(const BlockDefinition &def) + { + throw Exception("Not implemented"); + return BLOCKTYPEID_UNDEFINED; + } + + BlockTypeId add_block_generate_segments(const BlockSourceDefinition &def) + { + throw Exception("Not implemented"); + return BLOCKTYPEID_UNDEFINED; + } + + const BlockDefinition* get(const BlockTypeId &id) + { + return NULL; + } + + const BlockDefinition* get(const BlockName &name) + { + return NULL; + } + + // TODO: Network serialization + // TODO: Ability to track changes (just some kind of set_dirty()?) +}; + +BlockRegistry* createBlockRegistry(VoxelRegistry *voxel_reg) +{ + return new CBlockRegistry(voxel_reg); +} + +} +// vim: set noet ts=4 sw=4: diff --git a/src/impl/mesh.cpp b/src/impl/mesh.cpp new file mode 100644 index 0000000..7c0949c --- /dev/null +++ b/src/impl/mesh.cpp @@ -0,0 +1,102 @@ +// http://www.apache.org/licenses/LICENSE-2.0 +// Copyright 2014 Perttu Ahola +#include "interface/mesh.h" +#include +#include +#include +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wstrict-aliasing" +#include +#include +#include +#include +#include +#include +#include +#pragma GCC diagnostic pop + +namespace magic = Urho3D; +namespace pv = PolyVox; + +// Just do this; Urho3D's stuff doesn't really clash with anything in buildat +using namespace Urho3D; + +namespace interface { + +// Creates a model from a string; eg. (2, 2, 2, "11101111") +Model* create_simple_voxel_model(Context *context, + int w, int h, int d, const ss_ &source_data) +{ + if(w < 0 || h < 0 || d < 0) + throw Exception("Negative dimension"); + if(w * h * d != (int)source_data.size()) + throw Exception("Mismatched data size"); + pv::SimpleVolume volData(pv::Region( + pv::Vector3DInt32(-1, -1, -1), + pv::Vector3DInt32(w, h, d))); + size_t i = 0; + for(int z = 0; z < d; z++) + for(int y = 0; y < h; y++) + for(int x = 0; x < w; x++){ + char c = source_data[i++]; + volData.setVoxelAt(x, y, z, c == '0' ? 0 : 255); + } + + pv::SurfaceMesh pv_mesh; + pv::CubicSurfaceExtractorWithNormals> + surfaceExtractor(&volData, volData.getEnclosingRegion(), &pv_mesh); + surfaceExtractor.execute(); + + const sv_ &pv_indices = pv_mesh.getIndices(); + const sv_ &pv_vertices = pv_mesh.getVertices(); + + const size_t num_vertices = pv_vertices.size(); + const size_t num_indices = pv_indices.size(); + + sv_ vertex_data; + vertex_data.resize(num_vertices * 6); // vertex + normal + for(size_t i = 0; i < num_vertices; i++){ + vertex_data[i*6 + 0] = pv_vertices[i].position.getX() - w/2.0f - 0.5f; + vertex_data[i*6 + 1] = pv_vertices[i].position.getY() - h/2.0f - 0.5f; + vertex_data[i*6 + 2] = pv_vertices[i].position.getZ() - d/2.0f - 0.5f; + vertex_data[i*6 + 3] = pv_vertices[i].normal.getX(); + vertex_data[i*6 + 4] = pv_vertices[i].normal.getY(); + vertex_data[i*6 + 5] = pv_vertices[i].normal.getZ(); + } + + sv_ index_data; + index_data.resize(num_indices); + for(size_t i = 0; i < num_indices; i++){ + if(pv_indices[i] >= 0x10000) + throw Exception("Index too large"); + index_data[i] = pv_indices[i]; + } + + SharedPtr vb(new VertexBuffer(context)); + // Shadowed buffer needed for raycasts to work, and so that data can be + // automatically restored on device loss + vb->SetShadowed(true); + vb->SetSize(num_vertices, magic::MASK_POSITION | magic::MASK_NORMAL); + vb->SetData(&vertex_data[0]); + + SharedPtr ib(new IndexBuffer(context)); + ib->SetShadowed(true); + ib->SetSize(num_indices, false); + ib->SetData(&index_data[0]); + + SharedPtr geom(new Geometry(context)); + geom->SetVertexBuffer(0, vb); + geom->SetIndexBuffer(ib); + geom->SetDrawRange(TRIANGLE_LIST, 0, num_indices); + + Model *fromScratchModel = new Model(context); + fromScratchModel->SetNumGeometries(1); + fromScratchModel->SetGeometry(0, 0, geom); + fromScratchModel->SetBoundingBox(BoundingBox( + Vector3(-0.5f*w, -0.5f*h, -0.5f*d), Vector3(0.5f*w, 0.5f*h, 0.5f*d))); + + return fromScratchModel; +} + +} // namespace interface +// vim: set noet ts=4 sw=4: diff --git a/src/impl/voxel.cpp b/src/impl/voxel.cpp index 7566e51..7fbcf18 100644 --- a/src/impl/voxel.cpp +++ b/src/impl/voxel.cpp @@ -1,102 +1,49 @@ // http://www.apache.org/licenses/LICENSE-2.0 // Copyright 2014 Perttu Ahola #include "interface/voxel.h" -#include -#include -#include -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wstrict-aliasing" -#include -#include -#include -#include -#include -#include -#include -#pragma GCC diagnostic pop - -namespace magic = Urho3D; -namespace pv = PolyVox; - -// Just do this; Urho3D's stuff doesn't really clash with anything in buildat -using namespace Urho3D; namespace interface { -// Creates a model from a string; eg. (2, 2, 2, "11101111") -Model* create_simple_voxel_model(Context *context, - int w, int h, int d, const ss_ &source_data) +struct CVoxelRegistry: public VoxelRegistry { - if(w < 0 || h < 0 || d < 0) - throw Exception("Negative dimension"); - if(w * h * d != (int)source_data.size()) - throw Exception("Mismatched data size"); - pv::SimpleVolume volData(pv::Region( - pv::Vector3DInt32(-1, -1, -1), - pv::Vector3DInt32(w, h, d))); - size_t i = 0; - for(int z = 0; z < d; z++) - for(int y = 0; y < h; y++) - for(int x = 0; x < w; x++){ - char c = source_data[i++]; - volData.setVoxelAt(x, y, z, c == '0' ? 0 : 255); + TextureAtlasRegistry *m_atlas_reg; + sv_ m_defs; + sv_ m_cached_defs; + + CVoxelRegistry(TextureAtlasRegistry *atlas_reg): + m_atlas_reg(atlas_reg) + {} + + VoxelTypeId add_voxel(const VoxelDefinition &def) + { + throw Exception("Not implemented"); + return VOXELTYPEID_UNDEFINED; } - pv::SurfaceMesh pv_mesh; - pv::CubicSurfaceExtractorWithNormals> - surfaceExtractor(&volData, volData.getEnclosingRegion(), &pv_mesh); - surfaceExtractor.execute(); - - const sv_ &pv_indices = pv_mesh.getIndices(); - const sv_ &pv_vertices = pv_mesh.getVertices(); - - const size_t num_vertices = pv_vertices.size(); - const size_t num_indices = pv_indices.size(); - - sv_ vertex_data; - vertex_data.resize(num_vertices * 6); // vertex + normal - for(size_t i = 0; i < num_vertices; i++){ - vertex_data[i*6 + 0] = pv_vertices[i].position.getX() - w/2.0f - 0.5f; - vertex_data[i*6 + 1] = pv_vertices[i].position.getY() - h/2.0f - 0.5f; - vertex_data[i*6 + 2] = pv_vertices[i].position.getZ() - d/2.0f - 0.5f; - vertex_data[i*6 + 3] = pv_vertices[i].normal.getX(); - vertex_data[i*6 + 4] = pv_vertices[i].normal.getY(); - vertex_data[i*6 + 5] = pv_vertices[i].normal.getZ(); + const VoxelDefinition* get(const VoxelTypeId &id) + { + return NULL; } - sv_ index_data; - index_data.resize(num_indices); - for(size_t i = 0; i < num_indices; i++){ - if(pv_indices[i] >= 0x10000) - throw Exception("Index too large"); - index_data[i] = pv_indices[i]; + const VoxelDefinition* get(const VoxelName &name) + { + return NULL; } - SharedPtr vb(new VertexBuffer(context)); - // Shadowed buffer needed for raycasts to work, and so that data can be - // automatically restored on device loss - vb->SetShadowed(true); - vb->SetSize(num_vertices, magic::MASK_POSITION | magic::MASK_NORMAL); - vb->SetData(&vertex_data[0]); + const CachedVoxelDefinition* get_cached(const VoxelTypeId &id) + { + return NULL; + } - SharedPtr ib(new IndexBuffer(context)); - ib->SetShadowed(true); - ib->SetSize(num_indices, false); - ib->SetData(&index_data[0]); - SharedPtr geom(new Geometry(context)); - geom->SetVertexBuffer(0, vb); - geom->SetIndexBuffer(ib); - geom->SetDrawRange(TRIANGLE_LIST, 0, num_indices); + // TODO: Network serialization + // TODO: Ability to track changes (just some kind of set_dirty()?) +}; - Model *fromScratchModel = new Model(context); - fromScratchModel->SetNumGeometries(1); - fromScratchModel->SetGeometry(0, 0, geom); - fromScratchModel->SetBoundingBox(BoundingBox( - Vector3(-0.5f*w, -0.5f*h, -0.5f*d), Vector3(0.5f*w, 0.5f*h, 0.5f*d))); - - return fromScratchModel; +VoxelRegistry* createVoxelRegistry(TextureAtlasRegistry *atlas_reg) +{ + return new CVoxelRegistry(atlas_reg); } -} // namespace interface +} // vim: set noet ts=4 sw=4: diff --git a/src/interface/atlas.h b/src/interface/atlas.h new file mode 100644 index 0000000..9cf67e4 --- /dev/null +++ b/src/interface/atlas.h @@ -0,0 +1,73 @@ +// http://www.apache.org/licenses/LICENSE-2.0 +// Copyright 2014 Perttu Ahola +#pragma once +#include "core/types.h" +#include + +namespace Urho3D +{ + class Context; + class Texture2D; +} + +namespace interface +{ + namespace magic = Urho3D; + + struct AtlasSegmentReference + { + uint atlas_id = 0; + uint segment_id = 0; + }; + + struct AtlasSegmentDefinition + { + ss_ resource_name; + magic::IntVector2 total_segments; + magic::IntVector2 select_segment; + // TODO: Rotation + }; + + struct AtlasSegmentCache + { + bool valid = false; + magic::Texture2D *texture = NULL; + magic::Vector2 coord0; + magic::Vector2 coord1; + }; + + struct TextureAtlasDefinition + { + uint id; + magic::IntVector2 segment_resolution; + magic::IntVector2 total_segments; + sv_ segments; + }; + + struct TextureAtlasCache + { + bool valid = false; + magic::Texture2D *texture = NULL; + magic::IntVector2 segment_resolution; + magic::IntVector2 total_segments; + sv_ segments; + }; + + struct TextureAtlasRegistry + { + virtual ~TextureAtlasRegistry(){} + + virtual const AtlasSegmentReference add_segment( + const AtlasSegmentDefinition &segment_def) = 0; + + virtual const TextureAtlasDefinition* get_atlas_definition( + uint atlas_id) = 0; + virtual const AtlasSegmentDefinition* get_segment_definition( + const AtlasSegmentReference &ref) = 0; + virtual const AtlasSegmentCache* get_texture( + const AtlasSegmentReference &ref) = 0; + }; + + TextureAtlasRegistry* createTextureAtlasRegistry(magic::Context *context); +} +// vim: set noet ts=4 sw=4: diff --git a/src/interface/block.h b/src/interface/block.h new file mode 100644 index 0000000..a8144f6 --- /dev/null +++ b/src/interface/block.h @@ -0,0 +1,52 @@ +// http://www.apache.org/licenses/LICENSE-2.0 +// Copyright 2014 Perttu Ahola +#pragma once +#include "core/types.h" +#include +#include "interface/voxel.h" + +namespace interface +{ + namespace pv = PolyVox; + + typedef ss_ BlockName; + typedef uint32_t BlockTypeId; + static constexpr uint32_t BLOCKTYPEID_UNDEFINED = 0; + + struct BlockDefinition + { + BlockName name; + BlockTypeId id; + uint8_t num_rotations = 0; // Supported: 0, 4, 24 + pv::Vector3DUint8 size = pv::Vector3DUint8(0,0,0); // Size in voxels + sv_ segments; // Rotations*voxels + }; + + // Voxels and a BlockDefinition can be generated based on this + struct BlockSourceDefinition + { + BlockName name; + uint8_t num_rotations = 0; // Supported: 0, 4, 24 + pv::Vector3DUint8 size = pv::Vector3DUint8(0,0,0); // Size in voxels + sv_ side_textures; // 6 resource names + }; + + struct BlockRegistry + { + virtual ~BlockRegistry(){} + + virtual BlockTypeId add_block_with_predefined_segments( + const BlockDefinition &def) = 0; + virtual BlockTypeId add_block_generate_segments( + const BlockSourceDefinition &def) =0; + + virtual const BlockDefinition* get(const BlockTypeId &id) = 0; + virtual const BlockDefinition* get(const BlockName &name) = 0; + + // TODO: Network serialization + // TODO: Ability to track changes (just some kind of set_dirty()?) + }; + + BlockRegistry* createBlockRegistry(VoxelRegistry *voxel_reg); +} +// vim: set noet ts=4 sw=4: diff --git a/src/interface/mesh.h b/src/interface/mesh.h new file mode 100644 index 0000000..98859c1 --- /dev/null +++ b/src/interface/mesh.h @@ -0,0 +1,19 @@ +// http://www.apache.org/licenses/LICENSE-2.0 +// Copyright 2014 Perttu Ahola +#pragma once +#include "core/types.h" + +namespace Urho3D +{ + class Context; + class Model; +} + +namespace interface +{ + using namespace Urho3D; + + Model* create_simple_voxel_model(Context *context, int w, int h, int d, + const ss_ &source_data); +} +// vim: set noet ts=4 sw=4: diff --git a/src/interface/voxel.h b/src/interface/voxel.h index 98859c1..592e75f 100644 --- a/src/interface/voxel.h +++ b/src/interface/voxel.h @@ -2,18 +2,76 @@ // Copyright 2014 Perttu Ahola #pragma once #include "core/types.h" - -namespace Urho3D -{ - class Context; - class Model; -} +#include "interface/atlas.h" +#include namespace interface { - using namespace Urho3D; + namespace pv = PolyVox; - Model* create_simple_voxel_model(Context *context, int w, int h, int d, - const ss_ &source_data); + typedef uint32_t VoxelTypeId; + static constexpr uint32_t VOXELTYPEID_MAX = 1398101-1; + static constexpr uint32_t VOXELTYPEID_UNDEFINED = 0; + + struct VoxelName + { + ss_ block_name; // Name of the block this was instanced from + uint segment_x = 0; // Which segment of the block this was instanced from + uint segment_y = 0; + uint segment_z = 0; + uint rotation_primary = 0; // 4 possible rotations when looking at a face + uint rotation_secondary = 0; // 6 possible directions for a face to point to + }; + + struct VoxelDefinition + { + VoxelName name; + VoxelTypeId id = VOXELTYPEID_UNDEFINED; + // Textures (voxels have 6 sides) + // These must be definitions (not references) because each client has to + // be able to construct their atlases from different texture sizes + AtlasSegmentDefinition textures[6]; + // Other properties + ss_ handler_module; + // TODO: Flag for whether all faces should be always drawn (in case the + // textures contain holes) + // TODO: Flag for whether this voxel is physically solid + // TODO: Some kind of property for defining whether this is a thing for + // which adjacent voxels of the same thing type don't have faces, + // and what thing type that is in this case + }; + + // This definition should be as small as practical so that large portions of + // the definition array can fit in CPU cache + // (the absolute maximum number of these is VOXELTYPEID_MAX+1) + struct CachedVoxelDefinition + { + const AtlasSegmentCache *textures[6] = {NULL}; + ss_ handler_module; + }; + + struct VoxelRegistry + { + virtual ~VoxelRegistry(){} + + virtual VoxelTypeId add_voxel(const VoxelDefinition &def) = 0; + + virtual const VoxelDefinition* get(const VoxelTypeId &id) = 0; + virtual const VoxelDefinition* get(const VoxelName &name) = 0; + virtual const CachedVoxelDefinition* get_cached(const VoxelTypeId &id) = 0; + + // TODO: Network serialization + // TODO: Ability to track changes (just some kind of set_dirty()?) + }; + + VoxelRegistry* createVoxelRegistry(TextureAtlasRegistry *atlas_reg); + + struct VoxelInstance + { + uint32_t data; + + VoxelTypeId getId(){ return data & 0x001fffff; } + uint8_t getMSB(){ return (data>>24) & 0xff; } + }; } // vim: set noet ts=4 sw=4: diff --git a/src/lua_bindings/voxel.cpp b/src/lua_bindings/voxel.cpp index 719facb..9528910 100644 --- a/src/lua_bindings/voxel.cpp +++ b/src/lua_bindings/voxel.cpp @@ -3,7 +3,7 @@ #include "lua_bindings/util.h" #include "core/log.h" #include "client/app.h" -#include "interface/voxel.h" +#include "interface/mesh.h" #include #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wstrict-aliasing"