Skip to content

Commit

Permalink
First working heightmap layers
Browse files Browse the repository at this point in the history
  • Loading branch information
mohsenph69 committed Jul 26, 2023
1 parent 8946242 commit 77488f3
Show file tree
Hide file tree
Showing 13 changed files with 369 additions and 42 deletions.
54 changes: 52 additions & 2 deletions gdextension/src/mgrid.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ void MGrid::clear() {
_all_heightmap_image_list.clear();
_all_image_list.clear();
heightmap_layers.clear();
heightmap_layers_visibility.clear();
active_heightmap_layer=0;
UtilityFunctions::print("All heightmap image list size ", _all_heightmap_image_list.size());
}

Expand Down Expand Up @@ -145,8 +147,10 @@ void MGrid::update_regions_uniforms(Array input) {
}
}
update_all_image_list();
UtilityFunctions::print("heightmap_layers ", heightmap_layers.size());
for(int i=0;i<heightmap_layers.size();i++){
// We start from one because we don\t want to add background layer
// background layer will added in MImage automaticly
for(int i=1;i<heightmap_layers.size();i++){
UtilityFunctions::print(heightmap_layers[i]);
add_heightmap_layer(heightmap_layers[i]);
}
if(!has_normals){
Expand Down Expand Up @@ -854,6 +858,7 @@ void MGrid::set_brush_manager(MBrushManager* input){

void MGrid::draw_height(Vector3 brush_pos,real_t radius,int brush_id){
ERR_FAIL_COND(_brush_manager==nullptr);
ERR_FAIL_COND_EDMSG(!heightmap_layers_visibility[active_heightmap_layer], "Can not paint on invisible layer");
Vector2i bpxpos = get_closest_pixel(brush_pos);
if(bpxpos.x<0 || bpxpos.y<0 || bpxpos.x>grid_pixel_region.right || bpxpos.y>grid_pixel_region.bottom){
return;
Expand Down Expand Up @@ -953,6 +958,7 @@ void MGrid::set_active_layer(int input){
// We did not add background to heightmap layers so the error handling down here is ok
ERR_FAIL_COND(input>heightmap_layers.size());
ERR_FAIL_COND(input<0);
active_heightmap_layer = input;
for(int i=0;i<_all_heightmap_image_list.size();i++){
UtilityFunctions::print("setting active layer ",i," ",input);
_all_heightmap_image_list[i]->active_layer = input;
Expand All @@ -964,4 +970,48 @@ void MGrid::add_heightmap_layer(String lname){
for(int i=0;i<_all_heightmap_image_list.size();i++){
_all_heightmap_image_list[i]->add_layer(lname);
}
heightmap_layers_visibility.push_back(true);
}

void MGrid::merge_heightmap_layer(){
// Merging current active layer
for(int i=0;i<_all_heightmap_image_list.size();i++){
_all_heightmap_image_list[i]->merge_layer();
}
}

void MGrid::remove_heightmap_layer(){
// Removing current active layer
for(int i=0;i<_all_heightmap_image_list.size();i++){
_all_heightmap_image_list[i]->remove_layer();
// Correcting normals if is dirty
if(_all_heightmap_image_list[i]->is_dirty){
MPixelRegion pxr;
pxr.left = _all_heightmap_image_list[i]->grid_pos.x*region_pixel_size;
pxr.top = _all_heightmap_image_list[i]->grid_pos.z*region_pixel_size;
pxr.right = pxr.left + region_pixel_size;
pxr.bottom = pxr.bottom + region_pixel_size;
pxr.grow_all_side(grid_pixel_region);
generate_normals_thread(pxr);
}
}
update_all_dirty_image_texture();
}

void MGrid::toggle_heightmap_layer_visibile(){
bool input = !heightmap_layers_visibility[active_heightmap_layer];
for(int i=0;i<_all_heightmap_image_list.size();i++){
_all_heightmap_image_list[i]->layer_visible(input);
// Correcting normals if is dirty
if(_all_heightmap_image_list[i]->is_dirty){
MPixelRegion pxr;
pxr.left = _all_heightmap_image_list[i]->grid_pos.x*region_pixel_size;
pxr.top = _all_heightmap_image_list[i]->grid_pos.z*region_pixel_size;
pxr.right = pxr.left + region_pixel_size;
pxr.bottom = pxr.bottom + region_pixel_size;
pxr.grow_all_side(grid_pixel_region);
generate_normals_thread(pxr);
}
}
heightmap_layers_visibility.set(active_heightmap_layer,input);
}
8 changes: 8 additions & 0 deletions gdextension/src/mgrid.h
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,10 @@ class MGrid : public Object {
static void _bind_methods(){};

public:
int active_heightmap_layer=0;
// MImage does not check for visibility of layers
// Here we should check that in the case someone want to draw on them it should give an error
Vector<bool> heightmap_layers_visibility;
PackedStringArray heightmap_layers;
bool has_normals = false;
bool save_generated_normals=false;
Expand Down Expand Up @@ -206,6 +210,10 @@ class MGrid : public Object {

void set_active_layer(int input);
void add_heightmap_layer(String lname);
void merge_heightmap_layer();
void remove_heightmap_layer();
void toggle_heightmap_layer_visibile();

};

#endif
95 changes: 80 additions & 15 deletions gdextension/src/mimage.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#include "mimage.h"
#include <godot_cpp/classes/resource_saver.hpp>
#include <godot_cpp/variant/utility_functions.hpp>
#include <godot_cpp/classes/dir_access.hpp>
#include <godot_cpp/classes/file_access.hpp>

#include "mbound.h"

Expand Down Expand Up @@ -30,24 +32,28 @@ void MImage::load(){
}
is_save = true;
image_layers.push_back(&data);
layer_names.push_back("Background");
layer_names.push_back("background");
is_saved_layers.push_back(true);
}

// This must called alway after loading background image
void MImage::add_layer(String lname){
ERR_FAIL_COND_EDMSG(data.size()==0,"You must first load the background image and then the layers");
ERR_FAIL_COND_EDMSG(name!="heightmap","Layers is supported only for heightmap images");
String ltname = lname +"_x"+itos(grid_pos.x)+"_y"+itos(grid_pos.z)+ ".res";
String ltname = lname +"_x"+itos(grid_pos.x)+"_y"+itos(grid_pos.z)+ ".r32";
String layer_path = layerDataDir.path_join(ltname);
UtilityFunctions::print("Layer Path ",layer_path);
if(ResourceLoader::get_singleton()->exists(layer_path)){
if(FileAccess::file_exists(layer_path)){
UtilityFunctions::print("Found res ", layer_path);
Ref<Image> img_layer = ResourceLoader::get_singleton()->load(layer_path);
ERR_FAIL_COND(img_layer->get_format()!=Image::Format::FORMAT_RF);
Ref<FileAccess> file = FileAccess::open(layer_path, FileAccess::READ);
ERR_FAIL_COND(file->get_length() != data.size());
PackedByteArray* img_layer_data = memnew(PackedByteArray);
*img_layer_data = img_layer->get_data();
ERR_FAIL_COND(img_layer_data->size()!=data.size());
img_layer_data->resize(data.size());
uint8_t* ptrw = img_layer_data->ptrw();
for(int s=0;s<data.size();s++){
ptrw[s] = file->get_8();
}
file->close();
image_layers.push_back(img_layer_data);
layer_names.push_back(lname);
is_saved_layers.push_back(true);
Expand All @@ -63,6 +69,64 @@ void MImage::add_layer(String lname){
}
}

void MImage::merge_layer(){
String path = layer_names[active_layer] +"_x"+itos(grid_pos.x)+"_y"+itos(grid_pos.z)+ ".r32";
path = layerDataDir.path_join(path);
image_layers[active_layer]->resize(0);
layer_names[active_layer] = "null";
is_saved_layers.set(active_layer,true);
if(FileAccess::file_exists(path)){
UtilityFunctions::print("Removing layer ", path);
DirAccess::remove_absolute(path);
}
is_saved_layers.set(0,false);
is_save = false;
save(false);
}

void MImage::remove_layer(){
if(image_layers[active_layer]->size()==0){
return;
}
const uint8_t* ptr=image_layers[active_layer]->ptr();
for(uint32_t i=0;i<total_pixel_amount;i++){
((float *)data.ptrw())[i] -= ((float *)ptr)[i];
}
image_layers[active_layer]->resize(0);
is_saved_layers.set(active_layer,true);
is_saved_layers.set(0,false);
is_save = false;
is_dirty = true;
String path = layer_names[active_layer] +"_x"+itos(grid_pos.x)+"_y"+itos(grid_pos.z)+ ".r32";
path = layerDataDir.path_join(path);
layer_names[active_layer] = "null";
if(FileAccess::file_exists(path)){
DirAccess::remove_absolute(path);
}
save(false);
}

void MImage::layer_visible(bool input){
if(image_layers[active_layer]->size()==0){
return;
}
// There is no control if the layer is currently visibile or not
// These checks must be done in Grid Level
// We save before hiding the layer to not complicating the save system for now
save(false); // So we are sure in the save method we should do nothing
const uint8_t* ptr=image_layers[active_layer]->ptr();
if(input){
for(uint32_t i=0;i<total_pixel_amount;i++){
((float *)data.ptrw())[i] += ((float *)ptr)[i];
}
} else {
for(uint32_t i=0;i<total_pixel_amount;i++){
((float *)data.ptrw())[i] -= ((float *)ptr)[i];
}
}
is_dirty = true;
}

void MImage::create(uint32_t _size, Image::Format _format) {
width = _size;
height =_size;
Expand Down Expand Up @@ -152,6 +216,8 @@ real_t MImage::get_pixel_RF(const uint32_t&x, const uint32_t& y) const {
}

void MImage::set_pixel_RF(const uint32_t&x, const uint32_t& y,const real_t& value){
// not visibile layers should not be modified but as this called many times
// it is better to check that in upper level
uint32_t ofs = (x + y*width);
#ifdef M_IMAGE_LAYER_ON
// For when we have only background layer
Expand All @@ -167,13 +233,9 @@ void MImage::set_pixel_RF(const uint32_t&x, const uint32_t& y,const real_t& valu
image_layers[active_layer]->resize(data.size());
}
is_saved_layers.set(active_layer,false);
//float dif = value - ((float *)image_layers[active_layer]->ptr())[ofs];
//((float *)image_layers[active_layer]->ptrw())[ofs] = dif;
//float org_val = ((float *)data.ptr())[ofs];
float dif = value - ((float *)data.ptr())[ofs];
((float *)image_layers[active_layer]->ptrw())[ofs] += dif;
((float *)data.ptrw())[ofs] = value;
//UtilityFunctions::print("dif ", dif, " val ", value, " org_val ",org_val);
#else
((float *)data.ptrw())[ofs] = value;
#endif
Expand Down Expand Up @@ -225,12 +287,15 @@ void MImage::save(bool force_save) {
UtilityFunctions::print("is save size ", is_saved_layers.size());
if(!is_saved_layers[i]){
UtilityFunctions::print(layer_names);
String lname = layer_names[i]+"_x"+itos(grid_pos.x)+"_y"+itos(grid_pos.z)+ ".res";
String lname = layer_names[i]+"_x"+itos(grid_pos.x)+"_y"+itos(grid_pos.z)+ ".r32";
String layer_path = layerDataDir.path_join(lname);
UtilityFunctions::print("layer path ",layer_path);
Ref<Image> img = Image::create_from_data(width,height,false,format,*image_layers[i]);
godot::Error err = ResourceSaver::get_singleton()->save(img,layer_path);
ERR_FAIL_COND_MSG(err,"Can not save layer, image class erro: "+itos(err));
Ref<FileAccess> file = FileAccess::open(layer_path, FileAccess::WRITE);
const uint8_t* ptr = image_layers[i]->ptr();
for(int j=0;j<image_layers[i]->size();j++){
file->store_8(ptr[j]);
}
file->close();
is_saved_layers.set(i,true);
}
}
Expand Down
4 changes: 3 additions & 1 deletion gdextension/src/mimage.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ struct MImage {
MImage(const String& _file_path,const String& _layers_folder,const String& _name,const String& _uniform_name,MGridPos _grid_pos,const int& _compression);
void load();
void add_layer(String lname);
void merge_layer();
void remove_layer();
void layer_visible(bool input);
void create(uint32_t _size, Image::Format _format);
// This create bellow should not be used for terrain, It is for other stuff
void create(uint32_t _width,uint32_t _height, Image::Format _format);
Expand All @@ -71,7 +74,6 @@ struct MImage {
_FORCE_INLINE_ Color _get_color_at_ofs(const uint8_t *ptr, uint32_t ofs) const;
_FORCE_INLINE_ void _set_color_at_ofs(uint8_t *ptr, uint32_t ofs, const Color &p_color);
static int get_format_pixel_size(Image::Format p_format);

};


Expand Down
Loading

0 comments on commit 77488f3

Please sign in to comment.