diff --git a/meshers/isometric/terrain_2d_mesher_isometric.cpp b/meshers/isometric/terrain_2d_mesher_isometric.cpp index d460b17..5b0698c 100644 --- a/meshers/isometric/terrain_2d_mesher_isometric.cpp +++ b/meshers/isometric/terrain_2d_mesher_isometric.cpp @@ -33,7 +33,7 @@ void Terrain2DMesherIsometric::_add_chunk(Ref p_chunk) { ERR_FAIL_COND(chunk->get_margin_end() < 1); ERR_FAIL_COND(chunk->get_margin_start() < 1); - add_chunk_normal(chunk); + mesh_base(chunk); } void Terrain2DMesherIsometric::add_chunk_normal(Ref chunk) { @@ -340,6 +340,542 @@ void Terrain2DMesherIsometric::add_chunk_normal(Ref chunk } } +void Terrain2DMesherIsometric::mesh_base(Ref chunk) { + //if ((get_build_flags() & Terrain2DChunkDefault::BUILD_FLAG_GENERATE_AO) != 0) + // if (!chunk->get_channel(Terrain2DChunkDefault::DEFAULT_CHANNEL_AO)) + // chunk->generate_ao(); + + uint8_t *channel_type = chunk->channel_get(_channel_index_type); + + if (!channel_type) + return; + + uint8_t *channel_flags = chunk->channel_get(Terrain2DChunkDefault::DEFAULT_CHANNEL_FLAGS); + + if (!channel_flags) + return; + + int x_size = chunk->get_size_x(); + int y_size = chunk->get_size_y(); + int cell_size_x = get_cell_size_x(); + int cell_size_y = get_cell_size_y(); + + Transform2D mesh_transform_terrain = chunk->mesh_transform_terrain_get(); + Transform2D mesh_transform_wall_north = chunk->mesh_transform_wall_north_get(); + Transform2D mesh_transform_wall_south = chunk->mesh_transform_wall_south_get(); + Transform2D mesh_transform_wall_east = chunk->mesh_transform_wall_east_get(); + Transform2D mesh_transform_wall_west = chunk->mesh_transform_wall_west_get(); + + uint8_t *channel_color_r = NULL; + uint8_t *channel_color_g = NULL; + uint8_t *channel_color_b = NULL; + uint8_t *channel_ao = NULL; + uint8_t *channel_rao = NULL; + + Color base_light(_base_light_value, _base_light_value, _base_light_value); + Color light[4]{ Color(1, 1, 1), Color(1, 1, 1), Color(1, 1, 1), Color(1, 1, 1) }; + + bool use_lighting = (get_build_flags() & Terrain2DChunkDefault::BUILD_FLAG_USE_LIGHTING) != 0; + bool use_ao = (get_build_flags() & Terrain2DChunkDefault::BUILD_FLAG_USE_AO) != 0; + bool use_rao = (get_build_flags() & Terrain2DChunkDefault::BUILD_FLAG_USE_RAO) != 0; + + if (use_lighting) { + channel_color_r = chunk->channel_get_valid(Terrain2DChunkDefault::DEFAULT_CHANNEL_LIGHT_COLOR_R); + channel_color_g = chunk->channel_get_valid(Terrain2DChunkDefault::DEFAULT_CHANNEL_LIGHT_COLOR_G); + channel_color_b = chunk->channel_get_valid(Terrain2DChunkDefault::DEFAULT_CHANNEL_LIGHT_COLOR_B); + + if (use_ao) + channel_ao = chunk->channel_get_valid(Terrain2DChunkDefault::DEFAULT_CHANNEL_AO); + + if (use_rao) + channel_rao = chunk->channel_get_valid(Terrain2DChunkDefault::DEFAULT_CHANNEL_RANDOM_AO); + } + + Ref mcache; + + if (chunk->material_cache_key_has()) { + mcache = _library->material_cache_get(chunk->material_cache_key_get()); + } + + int texture_scale = get_texture_scale(); + + int margin_start = chunk->get_margin_start(); + //z_size + margin_start is fine, x, and z are in data space. + for (int y = margin_start; y < y_size + margin_start; ++y) { + for (int x = margin_start; x < x_size + margin_start; ++x) { + int indexes[4] = { + chunk->get_data_index(x, y), + chunk->get_data_index(x + 1, y), + chunk->get_data_index(x, y + 1), + chunk->get_data_index(x + 1, y + 1) + }; + + uint8_t type = channel_type[indexes[0]]; + + if (type == 0) + continue; + + Ref surface; + + if (!mcache.is_valid()) { + surface = _library->terra_surface_get(type - 1); + } else { + surface = mcache->surface_id_get(type - 1); + } + + if (!surface.is_valid()) + continue; + + int flags = channel_flags[indexes[0]]; + + if (use_lighting) { + for (int i = 0; i < 4; ++i) { + int indx = indexes[i]; + + light[i] = Color(channel_color_r[indx] / 255.0, + channel_color_g[indx] / 255.0, + channel_color_b[indx] / 255.0); + + float ao = 0; + + if (use_ao) + ao = channel_ao[indx] / 255.0; + + if (use_rao) { + float rao = channel_rao[indx] / 255.0; + ao += rao; + } + + light[i] += base_light; + + if (ao > 0) + light[i] -= Color(ao, ao, ao) * _ao_strength; + + light[i].r = CLAMP(light[i].r, 0, 1.0); + light[i].g = CLAMP(light[i].g, 0, 1.0); + light[i].b = CLAMP(light[i].b, 0, 1.0); + } + } + + Vector2 uvs[] = { + Vector2(0, 0), + Vector2(1, 0), + Vector2(0, 1), + Vector2(1, 1) + }; + + if (texture_scale > 1) { + for (int i = 0; i < 4; ++i) { + uvs[i] = surface->transform_uv_scaled(uvs[i], x % texture_scale, y % texture_scale, texture_scale); + } + } else { + for (int i = 0; i < 4; ++i) { + uvs[i] = surface->transform_uv(uvs[i]); + } + } + + Vector2 verts_normal[] = { + Vector2(0, 0), + Vector2(cell_size_x, 0), + Vector2(0, cell_size_y), + Vector2(cell_size_x, cell_size_y) + }; + + // Note that +y is down! + Vector2 verts_wall[] = { + Vector2(0, -cell_size_y), + Vector2(cell_size_x, -cell_size_y), + Vector2(0, 0), + Vector2(cell_size_x, 0) + }; + + bool render_normal = true; + + int vc = get_vertex_count(); + + bool hole = (flags & Terrain2DChunkDefault::FLAG_CHANNEL_WALL_HOLE) != 0; + + bool north_flag = (flags & Terrain2DChunkDefault::FLAG_CHANNEL_WALL_NORTH) != 0; + bool west_flag = (flags & Terrain2DChunkDefault::FLAG_CHANNEL_WALL_WEST) != 0; + + if (north_flag) { + render_normal = false; + + if (hole) { + Vector2 vert_start_offset = mesh_transform_terrain.xform(Vector2(x * cell_size_x, y * cell_size_y)); + + for (int i = 0; i < 4; ++i) { + if (use_lighting) { + add_color(light[i]); + } else { + add_color(Color(1, 1, 1, 1)); + } + + add_uv(uvs[i]); + add_vertex(mesh_transform_wall_north.xform(verts_normal[i]) + vert_start_offset); + } + + add_indices(vc + 0); + add_indices(vc + 1); + add_indices(vc + 2); + add_indices(vc + 1); + add_indices(vc + 2); + add_indices(vc + 3); + + vc += 4; + } + } + + if (west_flag) { + render_normal = false; + + if (hole) { + Vector2 vert_start_offset = mesh_transform_terrain.xform(Vector2(x * cell_size_x, (y + 1) * cell_size_y)); + + for (int i = 0; i < 4; ++i) { + if (use_lighting) { + add_color(light[i]); + } else { + add_color(Color(1, 1, 1, 1)); + } + + add_uv(uvs[i]); + + add_vertex(mesh_transform_wall_west.xform(verts_normal[i]) + vert_start_offset); + } + + add_indices(vc + 0); + add_indices(vc + 1); + add_indices(vc + 2); + add_indices(vc + 1); + add_indices(vc + 2); + add_indices(vc + 3); + + vc += 4; + } + } + + if ((flags & Terrain2DChunkDefault::FLAG_CHANNEL_WALL_SOUTH) != 0) { + render_normal = false; + + if (!west_flag || hole) { + Vector2 vert_start_offset = mesh_transform_terrain.xform(Vector2(x * cell_size_x, (y + 1) * cell_size_y)); + + for (int i = 0; i < 4; ++i) { + if (use_lighting) { + add_color(light[i]); + } else { + add_color(Color(1, 1, 1, 1)); + } + + add_uv(uvs[i]); + + if (hole) { + add_vertex(mesh_transform_wall_south.xform(verts_normal[i]) + vert_start_offset); + } else { + add_vertex(mesh_transform_wall_south.xform(verts_wall[i]) + vert_start_offset); + } + } + + add_indices(vc + 0); + add_indices(vc + 1); + add_indices(vc + 2); + add_indices(vc + 1); + add_indices(vc + 2); + add_indices(vc + 3); + + vc += 4; + } else { + Vector2 vert_start_offset = mesh_transform_terrain.xform(Vector2(x * cell_size_x, (y + 1) * cell_size_y)); + + Vector2 vtx1y = mesh_transform_wall_south.xform(verts_wall[1]); + Vector2 vtxy1 = mesh_transform_wall_south.xform(verts_wall[2]); + Vector2 vtx1y1 = mesh_transform_wall_south.xform(verts_wall[3]); + + float p1 = vtxy1.y - vtx1y.y; + float p2 = vtx1y1.y - vtx1y.y; + + float yrat = p1 / p2; + + vtx1y.y = vtxy1.y; + + //x+1,y + if (use_lighting) { + add_color(light[1].linear_interpolate(light[3], yrat)); + } else { + add_color(Color(1, 1, 1, 1)); + } + add_uv(uvs[1].linear_interpolate(uvs[3], yrat)); + add_vertex(vtx1y + vert_start_offset); + + //x,y+1 + if (use_lighting) { + add_color(light[2]); + } else { + add_color(Color(1, 1, 1, 1)); + } + add_uv(uvs[2]); + add_vertex(vtxy1 + vert_start_offset); + + //x+1,y+1 + if (use_lighting) { + add_color(light[3]); + } else { + add_color(Color(1, 1, 1, 1)); + } + add_uv(uvs[3]); + add_vertex(vtx1y1 + vert_start_offset); + + add_indices(vc + 0); + add_indices(vc + 1); + add_indices(vc + 2); + + vc += 3; + } + } + + if ((flags & Terrain2DChunkDefault::FLAG_CHANNEL_WALL_EAST) != 0) { + render_normal = false; + + if (!north_flag || hole) { + Vector2 vert_start_offset = mesh_transform_terrain.xform(Vector2((x + 1) * cell_size_x, (y + 1) * cell_size_y)); + + for (int i = 0; i < 4; ++i) { + if (use_lighting) { + add_color(light[i]); + } else { + add_color(Color(1, 1, 1, 1)); + } + + add_uv(uvs[i]); + + if (hole) { + add_vertex(mesh_transform_wall_east.xform(verts_normal[i]) + vert_start_offset); + } else { + add_vertex(mesh_transform_wall_east.xform(verts_wall[i]) + vert_start_offset); + } + } + + add_indices(vc + 0); + add_indices(vc + 1); + add_indices(vc + 2); + add_indices(vc + 1); + add_indices(vc + 2); + add_indices(vc + 3); + + vc += 4; + } else { + Vector2 vert_start_offset = mesh_transform_terrain.xform(Vector2((x + 1) * cell_size_x, (y + 1) * cell_size_y)); + + Vector2 vtxy = mesh_transform_wall_east.xform(verts_wall[0]); + Vector2 vtxy1 = mesh_transform_wall_east.xform(verts_wall[2]); + Vector2 vtx1y1 = mesh_transform_wall_east.xform(verts_wall[3]); + + float p1 = vtxy1.y - vtxy.y; + float p2 = vtx1y1.y - vtxy.y; + + float yrat = p2 / p1; + + vtxy.y = vtx1y1.y; + + //x,y + if (use_lighting) { + add_color(light[2].linear_interpolate(light[0], yrat)); + } else { + add_color(Color(1, 1, 1, 1)); + } + add_uv(uvs[2].linear_interpolate(uvs[0], yrat)); + add_vertex(vtxy + vert_start_offset); + + //x,y+1 + if (use_lighting) { + add_color(light[2]); + } else { + add_color(Color(1, 1, 1, 1)); + } + add_uv(uvs[2]); + add_vertex(vtxy1 + vert_start_offset); + + //x+1,y+1 + if (use_lighting) { + add_color(light[3]); + } else { + add_color(Color(1, 1, 1, 1)); + } + add_uv(uvs[3]); + add_vertex(vtx1y1 + vert_start_offset); + + add_indices(vc + 0); + add_indices(vc + 1); + add_indices(vc + 2); + + vc += 3; + } + } + + if (render_normal) { + Vector2 vert_start_offset = mesh_transform_terrain.xform(Vector2(x * cell_size_x, y * cell_size_y)); + + for (int i = 0; i < 4; ++i) { + if (use_lighting) { + add_color(light[i]); + } else { + add_color(Color(1, 1, 1, 1)); + } + + add_uv(uvs[i]); + add_vertex(mesh_transform_terrain.xform(verts_normal[i]) + vert_start_offset); + } + + add_indices(vc + 0); + add_indices(vc + 1); + add_indices(vc + 2); + add_indices(vc + 1); + add_indices(vc + 2); + add_indices(vc + 3); + } + } + } +} + +void Terrain2DMesherIsometric::reset() { + Terrain2DMesher::reset(); + + _meshes.resize(0); +} + +void Terrain2DMesherIsometric::next_mesh() { + Terrain2DMesherIsometricMesh m; + + m.vertices = _vertices; + m.indices = _indices; + + _vertices.resize(0); + _indices.resize(0); + + _meshes.push_back(m); +} + +int Terrain2DMesherIsometric::get_mesh_count() const { + return _meshes.size(); +} + +Array Terrain2DMesherIsometric::build_mesh_index(const int index) { + ERR_FAIL_INDEX_V(index, _meshes.size(), Array()); + + Array a; + a.resize(VisualServer::ARRAY_MAX); + + const Terrain2DMesherIsometricMesh &md = _meshes[index]; + + if (md.vertices.size() == 0) { + //Nothing to do + return a; + } + + { + PoolVector array; + array.resize(md.vertices.size()); +#if !GODOT4 + PoolVector::Write w = array.write(); +#endif + + for (int i = 0; i < md.vertices.size(); ++i) { +#if !GODOT4 + w[i] = md.vertices[i].vertex; +#else + array.set(i, md.vertices[i].vertex); +#endif + } + +#if !GODOT4 + w.release(); +#endif + + a[VisualServer::ARRAY_VERTEX] = array; + } + + if ((_format & VisualServer::ARRAY_FORMAT_COLOR) != 0) { + PoolVector array; + array.resize(md.vertices.size()); +#if !GODOT4 + PoolVector::Write w = array.write(); +#endif + + for (int i = 0; i < md.vertices.size(); ++i) { +#if !GODOT4 + w[i] = md.vertices[i].color; +#else + array.set(i, md.vertices[i].color); +#endif + } + +#if !GODOT4 + w.release(); +#endif + a[VisualServer::ARRAY_COLOR] = array; + } + + if ((_format & VisualServer::ARRAY_FORMAT_TEX_UV) != 0) { + PoolVector array; + array.resize(md.vertices.size()); +#if !GODOT4 + PoolVector::Write w = array.write(); +#endif + + for (int i = 0; i < md.vertices.size(); ++i) { +#if !GODOT4 + w[i] = md.vertices[i].uv; +#else + array.set(i, md.vertices[i].uv); +#endif + } + +#if !GODOT4 + w.release(); +#endif + + a[VisualServer::ARRAY_TEX_UV] = array; + } + + if (md.indices.size() > 0) { + PoolVector array; + array.resize(md.indices.size()); +#if !GODOT4 + PoolVector::Write w = array.write(); +#endif + + for (int i = 0; i < md.indices.size(); ++i) { +#if !GODOT4 + w[i] = md.indices[i]; +#else + array.set(i, md.indices[i]); +#endif + } + +#if !GODOT4 + w.release(); +#endif + a[VisualServer::ARRAY_INDEX] = array; + } + + return a; +} +void Terrain2DMesherIsometric::build_mesh_index_into(const int index, RID mesh) { + ERR_FAIL_COND(mesh == RID()); + + VS::get_singleton()->mesh_clear(mesh); + + if (_vertices.size() == 0) { + //Nothing to do + return; + } + + Array arr = build_mesh_index(index); + + VS::get_singleton()->mesh_add_surface_from_arrays(mesh, VisualServer::PRIMITIVE_TRIANGLES, arr); +} + Terrain2DMesherIsometric::Terrain2DMesherIsometric() { } @@ -348,4 +884,9 @@ Terrain2DMesherIsometric::~Terrain2DMesherIsometric() { void Terrain2DMesherIsometric::_bind_methods() { ClassDB::bind_method(D_METHOD("_add_chunk", "buffer"), &Terrain2DMesherIsometric::_add_chunk); + + ClassDB::bind_method(D_METHOD("next_mesh"), &Terrain2DMesherIsometric::next_mesh); + ClassDB::bind_method(D_METHOD("get_mesh_count"), &Terrain2DMesherIsometric::get_mesh_count); + ClassDB::bind_method(D_METHOD("build_mesh_index", "index"), &Terrain2DMesherIsometric::build_mesh_index); + ClassDB::bind_method(D_METHOD("build_mesh_index_into", "index", "mesh"), &Terrain2DMesherIsometric::build_mesh_index_into); } diff --git a/meshers/isometric/terrain_2d_mesher_isometric.h b/meshers/isometric/terrain_2d_mesher_isometric.h index 2df3151..ba9e1ac 100644 --- a/meshers/isometric/terrain_2d_mesher_isometric.h +++ b/meshers/isometric/terrain_2d_mesher_isometric.h @@ -44,12 +44,26 @@ public: void _add_chunk(Ref p_chunk); void add_chunk_normal(Ref chunk); + void mesh_base(Ref chunk); + + void reset(); + void next_mesh(); + int get_mesh_count() const; + Array build_mesh_index(const int index); + void build_mesh_index_into(const int index, RID mesh); Terrain2DMesherIsometric(); ~Terrain2DMesherIsometric(); protected: static void _bind_methods(); + + struct Terrain2DMesherIsometricMesh { + PoolVector vertices; + PoolVector indices; + }; + + Vector _meshes; }; #endif diff --git a/meshers/terrain_2d_mesher.h b/meshers/terrain_2d_mesher.h index 144c9b9..34ab99c 100644 --- a/meshers/terrain_2d_mesher.h +++ b/meshers/terrain_2d_mesher.h @@ -123,7 +123,7 @@ public: Rect2 get_uv_margin() const; void set_uv_margin(const Rect2 margin); - void reset(); + virtual void reset(); void add_chunk(Ref chunk);